我正在尝试使用以下标头加载一个假设的插件:
#ifndef _DLL_H_
#define _DLL_H_
#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
class DLLIMPORT DllClass
{
public:
virtual ~DllClass(void);
static DllClass* getPCFilter();
virtual int Process(int a, int b);
protected:
DllClass();
};
#endif /* _DLL_H_ */
我的主机代码确实:
HINSTANCE hinstDLL;
hinstDLL = LoadLibrary(L"PCFilter.dll");
if(hinstDLL)
{
typedef DllClass*(*Factory)();
Factory fun1;
fun1 = (Factory)GetProcAddress(hinstDLL, "DllClass::getPCFilter");
dll打开但GetProcAddress没有找到静态工厂方法。我不应该这样做吗?
我已经尝试去掉静态方法,而是在类声明之后,执行以下操作:
extern "C" DLLIMPORT void* getPCFilterInstance()
{
return (void*)new DllClass();
}
但是,在编译主机源时,链接器会抱怨:
In function `getPCFilterInstance'::
[Linker Error] undefined reference to `_imp___ZN8DllClassC1Ev'
这可以通过链接到.a lib来解决。但是不是DLL的想法在编译时不需要链接吗?
答案 0 :(得分:5)
您忽略了函数名称将被装饰(“名称重整”)或在DLL接口中(在给定名称下)完全不可见的事实。据我所知GetProcAddress
没有做任何名称解码,但是链接器会这样做(当静态导入带有导出类的DLL时)。
据我所知,最佳做法是提供一个具有未修饰名称和预定义调用约定的工厂函数,就像COM规定它一样(参见DllGetClassObject
)。
注意:我应该补充说,根据使用的编译器/链接器,装饰(“受损”)名称会有所不同。不同的编译器,不同的规则。因此,如果没有相当的修补,它们甚至可能在彼此之间不相容。
关于这个问题的编辑:
但是不是DLL的想法不需要在编译时链接?
嗯,是的,不是。在Windows上,整个过程与单独使用系统中的过程略有不同。加载器将负责解决依赖关系等问题,但重点是有两种加载DLL的方法。一种是通过静态导入DLL,在这种情况下,名称解析在程序运行之前完成(或在此之前失败),另一种是通过LoadLibrary
和朋友动态加载DLL,然后解决函数地址GetProcAddress
。后者有一个变体(由链接器支持)称为延迟加载。最终它仍然只是第二种方法。
延迟加载可能会提供您想要的内容,方法是让链接器关注要解析的名称,并且仍然能够处理加载DLL或在运行时解析名称的失败
答案 1 :(得分:1)
您可以使用extern "C"
语句导出您的函数,其名称与您在代码中声明的名称相同 - 否则它将被c ++编译器破坏