Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
407 views
in Technique[技术] by (71.8m points)

c++ - DLL call with __stdcall & GetProcAddress() in VS2013

I'm trying to call a function from my own DLL, but depending on the calling convention in the DLL project either I can't find the ProcAddress or my stack is getting corrupted. It works perfectly for 3rd Party DLLs so I would like to not change anything in the loading code itself if there is no major problem there. A minimal example:

#include <windows.h>
#include <cstdlib>
#include <iostream>

typedef long (__stdcall* tMyFunction)(int);

int main(int argc, char* argv[]){
  HINSTANCE m_dllHandle = LoadLibrary("MyDll.dll");
  if (m_dllHandle != NULL){
    tMyFunction function = (tMyFunction)GetProcAddress(m_dllHandle, "myFunction");
    if (function != NULL){
      long value = function(1);
      std::cout << value << std::endl;
    }else{
      std::cout << "GetProcAddress() failed" << std::endl;
    }

    FreeLibrary(m_dllHandle);
    m_dllHandle = NULL;
  }else{
    std::cout << "LoadLibrary() failed" << std::endl;
  }
  system("pause");
  return EXIT_SUCCESS;
}

In the DLL:

extern "C" __declspec(dllexport) long __stdcall myFunction(int a){
  return 10;
}

Result: GetProcAddress() fails

dumpbin /EXPORTS -> _myFunction@4 = _myFunction@4

extern "C" __declspec(dllexport) long __cdecl myFunction(int a){
  return 10;
}

Result: "Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention." (Because I use __stdcall in loading code and __cdecl in DLL).

dumpbin /EXPORTS -> _myFunction = _myFunction

In 3rd party DLLs, I can see, that "dumpbin /EXPORTS" only shows myFunction (no underscores, no @4) What can I do to accomplish the same and still be able to load it with the above defined type (typedef long (__stdcall* tMyFunction)(int);)? My compiler is "Visual Studio 2013".

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First, the calling convention used by the DLL function must match the function pointer definition you are using. Since it didn't match, you get the error that you've corrupted the stack.


Second, When you use GetProcAddress the function name you use in the call to GetProcAddress must match exactly the exported DLL function name. It has to match not only on characters, but casing also i.e. myFunction is not the same as MyFunction.

The exported name in your example is _myFunction@4. which means that accessing the function using GetProcAddress would be:

GetProcAddress(myModuleHandle, "_myFunction@4");

There is no getting around having to specify the name this way, since that is the name of the function.

So you have two options:

  1. Change the code as described above, that is, to use the actual name or
  2. Change the DLL so that the exported name is actually myFunction

Since we covered the first option, for the second option, you have to rebuild the DLL to use a Module Definition File (or simply known as a .DEF file) to redefine the name.

Here is a link to what a module definition file is:

Module Definition File

So a typical .DEF file would contain this:

LIBRARY MyDLL

EXPORTS
    myFunction  @2   

The @2 is the ordinal number. For your purposes, it is not important what this number is since there is only one function. I chose @2, but you can choose any number (@3, @4, or even @1000 if you desired). However, if there is more than 1 exported function, the ordinal numbers should be unique, i.e., you can't have two exported functions that have the same ordinal number.

If you save the above contents to a MyDll.DEF and added it to the project that builds the DLL (not the project that will use the DLL), you will then need to rebuild the DLL. Once that's done, the DLL will now have an exported name of myFunction without the @4 decoration and without the underscore.

(Note: As mentioned by the comment above, the extern "C" used does not turn off the decoration that Windows uses (the additional underscore and the @x appended to the name). All extern "C" does is turn off the C++ name mangling. To turn off the Windows name mangling, that requires the .DEF file.)

P.S. I use a tool called Dependency Walker to easily determine what the exported function names are in a DLL. Since Dependency Walker is a GUI app, the output is a little friendlier than dumpbin.exe

Dependency Walker

Just to add, you mention that the DLL works flawlessly in other applications. If those other applications use import libraries instead of using LoadLibrary and GetProcAddress to access the function, then those import libraries automatically handle the translation of myFunction to _myFunction@4.

That's why it works without issues for these types of applications. However, when you take the route of LoadLibrary and GetProcAddress, you are not afforded this "help" in getting the name translated, and you're basically on your own.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...