Dies ist nicht gerade ein heißes Thema, aber ich habe eine Factory-Klasse, mit der eine DLL eine Instanz erstellen und als DLL zurückgeben kann. Es ist das, wonach ich gesucht habe, aber nicht genau finden konnte.
Es heißt wie,
IHTTP_Server *server = SN::SN_Factory<IHTTP_Server>::CreateObject();
IHTTP_Server *server2 =
SN::SN_Factory<IHTTP_Server>::CreateObject(IHTTP_Server_special_entry);
Dabei ist IHTTP_Server die reine virtuelle Schnittstelle für eine Klasse, die entweder in einer anderen oder derselben DLL erstellt wurde.
DEFINE_INTERFACE wird verwendet, um einer Klassen-ID eine Schnittstelle zu geben. Platzieren Sie innerhalb der Schnittstelle;
Eine Schnittstellenklasse sieht aus wie:
class IMyInterface
{
DEFINE_INTERFACE(IMyInterface);
public:
virtual ~IMyInterface() {};
virtual void MyMethod1() = 0;
...
};
Die Header-Datei ist wie folgt
#if !defined(SN_FACTORY_H_INCLUDED)
#define SN_FACTORY_H_INCLUDED
#pragma once
Die Bibliotheken sind in dieser Makrodefinition aufgeführt. Eine Zeile pro Bibliothek / ausführbarer Datei. Es wäre cool, wenn wir eine andere ausführbare Datei aufrufen könnten.
#define SN_APPLY_LIBRARIES(L, A) \
L(A, sn, "sn.dll") \
L(A, http_server_lib, "http_server_lib.dll") \
L(A, http_server, "")
Dann definieren Sie für jede DLL / Exe ein Makro und listen seine Implementierungen auf. Def bedeutet, dass dies die Standardimplementierung für die Schnittstelle ist. Wenn dies nicht die Standardeinstellung ist, geben Sie einen Namen für die Schnittstelle an, mit der sie identifiziert wird. Dh, special, und der Name lautet IHTTP_Server_special_entry.
#define SN_APPLY_ENTRYPOINTS_sn(M) \
M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, def) \
M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, special)
#define SN_APPLY_ENTRYPOINTS_http_server_lib(M) \
M(IHTTP_Server, HTTP::server::server, http_server_lib, def)
#define SN_APPLY_ENTRYPOINTS_http_server(M)
Wenn alle Bibliotheken eingerichtet sind, verwendet die Header-Datei die Makrodefinitionen, um die erforderlichen zu definieren.
#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,
Dadurch wird eine Aufzählung für die Bibliotheken erstellt.
enum LibraryValues
{
SN_APPLY_LIBRARIES(DEFINE_LIBRARY_ENUM, "")
LastLibrary
};
#define DEFINE_ENTRY_ENUM(I, C, L, D) \
I##_##D##_entry,
Dadurch wird eine Aufzählung für Schnittstellenimplementierungen erstellt.
enum EntryValues
{
SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_ENUM)
LastEntry
};
long CallEntryPoint(long id, long interfaceId);
Dies definiert die Factory-Klasse. Hier ist nicht viel dran.
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
Dann ist der CPP,
#include "sn_factory.h"
#include <windows.h>
Erstellen Sie den externen Einstiegspunkt. Sie können überprüfen, ob es existiert, mit abhängige.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;
}
}
}
Die Makros richten alle benötigten Daten ein.
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();
// assert(defaultEntryArray[id] == interfaceId, "Request to create an object for the wrong interface.")
enum SN::LibraryValues l = libraryForEntryPointArray[id];
f_entrypoint f = libraryFunctionArray[l];
if (!f)
{
HINSTANCE hGetProcIDDLL = LoadLibraryA(libraryPathArray[l]);
if (!hGetProcIDDLL) {
return NULL;
}
// resolve function address here
f = (f_entrypoint)GetProcAddress(hGetProcIDDLL, "entrypoint");
if (!f) {
return NULL;
}
libraryFunctionArray[l] = f;
}
return f(id);
}
}
Jede Bibliothek enthält diese "cpp" mit einem Stub cpp für jede Bibliothek / ausführbare Datei. Alle spezifischen kompilierten Header-Inhalte.
#include "sn_pch.h"
Richten Sie diese Bibliothek ein.
#define SN_APPLY_CURRENT_LIBRARY(L, A) \
L(A, sn, "sn.dll")
Ein Include für den Haupt-CPP. Ich denke, dieser CPP könnte ein .h sein. Es gibt jedoch verschiedene Möglichkeiten, dies zu tun. Dieser Ansatz hat bei mir funktioniert.
#include "../inc/sn_factory.cpp"