我意識到一點點C#移植this application,允許從內存/流加載庫,而不是使用通過文件系統工作的LoadLibrary API函數。 用指針和不匹配的結果搞亂了一點點......最後我有一些按預期工作的東西。我唯一的問題是對DLLMain的調用總是失敗(我用Kernel32.dll和User32.dll試過)。我不明白爲什麼和我不知道如何調試問題。從內存中加載庫/模塊
這裏是我的項目(一個簡單的32位控制檯應用程序),其內容庫的主要功能,它分配到內存中,然後手動加載它:
public static UInt32 Load(String libraryName)
{
if (libraries.ContainsKey(libraryName))
return libraries[libraryName];
String libraryPath = Path.Combine(Environment.SystemDirectory, libraryName);
Byte[] libraryBytes = File.ReadAllBytes(libraryPath);
fixed (Byte* libraryPointer = libraryBytes)
{
HeaderDOS* headerDOS = (HeaderDOS*)libraryPointer;
if ((UInt16)((headerDOS->Magic << 8) | (headerDOS->Magic >> 8)) != IMAGE_DOS_SIGNATURE)
return 0;
HeadersNT* headerNT = (HeadersNT*)(libraryPointer + headerDOS->LFANEW);
UInt32 addressLibrary = VirtualAlloc(headerNT->OptionalHeader.ImageBase, headerNT->OptionalHeader.SizeOfImage, AllocationType.RESERVE, MemoryProtection.READWRITE);
if (addressLibrary == 0)
addressLibrary = VirtualAlloc(0, headerNT->OptionalHeader.SizeOfImage, AllocationType.RESERVE, MemoryProtection.READWRITE);
if (addressLibrary == 0)
return 0;
Library* library = (Library*)Marshal.AllocHGlobal(sizeof(Library));
library->Address = (Byte*)addressLibrary;
library->ModulesCount = 0;
library->Modules = null;
library->Initialized = false;
VirtualAlloc(addressLibrary, headerNT->OptionalHeader.SizeOfImage, AllocationType.COMMIT, MemoryProtection.READWRITE);
UInt32 addressHeaders = VirtualAlloc(addressLibrary, headerNT->OptionalHeader.SizeOfHeaders, AllocationType.COMMIT, MemoryProtection.READWRITE);
MemoryCopy((Byte*)headerDOS, (Byte*)addressHeaders, (headerDOS->LFANEW + headerNT->OptionalHeader.SizeOfHeaders));
library->Headers = (HeadersNT*)((Byte*)addressHeaders + headerDOS->LFANEW);
library->Headers->OptionalHeader.ImageBase = addressLibrary;
CopySections(library, headerNT, libraryPointer);
UInt32 locationDelta = addressLibrary - headerNT->OptionalHeader.ImageBase;
if (locationDelta != 0)
PerformBaseRelocation(library, locationDelta);
UInt32 libraryHandle = (UInt32)library;
if (!BuildImportTable(library))
{
Free(libraryName);
return 0;
}
FinalizeSections(library);
if (library->Headers->OptionalHeader.AddressOfEntryPoint == 0)
{
Free(libraryName);
return 0;
}
UInt32 libraryEntryPoint = addressLibrary + library->Headers->OptionalHeader.AddressOfEntryPoint;
if (libraryEntryPoint == 0)
{
Free(libraryName);
return 0;
}
LibraryMain main = (LibraryMain)Marshal.GetDelegateForFunctionPointer(new IntPtr(libraryEntryPoint), typeof(LibraryMain));
UInt32 result = main(addressLibrary, DLL_PROCESS_ATTACH, 0);
if (result == 0)
{
Free(libraryName);
return 0;
}
library->Initialized = true;
libraries[libraryName] = libraryHandle;
return libraryHandle;
}
}
這裏是如何使用它的一個示例:
private const Byte VK_Z_BREAK = 0x5A;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void KeyboardEventDelegate(Byte key, Byte scan, KeyboardFlags flags, Int32 extra);
[Flags]
private enum KeyboardFlags : uint
{
EXTENDEDKEY = 0x0001,
KEYUP = 0x0002,
}
public static void Main()
{
UInt32 libraryHandle = LibraryLoader.Load("User32.dll");
if (libraryHandle != 0)
{
UInt32 functionHandle = LibraryLoader.GetFunctionAddress("User32.dll", "keybd_event");
if (functionHandle != 0)
{
KeyboardEventDelegate s_KeyboardEvent = (KeyboardEventDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(functionHandle), typeof(KeyboardEventDelegate));
while (true)
{
s_KeyboardEvent(VK_Z_BREAK, VK_Z_SCANCODE, 0, 0);
s_KeyboardEvent(VK_Z_BREAK, VK_Z_SCANCODE, KeyboardFlags.KEYUP, 0);
}
}
}
Console.ReadLine();
}
如果你想給它一個快速嘗試,你可以從this link下載該項目。
[編輯]經過幾次嘗試,在調用DllMain之後使用Marshal.GetLastWin32Error(),我發現正在生成錯誤代碼14,它對應於ERROR_OUTOFMEMORY。如果我在DllMain調用失敗後繼續工作,並且我得到一個庫函數的地址,試圖使用委託來調用它,會產生PInvokeStackImbalance異常。任何關於這個的線索?^_^
如果不能完全移植它,它會不會更容易(也用於維護),但是在原始項目的頂部提供了一個小的C++/CLI層? – stijn
不要在這裏發佈你所有的代碼..只發布相關的部分,如果你有一個單一的方法所有的邏輯..然後我會建議重構它..我沒有時間看你的任何代碼if它超過了10-15行.. – MethodMan
你的DLLMain做了什麼,它作爲結果返回了什麼?它是否被調用(例如,是否可以在DLLMain中執行OutoutDebugString並查看輸出)? –