0

Usually we load a dll on windows and call it's functions which is marked as __declspec(dllexport), but can I call a function which is implemented in the loaded program from a dll?

Firstly speaking this can be done on Linux, check the question I asked earlier.

I wrote a test program to test this: CMakeList.txt (sorry I'm new to Windows programming and don't know how to use visual studio):

cmake_minimum_required(VERSION 3.16)
project(untitled4)

set(CMAKE_CXX_STANDARD 17)

add_library(lib1 SHARED lib1.cpp)

add_executable(untitled4 main.cpp)

main.cpp

#include "iostream"
#include "windows.h"
extern "C" {
  // this is the function I want to get called from dll
__declspec(dllexport)
float get_e() {
  return 2.71;
}
}
int main() {
  auto *handler = LoadLibrary("lib1.dll");
  if (!handler) {
    std::cerr << ERROR_DELAY_LOAD_FAILED << std::endl;
    exit(1);
  }
  auto p = (float (*)()) GetProcAddress(handler, "get_pi");
  std::cout << p() << std::endl;
}

lib1.cpp:

#include "iostream"
extern "C" {
  // implemented in main.cpp
__declspec(dllimport)
float get_e();

__declspec(dllexport)
float get_pi() {
  std::cout << get_e() << std::endl; // comment this line will compile, just like the normal case
  return 3.14;
}
}

The compile will fail when building lib1:

NMAKE : fatal error U1077: '"C:\Program Files\JetBrains\CLion 2020.1.1\bin\cmake\win\bin\cmake.exe"' : return code '0xffffffff'
Stop.
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.27.29110\bin\HostX86\x64\nmake.exe"' : return code '0x2'
Stop.
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.27.29110\bin\HostX86\x64\nmake.exe"' : return code '0x2'
Stop.
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.27.29110\bin\HostX86\x64\nmake.exe"' : return code '0x2'
LINK Pass 1: command "C:\PROGRA~2\MICROS~2\2019\BUILDT~1\VC\Tools\MSVC\1427~1.291\bin\Hostx86\x64\link.exe /nologo @CMakeFiles\lib1.dir\objects1.rsp /out:lib1.dll /implib:lib1.lib /pdb:C:\Users\derwe\CLionProjects\untitled\cmake-build-debug\lib1.pdb /dll /version:0.0 /machine:x64 /debug /INCREMENTAL kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:CMakeFiles\lib1.dir/intermediate.manifest CMakeFiles\lib1.dir/manifest.res" failed (exit code 1120) with the following output:
   Creating library lib1.lib and object lib1.exp
lib1.cpp.obj : error LNK2019: unresolved external symbol __imp_get_e referenced in function get_pi
lib1.dll : fatal error LNK1120: 1 unresolved externals
Stop.

So can I do this on windows? That is to call a function in main from a dll?

P.S. Ideas of adding a registry function in dll and pass get_e through it is thanks but cannot be considered in my real case.

psionic12
  • 185
  • 1
  • 11

1 Answers1

2

Since you are using late binding for your .dll, you might do the same thing for function defined in the executable. Just call GetProcAddress in the same way, using this process handle (as it is already in the address space). Here is some (pseudo) code:

auto proc = GetModuleHandle(nullptr);
auto get_e = reinterpret_cast<float (*) ()>(GetProcAddress(proc, "get_e"));
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Thanks for helping, it works. You may consider to change to `auto get_e = reinterpret_cast (GetProcAddress(proc, "get_e"));` and it will no longer being a pseudo code... – psionic12 Dec 01 '20 at 16:37
  • A better and safer design would be to have the calling program pass a pointer to `get_e()` to the DLL after loading the DLL. IOW, have the DLL export a function that the program can call to pass in pointers to functions that the DLL is allowed to call when needed. The DLL should not go hunting around the calling program looking for `get_e()` or any other function. – Remy Lebeau Dec 01 '20 at 18:40
  • 1
    @RemyLebeau this explicitly contradicts with asker's statement (" Ideas of adding a registry function in dll and pass get_e through it is thanks but cannot be considered in my real case.") I also do not find my solution "unsafe". It is more cumbersome (in my mind), than passing a pointer to the function, but OP might have their reasons. – SergeyA Dec 01 '20 at 22:47