如何通过命令行设置 VC++ 动态链接?
Posted
技术标签:
【中文标题】如何通过命令行设置 VC++ 动态链接?【英文标题】:How does one setup VC++ dynamic linking via command-line? 【发布时间】:2016-04-13 22:37:13 【问题描述】:虽然我通过 Visual Studio 设置运行时 DLL 加载没有问题,但我在通过 Visual Studio CLI 工具手动执行时遇到了一些问题。
假设我们要编译以下 2 个简单的 C++ 源文件,一个用于二进制可执行文件,一个用于 DLL:
main.cpp
void say_hello();
int main()
say_hello();
return 0;
say_hello.cpp
#include <stdio.h>
void say_hello()
printf("Hello DLL World!");
将say_hello.cpp
文件编译为 DLL 然后将其与来自main.cpp
的调用动态链接的步骤是什么?
根据我对 MSDN 文档的阅读,我能够成功编译 say_hello.dll
和应用程序,然后使用以下命令运行它:
cl say_hello.cpp /LD
lib say_hello.obj
cl say_hello.lib main.cpp
不幸的是,这似乎只允许通过say_hello.lib
文件静态链接应用程序(可以通过删除 .lib 和 .dll 文件来确认,这仍然可以让二进制文件成功运行)。
我必须将哪些命令/参数传递到编译/链接阶段才能让main.exe
使用 DLL 而不是静态库?
【问题讨论】:
cl say_hello.cpp /LD
应该在 .lib 文件中生成 DLL 和导入库。随后的lib
命令然后用静态库覆盖上述导入库。只是不要那样做。另一个问题是需要通过 DEF 文件或 __declspec(dllexport)
从 DLL 显式导出名称。正如所写,您没有导出任何内容,因此没有任何实际链接。
啊,是的...第一步cl say_hello.cpp /LD
的控制台输出似乎表明确实创建了“/imlib:say_hello.lib”,但我只看到say_hello.obj
和在目录中生成say_hello.dll
。是否有其他标志可以通过告诉它不要清理导入库?
啊,我明白了,要导出的函数前缀的__declspec(dllexport)
宏很关键,因为没有它,cl.exe 最终会在完成后立即删除导入库文件say_hello.lib
。
是的,因为没有什么要导入的 ;-)
【参考方案1】:
这是一个例子。并非每件事都是完全必要的(例如 DLLMain),但我认为这些是您应该查找的内容 ;-)
SayHello.cpp
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
// see https://msdn.microsoft.com/en-us/library/56h2zst2.aspx : Decorated Names
extern "C" // somehow making it superfluous to put the code in SayHello.cPP ...but anyway ;-)
// see https://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx : dllexport, dllimport
__declspec(dllexport) void say_hello()
printf("Hello DLL World!");
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx : (optional) DllMain entry point
BOOL WINAPI DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
switch (ul_reason_for_call)
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
return TRUE;
main.cpp
extern "C" __declspec(dllimport) void say_hello(); // we did this in SayHello.cpp, so we have to do it here too.
// otherwise the name wouldn't match
int main()
say_hello();
return 0;
然后编译/链接
cl /D_USRDLL /D_WINDLL SayHello.cpp /LD /link /OUT:SayHello.dll
/LD 告诉链接器构建一个 DLL和以使用 /MT,请参阅/MD, /MT, /LD (Use Run-Time Library)。 (通过 OUT: 参数您可以更改 .dll 的名称;这里是默认值,仅用于演示目的。如果您省略它,您也可以跳过 /link 参数,因为不再是链接器参数。)
cl.exe /MT main.cpp /link /SUBSYSTEM:CONSOLE "SayHello.lib"
匹配 dll 的运行时库设置,创建一个 console application(main.cpp 有一个 int main()
)并链接 SayHello 的存根库(而不是使用 LoadLibrary("SayHello.dll")/GetProcAddress(...))
【讨论】:
Doh,弄乱了第二个 cl.exe 命令的编译器/链接器参数。自我注意:先测试,然后发布。【参考方案2】:我将 VolkerK 的答案标记为正确,因为它包含一些其他重要的 Windows DLL API 细节,这些细节绝对值得一读,但我想总结一下最低限度可以使它获得基于 Igor Tandetnik 和 VolkerK 的 cmets 的最简单示例中的动态链接。
在 DLL 中至少一个要导出的函数之前添加宏 __declspec(dllexport)
是必不可少的,否则库编译命令将不会创建导入库来指示哪些函数可用于动态链接。
第二个“lib say_hello.obj”完全错误,因为 DLL 编译命令会正确生成 say_hello.lib(而 lib 命令只会生成一个静态库并最终覆盖第一个命令)。
这是完整的最基本的工作示例:
main.cpp
void say_hello();
int main()
say_hello();
return 0;
say_hello.cpp
#include <stdio.h>
__declspec(dllexport) void say_hello()
printf("Hello DLL World!");
编译命令:
cl say_hello.cpp /LD
cl main.cpp say_hello.lib
【讨论】:
以上是关于如何通过命令行设置 VC++ 动态链接?的主要内容,如果未能解决你的问题,请参考以下文章