Search code examples
c++dllloadlibrarygetprocaddress

How can I "start" a DLL like an executable at runtime?


I want to write a very, very small program that parses the launch arguments and chooses one of several DLLs to "boot into."

I've already written an application that I'd like to "run" as a DLL by writing it as an application, then changing the Visual Studio project properties to build it as a DLL instead. I know I need to use LoadLibrary and GetProcAddress in concert to get the functionality I want, but I'm having trouble finding clear and comprehensive documentation on this, as a lot of the use cases aren't really of this nature. Also, I have to go this route based on the project and platform restrictions.

I found this page, which has some information, but it's not clear enough for me to adapt for my purposes.

Edit: Here's where I'm at right now.

I have a DLL project whose main function signature looks something like this:

__declspec(dllexport) int cdecl main(int argc, char *argv[])

I also have an application project whose attempt at loading the DLL and running the above function looks like this:

typedef int (CALLBACK* LPFNDLLFUNC1)(int, char *);

...

        HMODULE dllHandle = NULL;
        BOOL freeResult, runTimeLinkSuccess = FALSE;
        LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer  
        if (args->IsEmpty())
        {
            dllHandle = LoadLibrary(L"TrueApplication.dll");
            if (NULL != dllHandle)
            {
                lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(dllHandle, "main");
                if (lpfnDllFunc1)
                {
                    int retVal = lpfnDllFunc1(0, "1");
                }

Currently, the LoadLibrary call works, but not GetProcAddress.


Solution

  • First of all, changing project type from executable to DLL is not enough to make a DLL. You also need to export some symbols to create your API. At least, you need to decorate the functions you are exporting with __declspec(dllexport). However, I recommend that you export C API, meaning extern "C" functions with C-compatible arguments. So, the functions you export should be prepended with extern "C" __declspec(dllexport).

    Once you have done that, you can dynamically load your DLL like this:

       const char* dllname = "myfile.dll";
       h = LoadLibrary(dllname);
       if( h == nullptr )
       {
           /*handle error*/
       }
    
       using myfunc_type = bool (*)(int x, double y); //example
       auto myfunc = reinterpret_cast<myfunc_type>(GetProcAddress(h, "myfunc"));       
       //......
       myfunc(x,y); //call the imported function
    

    This solution takes more work than static loading with /delayload shown by Jerry Coffin, but it has an advantage: if a DLL is required but not found, you can give users your own error message instead of relying on the message coming from Windows (which is often unacceptable for non-tech people). You can also include API version verification with its own custom error message in the API.

    Edit: the code sample will work if you change it like this

    extern "C" __declspec(dllexport) int main(int argc, char *argv[]){...}
    typedef int (* LPFNDLLFUNC1)(int, char **);