Eternal Rubyist
Eternal Rubyist

Reputation: 3635

How does one setup VC++ dynamic linking via command-line?

Though I have no problem setting up run-time DLL loading via Visual Studio, I'm having some trouble when doing it manually through the Visual Studio CLI tools.

Say we have the following 2 simple C++ source files we'd like to compile, one for the binary executable and one for the 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!");
}

What are the steps to compile say_hello.cpp file as a DLL then dynamically link it with the call from main.cpp?

Based on my read through of the MSDN docs, I was able to successfully compile the say_hello.dll and application then run it and using the following commands:

cl say_hello.cpp /LD
lib say_hello.obj
cl say_hello.lib main.cpp

Unfortunately, this seems to only allow for statically linking the application via the say_hello.lib file (which can be confirmed by deleting both the .lib and .dll file, which still lets the binary run successfully).

What commands/parameters do I have to pass to the compiling/linking phase to have main.exe use the DLL instead of a static library?

Upvotes: 2

Views: 2767

Answers (2)

Eternal Rubyist
Eternal Rubyist

Reputation: 3635

I marked the answer by VolkerK as correct since it includes some other important Windows DLL API details that are definitely worth a read, but I wanted to summarize the bare minimum changes one could make it get the dynamic linking working in the most simple example based on Igor Tandetnik and VolkerK's comments.

  • Adding the macro __declspec(dllexport) before at least one function to be exported in the DLL is essential, otherwise the library compiling command won't creating an import library to indicate which functions are available for dynamic linking.

  • The second "lib say_hello.obj" was totally wrong, as the DLL compiling command will correctly make the say_hello.lib (whereas that lib command would only generate a static library and wind up overwriting the first command).

Here's the full most basic working example:

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!");
}

Compilation commands:

cl say_hello.cpp /LD
cl main.cpp say_hello.lib

Upvotes: 1

VolkerK
VolkerK

Reputation: 96159

Here's an example. Not every thing is entirely necessary (DLLMain e.g.) but I think those are things you should look up ;-)

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;
}

and then compiling/linking

cl /D_USRDLL /D_WINDLL SayHello.cpp /LD /link /OUT:SayHello.dll

/LD tells the linker to build a DLL and to use /MT, see /MD, /MT, /LD (Use Run-Time Library). (via the OUT: parameter you could change the name of the .dll; here it's the default, just for demonstration purpose. If you leave it out, you can also skip the /link parameter since there would be no linker parameters anymore.)

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

matching the run-time lib settings for the dll, creating a console application (main.cpp has a int main()) and linking the stub lib of SayHello (instead of using LoadLibrary("SayHello.dll")/GetProcAddress(...))

Upvotes: 2

Related Questions