如何通过命令行设置 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:

ma​​in.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 命令只会生成一个静态库并最终覆盖第一个命令)。

这是完整的最基本的工作示例:

ma​​in.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++ 动态链接?的主要内容,如果未能解决你的问题,请参考以下文章

vc动态链接库的调用 LoadLibrary()路径问题

VC++命令行操作

VC6.0 设置动态链接库工程生成dll以及lib文件的位置

Visual Studio中怎么生成动态链接库的lib文件

如何链接到动态提升库?

VC++程序设计与应用--动态链接库