看了这篇文章之后,你将发现,你在使用dll的时候,即使用错了dllimport或者dllexport,也不是什么严重的问题,编译器可能会帮你把错误纠正过来。到底怎么回事,你看看就会明白了。
由于使用失误,我在一个工程里面引用了dll中的一个函数,在申明的时候把dllimport误写成dllexport,并一直用了很久,因为使用正常,所以竟从来也没有发现此错误。我对它何以一直能够正常使用感到很不解。
我的申明是这样写的:
extern
"C" __declspec(dll
ex
port
) BOOL
myfunc1
();
而正确的写法应该是:
extern
"C" __declspec(dllimport) BOOL
myfunc1
();
为了分析,我把这个exe文件放到IDA工具里面进行反汇编分析。有下面的发现:
在调用
myfunc1
接口函数的地方,汇编代码是这样写的:
call
_
myfunc1
C编译器在编译时将引入函数
myfunc1的名字Resolve为带下划线的_
myfunc1
。作为一个引入函数,仅仅加桑下划线完成Resolve直觉上感觉是不够的,于是再把鼠标放到_myfunc1上,右键单击选择‘Jump to operand’。我又看到了下面的代码片段:
_myfunc1 proc near
jmp ds:__imp__myfunc1
_ myfunc1 endp
这段汇编代码是_myfunc1的函数定义,可见所谓_myfunc1其实是一个桩函数(stub function),它将再调用了真正的引入函数__imp__myfunc1。函数名__imp__myfunc1正是对外部引入函数myfunc1真正的的Resolve名称,前缀__imp是import的简写。接着再看看__imp__myfunc1的内容,仍然右键选择‘Jump to operand’,我看到的是:
idata:00407028 ;
.idata:00407028 ; Imports from myDll
.idata:0040702C
extrn __imp__myfunc1:dword
它的意思是说,从myDll中引入接口函数__imp__myfunc1。后面的dword,说明函数指针是4字节大小。
现在可以总结一下了:因为我把dllimport误写成了dllexport,编译器在进行编译的时候当然发现了这个错误,采取了补救措施,不仅真正引入了myDll中的myfunc1函数,而且创建了一个桩函数_myfunc1来调用此引入函数,从而在进行myfunc1调用的地方都一律变成了_myfunc1(call _
myfunc1)调用了。
为了进一步地弄清问题,我修改错误后重新编译,再把新的exe进行IDA反汇编,看到_myfunc1已经不存在了,函数中调用myfunc1的地方被直截编译成:call
__imp__myfunc1。