• dll的def文件与__declspec(dllexport)导出函数方式比较


    首先对C和C++编译(extern "C")与调用约定(__cdecl、__stdcall、__fastcall)进行组合测试:
    【C++编译】
    __declspec(dllexport) int add(int, int);


    __declspec(dllexport) int __cdecl add(int, int);



    __declspec(dllexport) int __stdcall add(int, int);



    __declspec(dllexport) int __fastcall add(int, int);

    对于C++编译器的函数名修饰规则:不管__cdecl, __fastcall还是__stdcall调用方式,函数修饰名都是以"?"开始,后面是函数在名字,再后面是函数返回类型和参数类型按照代号拼出的参数表。对于__stdcall方式,参数表的开始标示是"@@YG”,对于__cdecl方式则是"@@YA”,对于__fastcall方式则是"@@YI”. 
    参数表后以"@Z”标示整个名字的结束,如果该函数无参数,则以"Z”标识结束。 
    更详细的dll基础知识请参考:
    http://hi.baidu.com/luosiyong/blog/item/92bbdcfe860435375c600812.html
    更深入的C++函数名修饰编码规则请参考:
    http://hi.baidu.com/wanggang2008/blog/item/cd43e60756021bc07a89470a.html


    【C编译】
    extern "C" __declspec(dllexport) int add(int, int);



    extern "C" __declspec(dllexport) int __cdecl add(int, int);



    extern "C" __declspec(dllexport) int __stdcall add(int, int);



    extern "C" __declspec(dllexport) int __fastcall add(int, int);

    如果创建dll和可执行文件都是使用的VC,那用__declspec(dllexport)足够解决问题。但是如果创建出来的dll要和别的厂商的工具包构建的可执行文件链接,那就有一些额外的问题来了。
    在开发dll的时候,一般不让编译器改变函数名,所以通常是以C方式编译,即加入了extern "C"说明。但是看上面的组合测试结果,__stdcall和__fastcall编译出来的函数名还是和原始的函数名不同。就拿__stdcall来说,它以C编译导出的时候,会在函数前面加入下划线,并在函数后面加入@和参数总大小的字节数。
    或许现在你就想,__cdecl不就是没有改变名称的方式吗,并且默认也是__cdecl调用约定,的确,我们自己写的dll几乎都可以使用__cdecl方式。但是,Windows中最普遍的调用方式都是__stdcall(比如CALLBACK、 WINAPI),一些常用的定义如下:
    #define CALLBACK __stdcall
    #define WINAPI __stdcall
    #define WINAPIV __cdecl
    #define APIENTRY WINAPI
    #define APIPRIVATE __stdcall
    #define PASCAL __stdcall
    现在假如用VC的__stdcall方式开发的一个dll,里面包含了上面那样的add函数,如果要在VB中使用,VB的程序员需要如下声明:
    Declare Function add Lib "win.dll" Alias"_add@8"() As Integer
    注意他需要写的名称是 "_add@8",而不是简单的"add",否则就会出现函数未定义的链接错误。
    【备注】
    __declspec(dllexport)的位置:
    To export functions, the __declspec(dllexport) keyword must appear to the left of the calling-convention keyword, if a keyword is specified.

    For example:
    __declspec(dllexport) void __cdecl Function1(void);


    To export all of the public data members and member functions in a class, the keyword must appear to the left of the class name as follows:
    class __declspec(dllexport) CExampleExport : public CObject

    {  class definition  };
    Reference:

    1. http://msdn.microsoft.com/en-us/library/d91k01sh(VS.80).aspx

    2. http://msdn.microsoft.com/en-us/library/a90k134d(VS.80).aspx

    【def文件导出方式】
    首先了解一下 使用def文件从dll导出:
    http://msdn.microsoft.com/zh-cn/library/d91k01sh(v=VS.80).aspx

    具体到测试实例,我们的def文件内容如下:
    LIBRARY "win"

    EXPORTS
     add @1

    其中LIBRARY指定dll的模块名称,即dll名字,EXPORTS后的每一行指定一个导出函数名字,这个名字和头文件中的声明一致,后面可以跟@序号指定该函数的序号(这个是可选的,后面按序号导入函数的时候再详细说)。

    然后再测试一下__stdcall和__fastcall是否会对导出函数改名,测试结果如下,均未改名:

    extern "C" int __stdcall add();



    extern "C" int __fastcall add();



    另外一种方案是在代码中给链接器指定导出函数名字:
    extern "C" __declspec(dllexport) int __fastcall add(int a, int b);
    #pragma comment(linker, "/export:add=@add@8")
    这里告诉链接器,导出一个函数名为add的函数,函数入口点和@add@8相同

    这样,我们既可以使用add,也可以使用@add@8了。
    __stdcall方式和这类似,为add=_add@8。

    【按序号而不是按名称从dll导出函数】
    http://msdn.microsoft.com/zh-cn/library/e7tsx612%28VS.80%29.aspx
    def文件定义如下:
    LIBRARY "win"

    EXPORTS
     add @1 NONAME

    函数名称和Hint都不见了。
    这样也可以用来隐藏dll中一些重要函数。

    隐藏了函数名称,在应用程序中使用序号来导入函数:
    #include <windows.h>
    #include <stdio.h>

    int main()
    {
     typedef int (* AddFunc)(int, int);
     HMODULE hModule = LoadLibrary("dll.dll");
     AddFunc add = (AddFunc)GetProcAddress(hModule, MAKEINTRESOURCE(1)); //注意这里序号的指定方式
     printf("%d ", add(1, 2));
     return 0;
    }

    【备注】
    def文件和__declspec(dllexport)方式优缺点对比:
    http://blog.vckbase.com/zaboli/archive/2006/10/29/22911.html

  • 相关阅读:
    第11章 接口与内部类
    第10章 多态
    API接口设计之token、timestamp、sign具体实现
    JDK 监控和故障处理工具
    分布式id生成方案
    SQL优化
    自定义对象存入Redis
    OAuth(开放授权)
    Hystrix使用
    Session机制详解
  • 原文地址:https://www.cnblogs.com/zhangxiaosong/p/3300861.html
Copyright © 2020-2023  润新知