Correct way to access functions defined in .exe from .dll

Viewed 133

I have a VS solution with an executable and a DLL.

In the executable (MAIN):

__declspec(dllexport) void testExe()
{
    printf("Hello from EXE");
}

__declspec(dllimport) void DoStuff();

int main()
{
    DoStuff();
}

while in the .dll (DLL)

__declspec(dllimport) void testExe();

__declspec(dllexport) void testDll()
{
    printf("Hello from Dll");
}

__declspec(dllexport) void DoStuff()
{
    testExe();
    testDll();
}

I linked Dll.lib in MAIN.exe, but I still get a linking error:

error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl testExe(void)" referenced in function "void __cdecl DoStuff(void)"

How can I achieve this?

3 Answers

Don't export functions from EXEs. Export a function from the DLL that accepts a function pointer as input, then have the EXE call that function at runtime.

EXE:

__declspec(dllimport) void SetFunc(void (*)());
__declspec(dllimport) void DoStuff(); 

void testExe()
{
    printf("Hello from EXE");
}

int main()
{
    SetFunc(testExe);
    DoStuff();
}

DLL:

typedef void (*lpFuncType)();

lpFuncType pExeFunc = NULL;

void testDll()
{
    printf("Hello from Dll");
}

__declspec(dllexport) void SetFunc(lpFuncType func)
{
    pExeFunc = func;
}

__declspec(dllexport) void DoStuff()
{
    if (pExeFunc) pExeFunc();
    testDll();
}

Circular dependency is a problem here. You have to break this cycle or you will have complex and fragile build process to create this cycle.

One way to break this cycle is Remy Lebeau answer.

Other way is to introduce another dll which will contain testExe() and this dll will be linked to executable and your initial dll, which contains testDll(). Advantage is you do not have to change code at all, just split executable to executable and extra dll.

Use GetModuleHandleW(NULL) to get the module handle of the executing EXE. Then use GetProcAddress to get the address of the function.

Watch out for C++ name mangling and calling conventions (using __stdcall will add something like @4) changing the function name. To avoid the function name changing, use extern "C" before exporting it.

Example:

In main.cpp for the EXE:

//Want to use extern "C" so that the function name doesn't get mangled using C++ mangling rules
extern "C"
{
    __declspec(dllexport) int DoStuff(int param1, int param2);
}

In the DLL that imports from the EXE:

//Declare a function pointer type named "DoStuff_FUNC", this makes it a lot easier to import functions
typedef (int DoStuff_FUNC)(int param1, int param2);

int DoStuff_WithinDll(int param1, int param2)
{
    //Get the module handle from the executing EXE
    HMODULE module = GetModuleHandleW(NULL);
    //Type-cast "FARPROC" to our desired function type
    DoStuff_FUNC DoStuff = (DoStuff_FUNC)GetProcAddress(module, "DoStuff");
    if (DoStuff != NULL)
    {
        return DoStuff(param1, param2);
    }
    return -1;
}

Related