Laden...

Wie übersetze ich einen Pointer of Pointer (C++) in C#?

Erstellt von Yeats vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.921 Views
Hinweis von Coffeebean vor 7 Jahren

Abgeteilt von Wie übersetze ich das Feld "char szAccess[2]" aus einer C++ lib in C#?

Y
Yeats Themenstarter:in
102 Beiträge seit 2005
vor 7 Jahren

@weismat, @Spook: Danke für eure Antwort. Glaub das ist was ich gesucht hab.

Habe dazu noch eine Frage:

typedef struct tagXCVarInfos
{
	XCVARLIST		hVarList;
	XCVarValue		**ppValues;
	XCSymbolDesc	**ppSymbolList;
	enum TypeClass	*pTypeClass;
	ULONG			ulNumberOfSymbols;
	ULONG			ulChannel;
}XCVarInfos;

Wie kann ich in aus dieser Struktur den Pointer of Pointer übersetzen?

W
872 Beiträge seit 2005
vor 7 Jahren

Schau Dir mal den Artikel an.
Es kommt auch ein bisschen darauf an, ob Du nur lesen oder auch schreiben musst.
Wenn Du nur lesen musst, dann würde ich einfach einen IntPtr nehmen und dann eine Funktion zum Auspacken bauen.

Y
Yeats Themenstarter:in
102 Beiträge seit 2005
vor 7 Jahren

Danke für die Antwort!

Habe nun mal versucht alles zusammen zu bringen.

BOOL  XC_CommUpdateValues(XCVarInfos* pInfos) 

ist die API Schnittestelle die ich ansprechen möchte. Diese Funktion nimmt einen Pointer of type XCVarInfos an. In *pInfos steht der Channel mit dem eine SPS angesprochen wird. Dort werden Daten aus einem Register gelesen und in einem Callback werden dann die Ergebnisse zurück geliefert.

Callback:

typedef BOOL  (CALLBACK *GATEWAYCALLBACK) (DWORD , ULONG, DWORD, LRESULT);

In C#:


[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void GatewayCallback(uint arg0, uint arg1, uint arg2, IntPtr arg3);

Der Callback scheint zumindest zu funktionieren, da ich hiermit z.B. Status der SPS, oder auch den Channel geliefert bekomme.

Laut der Doku soll nun die Funktion XC_CommUpdateValues ebenfalls einen Callback auslösen wo ich im arg3 den Pointer zu dem Result bekommen soll.

In meinem Projekt habe ich hierfür diese Wrapper Methode definiert:

        [DllImport(@"XC_Comm.dll",
            CharSet = CharSet.Ansi,
            EntryPoint = "XC_CommUpdateValues",
            SetLastError = true,
            CallingConvention = _callingConvention)]
        public static extern bool XC_CommUpdateValues(XCVarInfos pInfos);

Die zusätzlichen Typen die in der XCVarInfo Struktur benötigt werden:


typedef struct tagXCVarValue
{
	ULONG ulTimeStamp;
	BYTE  bQuality;
	BYTE  byData[1];
}XCVarValue;

typedef struct tagXCSymbolDesc
{
	LPSTR pszName;
	LPSTR pszType;
	WORD  uRefId;
	ULONG ulOffset;
	ULONG ulSize;
	char  szAccess[2];
}XCSymbolDesc;

typedef void* XCVARLIST;

Das ergibt dann bei mir alles zusammen:


    [StructLayout(LayoutKind.Sequential)]
    public class XCVarInfos
    {
        public IntPtr hVarList;
        public IntPtr ppValues;
        public IntPtr ppSymbolList;
        public TypeClass pTypeClass;
        public uint ulNumberOfSymbols;
        public uint ulChannel;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class XCSymbolDesc
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string pszName;

        [MarshalAs(UnmanagedType.LPStr)]
        public string pszType;
        public ushort uRefId;
        public uint ulOffset;
        public uint ulSize;

        [MarshalAs(UnmanagedType.ByValArray ,SizeConst = 2)]
        public char[] szAccess;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class XCVarValue
    {
        public uint ulTimeStamp;
        public char bQuality;

        [MarshalAs(UnmanagedType.ByValArray)]
        public char[] byData;
    }

Damit rufe ich nun meinen Wrapper auf:


_xcVarInfos = new XCVarInfos() { ulChannel = _channel , hVarList = new IntPtr()};

var value = new XCVarValue[5];
var pValues = (IntPtr)Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(XCVarValue)) * value.Length);
GCHandle gch = GCHandle.Alloc(pValues, GCHandleType.Pinned);
_xcVarInfos.ppValues = (IntPtr)gch.AddrOfPinnedObject();

var symbolList = new XCSymbolDesc[5];
var pSymbolList = (IntPtr)Marshal.AllocCoTaskMem(Marshal.SizeOf<XCSymbolDesc>() * symbolList.Length);
GCHandle gchSymbolList = GCHandle.Alloc(pSymbolList, GCHandleType.Pinned);
_xcVarInfos.ppSymbolList = (IntPtr)
gchSymbolList.AddrOfPinnedObject();
Wrapper.XC_CommUpdateValues(_xcVarInfos);

Habe ich hier nun mal vom Prinzip die Typen richtig definiert?

Mfg

W
872 Beiträge seit 2005
vor 7 Jahren

2 Tips noch:

  1. In C# gibt es genauso wie in C++ den sizeof Operator.
    Ich würde mittels zwei kleiner Testprogramme vergleichen, dass die Länge übereinstimmen. Unter Umständen gibt es noch Alignment Probleme, die Du so findest.

2.) Bei einem Callback musst Du aufpassen, dass Du mit einer statischen Variable für das Delegate im Managed Bereich arbeiten musst, damit der Callback nicht vom Garbage Collector nach dem Ende der Methode weggeräumt wird. Der Garbage Collector zählt nur Referenzen im Managed Bereich und denkt sonst, dass die Funktionsreferenz nicht mehr gebraucht wird. Das ist ein typisches Problem, was man oft erst in Produktion merkt ,wenn dann nach x Aufrufen auf einmal alles zusammenbricht.

Y
Yeats Themenstarter:in
102 Beiträge seit 2005
vor 7 Jahren

Erstmal, Danke weismat.

Habe noch eine Verständnisfrage.

Pointer of Pointer bedeutet ja so viel wie:
Es gibt einen Speicherbereich. Auf diesen Speicher zeigt ein Pointer. Der Speicherbereich dieses Pointers wird dann nochmals referenziert.

Wenn dies nun in einer IntPtr Variable steht, kann ich nun einfach mittels Marshal.PtrToStructure diesen Pointer zurück in meine Klasse umwandeln?

Laut Doku sollte ppValues auf ein Array zeigen.

var values = Marshal.PtrToStructure<XCVarValue>(info.ppValues);

Bei Marshal.PtrToStructure bekomme ich nun ein Objekt, aber kein Array. Wie kann ich das umsetzen?

Mfg

W
872 Beiträge seit 2005
vor 7 Jahren

Du solltest die Länge des Arrays wissen.
Dann kannst Du mittels Pointer-Arithmetik/StartAdresse + Index*sizeof(Struct) jedes Element des Arrays einzeln mit Marshal.PtrToStructure umwandeln und kopieren.

Y
Yeats Themenstarter:in
102 Beiträge seit 2005
vor 7 Jahren

Hallo,

Danke für deine Antwort!

Die Anzahl ist mir bekannt. Heißt das ich sollte das so schreiben können:


var list = new List<XCSymbolDesc>();

for (int index = 0; index < countSymbols; index++)
{
var pointer = info.ppSymbolList + Marshal.SizeOf<XCSymbolDesc>() * index;
list.Add(Marshal.PtrToStructure<XCSymbolDesc>(pointer));
}

Wobei hier ppSymbolList laut Api so in C++ definiert ist: XCSymbolDesc **ppSymbolList.

Mein Problem hierbei, sobald index = 1 bekomme ich eine "AccessViolationException: Attepmted to read or write protected memory."

Das nächste Problem ist, dass das erste Objekt das ich raus bekomme, die Daten falsch sind. Die Werte passen nicht mit dem zusammen was ich aus einer funktionierenden C++ Anwendung heraus bekomme.

Grüße

16.807 Beiträge seit 2008
vor 7 Jahren
Y
Yeats Themenstarter:in
102 Beiträge seit 2005
vor 7 Jahren

@Abt:
Danke für den Hinweis.
Soweit mir bekannt kann sizeof nur die Größe von Valuetypes ermitteln. Da die Klasse oder von mir aus auch das struct XCSymbolDesc aber zwei string Fields beinhaltet, kann doch sizeof nicht mehr verwendet werden, oder täusche ich mich?

W
872 Beiträge seit 2005
vor 7 Jahren

Du musst mit Structs, nicht mit Classes arbeiten.
C-Typen sind Werte, keine Referenzen.
Wie gesagt - schreibe Dir Dummy C++ und C# Programme, die nur mit sizeof die Länge der Strukturen vergleichen - erst wenn das übereinstimmt, dann darfst Du weitermachen.
Die Alternative in Deinem Fall wäre sonst Marshall.Copy zum Kopieren von Arrays am Stück.