11

我正在尝试将 RECT 结构的数组(如下所示)转换为 IntPtr,因此我可以使用 PostMessage 将指针发送到另一个应用程序。

[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 或具有布局信息。”)。我需要以某种方式使用 PostMessage 将这个 RECT 数组传递给另一个应用程序,所以我真的需要一个指向该数据的指针。

我在这里有什么选择?

更新:这似乎有效:

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

再次更新以修复仲裁者评论的内容。

4

4 回答 4

13

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

最后,您可以使用unsafe关键字,并直接使用指针。

于 2009-07-06T11:04:40.053 回答
1

关于如何编组结构数组,Arbiter 为您提供了一个很好的答案。对于像这样的 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 位进程,事情会变得有点麻烦。从 32 到 64 并不真正支持 VirtualAllocEx 和 WriteProcessMemory。您可以尝试强制 VirtualAllocEx 在 64 位内存空间的底部 4GB 中分配其内存,以便结果指针对 32 位进程 API 调用有效,然后写入用那个指针。此外,您可能在两种进程类型之间存在结构大小和打包差异。使用 RECT 没有问题,但可能需要逐个字段手动将其他一些具有打包或对齐问题的结构写入 64 位进程,以匹配 64 位结构布局。

于 2009-07-06T13:34:59.170 回答
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

完成后不要忘记释放所有内容。

于 2009-07-06T10:20:07.710 回答
0

我无法让这个解决方案发挥作用。所以,我做了一些搜索,这里给出的解决方案对我有用。

http://social.msdn.microsoft.com/Forums/en-US/clr/thread/dcfd6310-b03b-4552-b4c7-6c11c115eb45

于 2011-06-27T21:42:50.643 回答