2009-07-06 94 views
11

我試圖轉換RECT結構(以下給出)轉換成一個IntPtr的陣列,所以我可以發送使用PostMessage的到另一個應用程序的指針。轉換陣列結構到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 

這給出在最後一行一個ArgumentException(「指定的結構必須blittable或具有佈局信息。」)。我需要以某種方式將這個RECT數組傳遞給另一個使用PostMessage的應用程序,所以我確實需要一個指向這些數據的指針。

我在這裏有什麼選擇?

UPDATE:這似乎工作:

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))); 
} 

修訂AGAIN解決什麼仲裁者評論。

+0

什麼消息,你張貼的自動的4個RECTS數組做跨進程封送處理? – 2009-07-06 10:39:42

+0

我試圖告訴一個DLL(它是在另一個進程中託管的,因爲它是64位)忽略屏幕的某些區域。它不一定是4個RECT。 – 2009-07-06 10:45:06

回答

12

StructureToPtr預計結構對象,和Foo不是結構上,它陣列,這就是爲什麼出現異常。

我可以建議你寫週期(可悲的是,StructureToPtr沒有超載與指數)結構:

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)); 
} 

另一種選擇是編寫結構四個整數,使用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); 
} 

而最後,你可以使用不安全關鍵字,並直接與指針的工作。

0

你可以嘗試以下方法:

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 

而且不要忘記你完成後釋放一切。

1

仲裁者爲您提供了一個如何編組結構數組的良好答案。對於像這樣的blittable結構,我個人會使用不安全的代碼,而不是手動將每個元素編組到非託管內存。類似這樣的:

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

或者你可以使用GCHandle來固定數組。

不幸的是,你說你需要將這些信息發送到另一個進程。如果您發佈的消息不是Windows提供自動封送處理的消息之一,那麼您還有其他問題。由於指針是相對於本地進程的,所以在遠程進程中沒有任何意義,並且使用該指針發佈消息將導致意外的行爲,包括可能的程序崩潰。所以你需要做的是將RECT數組寫入其他進程的內存,而不是你自己的內存。爲此,您需要使用OpenProcess來獲取進程的句柄,VitualAllocEx在另一個進程中分配內存,然後使用WriteProcessMemory將該數組寫入其他進程的虛擬內存。

不幸的是,如果您要從32位進程轉到32位進程或從64位進程轉換到64位進程,事情非常簡單,但是從32位進程到64位進程可能會有點毛病。 VirtualAllocEx和WriteProcessMemory在32到64之間並不真正支持。您可能試圖強制VirtualAllocEx在64位內存空間的底部4GB中分配內存,從而使得結果指針對32位進程API調用有效,然後使用該指針進行寫入,從而取得成功。另外,您可能在兩種流程類型之間具有結構大小和打包差異。使用RECT是沒有問題的,但是有些包裝或對齊問題的其他結構可能需要手動逐個字段寫入64位進程以匹配64位結構佈局。