函数原型:
extern "C" __declspec(dllexport) int _stdcall GZipCompression(const char *, int , char *, int )
编译后的导出符号:
_GZipCompression@16
函数原型:
extern "C" __declspec(dllexport) int _cdecl GZipCompression(const char *, int , char *, int )
编译后的导出符号:
GZipCompression
函数原型:
__declspec(dllexport) int _cdecl GZipCompression(const char *, int , char *, int )
编译后的导出符号:
?GZipCompression@@YAHPBDHPADH@Z
函数原型:
__declspec(dllexport) int _stdcall GZipCompression(const char *, int , char *, int )
编译后的导出符号:
?GZipCompression@@YGHPBDHPADH@Z
extern "C" 按照c的方式进行符号修饰
{
c方式下的两种调用惯例所产生的符号
_cdecl 不产生任何修饰
_stdcall 下划线 + 函数原型名称 + @ + 参数的字节总和
}
c++方式进行的符号修饰相对复杂
{
...
}
::LoadLibrary(LPCTSTR szDLL)的返回值 既为对应DLL在进程虚拟地址空间中的虚拟地址
首个自定义被加载的dll 对应的地址应为0x10000000
而系统相关的dll 应该在进程加载前就已经位于内存中 且相应的代码为系统代码 应该处于进程地址空间中的高地址空间内
::GetProcAddress() 中所制定的符号 对应为dll的导出符号 而不是dll中函数的圆型
以上四种调用惯例 和 两种编译方式所产生出的符号均不相同
仅有调用惯例为_cdecl 符号修饰方式为c的声明才能产生和函数原型相对应的符号(*.def 文件的作用)
按照以上四种方式编译出来的dll
如果均以
::GetProcAddress(::LoadLibrary("*.dll"), "GZipCompression")
方式进行函数获取 仅有extern "C" + _cdecl 的方式编译的dll才能获取的到地址
其他方式编译出的dll 因为符号不匹配 GetProcAddress的返回值均为空
在c#中有所不同
如果按照extern "C" _cdecl的方式编译
[DllImport("Compressor.dll", EntryPoint = "GZipCompression", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
[DllImport("Compressor.dll", EntryPoint = "GZipCompression", ExactSpelling = false, CallingConvention = CallingConvention.Stdcall)]
都能进行正确的调用
而按照extern "C" _stdcall的方式编译
[DllImport("Compressor.dll", EntryPoint = "GZipCompression", ExactSpelling = false, CallingConvention = CallingConvention.Stdcall)]
能正常执行
[DllImport("Compressor.dll", EntryPoint = "GZipCompression", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
的方式将会产生运行时的异常
System.EntryPointNotFoundException
而以c++方式编译(没有 extern "C" 声明)得到的符号 无论_cdecl , _stdcall 均无法进行调用