我在一家由一臺主機上,我無法控制調用的DLL寫一些插件代碼。編寫定製的序言和結尾代碼裸體功能在Visual Studio
主機假定插件導出爲__stdcall函數。主機被告知函數的名稱和它期望的參數的細節,並通過LoadLibrary,GetProcAddress動態地調用它,然後手動將參數推送到堆棧上。
通常plugin dlls會暴露一個常量界面。我的插件公開了一個在dll加載時配置的接口。爲了實現這一點,我的插件公開了一組標準入口點,這些入口點是在編譯dll時定義的,並根據需要將它們分配給正在公開的內部功能。
每個內部功能可以採用不同的參數,但,這是與物理入口點名稱一起傳送到主機。我所有的物理dll入口點都被定義爲採用單個void *指針,並通過從第一個參數的偏移量和已傳送給主機的已知參數列表中進行處理,從而自我編排堆棧中的後續參數。
主機可以成功地調用我的插件的功能與正確的參數和一切運作良好。不過,我知道,A)作爲他們應該爲我的功能都沒有清理堆棧它們被定義爲採用4字節指針的__stdcall函數,因此即使調用者將更多參數推送到堆棧,它們也總是在最後執行'ret 4'。和b)我不能處理沒有參數的函數,因爲ret 4會在我返回時彈出4個字節。
已經描繪出我的插件插入主機的調用代碼,我可以看到,實際上是一個)不是什麼大不了的事;主機會丟失一些堆棧空間,直到它從調度呼叫返回時清除堆棧框架,清理垃圾;但是...
我可以通過切換到__cdecl和未清理的所有解決B)。我假設我可以通過切換到裸函數並編寫自己的泛型參數清理代碼來解決a)。
因爲我知道的參數空間由剛叫我曾希望,因爲這將是簡單的功能,常用量:
extern "C" __declspec(naked) __declspec(dllexport) void * __stdcall EntryPoint(void *pArg1)
{
size_t argumentSpaceUsed;
{
void *pX = RealEntryPoint(
reinterpret_cast<ULONG_PTR>(&pArg1),
argumentSpaceUsed);
__asm
{
mov eax, dword ptr pX
}
}
__asm
{
ret argumentSpaceUsed
}
}
但是,這並不爲RET工作需要編譯時不斷...有什麼建議?
更新:
感謝Rob肯尼迪的建議,我已經得到了這一點,這似乎工作...
extern "C" __declspec(naked) __declspec(dllexport) void * __stdcall EntryPoint(void *pArg1)
{
__asm {
push ebp // Set up our stack frame
mov ebp, esp
mov eax, 0x0 // Space for called func to return arg space used, init to 0
push eax // Set up stack for call to real Entry point
push esp
lea eax, pArg1
push eax
call RealEntryPoint // result is left in eax, we leave it there for our caller....
pop ecx
mov esp,ebp // remove our stack frame
pop ebp
pop edx // return address off
add esp, ecx // remove 'x' bytes of caller args
push edx // return address back on
ret
}
}
這是否正確?
我個人不會打擾幀指針,因爲它從來沒有使用過,但除此之外,你的解決方案看起來不錯。 – 2009-07-01 14:52:23
本來我是從框架指針索引args空間和返回值空間,但是之後我沒有將返回值從eax移出來,然後返回到它並且很好,我猜框架指針的需要消失了。我想我應該刪除它...感謝您的幫助。 – 2009-07-01 22:05:07