Преобразование массива структур в IntPtr

Я пытаюсь преобразовать массив структуры 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 или иметь информацию о макете."). Мне нужно как-то получить этот массив RECTs в другое приложение с помощью PostMessage, поэтому мне действительно нужен указатель на эти данные.

Каковы мои варианты здесь?

обновление: это, кажется, работает:

 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 ответов


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

другой вариант-записать структуру в виде четырех целых чисел, используя Маршал.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);
}

и последнее, вы можете использовать небезопасных ключевое слово и работа с указателями напрямую.


арбитр дал вам один хороший ответ на то, как маршалировать массивы структур. Для blittable структур, подобных этим, я лично использовал бы небезопасный код, а не вручную маршалинг каждого элемента в неуправляемую память. Что-то вроде этого:--2-->

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 выделить свою память в нижней части 4 ГБ 64-битной памяти пробел, так что результирующий указатель действителен для вызовов API 32-битного процесса, а затем записывает с этим указателем. Кроме того, у вас могут быть различия в размерах структуры и упаковки между двумя типами процессов. С RECT нет проблем, но некоторые другие структуры с проблемами упаковки или выравнивания могут быть вручную записаны поле за полем в 64-битный процесс, чтобы соответствовать макету 64-битной структуры.


вы можете попробовать следующее:

 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

и не забудьте освободить все после того, как вы закончите.


Я не смог заставить это решение работать. Итак, я сделал некоторый поиск, и решение, данное здесь, сработало для меня.

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