2016-05-17 11 views
0

Betrachten Sie die Mikrokern-Softwarearchitektur für eine Anwendung. Ich habe einen Kernel und eine Komponente.Reverse Dynamic Linking Funktionsaufruf

Die Komponente ist eine DLL, die während der Laufzeit vom Kernel geladen wird, unter Verwendung der LoadLibrary API in Windows; und natürlich können exportierte Funktionen unter Verwendung von GetProcAddress aufgerufen werden.

Jetzt muss die Komponente Nachrichten an den Kernel senden. Mit anderen Worten, die Komponente, die jetzt eine geladene DLL ist, muss Funktionen vom Kernel aufrufen. Was ist der richtige Mechanismus?

+0

Wenn du "Kernel" sagst, was meinst du damit? Das Wort "Kernel" wird in der Computerprogrammierung mehrfach verwendet und es ist nicht klar, welche der Bedeutungen hier relevant ist. Sie sollten auch [lesen Sie, wie Sie gute Fragen stellen] (http://stackoverflow.com/help/how-to-ask) und lernen, wie Sie ein [minimales, vollständiges und verifizierbares Beispiel] erstellen (http: // stackoverflow.com/help/mcve). –

+0

@JoachimPileborg Ändert sich die Antwort? Betrachten Sie die Mikrokern-Architektur. Der Kernel bildet das Herz der Anwendung. –

+0

Sie schreiben also Ihren eigenen * Betriebssystem * Kernel? Nicht z.B. ein CUDA-Kernel? Bitte aktualisieren Sie Ihre Tags, um dies zu berücksichtigen, oder schreiben Sie sie zumindest im Fragenhauptteil aus. –

Antwort

3

Es sollte arbeiten hier sehen: https://stackoverflow.com/a/30475042/1274747

Für die MSVC, Sie im Grunde die __declspec(dllexport) in der EXE verwenden würde. Der Compiler/Linker erzeugt die Importbibliothek für die .exe, die dann mit der DLL verknüpft werden kann, die DLL wird dann die Symbole von .exe verwenden.

Eine andere Möglichkeit ist, dies durch die "dependency inversion" zu lösen - die EXE exportiert die Symbole nicht, sondern stellt eine (rein virtuelle) Schnittstelle zur Verfügung, die innerhalb der .exe implementiert und übergeben wird (über Referenz oder Zeiger auf die Schnittstelle) in die DLL nach dem Laden. Die DLL kann Methoden auf der Schnittstelle aufrufen, die in der EXE enthalten sind. Aber in der Tat, wenn Sie von einem Mikrokernel sprechen, hängt es davon ab, ob der virtuelle Aufruf-Overhead für Sie akzeptabel ist (obwohl beim Exportieren einer Funktion aus der .exe die Methode AFAIK auch per Funktionszeiger aufgerufen wird, würde ich nicht erwarten irgendein signifikanter Unterschied).

EDIT

ich nur ein Beispiel geschaffen, die (nur ein kurzer Code, nicht viel Polieren, normalerweise würden Header verwendet werden, etc.) arbeiten für mich:

File „mydll.CPP „:

// resolved against the executable 
extern "C" __declspec(dllimport) 
int __stdcall getSum(int a, int b); 


extern "C" __declspec(dllexport) 
int __stdcall callSum(int a, int b) 
{ 
    return getSum(a, b); 
} 

File "myexe.cpp":

#include <iostream> 
using namespace std; 

#include <windows.h> 

// export from the .exe 
extern "C" __declspec(dllexport) 
int __stdcall getSum(int a, int b) 
{ 
    return a + b; 
} 


typedef int(__stdcall * callSumFn)(int a, int b); 

int main() 
{ 
    HMODULE hLibrary = LoadLibrary(TEXT("MyDll.dll")); 
    if (!hLibrary) 
    { 
     cerr << "Failed to load library" << endl; 
     return 1; 
    } 

    callSumFn callSum = (callSumFn)GetProcAddress(hLibrary, "[email protected]"); 
    if (!callSum) 
    { 
     cerr << "Failed to get function address" << endl; 
     FreeLibrary(hLibrary); 
     return 1; 
    } 

    cout << "callSum(3, 4) = " << callSum(3, 4) << endl; 

    FreeLibrary(hLibrary); 
    return 0; 
} 

Die DLL ist mit "MyExe.lib" verknüpft, die beim Erstellen der EXE erstellt wird. Die main() ruft die callSum()-Funktion von der DLL auf, die ihrerseits die von der EXE bereitgestellte getSum() aufruft.

Ich würde immer noch lieber die "dependency inversion" verwenden und eine Schnittstelle an die DLL übergeben - für mich scheint es sauberer und auch flexibler (z. B. Versionierung durch Vererbung von Schnittstellen, etc.).

EDIT # 2

Was die Abhängigkeit Inversionstechnik, kann es zum Beispiel so etwas wie diese:

Datei ikernel.hpp (lediglich vom Kernel ausgeführt bereitgestellt, nicht von der DLL):

#ifndef IKERNEL_HPP 
#define IKERNEL_HPP 

class IKernel 
{ 
protected: 
    // or public virtual, but then there are differences between different compilers 
    ~IKernel() {} 
public: 
    virtual int someKernelFunc() = 0; 
    virtual int someOtherKernelFunc(int x) = 0; 
}; 

#endif 

Datei "mydll.cav ":

#include "ikernel.hpp" 

// if passed the class by pointer, can be extern "C", i.e. loadable by LoadLibrary/GetProcAddress 
extern "C" __declspec(dllexport) 
int __stdcall callOperation(IKernel *kernel, int x) 
{ 
    return kernel->someKernelFunc() + kernel->someOtherKernelFunc(x); 
} 

File "myexe.cpp":

#include "ikernel.hpp" 

#include <iostream> 
using namespace std; 

#include <windows.h> 

// the actual kernel definition 
class KernelImpl: public IKernel 
{ 
public: 
    virtual ~KernelImpl() {} 
    virtual int someKernelFunc() 
    { 
     return 10; 
    } 
    virtual int someOtherKernelFunc(int x) 
    { 
     return x + 20; 
    } 
}; 

typedef int(__stdcall * callOperationFn)(IKernel *kernel, int x); 

int main() 
{ 
    HMODULE hLibrary = LoadLibrary(TEXT("ReverseDll.dll")); 
    if (!hLibrary) 
    { 
     cerr << "Failed to load library" << endl; 
     return 1; 
    } 

    callOperationFn callOperation = (callOperationFn)GetProcAddress(hLibrary, "[email protected]"); 
    if (!callOperation) 
    { 
     cerr << "Failed to get function address" << endl; 
     FreeLibrary(hLibrary); 
     return 1; 
    } 

    KernelImpl kernel; 

    cout << "callOperation(kernel, 5) = " << callOperation(&kernel, 5) << endl; 

    FreeLibrary(hLibrary); 
    return 0; 
} 

Wie gesagt dies ist flexibler und leichter wartbar IMHO, die Kernel verschiedene Rückrufe für verschiedene DLL-Aufrufe zur Verfügung stellen kann Falls erforderlich, der. DLL kann auch eine Implementierung einer Schnittstelle als Spezifizierer durch den Kernel bereitstellen, der zuerst von der DLL abgerufen wird und der Kernel Funktionen darauf aufruft.

Eine weitere Annehmlichkeit ist, dass die DLL nicht mit irgendwelchen verlinkt werden muss " kernel "library (die reine Virtuelle Schnittstelle muss nicht exportiert werden).

Dies funktioniert normalerweise sogar über Compiler hinweg (d. H. Die ausführbare Datei wird von einem anderen Compiler als der DLL kompiliert, z. B. MSVC und GCC) - vorausgesetzt, die virtuelle Tabellenimplementierung ist dieselbe. Das ist nicht vorgeschrieben, aber es ist tatsächlich Voraussetzung für das Funktionieren von COM (Compiler, die unterschiedliche Implementierung von Polymorphismus bieten, können keine COM-Aufrufe von Microsoft verwenden).

Aber besonders in diesem Fall müssen Sie unbedingt sicherstellen, dass in der DLL zugewiesene Objekte in der EXE nicht freigegeben werden und umgekehrt (sie können unterschiedliche Heaps verwenden). Wenn dies erforderlich ist, sollte die Schnittstelle eine reine virtuelle destroy() -Methode bereitstellen, die den polymorphen Aufruf von "delete this" im richtigen Speicherkontext gewährleistet. Aber das könnte sogar ein Problem sein, wenn man die Funktionen direkt aufruft (im Allgemeinen sollte man malloc() - ed auf der anderen Seite nicht freigeben). Auch C++ - Ausnahmen dürfen die API-Grenze nicht passieren.

+0

Vielen Dank. Ich erkunde die Antwort. –

+0

Eine Frage, benötigt diese Technik (mit der .lib-Datei) die Neukompilierung von Komponenten bei jeder Änderung am Kernel? Übrigens, gibt es ein Beispiel für die Abhängigkeit Inversionstechnik? –

+0

Ich glaube, ich habe die Antwort auf den ersten Teil der Frage bekommen. Es besteht keine Notwendigkeit, die Programme neu zu kompilieren, die .lib-Dateien verwendet haben. –

0

Erwägen Sie, Ihren Entwurf anders herum zu machen: das heißt, der "Kernel" wird zu einer DLL gemacht und von Ihrer Komponentenanwendung geladen. Da der Kernel Dienste für die Komponente bereitstellt und nicht umgekehrt, ist dies sinnvoller.

+0

Der Kernel soll die Infrastruktur für Komponenten bereitstellen, die über Message-Passing miteinander kommunizieren. Ich kann das nicht tun. –