将结构数组转换为IntPtr

时间:2022-04-26 00:59:00

I am trying to convert an array of the RECT structure (given below) into an IntPtr, so I can send the pointer using PostMessage to another application.

我试图将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

This gives an ArgumentException on the last line ("The specified structure must be blittable or have layout information."). I need to somehow get this array of RECTs over to another application using PostMessage, so I really need a pointer to this data.

这在最后一行给出了ArgumentException(“指定的结构必须是blittable或具有布局信息。”)。我需要以某种方式使用PostMessage将这个RECT数组转移到另一个应用程序,所以我真的需要一个指向这些数据的指针。

What are my options here?

我有什么选择?

UPDATE: This seems to work:

更新:这似乎工作:

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

UPDATED AGAIN to fix what arbiter commented on.

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

4 个解决方案

#1


14  

StructureToPtr expects struct object, and foo is not structure it is array, that is why exception occurs.

StructureToPtr需要struct对象,而foo不是结构,而是数组,这就是异常发生的原因。

I can suggest you to write structures in cycle (sadly, StructureToPtr does not have overload with Index):

我建议你在循环中编写结构(遗憾的是,StructureToPtr没有使用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));
}

Another option is to write structure as four integers, using Marshal.WriteInt32:

另一种选择是使用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);
}

And the last, you can use unsafe keyword, and work with pointers directly.

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

#2


1  

Arbiter has given you one good answer for how to marshal arrays of structs. For blittable structs like these I, personally, would use unsafe code rather than manually marshaling each element to unmanaged memory. Something like this:

Arbiter为你如何编组结构阵列提供了一个很好的答案。对于像这样的blittable结构,我个人会使用不安全的代码,而不是手动将每个元素编组到非托管内存。像这样的东西:

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

or you could pin the array using a GCHandle.

或者您可以使用GCHandle固定阵列。

Unfortunately, you say you need to send this information to another process. If the message you are posting is not one of the ones for which Windows provides automatic marshaling then you have another problem. Since the pointer is relative to the local process it means nothing in the remote process and posting a message with this pointer will cause unexpected behavior, including likely program crash. So what you need to do is write the RECT array to the other process' memory not your own. To do this you need to use OpenProcess to get a handle to the process, VitualAllocEx to allocate the memory in the other process and then WriteProcessMemory to write the array into the other process' virtual memory.

不幸的是,您说您需要将此信息发送给另一个进程。如果您发布的消息不是Windows提供自动封送的消息之一,那么您还有另一个问题。由于指针是相对于本地进程的,因此它在远程进程中没有任何意义,并且使用此指针发布消息将导致意外行为,包括可能的程序崩溃。所以你需要做的是将RECT数组写入另一个进程的内存而不是你自己的内存。要做到这一点,你需要使用OpenProcess来获取进程的句柄,使用VitualAllocEx在另一个进程中分配内存,然后使用WriteProcessMemory将数组写入另一个进程的虚拟内存。

Unfortunately again, if you are going from a 32bit process to a 32bit process or from a 64bit process to a 64bit process things are quite straightforward but from a 32bit process to a 64bit process things can get a little hairy. VirtualAllocEx and WriteProcessMemory are not really supported from 32 to 64. You may have success by trying to force VirtualAllocEx to allocate its memory in the bottom 4GB of the 64bit memory space so that the resultant pointer is valid for the 32bit process API calls and then write with that pointer. In addition, you may have struct size and packing differences between the two process types. With RECT there is no problem but some other structs with packing or alignment issues might need to be manually written field by field to the 64bit process in order to match the 64bit struct layout.

不幸的是,如果你从32位进程到32位进程或从64位进程到64位进程,事情是非常简单的,但从32位进程到64位进程,事情可能会有点毛茸茸。从32到64,实际上不支持VirtualAllocEx和WriteProcessMemory。您可以尝试强制VirtualAllocEx在64位内存空间的底部4GB中分配其内存,以便结果指针对32位进程API调用有效,然后写入用那个指针。此外,您可能在两种流程类型之间具有结构大小和包装差异。使用RECT没有问题,但是一些其他带有打包或对齐问题的结构可能需要手动逐字地写入64位进程以匹配64位结构布局。

#3


0  

You could try the following:

您可以尝试以下方法:

 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

And don't forget to free everything after you are finished.

完成后不要忘记释放所有东西。

#4


0  

I was unable to get this solution to work. So, I did some searching and the solution given here worked for me.

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

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

#1


14  

StructureToPtr expects struct object, and foo is not structure it is array, that is why exception occurs.

StructureToPtr需要struct对象,而foo不是结构,而是数组,这就是异常发生的原因。

I can suggest you to write structures in cycle (sadly, StructureToPtr does not have overload with Index):

我建议你在循环中编写结构(遗憾的是,StructureToPtr没有使用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));
}

Another option is to write structure as four integers, using Marshal.WriteInt32:

另一种选择是使用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);
}

And the last, you can use unsafe keyword, and work with pointers directly.

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

#2


1  

Arbiter has given you one good answer for how to marshal arrays of structs. For blittable structs like these I, personally, would use unsafe code rather than manually marshaling each element to unmanaged memory. Something like this:

Arbiter为你如何编组结构阵列提供了一个很好的答案。对于像这样的blittable结构,我个人会使用不安全的代码,而不是手动将每个元素编组到非托管内存。像这样的东西:

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

or you could pin the array using a GCHandle.

或者您可以使用GCHandle固定阵列。

Unfortunately, you say you need to send this information to another process. If the message you are posting is not one of the ones for which Windows provides automatic marshaling then you have another problem. Since the pointer is relative to the local process it means nothing in the remote process and posting a message with this pointer will cause unexpected behavior, including likely program crash. So what you need to do is write the RECT array to the other process' memory not your own. To do this you need to use OpenProcess to get a handle to the process, VitualAllocEx to allocate the memory in the other process and then WriteProcessMemory to write the array into the other process' virtual memory.

不幸的是,您说您需要将此信息发送给另一个进程。如果您发布的消息不是Windows提供自动封送的消息之一,那么您还有另一个问题。由于指针是相对于本地进程的,因此它在远程进程中没有任何意义,并且使用此指针发布消息将导致意外行为,包括可能的程序崩溃。所以你需要做的是将RECT数组写入另一个进程的内存而不是你自己的内存。要做到这一点,你需要使用OpenProcess来获取进程的句柄,使用VitualAllocEx在另一个进程中分配内存,然后使用WriteProcessMemory将数组写入另一个进程的虚拟内存。

Unfortunately again, if you are going from a 32bit process to a 32bit process or from a 64bit process to a 64bit process things are quite straightforward but from a 32bit process to a 64bit process things can get a little hairy. VirtualAllocEx and WriteProcessMemory are not really supported from 32 to 64. You may have success by trying to force VirtualAllocEx to allocate its memory in the bottom 4GB of the 64bit memory space so that the resultant pointer is valid for the 32bit process API calls and then write with that pointer. In addition, you may have struct size and packing differences between the two process types. With RECT there is no problem but some other structs with packing or alignment issues might need to be manually written field by field to the 64bit process in order to match the 64bit struct layout.

不幸的是,如果你从32位进程到32位进程或从64位进程到64位进程,事情是非常简单的,但从32位进程到64位进程,事情可能会有点毛茸茸。从32到64,实际上不支持VirtualAllocEx和WriteProcessMemory。您可以尝试强制VirtualAllocEx在64位内存空间的底部4GB中分配其内存,以便结果指针对32位进程API调用有效,然后写入用那个指针。此外,您可能在两种流程类型之间具有结构大小和包装差异。使用RECT没有问题,但是一些其他带有打包或对齐问题的结构可能需要手动逐字地写入64位进程以匹配64位结构布局。

#3


0  

You could try the following:

您可以尝试以下方法:

 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

And don't forget to free everything after you are finished.

完成后不要忘记释放所有东西。

#4


0  

I was unable to get this solution to work. So, I did some searching and the solution given here worked for me.

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

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