2009-07-06 13 views
11

Ich versuche, ein Array der RECT-Struktur (siehe unten) in eine IntPtr zu konvertieren, damit ich den Zeiger mit PostMessage an eine andere Anwendung senden kann.Konvertieren Array von Strukturen zu 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 

Dieses ein Argument in der letzten Zeile gibt („Die angegebene Struktur muss blitfähig sein oder Layout-Informationen haben.“). Ich muss irgendwie dieses Array von RECTs mit PostMessage auf eine andere Anwendung übertragen, also brauche ich wirklich einen Zeiger auf diese Daten.

Was sind meine Optionen hier?

UPDATE: Dies scheint zu funktionieren:

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

WIEDER AKTUALISIERT zu beheben, was Schiedsrichter kommentiert.

+0

Welche Botschaft Mitteilung verfassen Sie das tut automatisch Cross-Prozess Serialisieren einer Anordnung von 4 Rects? –

+0

Ich versuche, eine DLL (die in einem anderen Prozess gehostet wird, weil es 64-Bit ist) zu bestimmten Bereichen des Bildschirms zu ignorieren. Es sind nicht unbedingt 4 RECTs. –

+0

Entsprechend Ihrer Aktualisierung, Sie nicht genügend Speicherplatz zuweisen (intptr.size anstelle von Marshal.SizeOf (typeof (RECT))). Und die Zeigerarithmetik kann auf x64-Maschinen fehlschlagen, siehe meine Antwort. – arbiter

Antwort

12

StructureToPtr erwartet struct-Objekt, und foo ist keine Struktur, es ist Array, deshalb tritt eine Ausnahme auf.

kann ich Sie vorschlagen Strukturen in Zyklus schreiben (leider nicht StructureToPtr nicht Überlastung mit Index hat):

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

Eine weitere Option ist Struktur als vier ganzen Zahlen zu schreiben, mit 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); 
} 

Und das letzte, können Sie unsafe Schlüsselwort verwenden und mit Zeigern direkt arbeiten.

0

Sie könnten versuchen, die folgenden:

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 

Und vergessen Sie nicht, alles zu befreien, nachdem Sie fertig sind.

+3

Außer dass dies ein Array von Zeigern zu RECTs ist kein Array von RECTs. Und Sie ordnen Speicherblöcke mit Zeigergröße für jeden der Zeiger [i] zu, aber ordnen dann 16 Bytes (sizeof (RECT)) an diesen Punkt zu. Diese Art von Überlauf ist ein Rezept für eine Katastrophe. –

1

Arbiter hat Ihnen eine gute Antwort gegeben, wie Sie Arrays von Strukturen marshallen können. Für blitable Strukturen wie diese würde ich persönlich unsicheren Code verwenden, anstatt jedes Element manuell in den nicht verwalteten Speicher zu marshallen. Etwas wie das:

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

oder Sie könnten das Array mit einem GCHandle Pin.

Leider sagen Sie, dass Sie diese Informationen an einen anderen Prozess senden müssen. Wenn die Nachricht, die Sie verfassen, nicht zu denen gehört, für die Windows das automatische Marshalling bereitstellt, haben Sie ein anderes Problem. Da der Zeiger relativ zum lokalen Prozess ist, bedeutet dies nichts in dem entfernten Prozess und das Posten einer Nachricht mit diesem Zeiger führt zu unerwartetem Verhalten, einschließlich eines wahrscheinlichen Programmabsturzes. Sie müssen also das RECT-Array in den Speicher des anderen Prozesses schreiben, nicht in Ihren eigenen. Dazu müssen Sie OpenProcess verwenden, um eine Kennung für den Prozess zu erhalten, VitualAllocEx, um den Speicher in dem anderen Prozess zuzuweisen, und WriteProcessMemory, um das Array in den virtuellen Prozess des anderen Prozesses zu schreiben.

Leider, wenn Sie von einem 32-Bit-Prozess zu einem 32-Bit-Prozess oder von einem 64-Bit-Prozess zu einem 64-Bit-Prozess gehen, sind die Dinge ziemlich einfach, aber von einem 32-Bit-Prozess zu einem 64-Bit-Prozess können die Dinge ein wenig haarig werden. VirtualAllocEx und WriteProcessMemory werden nicht wirklich von 32 bis 64 unterstützt.Sie haben möglicherweise Erfolg, indem Sie versuchen, VirtualAllocEx zu zwingen, seinen Speicher in den unteren 4 GB des 64-Bit-Speicherbereichs zuzuweisen, so dass der resultierende Zeiger für die 32-Bit-Prozess-API-Aufrufe gültig ist und dann mit diesem Zeiger schreibt. Darüber hinaus haben Sie möglicherweise Strukturgröße und Packungsunterschiede zwischen den beiden Prozesstypen. Mit RECT gibt es kein Problem, aber einige andere Strukturen mit Pack- oder Ausrichtungsproblemen müssen möglicherweise Feld für Feld manuell in den 64-Bit-Prozess geschrieben werden, um dem 64-Bit-Strukturlayout zu entsprechen.

Verwandte Themen