Ce n'est pas exactement un sujet brûlant, mais j'ai une classe d'usine qui permet à une dll de créer une instance et de la renvoyer sous forme de DLL. C'est ce que je suis venu chercher mais je n'ai pas pu trouver exactement.
Il s'appelle comme,
IHTTP_Server *server = SN::SN_Factory<IHTTP_Server>::CreateObject();
IHTTP_Server *server2 =
SN::SN_Factory<IHTTP_Server>::CreateObject(IHTTP_Server_special_entry);
où IHTTP_Server est l'interface virtuelle pure pour une classe créée soit dans une autre DLL, soit dans la même.
DEFINE_INTERFACE est utilisé pour donner une interface à un identifiant de classe. Placez l'interface à l'intérieur;
Une classe d'interface ressemble à,
class IMyInterface
{
DEFINE_INTERFACE(IMyInterface);
public:
virtual ~IMyInterface() {};
virtual void MyMethod1() = 0;
...
};
Le fichier d'en-tête est comme ça
#if !defined(SN_FACTORY_H_INCLUDED)
#define SN_FACTORY_H_INCLUDED
#pragma once
Les bibliothèques sont répertoriées dans cette définition de macro. Une ligne par bibliothèque / exécutable. Ce serait cool si nous pouvions appeler un autre exécutable.
#define SN_APPLY_LIBRARIES(L, A) \
L(A, sn, "sn.dll") \
L(A, http_server_lib, "http_server_lib.dll") \
L(A, http_server, "")
Ensuite, pour chaque dll / exe, vous définissez une macro et listez ses implémentations. Def signifie qu'il s'agit de l'implémentation par défaut de l'interface. Si ce n'est pas la valeur par défaut, vous donnez un nom à l'interface utilisée pour l'identifier. Ie, special, et le nom sera 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)
Avec les bibliothèques toutes configurées, le fichier d'en-tête utilise les définitions de macro pour définir ce qui est nécessaire.
#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,
Cela crée une énumération pour les bibliothèques.
enum LibraryValues
{
SN_APPLY_LIBRARIES(DEFINE_LIBRARY_ENUM, "")
LastLibrary
};
#define DEFINE_ENTRY_ENUM(I, C, L, D) \
I##_##D##_entry,
Cela crée une énumération pour les implémentations d'interface.
enum EntryValues
{
SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_ENUM)
LastEntry
};
long CallEntryPoint(long id, long interfaceId);
Ceci définit la classe d'usine. Pas grand chose ici.
template <class I>
class SN_Factory
{
public:
SN_Factory()
{
}
static I *CreateObject(long id = I::Id )
{
return (I *)CallEntryPoint(id, I::Id);
}
};
}
#endif
Alors le RPC est,
#include "sn_factory.h"
#include <windows.h>
Créez le point d'entrée externe. Vous pouvez vérifier qu'il existe à l'aide de depend.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;
}
}
}
Les macros configurent toutes les données nécessaires.
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);
}
}
Chaque bibliothèque inclut ce "cpp" avec un stub cpp pour chaque bibliothèque / exécutable. Tout élément d'en-tête compilé spécifique.
#include "sn_pch.h"
Configurez cette bibliothèque.
#define SN_APPLY_CURRENT_LIBRARY(L, A) \
L(A, sn, "sn.dll")
Un include pour le cpp principal. Je suppose que ce cpp pourrait être un .h. Mais il existe différentes manières de procéder. Cette approche a fonctionné pour moi.
#include "../inc/sn_factory.cpp"