Laden...

Unmanaged Int-Pointer in managed float-Array umwandeln

Erstellt von dubbyconqueror vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.020 Views
D
dubbyconqueror Themenstarter:in
7 Beiträge seit 2009
vor 14 Jahren
Unmanaged Int-Pointer in managed float-Array umwandeln

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?

I
86 Beiträge seit 2006
vor 14 Jahren

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.

D
dubbyconqueror Themenstarter:in
7 Beiträge seit 2009
vor 14 Jahren

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

3.511 Beiträge seit 2005
vor 14 Jahren

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)

D
dubbyconqueror Themenstarter:in
7 Beiträge seit 2009
vor 14 Jahren

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

S
248 Beiträge seit 2008
vor 14 Jahren

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

D
dubbyconqueror Themenstarter:in
7 Beiträge seit 2009
vor 14 Jahren

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
1.361 Beiträge seit 2007
vor 14 Jahren

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

1.361 Beiträge seit 2007
vor 14 Jahren

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

D
dubbyconqueror Themenstarter:in
7 Beiträge seit 2009
vor 14 Jahren

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?

R
164 Beiträge seit 2008
vor 14 Jahren

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.

1.361 Beiträge seit 2007
vor 14 Jahren

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.

D
dubbyconqueror Themenstarter:in
7 Beiträge seit 2009
vor 14 Jahren

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);
}
1.361 Beiträge seit 2007
vor 14 Jahren

Hi

  1. In dem Speicherbereich, auf das hordePointer zeigt, liegen direkt die float-Daten drinne. Es sind keine Pointer.
IntPtr temp = Marshal.ReadIntPtr(hordePointer, i * Marshal.SizeOf(typeof(IntPtr)));

ist also totaler Quatsch. hordePointer ist schon der, den du brauchst.

  1. _Entweder _du geht in einer Schleife alles durch und extrahierst immer nur einen float-Wert, oder du verwendest Marshal.Copy. Beides zusammen ist wieder Quatsch 🙂

beste Grüße
zommi

D
dubbyconqueror Themenstarter:in
7 Beiträge seit 2009
vor 14 Jahren

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