2011-01-02 45 views
14

Hãy để tôi bắt đầu bằng cách nói rằng tôi đã xem và tìm thấy mô tả về việc sử dụng cố định {}, Marshal.AllocHGlobal() và GCHandle .Alloc() trong diễn đàn này và trong nhiều liên kết trên web. Tuy nhiên, tôi vẫn chưa tìm được lời giải thích ngắn gọn về thời điểm sử dụng lớp Marshal so với lớp GCHandle (có và không sử dụng cố định {}).Hiểu sự khác biệt giữa việc sử dụng cố định {}, Marshal.AllocHGlobal() và GCHandle.Alloc()

Tôi đang sử dụng thư viện .NET của bên thứ ba có phương thức được gọi là Readline() trong lớp "Bộ đệm". Sách hướng dẫn hiển thị mẫu thử nghiệm chức năng sau:

bool ReadLine (int x1, int y1, int x2, int y2, System.IntPtr bufData, out int numRead);

với một mô tả của bufData nói rằng: ... Diện tích bộ nhớ phải có một số byte lớn hơn hoặc bằng với lần chiều dài dòng giá trị trả về bởi tài sản BytesPerPixel.

Bây giờ sau trong cuốn hướng dẫn sử dụng họ làm đưa ra một ví dụ về truy cập vào bộ đệm (mà tôi đã điều chỉnh một chút cho ví dụ cụ thể của tôi):

// Create an array large enough to hold one line from buffer 
int size = 640; 
byte[] dataLine = new byte[size * 2]; // 2 bytes per pixel 

// Pin the array to avoid Garbage collector moving it 
GCHandle dataLineHandle = GCHandle.Alloc(dataLine, GCHandleType.Pinned); 
IntPtr dataLineAddress = dataLineHandle.AddrOfPinnedObject(); 

và tôi có thể làm theo các trên "Ví dụ" mã với:

// Read one line of buffer data 
success = buffer.ReadLine(0, 0, 639, 0, dataLineAddress, out numRead); 

// Unpin the array 
dataLineHandle.Free() 

đó có thể là sự kết thúc của câu chuyện (và tôi vẫn chưa kiểm tra mã trên), nhưng tôi đã kết thúc googling lớp GCHandle mà chở tôi xuống con đường của .NET interoperab ility, PInvoke vv

Vì vậy, câu hỏi của tôi ... 1) Tại sao tôi không thể sử dụng:

IntPtr dataLineAddress = Marshal.AllocHGlobal(size * 2); 

và vượt qua đó vào ReadLine()?

2) Tôi có thể cũng sử dụng đoạn mã sau đây (trích xuất và chỉnh từ các ví dụ trên web):

int size = 640; 
byte[] dataLine= new byte[size * 2]; // 2 bytes per pixel 

// prevent garbage collector from moving buffer around in memory 
fixed (byte* fixedDataLine = dataLine) 
{ 
    // get IntPtr representing address of first buffer element 
    IntPtr dataLineAddress= Marshal.UnsafeAddrOfPinnedArrayElement(fixedDataLine , 0); 
    success = buffer.ReadLine(0, 0, 639, 0, dataLineAddress, out numRead); 
} 

Tôi muốn được quan tâm trong bất cứ ai có thể làm sáng tỏ về các kỹ thuật nói trên và chỉ ra các lỗi của tôi trong việc triển khai cũng như chỉ ra khi các phương pháp trên phù hợp. Cuối cùng, ngay cả khi các phương pháp trên đều hợp lệ, có một sự thúc đẩy chung trong vài năm qua đối với một cách tiếp cận này hay cách khác không?

Cảm ơn trước !! Hyped

+0

Điều đó nghe giống như một API khủng khiếp. – SLaks

Trả lời

2

Vâng, giải pháp thay thế cũng có thể hoạt động. Nhưng mẫu Marshal.AllocHGlobal không hoàn chỉnh, bây giờ bạn đã có dữ liệu trong bộ nhớ không được quản lý. Bạn vẫn cần phải làm việc để đưa nó vào một đối tượng được quản lý (mảng) để bạn có thể dễ dàng truy cập nó, bạn phải gọi Marshal.Copy(). Không hiệu quả vì sao chép dữ liệu hai lần. Và đừng quên gọi Marshal.FreeHGlobal().

Mẫu cố định thực hiện giống như mẫu của nhà cung cấp, nó ẩn hoàn toàn bộ nhớ. Lúng túng ở đây là API lấy IntPtr, không phải là byte *. Và bạn phải thay đổi cài đặt biên dịch để cho phép từ khóa không an toàn. Nó không phải là cách khác hiệu quả hơn.

Bạn sẽ không đi trước bằng cách thực hiện việc này theo cách khác.

+0

Hans - nếu tôi có một hàm DLL không được quản lý mà tôi đã gọi yêu cầu một con trỏ byte tới một khối bộ nhớ mà nó sẽ "lấp đầy", thì có ưu tiên khi sử dụng "Marshal" so với "GCHandle" không? Cảm ơn câu trả lời của bạn ở trên. – Hyped

+0

@Hyped - sở thích của bạn nên * luôn luôn * để cho P/Invoke marshaller làm điều đó. Điều đó gần như luôn luôn có thể. Bạn sẽ khai báo đối số là một mảng trong ví dụ của bạn. Giống như byte [] hoặc PixelData [] trong đó PixelData là một cấu trúc có hai thành viên byte. –

Các vấn đề liên quan