我正在尝试将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)));
}
再次更新以修复仲裁者评论的内容。
答案 0 :(得分:14)
StructureToPtr需要struct对象,而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 关键字,直接使用指针。
答案 1 :(得分: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位结构布局。
答案 2 :(得分: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
在完成后不要忘记释放所有内容。
答案 3 :(得分:0)
我无法让这个解决方案起作用。所以,我做了一些搜索,这里给出的解决方案对我有用。
http://social.msdn.microsoft.com/Forums/en-US/clr/thread/dcfd6310-b03b-4552-b4c7-6c11c115eb45