这不是一个热门话题,但我有一个工厂类,允许一个dll创建一个实例并将其作为DLL返回。这正是我寻找的,但无法完全找到。
它被称为:
IHTTP_Server *server = SN::SN_Factory<IHTTP_Server>::CreateObject()
IHTTP_Server *server2 =
SN::SN_Factory<IHTTP_Server>::CreateObject(IHTTP_Server_special_entry)
其中,IHTTP_Server是一个纯虚接口类,可以在另一个DLL或同一DLL中创建。
DEFINE_INTERFACE用于为类ID指定一个接口。将其放置在interface内部;
接口类的样式如下:
class IMyInterface
{
DEFINE_INTERFACE(IMyInterface);
public:
virtual ~IMyInterface() {};
virtual void MyMethod1() = 0;
...
};
头文件就像这样
这个宏定义列出了库的清单。每个库/可执行文件一行。如果我们能够调用另一个可执行文件,那将非常棒。
L(A, sn, "sn.dll") \
L(A, http_server_lib, "http_server_lib.dll") \
L(A, http_server, "")
然后对于每个dll/exe,您需要定义一个宏并列出其实现。Def表示它是接口的默认实现。如果不是默认值,则为其提供用于标识的接口名称。例如,special,名称将为IHTTP_Server_special_entry。
M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, def) \
M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, special)
M(IHTTP_Server, HTTP::server::server, http_server_lib, def)
使用库文件全部设置完成后,头文件使用宏定义来定义必要内容。
#define APPLY_ENTRY(A, N, L) \
SN_APPLY_ENTRYPOINTS_##N(A)
#define DEFINE_INTERFACE(I) \
public: \
static const long Id = SN::I##_def_entry; \
private:
namespace SN
{
#define DEFINE_LIBRARY_ENUM(A, N, L) \
N##_library,
这创建了一个库的枚举类型。
enum LibraryValues
{
SN_APPLY_LIBRARIES(DEFINE_LIBRARY_ENUM, "")
LastLibrary
};
I
这将为接口实现创建一个枚举。
enum EntryValues
{
SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_ENUM)
LastEntry
};
long CallEntryPoint(long id, long interfaceId);
这定义了工厂类。在这里没有太多东西。
template <class I>
class SN_Factory
{
public:
SN_Factory()
{
}
static I *CreateObject(long id = I::Id )
{
return (I *)CallEntryPoint(id, I::Id);
}
};
}
#endif //SN_FACTORY_H_INCLUDED
然后 CPP 是,
创建外部入口点。您可以使用depends.exe检查其是否存在。
extern "C"
{
__declspec(dllexport) long entrypoint(long id)
{
#define CREATE_OBJECT(I, C, L, D) \
case SN::I##_##D##_entry: return (int) new C();
switch (id)
{
SN_APPLY_CURRENT_LIBRARY(APPLY_ENTRY, CREATE_OBJECT)
case -1:
default:
return 0;
}
}
}
宏设置了所需的所有数据。
namespace SN
{
bool loaded = false;
char * libraryPathArray[SN::LastLibrary];
#define DEFINE_LIBRARY_PATH(A, N, L) \
libraryPathArray[N##_library] = L;
static void LoadLibraryPaths()
{
SN_APPLY_LIBRARIES(DEFINE_LIBRARY_PATH, "")
}
typedef long(*f_entrypoint)(long id);
f_entrypoint libraryFunctionArray[LastLibrary - 1];
void InitlibraryFunctionArray()
{
for (long j = 0; j < LastLibrary; j++)
{
libraryFunctionArray[j] = 0;
}
#define DEFAULT_LIBRARY_ENTRY(A, N, L) \
libraryFunctionArray[N##_library] = &entrypoint;
SN_APPLY_CURRENT_LIBRARY(DEFAULT_LIBRARY_ENTRY, "")
}
enum SN::LibraryValues libraryForEntryPointArray[SN::LastEntry];
#define DEFINE_ENTRY_POINT_LIBRARY(I, C, L, D) \
libraryForEntryPointArray[I##_##D##_entry] = L##_library;
void LoadLibraryForEntryPointArray()
{
SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_POINT_LIBRARY)
}
enum SN::EntryValues defaultEntryArray[SN::LastEntry];
#define DEFINE_ENTRY_DEFAULT(I, C, L, D) \
defaultEntryArray[I##_##D##_entry] = I##_def_entry;
void LoadDefaultEntries()
{
SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_DEFAULT)
}
void Initialize()
{
if (!loaded)
{
loaded = true;
LoadLibraryPaths();
InitlibraryFunctionArray();
LoadLibraryForEntryPointArray();
LoadDefaultEntries();
}
}
long CallEntryPoint(long id, long interfaceId)
{
Initialize();
enum SN::LibraryValues l = libraryForEntryPointArray[id];
f_entrypoint f = libraryFunctionArray[l];
if (!f)
{
HINSTANCE hGetProcIDDLL = LoadLibraryA(libraryPathArray[l]);
if (!hGetProcIDDLL) {
return NULL;
}
f = (f_entrypoint)GetProcAddress(hGetProcIDDLL, "entrypoint");
if (!f) {
return NULL;
}
libraryFunctionArray[l] = f;
}
return f(id);
}
}
每个库都包括一个名为“cpp”的存根cpp文件,用于每个库/可执行文件。任何特定的编译头文件内容。
设置这个库。
L(A, sn, "sn.dll")
一个用于主cpp的包含文件。我猜这个cpp可能是.h文件。但你可以有不同的方法来做这件事。这种方法适合我。
f_funci
实际上是一种类型(而不是拥有类型)。类型f_funci
表示“指向返回int
且不带参数的函数的指针”。有关C语言中函数指针的更多信息,请访问http://www.newty.de/fpt/index.html。 - Niklas B.typedef int (_stdcall*f_funci)();
- badermanFreeLibrary()
。 - Lundin__stdcall
?在Windows 10上,使用VS2013会导致名称混淆,从而防止GetProcAddress()
找到函数。对我来说,__cdecl
可以工作。此外,在加载dll时链接所有dll函数并不是不可能的(但很可能是不可取的)。我不知道Windows上的等效方法,但在Linux上,可以通过向dlopen()
传递RTLD_NOW
来完成此操作。这是一种不寻常的动态加载方式,但基本上就是动态链接所发生的事情。 - Praxeolitic