DLL调用VS2013中的__stdcall和GetProcAddress()

Con*_*tin 1 c++ dll visual-studio-2013

我试图从我自己的DLL调用一个函数,但根据DLL项目中的调用约定,我找不到ProcAddress或我的堆栈已损坏.它适用于第三方DLL,所以如果没有重大问题,我想在加载代码本身中不做任何改变.一个最小的例子:

#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;
}
Run Code Online (Sandbox Code Playgroud)

在DLL中:

extern "C" __declspec(dllexport) long __stdcall myFunction(int a){
  return 10;
}
Run Code Online (Sandbox Code Playgroud)

结果:GetProcAddress()失败

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

extern "C" __declspec(dllexport) long __cdecl myFunction(int a){
  return 10;
}
Run Code Online (Sandbox Code Playgroud)

结果:"运行时检查失败#0 - ESP的值未在函数调用中正确保存.这通常是调用使用一个调用约定声明的函数,其中函数指针使用不同的调用约定声明." (因为我在加载代码中使用__stdcall而在DLL中使用__cdecl).

dumpbin/EXPORTS - > _myFunction = _myFunction

在第三方DLL中,我可以看到,"dumpbin/EXPORTS"只显示myFunction(没有下划线,没有@ 4)我可以做些什么来完成相同的操作并且仍然能够使用上面定义的类型(typedef long (__stdcall* tMyFunction)(int);)加载它?我的编译器是"Visual Studio 2013"​​.

Pau*_*zie 8

首先,DLL函数使用的调用约定必须与您正在使用的函数指针定义匹配.由于它不匹配,您会收到已损坏堆栈的错误.


其次,当您使用GetProcAddress在调用中使用的函数名称时,GetProcAddress必须与导出的DLL函数名称完全匹配.它不仅要匹配字符,还要匹配,即myFunction不一样MyFunction.

您的示例中的导出名称是_myFunction@4.这意味着使用函数访问GetProcAddress将是:

GetProcAddress(myModuleHandle, "_myFunction@4");
Run Code Online (Sandbox Code Playgroud)

没有必须以这种方式指定名称,因为这函数的名称.

所以你有两个选择:

  1. 如上所述更改代码,即使用实际名称或
  2. 更改DLL,以便实际导出名称 myFunction

由于我们介绍了第一个选项,因此对于第二个选项,您必须重建DLL以使用Module Definition File(或简称为.DEF文件)来重新定义名称.

以下是模块定义文件的链接:

模块定义文件

因此,典型的.DEF文件将包含以下内容:

LIBRARY MyDLL

EXPORTS
    myFunction  @2   
Run Code Online (Sandbox Code Playgroud)

@2ordinal number.出于您的目的,这个数字并不重要,因为只有一个功能.我选择@2,但是你可以选择任何号码(@3,@4,甚至@1000如果你需要的话).但是,如果有多个导出函数,则序数应该是唯一的,即,您不能有两个具有相同序号的导出函数.

如果将上述内容保存到a MyDll.DEF并将其添加到构建DLL的项目(而不是将使用DLL的项目),则需要重建DLL.一旦完成,DLL现在将具有myFunction没有@4装饰且没有下划线的导出名称.

(注:由于评论如上所述,extern "C"使用不关闭Windows把装饰(附加下划线和@x附加到该名称)所有.extern "C"确实是关闭C++名称重整要关闭视窗名字粉碎. ,这需要.DEF文件.)

PS我使用一个工具Dependency Walker来轻松确定DLL中导出的函数名称.由于Dependency Walker是一个GUI应用程序,输出比它更友好dumpbin.exe

依赖沃克

只是补充一点,你提到DLL在其他应用程序中完美运行.如果那些其他应用程序使用import libraries而不是使用LoadLibraryGetProcAddress访问该函数,那么这些导入库会自动处理myFunctionto 的转换_myFunction@4.

这就是为什么它适用于这些类型的应用程序没有问题.然而,当你走的路线LoadLibraryGetProcAddress,你是不是在提供获取名称翻译这种"帮助",你基本上是你自己的.