2011-10-10 1 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로 변경되었습니다.

, 나는 이전에()는의 Kernal32 기능의 DeviceIoControl에 전화로 근무했습니다과 방법은 전화를 걸) DeviceIoControl을 (호출에 사용되는 데이터 구조에 고정 GCHandle의 인스턴스가 있었다 사용 밖으로 복사 적절한 데이터를 입력 한 다음 핸들을 비 웁니다. 이 접근법은 오류가 발생하기 쉽지 않으며 가능한 한 빨리 관리되지 않는 메모리를 할당하고 해제하는 모델과 일관성을 유지합니다.

앞에서 언급했듯이 내가 지금 사용하고있는 코드에서 사용 된 접근 방식은 "느끼기"쉽지 않지만 왜 그런지는 잘 모르겠습니다. 나는 "alloc 후에 c 날카로운 메모리 주소가 바뀌 었습니다."와 같은 것들에 대해서 구글 검색에서 아무것도 발견하지 못했습니다. 이 접근 방식을 변경해야합니까? (과거에는 고정 된 GC 핸들을 사용했습니다.) 위의 접근 방식이 효과가 있습니까? 그럼에도 불구하고, 포인터가 프로그램 시작 중에 하나의 메모리 주소를 말하고, 왜 ReadFromDevice() 호출이 실제로 수행 될 때 다른 포인터를 말합니까? 이 글을 쓰면서 나는 처음 생각한 것처럼 주소가 "이상하게"바뀌 었는지 궁금합니다. 그럼에도 불구하고, 나는 포인터의 사용에 대해 여전히 의문을 품고있다. 제발 조언.

덕분에,

답변

1

이 작업은 꼭 필요한 작업이 아닙니다. pinvoke marshaller에게 맡겨서 올바른 작업을 수행 할 수 있습니다. 호출에 맞게 사용자 정의 된 인수를 전달하는 함수 오버로드를 제공하기 만하면됩니다.

[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

앤디는이 일을하거나하지 않습니다? 작동하는 무언가를 고치려고하고 있습니까?

저는 VirtualAlloc을 C#에서 필요로하지 않았습니다. 메모리 버퍼가 필요한 경우 C#에서 직접 할당하지 않는 것이 좋습니다. 나는 너에게 VirtualAlloc이 필요 없다고 생각한다. 직접 C#에서 관리되지 않는 고정 배열을 할당하거나 일반 배열을 할당하고 정렬하여 IOCTL 함수에 전달하십시오.

+0

그것은 작동 생각하고, 대부분의 시간은 같다 예를 들어. 나는이 관리되지 않는 메모리의 사용과 직접적으로 관련이 있다고 생각되는 결함에 대해 작업하고 있습니다. –