12

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.

[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.

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
  • What message are you posting that automatically does cross-process marshaling of an array of 4 RECTS? Commented Jul 6, 2009 at 10:39
  • I am attempting to tell a DLL (which is hosted in another process because it is 64-bit) to ignore certain regions of the screen. It is not necessarily 4 RECTs. Commented Jul 6, 2009 at 10:45
  • According to your update, you do not allocating enough space (intptr.size instead of Marshal.SizeOf(typeof(RECT))). And you pointer arithmetic can fail on x64 machines, see my answer. Commented Jul 6, 2009 at 11:10
  • Actually, the pointer arithmetic won't fail, as the executable this code is in is strictly 32-bit, and can never become 64-bit. :) But you are correct about the allocations. Commented Jul 6, 2009 at 11:52

3 Answers 3

14

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

I can suggest you to write structures in cycle (sadly, StructureToPtr does not have overload with 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:

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.

Sign up to request clarification or add additional context in comments.

Comments

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:

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

or you could pin the array using a 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.

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.

Comments

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.

1 Comment

Except that this is an array of pointers to RECTs not an array of RECTs. And you are allocating pointer sized blocks of memory for each of the pointers[i] but then marshaling 16 bytes (sizeof(RECT)) to that spot. This type of overflow is a recipe for disaster.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.