2011-10-10 87 views
3

我想知道從C#內部分配內存到指針(C/C++風格)的正確方法。然後,長時間保持該內存。此外,這個分配的內存用於調用DeviceIoControl()。考慮這個類:非託管內存分配到託管對象

class Example { 
    const uint memCommit = 0x1000; 
    const uint pgReadWrite = 0x04; 

    [DllImport("Kernel32.dll", SetLastError = true)] 
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect); 

    [StructLayout(LayoutKind.Sequential)] 
    struct AStruct { 
     uint val1; 
     uint val2; 
     uint val3; 
    } 

    private static unsafe AStruct* pStruct = (AStruct*)VirtualAlloc(IntPtr.Zero, (UIntPtr)(sizeof(AStruct)), memCommit, pgReadWrite).ToPointer(); 


    public static unsafe void ReadFromDevice() { 
     // setup the structure for the IOCTL call 
     pStruct->val1 = 70; // 
     pStruct->val2 = 0; 
     pStruct->val3 = 0x0f; 

     // call P/Invoked DeviceIoControl() here with pStruct as both the in/out pointers 

     // check that all is well 
    } 
} 

這整個事情沒有「感覺」我的權利,但我已經夠學到了不立即執行問題,不研究它的年。這就是我帶到這個論壇的原因。我看到了以前從未見過的行爲。

使用調試器,我在通過調用VirtualAlloc()實例化指針的位置放置一個斷點。我記下給我的地址(例如,0x03bf78cc)。我還在另一個調用上述方法ReadFromDevice()的函數中放置了一個斷點。當我逐步通過ReadFromDevice()時,我注意到pStruct包含的地址與程序第一次啓動時分配的地址不同。 pStruct的值已經從我上面的數字變成了,比方說0x03cd9004。

我已經調用了Kernel32函數DeviceIoControl()之前,並且使用的方法是實例化一個固定的GCHandle到在調用DeviceIoControl()中使用的數據結構,進行調用,複製出適當的數據,然後釋放句柄。這種方法似乎不太容易出錯,並與儘可能快地分配和釋放非託管內存的模型保持一致。

正如我所提到的,我現在所使用的代碼中使用的方法並不是「感覺」正確,但我不確定原因。我在谷歌搜索上沒有找到任何東西,比如「c語言內存地址在alloc後更改」等等。這種方法應該改變嗎? (過去,我使用固定的GC手柄。)上述方法是否正確?無論如何,指針在程序啓動時會說一個內存地址,而在ReadFromDevice()調用實際執行時會有另一個內存地址?當我寫這篇文章時,我想知道地址變化是否像我第一次想到的那樣「奇怪」。不過,我仍然質疑指針的使用。請指教。

感謝, 安迪

回答

1

這不是必要的,你可以讓它停止向編譯器做正確的事情。只需提供函數的重載以傳遞參數,並根據調用進行自定義。例如:

[DllImport("kernel32.dll", SetLastError = true)] 
static extern bool DeviceIoControl(
    IntPtr hDevice, uint dwIoControlCode, 
    IntPtr lpInBuffer, uint nInBufferSize, 
    out AStruct lpOutBuffer, uint nOutBufferSize, 
    out uint lpBytesReturned, IntPtr lpOverlapped); 

而且你會這樣稱呼它:

AStruct result; 
uint resultSize = Marshal.SizeOf(result); 
uint bytesReturned; 
bool okay = DeviceIoControl(hDev, somecode, IntPtr.Zero, 0, 
       out result, resultSize, 
       out bytesReturned, IntPtr.Zero); 
if (!okay) throw new Win32Exception(); 
System.Diagnostic.Debug.Assert(bytesReturned == resultSize); 
+0

這就是我習慣這樣做的方式。或者,正如我在帖子中提到的那樣,通過使用固定對象。基本上,我想知道是否使用的實現是好的,還是應該將其更改爲您發佈的內容?我想知道,在使用的方法中是否存在危險?即具有這樣的靜態不安全指針? –

0

是否行得通不?你想解決一些有效的或什麼?

我從來不需要c#中的VirtualAlloc。如果你需要一個內存緩衝區,爲什麼不直接在C#中分配它。我認爲你不需要VirtualAlloc。直接在c#中分配一個非託管的固定數組,或者分配一個常規數組並通過編組將其傳遞給IOCTL函數。

+0

它認爲它的工作原理,以及大部分的時間似乎。我正在研究一個我認爲與使用非託管內存直接相關的缺陷。 –