11

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

アービターがコメントした内容を修正するために再度更新しました。

4

4 に答える 4

13

StructureToPtr は構造体オブジェクトを想定しており、foo は構造体ではなく配列であるため、例外が発生します。

サイクルで構造を記述することをお勧めします (残念ながら、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));
}

もう 1 つのオプションは、Marshal.WriteInt32 を使用して、構造体を 4 つの整数として書き込むことです。

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キーワードを使用して、ポインターを直接操作できます。

于 2009-07-06T11:04:40.053 に答える
1

アービターは、構造体の配列をマーシャリングする方法について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ビットプロセスに手動で書き込む必要がある場合があります。

于 2009-07-06T13:34:59.170 に答える
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

終了したら、すべてを解放することを忘れないでください。

于 2009-07-06T10:20:07.710 に答える
0

このソリューションを機能させることができませんでした。だから、私はいくつかの検索を行い、ここで与えられた解決策が私のために働いた.

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

于 2011-06-27T21:42:50.643 に答える