DLL调用__stdcall& VS2013中的GetProcAddress()

时间:2015-07-08 02:47:52

标签: 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;
}

在DLL中:

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

结果:GetProcAddress()失败

dumpbin / EXPORTS - &gt; _myFunction @ 4 = _myFunction @ 4

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

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

dumpbin / EXPORTS - &gt; _myFunction = _myFunction

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

1 个答案:

答案 0 :(得分:8)

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

其次,使用GetProcAddress时,在GetProcAddress调用中使用的函数名称必须与完全导出的DLL函数名称匹配。它不仅要匹配字符,还要匹配外壳,即myFunctionMyFunction不一样。

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

GetProcAddress(myModuleHandle, "_myFunction@4");

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

所以你有两个选择:

  1. 如上所述更改代码,即使用实际名称或
  2. 更改DLL,使导出的名称实际为myFunction
  3. 由于我们介绍了第一个选项,对于第二个选项,您必须重建DLL以使用Module Definition File(或简称为.DEF文件)来重新定义名称。

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

    Module Definition File

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

    LIBRARY MyDLL
    
    EXPORTS
        myFunction  @2   
    

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

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

    (注意:正如上面的评论中提到的,extern "C"使用了而不是关闭Windows使用的装饰(附加的下划线和@x附加到所有extern "C"都关闭了C ++名称修改。要关闭Windows名称修改,需要.DEF文件。)

    P.S。我使用一个名为Dependency Walker的工具来轻松确定DLL中导出的函数名称。由于Dependency Walker是一个GUI应用程序,因此输出比dumpbin.exe

    更友好一些

    Dependency Walker

    添加,您提到DLL在其他应用程序中完美运行。如果其他应用程序使用import libraries而不是使用LoadLibraryGetProcAddress来访问该函数,那么这些导入库会自动处理myFunction_myFunction@4的转换。

    这就是为什么它适用于这些类型的应用程序没有问题。但是,当您选择LoadLibraryGetProcAddress的路线时,您无法获得翻译名称的“帮助”,而且您基本上是靠自己。