Hallo zusammen!
Ich verwende eine Funktion aus einer Library, deren Signatur die folgende ist:
public static bool getNodeTransformMatrices(int node, out IntPtr relMat, out IntPtr absMat)
Letztendlich soll mir diese Funktion über den out-Paramerter absMat eine 4x4-Matrix liefern, die mit float-Werten gefüllt ist. Mit diesen float-Werten möchte ich mir dann meine eigene C#-Matrix befüllen. Zunächst einmal möchte ich erstmal alle Werte der von der Funktion zurückgegebenen Matrix ausgeben.
Dafür hole ich mir erstmal den IntPointer aus der Funktion per
IntPtr hordePointer;
// Matrix in intPointer schreiben (den Pointer "test" brauche ich nicht, ist hier nebensächlich)
Horde3D.getNodeTransformMatrices(env, out test, out hordePointer);
Ich erhalte einen Intpointer, der nun selbst laut Marshal.SizeOf(hordePointer) wieder aus 4 Intpointern besteht. Ich baue mir also ein Intpointer-Array auf:
IntPtr[] testArray = new IntPtr[4];
// get the 4 intpointers out of the hordepointer
for (int i = 0; i < Marshal.SizeOf(hordePointer); i++) {
testArray[i] = Marshal.ReadIntPtr(hordePointer, i * Marshal.SizeOf(typeof (IntPtr)));
}
Jeder dieser 4 Int-Pointer zeigt nun auf jeweils 4 Float-Werte, bzw. enthält jeder dieser 4 Pointer (meiner Meinung nach) wiederum 4 Pointer auf jeweils 1 Float-Wert.
Meine Frage nun: Wie hole ich mir diese Float-Werte aus den Pointern heraus? Habe schon verschiedenstes versucht, leider scheine ich aber an meinem fehlenden Pointer-Verständnis zu scheitern...
Hat jemand schonmal mit Pointern in C# gearbeitet und weiss wie man das handlen kann?
Ich erhalte einen Intpointer, der nun selbst laut Marshal.SizeOf(hordePointer) wieder aus 4 Intpointern besteht.
Wie kommst du darauf, dass der Pointer aus 4 anderen Pointern besteht? Welchen Wert gibt die Funktion zurück?
Meiner Meinung nach zeigt der Pointer auf den ersten der 16 Float-Werte.
Console.WriteLine(Marshal.SizeOf(hordePointer))
>Ausgabe ist 4
Dachte auch zuerst das es wohl 16 sind, aber wenn ich da 4 rausbekommen werdens wohl 4x4 sein
Marshal.SizeOf liefert dir zur zurück, wieviel Byte für die übergebene Variable verbraten werden. Da ein IntPtr auf einem x86 System immer 4 Byte groß ist, kommt natürlich 4 zurück. Das heißt ja nicht, das da wiederum 4 weitere drin sind.
Hat die Library die du dazu benutzt keine weiteren Infos? Oder gibt es in der Library ein struct welches auf diesen Pointer zeigt?
"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)
Logisch, danke schonmal für den Tipp. Natürlich sind es 4 Bytes... Okay meine Annahme war ziemlich unsinnig.
Also die Library ist übrigens eine Wrapper-Library, die die .net-aufrufe auf eine C-Library umsetzt und die Rückgabe an .net liefert, mithilfe dieser out-Variablen.
Im C-Teil würde man die Funktion aufrufen, in dem man ihr direkt einen Float-Pointer übergibt - am Ende hat man dort dann die komplette Matrix drin, mit 16 Elementen.
float *pxf;
Horde3D :: getNodeTransformMatrices (plane , NULL , ( const float **) &pxf );
Dementsprechend müsste ich bei C# also auch einen IntPtr mit 16 Elementen zurückbekommen. Leider versteh ich immernoch nicht so ganz, wie ich die float-Elemente nun korrekt herausziehen kann.
Habe das erstmal so:
IntPtr nullPointer;
IntPtr hordePointer;
// nullPointer wird nicht benötigt
Horde3D.getNodeTransformMatrices(env, out nullPointer, out hordePointer);
for (int i = 0; i < 16; i++) {
Console.WriteLine(Marshal.ReadIntPtr(hordePointer, i * Marshal.SizeOf(typeof(IntPtr))));
}
Als Ausgabe bekommen ich
1050253722
0
0
0
0
1050253722
0
0
0
0
1050253722
0
0
0
0
1065353216
Wie muss ich diese Werte jetzt interpretieren? Sind das Speicheradressen? Zu Intpointern? Wie komme ich nun an die float-Werte ran?
Zur Not könnte ich auch einen unsafe-Codebereich in C# mit Pointern benutzen (schöner wär aber ohne).
Hallo,
public static float[][] GetMatrix(IntPtr ptr)
{
float[][] result = new float[4][];
for (int i = 0; i < 4; i++)
{
IntPtr arrayPtr = Marshal.ReadIntPtr(ptr, i);
float[] floats = new float[4];
Marshal.Copy(arrayPtr, floats, 0, 4);
result[i] = floats;
}
return result;
}
So könnte es funktioneren, wenn es ein einfach verschachteltes Array ist.
Spooky
Vielen Dank spooky, das hört sich an sich schonmal ziemlich gut an.
Habe das mal eingebaut und getestet. Leider bekomme ich beim Ausführen eine Exception:
chance exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Diese tritt bei jedem der 4 Aufrufe von Marshal.Copy auf. Habe mit Marshal.Copy noch etwas herumprobiert... Egal was ich versuche, ich bekomme immer diese Exception. Auch wenn ich z.B. nur die ersten Elemente haben will:
IntPtr nullPointer;
IntPtr hordePointer;
// nullPointer wird nicht benötigt
Horde3D.getNodeTransformMatrices(_cam, out nullPointer, out hordePointer);
IntPtr arrayPtr = Marshal.ReadIntPtr(hordePointer, 0); // ab offset 0 lesen
float[] floats = new float[4];
Marshal.Copy(arrayPtr, floats, 0, 4); // von offset 0 an kopieren
Hi,
ich vermute mal, dass es kein verschachteltes Array (a la [4][4]) ist sondern hintereinander weg (also[16]). Somit ist es klar, dass du eine System.AccessViolationException bekommst, da das, was da drin steht eben kein Pointer auf einen gültigen Datenbereich ist, sondern bereits deine Daten!
beste Grüße
zommi
Deine Daten oben (die 16 Stück hintereinander weg) sind übrigens vollkommen korrekt ermittelt. Es sind eben nur keine IntPtr, sondern gleich floats.
Wenn du 1050253722 binär als float interpretierst ist das 0,3
und 1065353216 entspricht 1,0.
Ergibt also:
[PRE]0,3 0 0 0
0 0,3 0 0
0 0 0,3 0
0 0 0 1,0[/PRE]
zommi
uiuiui... danke für den tipp zommi. das klingt schon sehr nach der matrix die ich bekommen will... wie wandle ich den wert des intptr denn in float um? Eigentlich müsste ich da langsam selbst drauf kommen aber vielleicht hast du da ja noch nen schnelle tipp?
Mit der Marshal.ReadByte-Methode kann man einzelne Bytes aus einer Bytesequenz lesen, auf die der IntPtr zeigt. Ein float ist ein 32-Bit-Typ, er besteht also aus 4 Bytes. Die 4 Bytes müssen nun ausgelesen und mit BitConverter.ToSingle hintereinander gereiht werden.
byte[] readBytes = new byte[4];
readBytes[0] = Marshal.ReadByte(ptrToFloat, 0);
readBytes[1] = Marshal.ReadByte(ptrToFloat, 1);
readBytes[2] = Marshal.ReadByte(ptrToFloat, 2);
readBytes[3] = Marshal.ReadByte(ptrToFloat, 3);
float result = BitConverter.ToSingle(readBytes, 0);
Man kann auch die Marshal.PtrToStructure-Methode verwenden. Ich würde diese Möglichkeit bevorzugen, weil sie kürzer und möglicherweise schneller ist.
float result = (float)Marshal.PtrToStructure(ptrToFloat, typeof(float));
Ich habe den Code nicht getestet und weiß nicht, ob er funktioniert.
Man sollte wohl lieber den von Spook vorgeschlagenen Weg verfolgen.
Zumal es sich um ein Array von Floats handelt.
(Nur hatte Spook die Array-Anordnung anders interpretiert)
also:
float[] floats = new float[16];
Marshal.Copy(pointer, floats, 0, 16);
beste Grüße
zommi
Und ja:
Eigentlich müsste ich da langsam selbst drauf kommen
solltest du.
Das hört sich ja alles gut an, aber letztendlich lande ich aber jedem Versuch wieder bei einer AccessViolationException. Wobei diese Exceptions immer schon im ersten Schleifendurchlauf auftreten - ich schaffe es nichtmal, einen einzelnen float zu extrahieren.. Hier nochmal anschaulicherweise alle bisherigen Tipps in meinem Programm zusammengefasst (habe immer nur eine sache auf einmal probiert und die anderen auskommentiert):
IntPtr nullPointer;
IntPtr hordePointer;
// nullPointer wird nicht benötigt
Horde3D.getNodeTransformMatrices(_cam, out nullPointer, out hordePointer);
for (int i = 0; i < 16; i++) {
IntPtr temp = Marshal.ReadIntPtr(hordePointer, i * Marshal.SizeOf(typeof(IntPtr)));
// AccessViolationException, die bis zur Main aufsteigt
float result = (float)Marshal.PtrToStructure(temp, typeof(float));
// AccessViolationException, die direkt hier auftritt
float[] floats = new float[16];
Marshal.Copy(temp, floats, 0, 16);
// schon im ersten readBytes tritt eine Exception auf (ratet mal, welche)
byte[] readBytes = new byte[4];
readBytes[0] = Marshal.ReadByte(temp, 0);
readBytes[1] = Marshal.ReadByte(temp, 1);
readBytes[2] = Marshal.ReadByte(temp, 2);
readBytes[3] = Marshal.ReadByte(temp, 3);
float result = BitConverter.ToSingle(readBytes, 0);
}
Hi
IntPtr temp = Marshal.ReadIntPtr(hordePointer, i * Marshal.SizeOf(typeof(IntPtr)));
ist also totaler Quatsch. hordePointer ist schon der, den du brauchst.
beste Grüße
zommi
ahem... das war tatsächlich totaler Quatsch. Im Hordepointer stecken natürlich alle floats drin. Jedenfalls... es FUNKTIONIERT!!! Es steckt auch genau die Matrix drin, die ich erwartet habe.
Man war das ein K(r)ampf 😉
Vielen Dank an alle Helfer, ihr seid super!
Hier meine finale Lösung:
IntPtr nullPointer;
IntPtr hordePointer;
// nullPointer wird nicht benötigt
Horde3D.getNodeTransformMatrices(_cam, out nullPointer, out hordePointer);
float[] floats = new float[16];
Marshal.Copy(hordePointer, floats, 0, 16);