3

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?

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
AathakA
  • 117
  • 7
  • How/when you plan to use it? – i486 Mar 01 '22 at 10:11
  • You also need to make import library for exe and link it to dll. – user7860670 Mar 01 '22 at 10:32
  • @user7860670 I guess that is what I'm missing... Do you know how to achieve that in visual studio 2019? – AathakA Mar 01 '22 at 10:38
  • @i486 I'm trying to create a small example that replicates a big codebase that implements something like that. – AathakA Mar 01 '22 at 10:38
  • You need to write a .def file containing mangled name of function to be exported from exe and convert it to .lib using lib tool, see https://learn.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-def-files?view=msvc-170 And finally link this .lib file to the .dll. – user7860670 Mar 01 '22 at 10:42
  • @user7860670 Okay turns out I can just link MAIN.lib (that I didn't know it would be generated since MAIN is an executable) to the DLL project and it runs fine. Tho I need to build the project separately, cause if I build the solution as a whole I get `lnk1114 Cannot overwrite the original file "xxx.lib", error code 5` – AathakA Mar 01 '22 at 10:49
  • Yeah, directly using an export .lib generated from executable is also an option. However making lib from a .def file is a more reliable solution because it does not require .exe to be compiled in advance. Basically it breaks circular dependency. As for "lnk1114 Cannot overwrite the original file" it is really difficult to tell where it comes from. Perhaps you simply have some file opened? – user7860670 Mar 01 '22 at 10:52
  • 2
    https://stackoverflow.com/questions/2727020/what-is-the-use-of-exp-and-what-is-the-difference-between-lib-and-dll – Hans Passant Mar 01 '22 at 10:54
  • @AathakA Have you got any updates? If your case has been solved, please help to mark answers. If not, just feel free to contact us. Your understanding and cooperation will be grateful. – Jeaninez - MSFT Mar 11 '22 at 09:06

3 Answers3

6

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();
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • There is nothing wrong with exporting functions from exe. – user7860670 Mar 01 '22 at 10:33
  • 3
    circular dependency is very wrong. – Marek R Mar 01 '22 at 10:38
  • 1
    @Remy Lebeau Thanks for the example but I actually need the example I posted to work. I'm working on a codebase that does exactly what I showed and I need to replicate it in a smaller example on VS2019 – AathakA Mar 01 '22 at 10:40
  • "I'm working on a codebase that does exactly what I showed and I need to replicate it in a smaller example on VS2019" Why don't you just copy what this codebase does? – David Heffernan Mar 01 '22 at 10:56
  • Hi, if I remember correctly, under Linux, it is allowed to export symbols from a host executable. I'm not sure what is the "circular dependency" here in the OP's code. The host exe is not depend on the dll. And the dll use some service from the exe, I don't see there are something wrong here. See the Fig.3 of this web page: Enhanced Dynamic Linking for MS-Windows — http://edll.sourceforge.net/ – ollydbg23 Aug 18 '22 at 03:42
2

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.

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • 1
    Hi, I think the method in your answer is the way like the Fig.2 of this web page: Enhanced Dynamic Linking for MS-Windows — http://edll.sourceforge.net/ – ollydbg23 Aug 18 '22 at 03:56
2

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

Dwedit
  • 618
  • 5
  • 11
  • `extern "C"` doesn't inhibit name mangling. It merely chooses C's [name decoration](https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names#FormatC) scheme. Name decoration is different for 32-bit and 64-bit targets. – IInspectable Mar 01 '22 at 21:57