VC 中如何使用 BCB 编译的库文件

在 Visual C++ 或者 Visual Studio 中, 是无法直接使用 BCB 工程编译产生的库文件的. 究其原因, 是由于微软 Visual C++ lib 文件格式与 BCB 工程的 lib 文件格式不同所导致. Lib 文件中存放的是动态链接库的接口信息, 而不会含有任何函数的内部实现细节. 因此, 我们可以直接通过 Dll 文件来反向生成特定格式的 lib 文件, 以便在 VC 和 BCB 中交叉使用各个编译的动态链接库.

以 MTK 平台的多路下载工具 SP_MDT 为例, 演示在 VC++ 中直接使用 BCB 编译的 lib 库的问题及解决方案. 我们以 Eboot 为例, 源代码目录下关于 Eboot 的文件有以下几个:
Eboot 头文件定义, 路径: SP_MDT_SRC\Eboot
Eboot lib 库文件, 路径: SP_MDT_SRC\Lib
Eboot 动态链接库文件, 路径: SP_MDT_SRC\output

我们新建一个 VC++ MFC 工程, 将上述文件全部拷贝到 MFC 工程目录下, 同时在对话框中实现如下代码段:

#include "interface.h"
ANDROID_DL_HANDLE_T handle;
Android_DL_Create(&handle);

此时, 编译工程会报出如下错误:

error LNK2019: 无法解析的外部符号 _Android_DL_Create@4
该符号在函数 "public: void __thiscall CLibDemoDlg::OnBnClickedOk(void)" 中被引用

原因在于我们仅仅包含了相应函数的头文件, 而并没有导入任何的函数实现(如 cpp 源文件或者 lib 库文件). 接下来, 我们尝试直接在 VC++ 中使用 BCB 编译生成的 lib 库文件, 加入以下代码:

#pragma comment(lib, "eboot.lib")

编译时报出如下错误:

eboot.lib : warning LNK4003: 无效的库格式; 已忽略库

显然, VC++ 并不能正确的识别 BCB 所生成的 lib 库文件. 那么如何解决这个问题呢? 一般来说有两种方法:

  1. 动态加载 dll 动态链接库
    最直接的方法是, 既然有头文件, 那么就可以知道各个函数的定义, 而 dll 动态链接库则会将这些公开的函数导出. 因此, 可以直接调用 LoadLibrary 载入动态链接库, 并查找到相应的函数地址, 完成调用. 这种方法简单粗暴, 其好处是写出的源代码无论是在 BCB 还是在 VC++ 平台都能够通用. 但其缺点也很明显, 需要改写头文件, 定义各种各样的函数指针, 如果使用到的导出函数很多, 则工作量较大.
  2. 使用 VC++ 编译工具生成 lib 文件
    那么既然原有的工程提供了头文件, 为了最大程度的减少工作量, 可以通过 BCB 生成的 dll 反向输出 VC++ 的 lib 文件, 实现静态加载 dll 动态链接库. 方法如下:
    (1) 使用 dumpbin 生成 .def 文件
    dumpbin 位于 [VS_DIR]\VC\bin 目录中, 执行如下命令产生 .def 文件:
dumpbin eboot.dll /EXPROTS /OUT:eboot.def

此时, eboot.def 文件内容为:

Dump of file E:\eboot.dll
File Type: DLL
  Section contains the following exports for eboot.dll
    00000000 characteristics
    4E3F5BF7 time date stamp Mon Aug 08 11:45:59 2011
        0.00 version
           1 ordinal base
         101 number of functions
          44 number of names
 
    ordinal hint RVA      name
         49    0 000021F0 Android_ADV_Connect
         41    1 00001000 Android_ADV_Create
         42    2 00001150 Android_ADV_Destory
         47    3 00001B00 Android_ADV_FreeBuffer
         46    4 000015D0 Android_ADV_LoadBuffer
         51    5 00002A00 Android_ADV_Reboot
         48    6 00001B30 Android_ADV_SendBuffer
         50    7 00002640 Android_ADV_SendImage
         43    8 000011D0 Android_ADV_SetBootArg
         44    9 00001340 Android_ADV_SetDownloadArg
         45    A 000015A0 Android_ADV_SetRemoteArg
         13    B 00002FD0 Android_Boot_As_Download
         18    C 00003150 Android_DA_Download
         31    D 00003420 Android_DL_Create
         32    E 000034E0 Android_DL_Destroy
         33    F 00003550 Android_DL_Rom_Load
         34   10 00003B20 Android_DL_Rom_UnloadAll
         14   11 00003080 Android_Flash_Download
         35   12 00003B60 Android_Remote_Download
         71   13 00003C30 Android_Secured_Download
         15   14 00003330 Android_Set_Lock
         19   15 00003240 Android_Write_Trace
          9   16 000140B0 CloseActiveSync
        101   17 00010940 Eboot_DebugChangePath
          4   18 00010910 Eboot_DebugClear
          3   19 00010900 Eboot_DebugOff
          5   1A 000108F0 Eboot_DebugOn
          6   1B 00010920 Eboot_GetDLLInfo
         12   1C 000108C0 Eboot_Log
         64   1D 000154E0 GetAllDeviceNumber
         63   1E 000155F0 GetAllPresentDevicePath
         62   1F 000158B0 GetDeviceNumber
         65   20 00016400 GetPortsDriverVersion
         61   21 000159D0 GetPresentDevicePath
         10   22 000140E0 RestartActiveSync
         16   23 0000F2D0 SP_BootAsAdvmeta
         21   24 0000F680 SP_BootAsAdvmetaByUSB
          7   25 0000FA60 SP_BootAsFactoryNormalMode
         20   26 0000FDD0 SP_BootAsFactoryNormalModeByUSB
          2   27 0000EB70 SP_BootAsMeta
         17   28 0000EF00 SP_BootAsMetaByUSB
         11   29 00011260 SP_Flash_Direct_Download
          1   2A 000111C0 SP_Flash_Download
          8   2B 00013EF0 SP_MPDownload
  Summary
      106000 .data
        4000 .rdata
        3000 .reloc
        1000 .rsrc
       18000 .text

(2) 为 .def 文件添加 lib 说明
在 .def 文件开头处加入如下描述:

LIBRARY "eboot"

(3) 整理 .def 中的 EXPORT 信息
删除除 EXPORTS 段以外的其他无用信息, 同时根据头文件将 EXPORTS 段的 ordinal 和 name 字段进行整理:

  • 如果是 stdcall 调用, 则整理为 name@param_size @ordinal 的形式; 注意, name 和 param size 之间不能有空格.
  • 如果是 cdecl 调用, 则整理为 name@ordinal 的形式;

例如, Android_DL_Create 的定义为:

extern int __stdcall Android_DL_Create(ANDROID_DL_HANDLE_T *p_dl_handle);

可以看到该函数是一个 stdcall 调用, 其参数为 1 个指针, 占用 4 个字节, 因此整理为如下形式:

EXPORTS ...... Android_DL_Create@4 @31

(4) 根据 .def 生成 .lib 文件
在 .def 文件整理完毕之后, 使用 lib.exe 生成 .lib 文件, 执行如下命令:

lib /def:eboot.def /machine:i386 /out:eboot.lib

即可生成 eboot.exp 和 eboot.lib 文件. 将 eboot.lib 文件拷贝到 MFC 工程目录下替换原有的 lib 文件,
并重新编译, 通过. 将 eboot.dll 拷贝到与 exe 相同路径下, 运行正常.

你可能感兴趣的:(VC 中如何使用 BCB 编译的库文件)