Unmanaged memory allocation to managed objects
I'm wondering about the proper way of allocating memory to a pointer (C/C++ style) from within C#. Then, holding onto that memory for an extended period of time. Also, this allocated memory is meant to be used for calls to DeviceIoControl(). Consider this class:
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 i开发者_开发百科n/out pointers
// check that all is well
}
}
This whole thing doesn't "feel" right to me, but I've learned enough over the years to not immediately question implementation without studying it. That's what brings me to this forum. I'm seeing behavior I've never seen before.
Using the debugger, I place a break point at the point the pointer is instantiated with the call to VirtualAlloc(). I make note of the address that's given to me (for example, 0x03bf78cc). I also have a break point placed at another function which calls the method, ReadFromDevice(), above. As I step through ReadFromDevice(), I notice that pStruct contains a different address than the one that was allocated when the program first began. The value of pStruct has changed from my number above to, let's say, 0x03cd9004.
I've worked with calling into the Kernel32 function DeviceIoControl() before and the method used there was to instantiate a pinned GCHandle to the data structure being used in the call to DeviceIoControl(), make the call, copy out the appropriate data and then free the handle. This approach seems less error prone and keeps in consistency with the model of allocating and freeing unmanaged memory as quickly as possible.
As I mentioned, the approach used in the code I'm into now just doesn't "feel" right, but I'm not sure about why. I've turned up nothing with searches on Google for things like "c sharp memory address changed after alloc" and so forth. Should this approach be changed? (In the past, I've used pinned GC Handles.) Is the above approach sound? Regardless, why would the pointer say one memory address during program startup and then another when the ReadFromDevice() call is actually performed? As I write this post, I'm wondering if the change in address is as "odd" as I first thought. Nevertheless, I'm still questioning this use of pointers. Please advise.
Thanks, Andy
This is not necessary, you can leave it up to the pinvoke marshaller to do the right thing. Simply provide overloads of the function to pass the arguments, customized to fit the call. For example:
[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);
And you'd call it like this:
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);
Does it work or not? Are you trying to fix something that works or what?
I never needed VirtualAlloc in c#. If you need a memory buffer, why don't you allocate it directly in c#. I think you don't need VirtualAlloc
. Either directly allocate an unmanaged fixed array in c#, or allocate a regular array and pass it to the IOCTL function by marshalling.
精彩评论