使用定义文件和WINAPI宏编写自己的API动态库

原创 2002年04月23日 09:17:00

<!--StartFragment-->
    如果你想要自己编写的动态库可以适用更多的其它编程语言,你可以使用定义文件和WINAPI宏来编写自己的API动态库。你将会发现,使用这样的动态库输出函数就象使用API函数一样。
   
    一、为什么要使用DEF文件?

    因为微软的MFC动态库都是使用DEF文件创建的。
    这回答够有份量吧,但有点大帽子压人的感觉。下面呢,我从原理上来说说为什么要使用DEF文件。
    无论使用C语言或者C++语言来编写动态库,其编译器都会为每个函数甚至变量生成一个对应的修饰名(我是这样翻译的。原文是the decorated names),连接器将编译后的目标代码连接成DLL,其输出函数名或变量名依旧是编译后的修饰名。并且修饰名是与编译器相关的,也就是说你的源程序是C,生成的修饰名是一个样子;如果你的源程序是C++,则生成的修饰名是另一种样子。(关于修饰名的讨论,我将放在一个单独的章节进行,敬请等待。粮草未动,广告先行。真是的....)而我们的应用习惯是直接使用函数名,而非修饰名,我们在用API时一直就是如此。那么,问题就来了,比如你在VB6中使用VC6写的动态库:你先在VB6中使用函数名来描述你要调用的函数,然后写好调用代码,接下来运行,你的VB6这时会告诉你,它在动态库中找不到你刚刚描述过的函数的入口点,你的程序拒绝执行了。怎么办?解决问题的方法至少有两种:1、修改你的VB6代码中对动态库输出函数的描述部分,在别名栏添加动态库输出函数的修饰名。2、修改你的动态库,添加一个DEF文件,并使用DEF文件的EXPORTS项来输出你的动态库函数(在其下简单地列出你要输出的函数名即可)。虽然,这样一来问题是解决了(还没有解决?可能吗?有可能啦。但这里先停一下,随后再说。),但我们有必要将该问题进一步讨论下去。我们写动态库的目的大多是想让我们的动态库不局限于某一种编程语言,是为了更加广泛地被应用,我是喜欢用VB写界面,而用VC来完成更重要的工作,如对数据进行分析、访问硬件端口等,而且VC写好的动态库很少再改动,VB写的界面倒是一改再改。说这么多,目的只有一个,写动态库要着眼于“大局”,要一切符合“标准”。什么是大局?大局就是走可持续发展的道路,就是复用(好象是在做政治报告)。什么是标准呢?就是符合API的标准(也就是使用DEF文件输出函数,就象微软的MFC动态库)。
    其实,使用DEF文件来输出函数的一个最主要目的就是:将编译器生成的函数修饰名去掉,用更加自然的、容易理解的、容易记忆的名字,而不是修饰名来输出函数。这里的名字可以不是函数名,这时须使用DEF文件的NAME格式。但由于习惯,大多情况下,只使用函数名,因为这样最简单省事。是否存在偷懒的嫌疑?我的理解,不管是罗列函数名,还是其它输出名,其本质上是一样的,即都是使用了定义文件的NAME格式。直接罗列函数名,就相当于“函数名”=“函数修饰名”,只是可以忽略等号后面的部分,而连接器会自动完成函数入口的匹配和设置工作。而一旦决定使用非函
数名的其它名字输出函数,则必须书写完整的格式,即“函数输出名”=“要输出的函数修饰名”,这里等号后面的部分必须书写正确,否则,连接时就通不过了。举个例子:假设动态库中一个函数描述如下,
int WINAPI TestAdd(int A,int B)
{
    return (A+B);
}

其DEF文件的EXPORTS段描述如下,
EXPORTS
  TestAdd
  Add=?TestAdd@@YGHHH@Z

这里TestAdd和Add实际上指向同一个入口。如果在VB程序中调用TestAdd和Add,其结果是一样的。
   
    二、为什么要使用WINAPI宏?
    看看上面的举例,在函数前加了一个WINAPI宏。这一点很重要,它直接关系着函数输出什么样子的修饰名,使用WINAPI宏的TestAdd函数,对应的输出修饰名就是“?TestAdd@@YGHHH@Z”。
    为什么要使用WINAPI呢?这牵涉到动态库的另一个特征,调用协议(Calling convention)。如果没有一定的协议,动态库的调用是不可想象的。一般常用的动态库调用协议有:
   __cdecl
   __stdcall
   __fastcall
这些协议各有各的长处,这里暂不一一描述。上面在谈到解决VB程序调用VC写的动态库时,曾列举两种解决方法,但并不一定可以实现,它还取决于所使用的调用协议。VB所遵循的是PASCAL协议,如果在动态库中没有使用相应的协议,则VB程序执行时就会报告“调用协议错”。而PASCAL协议在VC6中已被废弃,取而代之的是__stdcall,即标准调用协议,这也是大多32位编程语言支持的一种通用协议。在WINDOWS.H中WINAPI也是被定义为__stdcall。这里提议使用WINAPI的理由也就在这,它能够表达出更加多的信息----这样定义的输出函数(的调用协议)和 WINDOWS API函数(的调用协议)一样。
    其它一些使用WINAPI宏的理由:你只要在所定义的函数前加上该宏,就不必要在每次连接时再去理会各种与调用协议相关的设置。况且,你可能并不需要将所有定义的函数都输出,为了提高执行速度,你可能会将没有输出的函数使用__fastcall来定义,为了使用变参,你可能使用__cdecl来定义某些非输出函数,或者诸如此类的理由......需要提醒的是,VC默认的调用协议是__cdecl。如果你在没有修改调用协议的情况下,直接使用DEF文件输出函数,编译连接都不会出错,但是VB调用的时候肯定出错。而如果使用了WINAPI宏,你不必再去理会这些。编译器自动使用WINAPI的定义替代集成环境里的相关设置,这里函数前的说明优先级最高。
    回过头来再看上面有关输出函数的修饰名的讨论,上面提到修饰名与语言有关,另外,它还与调用协议有关。如果需要使用非函数名的名字用来输出,你必须清楚你使用的调用协议及语言种类,也就是你必须清楚修饰名的生成规则,或者你采用一些技巧,让DUMPBIN.EXE工具来帮忙。

    三、总结
    一句话,如果想建立自己的标准API动态库,建议使用WINAPI描述你要输出的函数,然后使用定义文件输出它。

使用定义文件和WINAPI宏编写自己的API动态库

http://blog.csdn.net/wx_zzm/archive/2004/06/16/11130.aspx
  • CTeX
  • CTeX
  • 2006年05月06日 01:11
  • 906

linux下动态库和静态库的制作、寻找、比较及相关Makefile的编写

一.库的定义 什么是库,在windows平台和linux平台下都大量存在着库。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。 由于windows和linux的本质不同,因此二...
  • LYX_WIN
  • LYX_WIN
  • 2016年08月13日 23:41
  • 756

Excel如何使用宏编写函数

excel支持VBA编程。如果熟悉VB编程,就可以通过编程控制Execl的数据处理。 里面涉及到事件,这个跟delphi有点像。 下面是一个例子:如果当单元格的内容发生变化,在后面第6的单元格复制...
  • u010673851
  • u010673851
  • 2015年11月26日 13:28
  • 589

WINAPI宏

一直搞不懂为什么在函数前面加上WINAPI、CALLBACK等是什么意思 又不是返回值 为什么加在前面 今天终于知道了 这是一个呼叫声明(姑且称之吧)。 引子: 看看这个函数: in...
  • slj_win
  • slj_win
  • 2014年06月23日 14:54
  • 838

动态库Dll的宏定义头文件双向使用方法小结

动态库Dll的宏定义头文件双向使用方法小结 这里需要提到的一点的是,在显示调用的情况下,才需要这种头文件,隐式调用的情况下,其实这些东西有的不必写,请有基础的同学自己思考. 如果想在自己的...
  • qq_24571549
  • qq_24571549
  • 2017年02月19日 20:19
  • 568

linux库文件编写入门

 linux库文件的编写作者: laomai地址: http://blog.csdn.net/laomai本文主要参考了如下资料⑴hcj写的"Linux静态/动态链接库的创建和使用"地址 http:/...
  • laomai
  • laomai
  • 2007年02月16日 10:16
  • 16577

WinAPI编程入门笔记

今天写的这篇文章的主要意图就是给winAPI编程实践的一个小小的启发; 因为winAPI编程时,我们用到很多的函数都是带有很多的参数,而且有时要进行相应的强制类型转换,所以熟悉常用的一些类型是非常重...
  • fucumt
  • fucumt
  • 2013年01月27日 22:30
  • 10024

【Win32】利用WindowsAPI增删改文件+剪贴板的使用

Win32文件操作 1、文件 文件在OS中存放多是以文件索引+文件数据形式分开存放的(这里就要注意删除文件内容的时候了)。 2、windowsAPI 文件操作 ①打开文件 HANDLE WI...
  • yzt33
  • yzt33
  • 2015年04月05日 11:37
  • 852

使用Windows API改变字体风格

#include #pragma comment(linker,"/subsystem:windows") HWND hs; typedef struct tagSetFont{ HWND hwn...
  • u013078986
  • u013078986
  • 2014年05月29日 13:57
  • 1038

Windows API实现最最简单的窗口程序

弹出消息框: #include int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, ...
  • jiangtaohu123
  • jiangtaohu123
  • 2012年05月31日 21:27
  • 5265
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:使用定义文件和WINAPI宏编写自己的API动态库
举报原因:
原因补充:

(最多只允许输入30个字)