class A
{
private:
int m_nNum;
public:
A();
A(int n);
virtual ~A();
virtual void SetNum(int n);
virtual int GetNum();
};
Als nächstes muß das Objekt in der DLL erzeugt werden. Da ein explizites Linken erfolgen soll, muß in der DLL eine globale Funktion enthalten sein, die eine Instanz des Objekts über den Operator new() erzeugen kann, und die explizit gelinkt werden kann. Erst durch den Umweg über diese globale Funktion kann auf das Objekt zugegriffen werden.
Da die Klasse zwei Konstruktoren kennt, können zwei Funktionen CreateObjectofA() und CreateObjectofA1(int) implementiert und exportiert werden.
extern "C" __declspec(dllexport) A* CreateObjectofA1()
{
return new A();
}
Wichtig ist dabei, daß die Instanz des Objekts durch den Operator new erzeugt wird, damit die Clientanwendung später das Objekt sicher wieder über den Operator delete freigeben kann. Auf die erzeugten Objekte kann dann wie folgt zugegriffen werden:
typedef A* (*PFNCreateA1)();
PFNCreateA1 pfnCreateA1 = (PFNCreateA1)GetProcAddress(
hMod, TEXT("CreateObjectofA1"));
A* a = (pfnCreateA1)();
// Objektzugriffe aller Art
delete a;
Diese Vorgehensweise ist sehr hilfreich, wenn der Anwender in die Lage versetzt werden soll, PlugIns zu integrieren. Der Nachteil dieser Methode besteht darin, daß der Speicher für die Klasse immer in der DLL allokiert werden muß, so daß insbesondere der Client keine Möglichkeit hat, Speicher auf anderem Wege zu allokieren.
Direkte Aufrufe via GetProcAddress()
Die nächste Vorgehensweise basiert auf dem direkten Adressieren der Funktionen durch GetProcAddress() und dem folgendem Aufruf über Zeiger. Der Trick dabei ist, den von GetProcAddress() returnierten FARPROC-Zeiger in einen C++-Zeiger auf Memberfunktionen zu konvertieren. Dies kann glücklicherweise aufgrund des C++-Features von Templates und Unions einfach erfolgen. Dazu muß lediglich eine Funktion wie das nachfolgende Template definiert werden.
Dest force_cast(Src src)
{
union
{
Dest d;
Src s;
} convertor;
convertor.s = Src;
return convertor.d;
}
Diese Funktion erlaubt das Konvertieren beliebiger Typen und ist mächtiger als reinterpret_cast. Wird zum Beispiel ein Zeiger definiert als
typedef void (A::*PSetNum)(int);
kann ein Zeiger fp vom Typ FARPROC auf PSetNum konvertiert werden, indem er einfach verwendet wird.
FARPROC fp;
PSetNum psn = force_cast(fp);
Diese Lösung ist mit reinterpret_cast oder einem C-Style Cast nicht möglich. Nachdem dieser Weg zum Konvertieren eines FARPROC-Zeigers gefunden ist, kann man daran gehen, die Memberfunktionen der C++-Klassen mit benutzerfreundlichen Namen zu exportieren. Dies kann über DEF-Dateien erfolgen.
Der erste Schritt besteht darin, die dekorierten Namen der einzelnen Funktionen, die exportiert werden sollen, zu finden. Möglich wird dies wahlweise über eine MAP-Datei oder durch Sichten des Assembler-Listings. Sind die Namen erst einmal bekannt, können sie durch benutzerfreundliche Namen gemäß der nachfolgenden Syntax in der DEF-Datei ersetzt werden.
|