GetProcAddress 函数返回 NULL
GetProcAddress function returning NULL
我尝试动态加载 C++ dll,首先我使用 "LoadLibrary" 函数加载了 dll,它正在正确获取其句柄。之后我尝试使用 "GetProcAddress" 获取 DLL 文件函数的函数指针,它返回 NULL。请找到我的 DLL 代码和测试应用程序代码,让我知道代码中哪里出了问题。
dummy2.h
namespace newer
{
class dllclass
{
public:
static __declspec(dllexport) int run(int a,int b);
};
}
dummy2.cpp
#include <iostream>
using namespace std;
#include "dummy2.h"
namespace newer
{
int dllclass::run(int a,int b)
{
return a+b;
}
}
dummy1.cpp
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
typedef int (*Addition)(int,int);
int _tmain(int argc, _TCHAR* argv[])
{
Addition add;
HINSTANCE hDLL;
hDLL = LoadLibrary(TEXT("Dummy2.dll"));
add = (Addition)GetProcAddress(hDLL, "run");
getchar();
return 0;
}
请参考上面的代码并指导我。
事实上,DLL 是在 C 时代引入的。从那时起,C++ 引入了函数名称重载(取决于参数的类型)和称为 "mangled names" 的东西,以允许使用正确的名称链接函数调用. C++ 标准没有指定这个名字应该是什么样子。不同的编译器以不同的方式将参数类型嵌入到名称中。
C++ 理解这个问题,有时需要有可预测的名称。 C++ 中有一个特殊的构造:
extern "C"
{
int run(int, int);
}
当您在 GetProcAddress
中指定函数名称时,它应该与从 DLL 导出的名称完全相同。您可以使用 DependencyWalker.
等特殊实用程序查看这些名称
这是因为名称被破坏了(即函数的名称不是 "run" 而是不同的名称)。
您的代码将适用于(对于我测试过的 MSVC 2013):
add = (Addition)GetProcAddress(hDLL, "?run@dllclass@newer@@SAHHH@Z");
cout << add(1, 2) << endl;
一般来说,如果您想通过插件加载class,最好的办法是使用虚拟接口。一个例子:
//dummy2.h
namespace newer
{
class dllclass_interface
{
public:
virtual int run(int a,int b) = 0;
};
}
extern "C" __declspec(dllexport) newer::dllclass_interface* getDllClass();
//dummy2.cpp
#include <iostream>
using namespace std;
#include "dummy2.h"
namespace newer
{
class dllclass: public dllclass_interface
{
public:
virtual int run(int a,int b);
};
int dllclass::run(int a,int b)
{
return a+b;
}
}
extern "C" newer::dllclass_interface* getDllClass()
{
static newer::dllclass instance;
return &instance;
}
typedef newer::dllclass_interface* (*GetClassFunc)();
GetClassFunc getClassFunc = (GetClassFunc)GetProcAddress(hDLL, "getDllClass");
newer::dllclass_interface* dllClass = getClassFunc();
cout << dllClass->run(a, b) << endl;
我尝试动态加载 C++ dll,首先我使用 "LoadLibrary" 函数加载了 dll,它正在正确获取其句柄。之后我尝试使用 "GetProcAddress" 获取 DLL 文件函数的函数指针,它返回 NULL。请找到我的 DLL 代码和测试应用程序代码,让我知道代码中哪里出了问题。
dummy2.h
namespace newer
{
class dllclass
{
public:
static __declspec(dllexport) int run(int a,int b);
};
}
dummy2.cpp
#include <iostream>
using namespace std;
#include "dummy2.h"
namespace newer
{
int dllclass::run(int a,int b)
{
return a+b;
}
}
dummy1.cpp
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
typedef int (*Addition)(int,int);
int _tmain(int argc, _TCHAR* argv[])
{
Addition add;
HINSTANCE hDLL;
hDLL = LoadLibrary(TEXT("Dummy2.dll"));
add = (Addition)GetProcAddress(hDLL, "run");
getchar();
return 0;
}
请参考上面的代码并指导我。
事实上,DLL 是在 C 时代引入的。从那时起,C++ 引入了函数名称重载(取决于参数的类型)和称为 "mangled names" 的东西,以允许使用正确的名称链接函数调用. C++ 标准没有指定这个名字应该是什么样子。不同的编译器以不同的方式将参数类型嵌入到名称中。
C++ 理解这个问题,有时需要有可预测的名称。 C++ 中有一个特殊的构造:
extern "C"
{
int run(int, int);
}
当您在 GetProcAddress
中指定函数名称时,它应该与从 DLL 导出的名称完全相同。您可以使用 DependencyWalker.
这是因为名称被破坏了(即函数的名称不是 "run" 而是不同的名称)。
您的代码将适用于(对于我测试过的 MSVC 2013):
add = (Addition)GetProcAddress(hDLL, "?run@dllclass@newer@@SAHHH@Z");
cout << add(1, 2) << endl;
一般来说,如果您想通过插件加载class,最好的办法是使用虚拟接口。一个例子:
//dummy2.h
namespace newer
{
class dllclass_interface
{
public:
virtual int run(int a,int b) = 0;
};
}
extern "C" __declspec(dllexport) newer::dllclass_interface* getDllClass();
//dummy2.cpp
#include <iostream>
using namespace std;
#include "dummy2.h"
namespace newer
{
class dllclass: public dllclass_interface
{
public:
virtual int run(int a,int b);
};
int dllclass::run(int a,int b)
{
return a+b;
}
}
extern "C" newer::dllclass_interface* getDllClass()
{
static newer::dllclass instance;
return &instance;
}
typedef newer::dllclass_interface* (*GetClassFunc)();
GetClassFunc getClassFunc = (GetClassFunc)GetProcAddress(hDLL, "getDllClass");
newer::dllclass_interface* dllClass = getClassFunc();
cout << dllClass->run(a, b) << endl;