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.
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). –
@JoachimPileborg Ändert sich die Antwort? Betrachten Sie die Mikrokern-Architektur. Der Kernel bildet das Herz der Anwendung. –
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. –