zoukankan      html  css  js  c++  java
  • DLL导出与调用约定

        一般来说,从DLL导出函数有两种方法。一种是使用.def文件;另一种是使用__declspec(dllexport)。

        使用上面两种方法各有优缺点。使用.def文件就是需要额外维护,当导出函数更改名字或者追加导出函数。而使用__declspec(dllexport)则需要注意使用的调用约定,在使用C++编译器时。

    调用约定

        在使用C++或者在常见的windows API的声明头文件常见的WINAPI,而WINAPA就是__stdcall。在C++还有__cdecl。而__pascal和__thiscall都已忽略了。宏PASCAL也用的是__stdcall。

        一般来说,调用约定只关乎参数的压栈和最后的清理工作。还有就是名字修饰。比如:

    void func(char, short, int, double)

        使用__cdecl调用:

        __cdecl

        使用C编译器编译后的修饰名字为:_func。清理栈的工作由调用方做。

        使用__stdcall和__thiscall调用:

        __stdcall

        使用C编译器编译后的修饰名字为_func@20。清理栈的工作由被调方做。

        使用__fastcall调用:

        __fastcall

        使用C编译器编译后的修饰名字为@func@20。清理栈的工作由被调方做。

    名字修饰

        从上面的图也可看见,压栈都是从右到左,而名字修饰的方法却不一样。C编译器的修饰方法相对来说比较简单。以_func@20为例,_是追加的,func就是函数的名字,@是追加的,20为参数的字节数。C++的修饰方法相对来说比较复杂。下图是使用C++编译器产生的名字,使用__declspec(dllexport)和__stdcall。

    FZH6GCC9J8YPX@YVBL8$FIP

        下图是使用__cdecl编译:

    QQ图片20130829205329

       大致一看都差不多,只是在名字后的有个字母不一样,__stdcall得是YG,而__cdecl的是YA。而这也正好指明了C++的名字修饰的规则,简单来说就是:

    ・名字(可以包含命名空间)

    ・调用约定

    ・返回值

    ・参数列表

    调用约定修饰规则:

    代号 调用约定
    @@YG __stdcall
    @@YA __cdecl
    @@YI __fastcall

    类型修饰规则:

    代号 含义
    X void
    D char
    E unsigned char
    F short
    H int
    I unsigned int
    J long
    K unsigned long(DWORD)
    M float
    N double
    _N bool
    U struct
    PA pointer
    PB const pointer

    参数表后以”@Z”标示整个名字的结束,如果该函数无参数,则以”Z”标识结束。

        最后一点:如果我们直接使用__declspec(dllexport)导出函数的话,最好使用C编译器。如果直接用C++编译器,导出的函数名就像上面一样。在动态使用DLL时调用GetProcAddress使用原始函数名称一般来说会失败。例如:

    GetProcAddress(handle, "func");

    所以一般使用C编译器,比如这样:

    #ifdef __cplusplus    // If used by C++ code
    extern "C" {        // we need to export the C interface
    #endif
    
    __declspec(dllexport) VOID __cdecl func(char, short, int, double)
    {
      ...
    }
    
    #ifdef __cplusplus
    }
    #endif

    这时,修饰后的名字就是这样:

    O%X`$S0PGAB4EA%T9MF%VDW

        一般导入函数时也建议这样:

    #ifdef __cplusplus
    extern "C" {
    #endif
    
        // _declspec(dllimport) VOID func(char, short, int, double);
        // or
        VOID func(char, short, int, double);
    
    #ifdef __cplusplus
    }
    #endif

    第一种写法在效率上来说会更好点。

    参考:http://hi.baidu.com/luosiyong/item/c8664533f56b66c31b9696e3

            http://msdn.microsoft.com/en-us/library/k2b2ssfy.aspx

  • 相关阅读:
    CopyOnWriteArrayList设计思路与源码分析
    点击页面按钮以excel保存到本地
    上传图片
    关于重复点击的
    去首尾空格还有换行问题//把数字换位大写字母//向后台传输数据
    判断输入的时间与当前的时间(判断时间是今天还是以前的)
    前端的一些小技巧
    git 操作大全
    移动web开发常见问题解决方案
    响应式布局
  • 原文地址:https://www.cnblogs.com/navono007/p/3290375.html
Copyright © 2011-2022 走看看