考慮應用程序的微內核軟件體系結構。 我有一個內核和一個組件。反向動態鏈接函數調用
該組件是在運行時使用LoadLibrary
API在Windows中由內核加載的DLL;當然,可以使用GetProcAddress
來調用導出的函數。
現在組件需要將消息發送到內核。換句話說,現在是一個加載的DLL的組件需要從內核調用函數。什麼是正確的機制?
考慮應用程序的微內核軟件體系結構。 我有一個內核和一個組件。反向動態鏈接函數調用
該組件是在運行時使用LoadLibrary
API在Windows中由內核加載的DLL;當然,可以使用GetProcAddress
來調用導出的函數。
現在組件需要將消息發送到內核。換句話說,現在是一個加載的DLL的組件需要從內核調用函數。什麼是正確的機制?
它應該工作,在這裏看到:https://stackoverflow.com/a/30475042/1274747
對於MSVC,你基本上是在該.exe使用__declspec(dllexport)
。編譯器/鏈接器爲.exe生成導入庫,然後可以將該導入庫與DLL鏈接,然後DLL將使用.exe中的符號。
另一個選擇是通過「依賴倒置」來解決這個問題 - .exe不會導出符號,但會提供一個(純虛擬)接口,它將在.exe中執行並通過(通過引用或指向接口的指針)加載到DLL後。然後DLL可以調用接口上的方法,該方法位於.exe內部。但事實上,正如你所說的微內核,它取決於虛擬調用開銷是否可以接受(儘管從.exe導出函數時,方法也是通過函數指針調用的AFAIK,所以我不希望任何顯着差異)。
編輯
我剛剛創建了一個例子,這是爲我工作(只是一個簡單的代碼,沒有太大的拋光,通常頭將用於等):
文件「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);
}
文件 「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;
}
DLL與建立EXE時創建的「MyExe.lib」鏈接。 main()
從DLL中調用callSum()
函數,DLL又調用由EXE提供的getSum()
。這就是說,我仍然更願意使用「依賴倒置」並將接口傳遞給DLL--對我來說,它似乎更乾淨,也更靈活(例如通過接口繼承進行版本控制等)。
EDIT#2
至於依賴性反演技術,它可以是例如這樣的事情:
文件ikernel.hpp(由內核可執行提供,而不是由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
文件「mydll。CPP「:
#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);
}
文件‘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;
}
至於說這是更靈活,更容易恕我直言維護;內核可以提供不同的DLL調用不同的回調如果有必要,。 DLL可能會提供一些接口作爲內核說明符的實現,它將首先從DLL中獲取,並且內核將調用函數到其上。
另一個便利是DLL不需要鏈接到任何「內核「庫(純粹的虛擬接口不需要導出)。
即使在編譯器(即由不同於DLL的編譯器編譯的可執行文件,例如MSVC和GCC)中,這通常也可以工作 - 假設虛擬表的實現是相同的。這不是強制性的,但它實際上是COM工作的先決條件(提供不同實現多態性的編譯器不能使用Microsoft COM調用)。
但特別是在這種情況下,您絕對必須確保在DLL中分配的對象不會在EXE中釋放,反之亦然(它們可能會使用不同的堆)。如果這是必要的,那麼接口應該提供一個純粹的虛擬destroy()方法,它可以確保在正確的內存環境中「刪除這個」的多態調用。但是,即使直接調用函數,這也可能是個問題(仍然一般不應該在另一端釋放()memory malloc())。另外,不得允許C++異常通過API邊界。
非常感謝。我正在探索答案。 –
一個問題,這個技術(使用.lib文件)每次修改內核時都需要重新編譯組件嗎?順便說一下,有沒有任何依賴反轉技術的例子? –
我想我得到了問題第一部分的答案。沒有必要重新編譯使用.lib文件的程序。 –
考慮讓你的設計相反:也就是說,'內核'被製作成一個DLL並由組件應用程序加載。由於它是爲組件提供服務的內核,而不是其他方式,所以這樣做更有意義。
內核應該爲組件提供通過消息傳遞相互通信的基礎結構。我不能那樣做。 –
當你說「內核」你是什麼意思?在計算機編程中,「內核」這個詞有多種用法,這裏不清楚究竟哪一種含義是相關的。你還應該[閱讀如何提出好問題](http://stackoverflow.com/help/how-to-ask),並學習如何創建[最小,完整和可驗證示例](http:// stackoverflow.com/help/mcve)。 –
@JoachimPileborg它改變了答案嗎?考慮微內核體系結構。內核構成了應用程序的核心。 –
所以你正在編寫自己的*操作系統*內核?沒有例如一個CUDA內核?請更新您的標籤以反映這一點,或者至少在問題主體中進行拼寫。 –