2009-07-06 3 views
11

Tôi đang cố gắng chuyển đổi một mảng cấu trúc RECT (được đưa ra dưới đây) thành IntPtr, vì vậy tôi có thể gửi con trỏ bằng cách sử dụng PostMessage đến một ứng dụng khác.Chuyển đổi mảng cấu trúc thành IntPtr

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int Left; 
    public int Top; 
    public int Right; 
    public int Bottom; 

    // lots of functions snipped here 
} 

// so we have something to send, in reality I have real data here 
// also, the length of the array is not constant 
RECT[] foo = new RECT[4]; 
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(foo[0]) * 4); 
Marshal.StructureToPtr(foo, ptr, true); // -- FAILS 

Điều này mang lại cho một ArgumentException trên dòng cuối cùng ("Cấu trúc được chỉ định phải có thể ghi được hoặc có thông tin bố cục"). Tôi cần bằng cách nào đó lấy mảng RECT này cho một ứng dụng khác bằng cách sử dụng PostMessage, vì vậy tôi thực sự cần một con trỏ tới dữ liệu này.

Tùy chọn của tôi ở đây là gì?

CẬP NHẬT: Điều này dường như làm việc:

IntPtr result = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.RECT)) * foo.Length); 
IntPtr c = new IntPtr(result.ToInt32()); 
for (i = 0; i < foo.Length; i++) 
{ 
    Marshal.StructureToPtr(foo[i], c, true); 
    c = new IntPtr(c.ToInt32() + Marshal.SizeOf(typeof(Win32.RECT))); 
} 

CẬP NHẬT LẠI để sửa chữa những gì trọng tài nhận xét về.

+0

nhắn bạn gửi bài tự động thực hiện quá trình cross-marshaling của một mảng của 4 dễ phân biệt gì? –

+0

Tôi đang cố gắng nói với một DLL (được lưu trữ trong một tiến trình khác vì nó là 64-bit) để bỏ qua các vùng nhất định của màn hình. Nó không nhất thiết phải là 4 RECT. –

+0

Theo cập nhật của bạn, bạn không phân bổ đủ không gian (intptr.size thay vì Marshal.SizeOf (typeof (RECT))). Và con trỏ số học có thể thất bại trên máy x64, xem câu trả lời của tôi. – arbiter

Trả lời

12

StructureToPtr mong đợi đối tượng struct, và foo không phải cấu trúc nó là mảng, đó là lý do tại sao ngoại lệ xảy ra.

tôi có thể đề nghị bạn viết cấu trúc trong chu kỳ (buồn bã, StructureToPtr không có tình trạng quá tải với Index):

long LongPtr = ptr.ToInt64(); // Must work both on x86 and x64 
for (int I = 0; I < foo.Length; I++) 
{ 
    IntPtr RectPtr = new IntPtr(LongPtr); 
    Marshal.StructureToPtr(foo[I], RectPtr, false); // You do not need to erase struct in this case 
    LongPtr += Marshal.SizeOf(typeof(Rect)); 
} 

lựa chọn khác là để viết cấu trúc như bốn số nguyên, sử dụng Marshal.WriteInt32:

for (int I = 0; I < foo.Length; I++) 
{ 
    int Base = I * sizeof(int) * 4; 
    Marshal.WriteInt32(ptr, Base + 0, foo[I].Left); 
    Marshal.WriteInt32(ptr, Base + sizeof(int), foo[I].Top); 
    Marshal.WriteInt32(ptr, Base + sizeof(int) * 2, foo[I].Right); 
    Marshal.WriteInt32(ptr, Base + sizeof(int) * 3, foo[I].Bottom); 
} 

Và cuối cùng, bạn có thể sử dụng trực tiếp không an toàn từ khóa và làm việc với con trỏ trực tiếp.

0

Bạn có thể thử như sau:

RECT[] rects = new RECT[ 4 ]; 
IntPtr[] pointers = new IntPtr[4]; 
IntPtr result = Marshal.AllocHGlobal(IntPtr.Size * rects.Length); 
for (int i = 0; i < rects.Length; i++) 
{ 
    pointers[i] = Marshal.AllocHGlobal (IntPtr.Size); 
    Marshal.StructureToPtr(rects[i], pointers[i], true); 
    Marshal.WriteIntPtr(result, i * IntPtr.Size, pointers[i]); 
} 
// the array that you need is stored in result 

Và đừng quên để giải phóng tất cả mọi thứ sau khi bạn kết thúc.

+3

Ngoại trừ đây là một chuỗi các con trỏ tới RECT không phải là một mảng RECT. Và bạn đang phân bổ các khối con trỏ có kích thước bộ nhớ cho mỗi con trỏ [i] nhưng sau đó marshaling 16 byte (sizeof (RECT)) đến điểm đó. Loại tràn là một công thức cho thảm họa. –

1

Trọng tài đã cho bạn một câu trả lời hay nhất về cách sắp xếp các mảng cấu trúc. Đối với các cấu trúc blittable như thế này, tôi, cá nhân, sẽ sử dụng mã không an toàn hơn là marshaling thủ công từng phần tử vào bộ nhớ không được quản lý. Một cái gì đó như thế này:

RECT[] foo = new RECT[4]; 
unsafe 
{ 
    fixed (RECT* pBuffer = foo) 
    { 
     //Do work with pointer 
    } 
} 

hoặc bạn có thể ghim mảng bằng GCHandle.

Thật không may, bạn nói rằng bạn cần gửi thông tin này đến một quy trình khác. Nếu thư bạn đang đăng không phải là một trong những thư mà Windows cung cấp tính năng tự động marshaling thì bạn có một vấn đề khác. Vì con trỏ có liên quan đến quá trình cục bộ nên nó không có nghĩa gì trong quá trình từ xa và đăng một thông điệp với con trỏ này sẽ gây ra hành vi không mong muốn, bao gồm cả sự cố chương trình. Vì vậy, những gì bạn cần làm là viết mảng RECT cho bộ nhớ của quá trình khác không phải của riêng bạn. Để làm điều này, bạn cần sử dụng OpenProcess để có được một xử lý cho quá trình, VitualAllocEx để cấp phát bộ nhớ trong quá trình khác và sau đó WriteProcessMemory để ghi mảng vào bộ nhớ ảo của quá trình khác. Thật không may một lần nữa, nếu bạn đang đi từ một quá trình 32bit đến một quá trình 32bit hoặc từ một quá trình 64bit đến một quá trình 64bit mọi thứ khá đơn giản nhưng từ một quá trình 32bit đến một quá trình 64bit mọi thứ có thể nhận được một chút lông. VirtualAllocEx và WriteProcessMemory không thực sự được hỗ trợ từ 32 đến 64.Bạn có thể có thành công bằng cách cố gắng ép buộc VirtualAllocEx phân bổ bộ nhớ của nó ở phần dưới cùng của không gian bộ nhớ 64 bit sao cho con trỏ kết quả hợp lệ cho các cuộc gọi API quá trình 32 bit và sau đó viết với con trỏ đó. Ngoài ra, bạn có thể có kích thước cấu trúc và sự khác biệt về đóng gói giữa hai loại quy trình. Với RECT không có vấn đề gì nhưng một số cấu trúc khác với các vấn đề đóng gói hoặc sắp xếp có thể cần phải được viết bằng tay theo trường theo quy trình 64bit để phù hợp với bố cục cấu trúc 64bit.