只有 DLL、没有头文件时,如何声明并链接其中的函数?
这样的声明是否足够?
只要函数签名、调用约定和符号名称都正确,且链接了匹配的导入库,头文件中的函数声明就能满足调用需要。对于 MSVC,通常建议在函数声明中添加 __declspec(dllimport),但函数导入并不强制要求它,添加后也仍需导入库。Microsoft 的导入声明说明
目前还没有确认 DLL 的真实接口。仅凭 _DllFunc1@8,不能认定它是一个接收两个 int、返回 int 的函数,因此现有材料不足以证明示例声明正确。
你希望通过导入库进行隐式链接。编译器根据头文件生成调用代码,链接器通过 .lib 处理导入符号。DLL 仍在程序运行时加载,其函数实现不会被编译进程序。Microsoft 的 DLL 链接说明
先确认函数签名,不能只看导出名称
在典型的 MSVC x86 C linkage 命名规则下,_DllFunc1@8 符合 __stdcall 的名称修饰形式。其中 @8 表示参数列表对应的字节数,不能据此认定参数是两个 int,也无法得知返回类型。_DllFunc2@0 同样不能证明函数返回 int。Microsoft 的 __stdcall 说明
至少需要确认:
- 返回类型,以及每个参数的类型和顺序。
- 调用约定与目标架构,例如 x86 或 x64。
- 字符串编码、缓冲区长度、指针是否允许为空等参数约束。
- 涉及结构体时的字段布局、对齐和打包方式。
- 涉及内存分配时,由哪一方分配、释放,以及应调用哪个释放接口。
优先查找 DLL 提供方的文档、头文件、源码或可靠的调用示例。如果只有二进制文件,反汇编和现有调用点可以提供线索,但仅凭导出表无法还原完整的接口契约。即使改用 GetProcAddress,调用时仍需要正确的函数指针类型。
已确认接口后,头文件可以这样写
下面是一个示例,假设已经确认:DLL 提供两个具有 C linkage 的函数,目标为 x86,调用约定是 __stdcall,真实签名分别为 int DllFunc1(int, int) 和 int DllFunc2(void),且导入库与它们匹配。
#ifndef DLL_FUNCS_H
#define DLL_FUNCS_H
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllimport) int __stdcall DllFunc1(int arg1, int arg2);
__declspec(dllimport) int __stdcall DllFunc2(void);
#ifdef __cplusplus
}
#endif
#endif
声明中的几个部分各有不同的作用:
__stdcall指定调用约定,必须与实际函数一致。__declspec(dllimport)告诉编译器该函数来自 DLL。extern "C"在 C++ 编译时指定 C linkage,避免产生 C++ 风格的名称修饰。它不会消除 x86__stdcall的_名称@字节数修饰,也不能把已有的 C++ 接口转换为 C 接口。Microsoft 的名称修饰说明
无参数函数写成 (void),可以让声明在 C 和 C++ 中都明确表示不接收参数。如果 DLL 实际导出的是 C++ 函数,则需要匹配其 C++ 声明和 ABI,不能直接套用上面的 extern "C"。
FriendlyName 别名还需要核对
原示例中有这样一行:
FriendlyName1=_DllFunc1@8
这是 .def 的导出别名语法。构建 DLL 时,它可以用来指定导出名称与内部符号的对应关系;对于已经存在的 DLL,单独生成一个 .lib 不会修改 DLL 的导出表。Microsoft 的 EXPORTS 语法说明
因此,单凭这行配置,无法认定调用方的 FriendlyName1 一定能在运行时解析到已有 DLL 的 _DllFunc1@8。还需要核对调用方生成的符号、导入库提供的符号,以及最终程序请求的 DLL 导出名称。
更容易维护的做法是先按真实接口建立导入声明,再在自己的代码中提供友好名称。例如,上面的头文件可用后,可以为 C++ 调用方编写包装函数:
inline int FriendlyName1(int arg1, int arg2)
{
return DllFunc1(arg1, arg2);
}
inline int FriendlyName2()
{
return DllFunc2();
}
这些包装函数只负责转发调用,不会改变 DLL 的 ABI。
导入库与链接应如何配置?
在 Visual Studio Developer Command Prompt 中,可以先查看实际导出名称和目标架构。以下使用 Vendor.dll 作为示例文件名:
dumpbin /exports Vendor.dll
dumpbin /headers Vendor.dll
根据实际导出情况准备 Vendor.def。确认它与 DLL 匹配后,可以使用 lib.exe 创建导入库。例如,目标确实是 x86 时:
lib /def:Vendor.def /machine:x86 /out:Vendor.lib
这是工具用法示例,使用时仍要确认 .def 内容和目标架构。LIB /DEF 根据提供的导出规格生成文件,不会验证真实函数的参数和返回类型。Microsoft 的导入库生成说明
随后将 Vendor.lib 加入项目的链接器输入,并配置它所在的库目录。普通调用方需要链接导入库,通常不需要把生成的 .exp 加入应用程序链接过程。
还可以检查最终程序实际记录的导入项:
dumpbin /imports app.exe
如果链接阶段出现未解析符号,优先检查导入库、调用约定、名称修饰和架构。如果程序启动时提示找不到入口点,检查程序请求的名称是否存在于实际加载的 DLL 中。若能加载但调用后崩溃,应继续检查函数签名、结构体布局和参数约束;编译、链接成功并不能证明这些内容正确。
x86 的 _名称@字节数 规则不能直接套用到 x64。在 MSVC x64 目标上,__stdcall 会被接受并忽略,调用遵循对应的 x64 ABI。Microsoft 的 __stdcall 平台说明
备注:内容仅供参考。
