Laden...

Point-array von c# nach c++

Erstellt von onfire84 vor 14 Jahren Letzter Beitrag vor 12 Jahren 4.010 Views
O
onfire84 Themenstarter:in
11 Beiträge seit 2009
vor 14 Jahren
Point-array von c# nach c++

Hallo zusammen,

Ich habe zu dem folgendem Thema nichts gefunden, vllt. könnt ihr mir ja helfen.

Ich habe eine c++ DLL in der folgende Strukturen vorhanden sind:



typedef struct tagApiPolygonObjectV1_NEW 
{
   IN_OBJECT_HEADER      header;
   IN_V1_POLYGON_PARMS   parms;
   USHORT                pointCount;
   IN_POINT				 points[1];
} IN_V1_POLYGON_OBJECT_NEW;

typedef struct tagApiPoint {
   DOUBLE  x;
   DOUBLE  y;
} IN_POINT, FAR *IN_POINT_PTR;


Es geht mir an dieser Stelle um


IN_POINT				 points[1];

Nun versuche ich diese Strukturen in meiner eigenen c# DLL nachzubauen um dann mit meiner DLL auf die c++ DLL zuzugreifen.

Hierzu habe ich folgendes.


public struct tagApiPolygonObjectV1 
{
       public tagApiObjectHeader      header;
       public tagApiPolygonParmsV1    parms;
       public ushort                  pointCount;
       public point[]                  points;
}

public struct point
{
        public double x;
        public double y;
}

Mit folgendem Code habe ich versucht die dynamische allokierung des Point-arrays nachzubauen.


Points = new Kinapidll32.point[4];

 

            Points[0].x = 1.0;
            Points[0].y = 1.1;
            Points[1].x = 1.6;
            Points[1].y = 1.1;
            Points[2].x = 1.6;
            Points[2].y = 1.3;
            Points[3].x = 1.0;
            Points[3].y = 1.3;

            psBoxObject.points = Points;

Ich bekomme jedoch immer folgende Fehlermeldung:

Falscher Parameter. (Exception from HRESULT: 0x80070057 (E_INVALIDARG))

Wie muss ich diese Sache angehen damit ich diese Point-array-struktur in die c++ DLL überführen kann?

Btw. die c++ DLL ist auch von mir, d.h. es können an beiden DLL's Änderungen vorgenommen werden.

4.931 Beiträge seit 2008
vor 14 Jahren

Das Problem ist, daß in der C++ Deklaration die Struktur dynamisch ist (d.h. die Größe sich durch den Wert in pointCount bestimmt).
Mit der C# Deklaration 'point[] points' definierst du aber nur eine Referenz auf ein Array, d.h. die Größe deiner C#-Struktur bleibt konstant).

Ich denke, du mußt dafür stattdessen Marshal.AllocHGlobal(...) zum Reservieren der Struktur verwenden und die Größe dann entprechend selber ausrechnen.

S
8.746 Beiträge seit 2005
vor 14 Jahren

So ist es, also

public struct tagApiPolygonObjectV1
{
       public tagApiObjectHeader      header;
       public tagApiPolygonParmsV1    parms;
       public ushort                  pointCount;
       public IntPtr                  points;
}

O
onfire84 Themenstarter:in
11 Beiträge seit 2009
vor 14 Jahren

Das sind schonmal sehr hilfreiche Antworten. Werde ich Morgen früh direkt mal testen und euch das Ergebnis schreiben. Danke schonmal =)

S
248 Beiträge seit 2008
vor 14 Jahren

Hallo,

der Member points ist kein Zeiger auf ein PointArray sondern ein in die Struct eingebettetes Array von konstanter Größe 1.
Dadurch kann man wie bei einem Array auf die Punkte zugreifen. Es muss natürlich, wie Th69 schon geschrieben hatte, für jeden weiteren Punkt auch mehr Speicher allokiert werden:

tagApiPolygonObjectV1_NEW *poly =
   (tagApiPolygonObjectV1_NEW *)malloc(sizeof(tagApiPolygonObjectV1_NEW) + (sizeof(IN_POINT) * 3));
poly->pointCount = 4;
poly->points[0] = ...;
poly->points[1] = ...;
poly->points[2] = ...;
poly->points[3] = ...;

Spooky

S
8.746 Beiträge seit 2005
vor 14 Jahren

Tja, dann wirds mit .NET extrem unschön. Eingebettete Felder müssen für PInvoke konstant sein. Da muss die ganze Struktur über IntPtr verarbeitet werden.

S
248 Beiträge seit 2008
vor 14 Jahren

Hallo,

eine Möglichkeit wäre einen Custom Marshaler zu implementieren. Dann könnten die PInvokes zB so aussehen:

[DllImport("some.dll")]
private static extern bool SomeFunction(
    [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PolygonMarshaler))] Polygon polygon);

[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PolygonMarshaler))]
[DllImport("some.dll")]
private static extern Polygon SomeOtherFunction();

Die Implementierung könnte dann so aussehen:

public sealed class Polygon
{
    public Header header;
    public Parms parms;
    public PointD[] points;
}

[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct Header { }

[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct Parms { }

public struct PointD
{
    public double x;
    public double y;
}

internal sealed class PolygonMarshaler : ICustomMarshaler
{
    private unsafe struct Poly
    {
        public Header header;
        public Parms parms;
        public ushort pointCount;
        public fixed PointD points[1];
    }

    private static ICustomMarshaler m = new PolygonMarshaler();

    private static ICustomMarshaler GetInstance(string s)
    {
        return m;
    }

    #region ICustomMarshaler Member

    public void CleanUpManagedData(object ManagedObj)
    {

    }

    public void CleanUpNativeData(IntPtr pNativeData)
    {
        Marshal.FreeHGlobal(pNativeData);
    }

    public unsafe int GetNativeDataSize()
    {
        return sizeof(Poly);
    }

    public unsafe IntPtr MarshalManagedToNative(object ManagedObj)
    {
        if (!(ManagedObj is Polygon))
            return IntPtr.Zero;

        Polygon polygon = (Polygon)ManagedObj;

        int size = sizeof(Poly);
        if ((polygon.points != null) && (polygon.points.Length > 1))
            size += ((polygon.points.Length - 1) * sizeof(PointD));
        Poly* ptr = (Poly*)Marshal.AllocHGlobal(size);

        ptr->header = polygon.header;
        ptr->parms = polygon.parms;
        ptr->pointCount = (ushort)(polygon.points == null ? 0 : polygon.points.Length);
        for (int i = 0; i < ptr->pointCount; i++)
            ptr->points[i] = polygon.points[i];

        return (IntPtr)ptr;
    }

    public unsafe object MarshalNativeToManaged(IntPtr pNativeData)
    {
        if (pNativeData == IntPtr.Zero)
            return null;

        Polygon result = new Polygon();
        Poly* ptr = (Poly*)pNativeData;

        result.header = ptr->header;
        result.parms = ptr->parms;
        result.points = new PointD[ptr->pointCount];

        for (int i = 0; i < ptr->pointCount; i++)
            result.points[i] = ptr->points[i];

        return result;
    }

    #endregion
}

Hier fehlen noch die konkreten Implementierungen von Header und Parms.

Ich hoffe du kannst damit einen Einstieg finden. Vielleicht lässt sich das auch eleganter nur unter der Verwendung der unsafe struct Poly lösen, kommt ganz drauf an, ob und wie du die eigentliche Klasse verwenden willst.

Spooky

O
onfire84 Themenstarter:in
11 Beiträge seit 2009
vor 14 Jahren

So, konnte das Problem nun wie folgt lösen:

Da meine C# DLL mit der C++ DLL kommunizieren soll habe ich aus den Strukturen von beiden DLLs den Point-Array rausgenommen.

Dieser Array wird nun als Methodenparameter übergeben, z.B.:


Kinapidll32.point[] test = new Kinapidll32.point[4];

                test[0].x = 1;
                test[0].y = 1.1;
                test[1].x = 1.6;
                test[1].y = 1.1;
                test[2].x = 1.6;
                test[2].y = 1.3;
                test[3].x = 1.0;
                test[3].y = 1.3;

                iError = Kinapidll32.IN_PlaceVectorObject_Polygon(Testuint[3], Test_Polygon(), test);


IN_PlaceVectorObjectPolygon( IN_LAYER layerID, IN_V1_POLYGON_OBJECT_NEW pObjectDesc, IN_POINT *point)
{
  ...


for(i = 0; i < pObjectDesc.pointCount; i++ )
	{	
		psBoxObject->points[i].x = point[i].x;
		psBoxObject->points[i].y = point[i].y;
	}
}

Danke nochmals für eure Hilfe =)

G
10 Beiträge seit 2011
vor 12 Jahren
Ähnliches Problem

Hallo zusammen!

Ich habe ein sehr ähnliches Problem. Bei mir handelt es sich um eine C DLL mit der
Struktur:


typedef struct tScanPointCloud
 {
  int        PointCnt;
  tScanPoint *Point;
 };

typedef struct tScanPoint
 {
  tPoint3Df     Position;    // 3D Position
  tVector3Df    NormVect;    // 3D Surface Normal Vector (estimated)
  float         Quality;
 };

Die DLL Funktion, der diese Struktur übergeben wird, sieht so aus:


get3DDataCloudUpdate(t3DScannerThreadHandle Hnd, tScanPointCloud *pScanPointCloud);

Aufgerufen wird die Funktion in C/C++ also wie folgt:


tScanPointCloud pointCloud;
get3DDataCloudUpdate(ThreadHnd, &pointCloud);

Die Funktion wird in einem Callback aufgerufen, das mir u.a. die Anzahl der Punkte mitteilt.
Jetzt möchte ich die DLL in einem WPF C# Projekt nutzen. Meine Ansätze bis jetzt waren:

So implmentierte ich die Struktur in C#:

//[System.Runtime.InteropServices.StructLayoutAttribute (System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct TScanPointCloud
    {
        public int        PointCnt;
        public TScanPoint[] scanPoints;
        public TScanPointCloud(int pointCount)
        {
            PointCnt = new int();
            PointCnt = pointCount;
            scanPoints = new TScanPoint[pointCount];
        }
    };

Das Callback in dem ich die Funktion aufrufe sieht wie folgt aus:

private unsafe static void pointsCallback(int New3DPointCnt, IntPtr pUserData)
        {
            Param param = new Param();
            // Set this Param to the value of the Param in unmanaged memory. 
            param = (Param)Marshal.PtrToStructure(pUserData, typeof(Param));

            if (New3DPointCnt > 0)
            {
                tScanPointCloud parameter = new tScanPointCloud(New3DPointCnt);
                IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(parameter));
               
                // Copy the struct to unmanaged memory.
                Marshal.StructureToPtr(parameter, pnt, false);
                             
                get3DDataCloudUpdate(param.Handle, pnt);

                tScanPointCloud result = new tScanPointCloud();
                result = (TScanPointCloud)Marshal.PtrToStructure(pnt, typeof(tScanPointCloud));

                Marshal.FreeHGlobal(pnt);
            }            
        }

Bei der Zeile Marshal.StructurToPtr(... bekomme ich dann die ArgumentException (siehe Anhang).

Ich habe leider nicht so einfach die Möglichkeit (wie onfire84) das Struktur-Array aus der Struktur rauszunehmen und muss idealerweise eine anderer Möglichkeit finden...

Wär toll wenn jemand weiterhelfen kann! Danke im Voraus!

309 Beiträge seit 2008
vor 12 Jahren

Hallo,

also deine Struktur passt nicht.

  1. Warum ist das StructLayoutAttribute auskommentiert? Ohne Sequentielles Layout funktionierts nicht.

  2. Die Struktur in C# hat irgendwie nichts mit der in C++ zu tun. Wenn du einen Pointer auf eine Struktur hast ist es in C# ein IntPtr.

Also musste tScanPointCloud in C# so aussehen:


[System.Runtime.InteropServices.StructLayoutAttribute (System.Runtime.InteropServices.LayoutKind.Sequential)]
     public struct TScanPointCloud
     {
         public int        PointCnt;
         public IntPtr    Point;
     }

Dann musst dir Point ebenfalls erstmal mit PtrToStructure() marschallen.

Was sind tVector3Df ud tPoint3Df ? Ebenfalls Strukturen? Dann musst diese ebenfalls in C# definieren und marschallen.

Ist alles ein bisschen umständlich, aber funktioniert.

using System;class H{static string z(char[]c){string r="";for(int x=0;x<(677%666);x++)r+=c[
x];return r;}static void Main(){int[]c={798,218,229,592,232,274,813,585,229,842,275};char[]
b=new char[11];for(int p=0;p<((59%12));p++)b[p]=(char)(c[p]%121);Console.WriteLine(z(b));}}

G
10 Beiträge seit 2011
vor 12 Jahren

Danke für deine rasche Antwort!

Ich hab nun mal wie folgt umgebaut:

[System.Runtime.InteropServices.StructLayoutAttribute (System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct tPoint3Df
    {
        public float x, y, z;
    };

[System.Runtime.InteropServices.StructLayoutAttribute (System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct tVector3Df
    {
        public float dx, dy, dz;
    };

[System.Runtime.InteropServices.StructLayoutAttribute (System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct tScanPoint
    {
        public IntPtr Position;
        public IntPtr NormVect;    
        public float  Quality;
        public tScanPoint(IntPtr pos, IntPtr vec)
        {
            Position = pos;
            NormVect = vec;
            Quality = (float)0.0;
        }
    };

[System.Runtime.InteropServices.StructLayoutAttribute (System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct tScanPointCloud
    {
        public int PointCnt;
        public IntPtr Point;
    }

Sollte der Konstruktor von tScanPoint so aussehen? Brauche ich den überhaupt? Du hast ja für tScanPointCloud auch keinen vorgesehen. Aber wie definiere ich dann die dynamische Grösse, die sich ja bei jedem Callback - Aufruf ändert?


tPoint3Df point = new tPoint3Df();
IntPtr pntPoint = Marshal.AllocHGlobal(Marshal.SizeOf(point));
Marshal.StructureToPtr(point, pntPoint, false);

tVector3Df vector = new tVector3Df();
IntPtr pntVector = Marshal.AllocHGlobal(Marshal.SizeOf(vector));
Marshal.StructureToPtr(vector, pntVector, false);

tScanPoint scanPoint = new tScanPoint(pntPoint, pntVector); // ???
IntPtr pntScanPoint = Marshal.AllocHGlobal(Marshal.SizeOf(scanPoint));
Marshal.StructureToPtr(scanPoint, pntScanPoint, false);

tScanPointCloud parameter = new tScanPointCloud(New3DPointCnt); // ???


309 Beiträge seit 2008
vor 12 Jahren

Ah,

also ist "tScanPoint *Point" ein Zeiger auf ein Array von tScanPoint's?

Dann musst du schauen ob du dir dir Länge des Array/Anzahl der Array Elemente übergeben lassen kannst, damit genug Speicher reserviert werden muss.

Hab jetzt nicht viel Zeit. Heute Abend kann ich noch mal genauer antworten.

Edit:
Jetzt also etwas genauer: 😁
Hab überlesen das du ja einen Counter hast der die Elemente angibt. ^^

Probier mal folgendes aus (ungetestet), funktioniert aber nur wenn das Array auf dem Heap liegt im Stack wird's dir abgeräumt.

Die Konstruktoren in den structs sind unnötig, der Speicher muss manuell reserviert werden.


// Speicher für tScanPointCloud reservierem
int scanPointCloudSize = Marshal.SizeOf(typeof(tScanPointCloud));
IntPtr ptr = Marshal.AllocHGlobal(scanPointCloudSize);

get3DDataCloudUpdate(param.Handle, ptr);

// In die verwaltete Struktur kopieren
tScanPointCloud scanPointCloud = (tScanPointCloud)Marshal.PtrToStructure(ptr, typeof(tScanPointCloud));

int tScanPointSize = Marshal.SizeOf(typeof(tScanPoint));

// Gesamtes Array in verwalteten Buffer kopieren
byte[] buffer = new byte[tScanPointSize * scanPointCloud.PointCnt];
Marshal.Copy(scanPointCloud.Point, buffer, 0, buffer.Length);

// Verwaltetes Array 
tScanPoint[] scanPointArray = new tScanPoint[scanPointCloud.PointCnt];

int offset = 0;
for (int i = 0; i < scanPointCloud.PointCnt; i++)
{
    scanPointArray[i] = (tScanPoint)ByteArrayToStruct(buffer, offset, typeof(tScanPoint));
    offset += tScanPointSize;
}

Die ByteArrayToStruct() gibts hier: Byte-Array direkt in ein struct "kopieren"

using System;class H{static string z(char[]c){string r="";for(int x=0;x<(677%666);x++)r+=c[
x];return r;}static void Main(){int[]c={798,218,229,592,232,274,813,585,229,842,275};char[]
b=new char[11];for(int p=0;p<((59%12));p++)b[p]=(char)(c[p]%121);Console.WriteLine(z(b));}}