1

虽然我通过 Visual Studio 设置运行时 DLL 加载没有问题,但在通过 Visual Studio CLI 工具手动执行时遇到了一些麻烦。

假设我们要编译以下 2 个简单的 C++ 源文件,一个用于二进制可执行文件,一个用于 DLL:

主文件

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 而不是静态库?

4

2 回答 2

2

这是一个例子。并非所有事情都是完全必要的(例如 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;
    }
}

主文件

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(使用运行时库)。(通过 OUT: 参数,您可以更改 .dll 的名称;这里是默认值,仅用于演示目的。如果您省略它,您也可以跳过 /link 参数,因为不再有链接器参数。)

cl.exe /MT main.cpp /link /SUBSYSTEM:CONSOLE "SayHello.lib" 

匹配 dll 的运行时库设置,创建控制台应用程序(main.cpp 有一个int main())并链接 SayHello 的存根库(而不是使用LoadLibrary("SayHello.dll") / GetProcAddress(...)

于 2016-04-13T23:24:49.317 回答
0

我将 VolkerK 的答案标记为正确,因为它包含一些其他重要的 Windows DLL API 细节,这些细节绝对值得一读,但我想总结一下可以使它在最简单的示例中实现动态链接工作的最小更改Igor Tandetnik 和 VolkerK 的评论。

  • __declspec(dllexport)在 DLL 中要导出的至少一个函数之前 添加宏是必不可少的,否则库编译命令将不会创建导入库来指示哪些函数可用于动态链接。

  • 第二个“lib say_hello.obj”是完全错误的,因为 DLL 编译命令会正确生成 say_hello.lib(而 lib 命令只会生成一个静态库并最终覆盖第一个命令)。

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

主文件

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
于 2016-04-14T00:04:10.690 回答