2009-07-06 95 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位结构布局。