2009-07-06 44 views
11

RECT構造体の配列をIntPtrに変換しようとしています。そのため、PostMessageを使用して別のアプリケーションにポインタを送信できます。構造体の配列をIntPtrに変換する

[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 

これが最後の行(「指定された構造は、blittable型であるか、レイアウト情報を持っていなければなりません。」)にArgumentExceptionがを与えます。私は何とかこの配列のRECTをPostMessageを使って別のアプリケーションに渡す必要があるので、本当にこのデータへのポインタが必要です。

ここには何がありますか?

UPDATE:これは動作するようです:

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

がアービタにコメントして修正しAGAINを更新しました。

+0

自動的4 RECTSの配列のクロスプロセスマーシャリングを行うことは何のメッセージを投稿していますか? –

+0

画面の特定の領域を無視するDLL(64ビットであるため、別のプロセスでホストされている)に伝えようとしています。必ずしも4つのRECTであるとは限りません。 –

+0

更新プログラムに応じて、十分な領域(Marshal.SizeOf(typeof(RECT))ではなくintptr.size)を割り当てないでください。そして、x64マシンでポインタ演算が失敗することがあります。私の答えを見てください。 – arbiter

答えて

12

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

別のオプションを使用して、4つの整数のような構造を記述すること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); 
} 

最後にキーワードを使用して、というキーワードを使用して、ポインタで直接作業することができます。

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

これは、RECTの配列ではなく、RECTへのポインタの配列です。そして、ポインタ[i]ごとにポインタサイズのメモリブロックを割り当てていますが、その位置に16バイト(sizeof(RECT))をマーシャリングしています。このタイプのオーバーフローは災害のレシピです。 –

1

Arbiterは、構造体の配列をマーシャリングする方法について1つの良い答えを与えました。これらのようなblittable構造体では、個人的に、各要素を管理されていないメモリに手動でマーシャリングするのではなく、安全でないコードを使用します。

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

またはGCHandleを使用して配列を固定することができます。

残念ながら、この情報を別のプロセスに送信する必要があるとします。投稿しているメッセージが、Windowsが自動マーシャリングを提供するメッセージの1つでない場合、別の問題があります。ポインタはローカルプロセスからの相対的なものであるため、リモートプロセスでは何も意味しません。このポインタを使ってメッセージを送信すると、予期しないプログラムクラッシュなどの動作が発生します。ですから、あなたがしなければならないことは、自分のものではなく、他のプロセスのメモリーにRECT配列を書き込むことです。これを行うには、OpenProcessを使用してプロセスへのハンドルを取得し、VitualAllocExを使用して他のプロセスにメモリを割り当て、次にWriteProcessMemoryを使用してアレイを他のプロセスの仮想メモリに書き込む必要があります。

残念なことに、32ビットプロセスから32ビットプロセス、または64ビットプロセスから64ビットプロセスに移行する場合は、かなり簡単ですが、32ビットプロセスから64ビットプロセスに移行すると少し毛深くなります。 VirtualAllocExとWriteProcessMemoryは実際には32から64までサポートされていません。VirtualAllocExに64ビットメモリ空間の下位4GBにそのメモリを割り当てるように強制して、結果のポインタが32ビットプロセスAPI呼び出しに対して有効であるようにし、そのポインタで書き込むことで、成功する可能性があります。さらに、2つのプロセスタイプの間で構造体サイズとパッキングの違いがあるかもしれません。 RECTでは問題はありませんが、64ビット構造体のレイアウトに合わせるために、パッキングやアライメントの問題がある他の構造体をフィールド単位で64ビットプロセスに手作業で書き込む必要があるかもしれません。

関連する問題