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

  • 相关阅读:
    测试开发知识体系
    渗透测试全套教程(从原理到实战)
    解决edittext输入多行可以滑动的问题
    SharedPreferences实现自动登录记住用户名密码
    listview当选中某一个item时设置背景色其他的不变
    转 -android:程序无响应,你该如何定位问题?
    区分listview的item和Button的点击事件
    转--2014年最新810多套android源码2.46GB免费一次性打包下载
    转-android 支付宝SDK集成
    转-封装网络请求库,统一处理通用异常 (基于volley网络请求库)
  • 原文地址:https://www.cnblogs.com/navono007/p/3290375.html
Copyright © 2011-2022 走看看