zoukankan      html  css  js  c++  java
  • zlib报“LNK2001:无法解析的外部符号”错误

      这个错误一般是由使用导出dll时未加载对应的lib文件导致的,但是工程在正确配置了lib文件的情况下仍然报这个错误,经查,是由于dll导入工程和dll导出工程的函数调用约定不一致导致的。

    一、函数调用约定

      首先,我们由函数的调用约定说起,microsoft的vc默认的是__cdecl方式,而windows API则是__stdcall,如果用vc开发dll给其他语言用,则应该指定__stdcall方式。堆栈由谁清除这个很重要,如果是要写汇编函数给C调用,一定要小心堆栈的清除工作,如果是__cdecl方式的函数,则函数本身(如果不用汇编写)则不需要关心保存参数的堆栈的清除,但是如果是__stdcall的规则,一定要在函数退出(ret)前恢复堆栈。

    windows有如下五种调用约定:

    1.__cdecl
           所谓的C调用规则。按从右至左的顺序压参数入栈,由调用者把参数弹出栈。切记:对于传送参数的内存栈是由调用者来维护的。返回值在EAX中因此,对于象printf这样变参数的函数必须用这种规则。编译器在编译的时候对这种调用规则的函数生成修饰名的饿时候,仅在输出函数名前加上一个下划线前缀,格式为_functionname
    2.__stdcal
            按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,切记:函数自己在退出时清空堆栈,返回值在EAX中。  __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。如函数int func(int a, double b)的修饰名是_func@12
    3.__fastcall
           __fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@functionname@number。这个和__stdcall很象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,即第一个参数进ECX,第2个进EDX,其他参数是从右向左的入stack。返回仍然通过EAX。
    4.__pascal
           这种规则从左向右传递参数,通过EAX返回,堆栈由被调用者清除。

    5.__thiscall

            仅仅应用于"C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

    二、解决方法

      之所以会报LNK2001,是因为编译zlib(dll)的工程默认有预处理器定义ZLIB_WINAPI,ZEXPORT在zconf.h中的定义为WINAPI:

    /* If building or using zlib with the WINAPI/WINAPIV calling convention,
    * define ZLIB_WINAPI.
    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
    */
    # ifdef ZLIB_WINAPI
    # ifdef FAR
    # undef FAR
    # endif
    # include <windows.h>
    /* No need for _export, use ZLIB.DEF instead. */
    /* For complete Windows compatibility, use WINAPI, not __stdcall. */
    # define ZEXPORT WINAPI
    # ifdef WIN32
    # define ZEXPORTVA WINAPIV
    # else
    # define ZEXPORTVA FAR CDECL
    # endif
    # endif
    #define WINAPI      __stdcall

    所以,zlib(dll)的导出函数调用约定为__stdcall,和zlib(dll)的导入函数调用约定__cdecl不一致。

    针对zlib编译dll文件编译出错,有以下两种解决办法:

    1. 在zlivc工程中去掉预处理器定义ZLIB_WINAPI(预处理定义:vs工程属性->C/C++->预处理器)

    去掉该定义后,宏定义ZEXPORT在zconf.h文件中实际定义如下:

    #ifndef ZEXPORT
    #  define ZEXPORT
    #endif

    所以导出函数unzGoToNextFile

    extern int ZEXPORT unzGoToNextFile (unzFile  file)

    等价于

    extern int unzGoToNextFile (unzFile  file)

    也等价于(vs默认是__cdecl函数调用约定)

    extern int __cdecl unzGoToNextFile (unzFile  file)

    所以在新的工程中导入zlib相关的库文件和dll文件后,编译dll的工程和引入dll的工程的函数调用约定均为__cdecl方式,导出函数能够正确解析了,编译自然就通过了。由上宏定义注释可知,标准编译ZLIB1.DLL是没有ZLIB_WINAPI预处理定义的,函数调用约定也是__cdecl,所以这里推荐该解决方案。

    2. 在引入zlib工程中添加预处理器定义ZLIB_WINAPI

    在引入dll工程中添加ZLIB_WINAPI定义后,宏定义ZEXPORT在zconf.h文件中实际定义如下:

    #    define ZEXPORT WINAPI
    #define WINAPI      __stdcall

    与导出zlib的dll工程均为__stdcall函数调用约定,编译也能顺利通过。

  • 相关阅读:
    SecureCRT设置linux终端显示颜色
    OpenCV空洞填充算法
    祝贺自己的软件《万能数据库查询分析器》在非凡软件站和太平洋电脑的下载排行榜分别名列第1和第2
    linux磁盘分区 简要
    linux压缩解压命令
    【可视化】Echarts3坐标系倒映
    【可视化】Echarts3 在世界地图中绘制中国各省份的轮廓
    【Maven】解决linux下安装maven update-alternative --display mvn链接层数过多
    【Ubuntu 16】启动Eclipse Indigo报错 error code1 jdk没有配置好
    oracle体系结构
  • 原文地址:https://www.cnblogs.com/chenyangchun/p/6809868.html
Copyright © 2011-2022 走看看