【问题标题】:Creating a DLL in C++ to import C++ DLL in VS 2005在 C++ 中创建 DLL 以在 VS 2005 中导入 C++ DLL
【发布时间】:2015-02-16 18:24:23
【问题描述】:

我正在尝试将 C++ DLL 链接到我将创建的新 C++ DLL,

我已经逐步按照下面的教程和其他许多教程进行操作,但是“GetProcAddress”函数返回 NULL “http://www.dreamincode.net/forums/topic/118076-dlls-explicit-linking/”出了点问题

这是我尝试从 DLL 调用的函数的原型:

int RemoveAllDataFile(unsigned int id);

函数返回 1,因此 DLL 加载成功。

typedef int (*funcRemoveAllDataFile) (int);

int load_dll_ARbnet(int x)
{
    /* Retrieve DLL handle.*/
    HINSTANCE hDLL = LoadLibrary("ArbNet2Remote.dll");   
    if (hDLL == NULL)
    {
        return 0;
    }    
    else
    {
    }
    /*Get the function address*/
    funcRemoveAllDataFile RemoveAllDataFile = (funcRemoveAllDataFile)GetProcAddress(hDLL, "RemoveAllDataFile");
    if (RemoveAllDataFile)
    {
        return 2;
    }
    else
    {
        return 1;
    }

}

【问题讨论】:

  • 查找“C++ 名称修改”

标签: c++ dll dllimport getprocaddress


【解决方案1】:

如果使用 C++ 源代码,您从其他 DLL 导出的函数应声明为 extern "C"。如果应该使用.def 文件或使用__declspec(dllexport) 导出:

这是一个典型的 DLL 头文件,它适用于 .c.cpp 以及两种调用约定:

#ifdef MYAPI_EXPORTS
#define MYAPI __declspec(dllexport)
#else
#define MYAPI __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif

MYAPI int __cdecl   func1(int x);
MYAPI int __stdcall func2(int x);

#ifdef __cplusplus
}
#endif

DLL 源代码:

#define MYAPI_EXPORTS
#include "x.h"
int func1(int x)
{
    return x*2;
}

int __stdcall func2(int x)
{
    return x*3;
}

及用法:

#include <windows.h>
#include <stdio.h>

typedef int (__cdecl   *FUNC1)(int);
typedef int (__stdcall *FUNC2)(int);
int main()
{

    HINSTANCE hDLL = LoadLibrary("x");
    FUNC1 func1 = (FUNC1)GetProcAddress(hDLL, "func1");
#ifdef _WIN64
    FUNC2 func2 = (FUNC2)GetProcAddress(hDLL, "func2");
#else
    FUNC2 func2 = (FUNC2)GetProcAddress(hDLL, "_func2@4");
#endif
    printf("%d %d\n",func1(5),func2(5));
}

可以使用dumpbin /exports &lt;dll&gt; 发现名称装饰。请注意,x64 和 x86 不同。以下是针对 x86 的:

   ordinal hint RVA      name

         2    0 00001010 _func2@4
         1    1 00001000 func1

【讨论】:

  • extern "C" 将删除 C++ 名称修改,但不会删除前导 _,您必须为此使用 .def 文件。因此,您需要同时使用extern "C"__declspec(dllexport).def 才能导出不带任何修饰的__cdecl 函数。
  • __cdecl 调用约定(此代码正在使用,因为它是大多数 C/C++ 编译器的默认调用约定,除非另有配置)在使用 extern "C" 时仍输出前导下划线本身(或源代码编译为 C 而不是 C++)。需要 .def 文件来创建不带下划线的导出别名。
  • 尽管如此,上述代码在x86x64 VS2012 版本上都适用于.c.cpp 文件。 dumpbin 显示未修饰的名称。 __stdcall 显示 _funcRemoveAllDataFile@4
  • 我刚刚阅读了MSDN documentation for _cdecl:“下划线字符 (_) 是名称的前缀,除非导出使用 C 链接的 __cdecl 函数。”我不使用 VC++,这种行为与我使用的其他 C++ 编译器不同,后者会在 C 链接函数上导出下划线。
  • 是的,Microsoft 喜欢与众不同 :^) 即​​使不使用下划线,也可以在没有 .def 的情况下使用 GetProcAddress(hDll,"_funcRemoveAllDataFile")。在.obj 文件中,名称确实 有一个前导下划线。这正是微软在 DLL 名称表中导出它的方式。
【解决方案2】:

查看 DLL 的实际导出,例如使用 TDUMP 等报告工具或类似工具。您正在寻找的功能没有像您期望的那样被导出为"RemoveAllDataFile"。它实际上是像 "_RemoveAllDataFile" 那样导出的,甚至像 "_RemoveAllDataFile@4" 这样的东西。

如果您正在编译原始 DLL,并希望将函数导出为 "RemoveAllDataFile",则必须使用 extern "C" 包装导出函数的声明,以从导出的名称中删除任何 C++ 名称修改。根据您的 C++ 编译器,您可能还需要使用 .def 文件来删除由 __cdecl 调用约定强加的前导下划线。使用 C 链接时,一些 C++ 编译器导出 __cdecl 函数时带有前导下划线(例如 Borland),而有些则不导出(例如 Microsoft)。

但如果您不重新编译原始 DLL,那么您别无选择,只能查看 DLL 的导出并更改您的 GetProcAddress() 调用以使用实际导出的正确名称。

【讨论】:

  • 现在工作正常,dll 将函数导出为“?RemoveAllDataFile@@YAHI@Z”而不是 RemoveAllDataFile。为了得到我使用virustotal.com的真实出口,真实姓名列在“文件详细信息”下的“PE出口”下,只是一个快速的在线解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-27
  • 2016-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多