Laden...

Struct von C++ nach C#

Erstellt von clever vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.563 Views
C
clever Themenstarter:in
5 Beiträge seit 2009
vor 14 Jahren
Struct von C++ nach C#

Hi,

Hab ein C++ header file, dass ich nach C# exportieren muß.


// C++

typedef struct {
	LVBoolean error;
	long code;
	LStrHandle source;
	} TD1;

typedef struct {
	LStrHandle sourceValue;
	LStrHandle measureValue;
	LStrHandle delayValueSec;
	LStrHandle timeValueSec;
	} TD3;

typedef struct {
	long dimSize;
	TD3 elt[1];
	} TD2;
typedef TD2 **TD2Hdl;


void __stdcall ReadMeasurement(char GPIBAddress[], TD1 *errorIn, 
	double *suppressionValue, char selectedFunction[], TD2Hdl *display, 
	TD1 *errorOut, long len);

Habs versucht jedoch noch ohne Erfolg

// C#

[StructLayout(LayoutKind.Sequential)]
public struct TD1
{
    public ushort error;
    public long code;
    public string source;
};


[StructLayout(LayoutKind.Sequential)]
public struct TD3
{
    public string sourceValue;
    public string measureValue;
    public string delayValueSec;
    public string timeValueSec;
};


[StructLayout(LayoutKind.Sequential)]
public struct TD2
{
    public long dimSize;
    public TD3 elt;
};


[DllImport("Read_Measurement.dll", EntryPoint = "ReadMeasurement",
    ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
    public static extern int ReadMeasurement(string GPIBAddress, ref TD1 errorIn, 
	                                                 ref double suppressionValue, string selectedFunction, 
                                                     ref TD2 display, ref TD1 errorOut, long len);



private void button1_Click(object sender, EventArgs e)
{
   errorIn = new TD1();
   errorOut = new TD1();
   display = new TD2();

error =ReadMeasurement(16, ref errorIn, ref suppresionValue, "", ref display, ref errorOut, 1);
}

Das Objekt display (TD3 sctruct) zeigt immer null Werte.

Wahrscheinlich liegt es an dem struct, dass ich nicht korrekt in C# umwandeln kann.

typedef struct {
	long dimSize;
	TD3 elt[1];
	} TD2;
typedef TD2 **TD2Hdl;

Weiß jemand Rat?

Vielen Dank im Vorraus.

1.361 Beiträge seit 2007
vor 14 Jahren

Hi,

da in deinen C/C++ Strukturen spezielle Datentypen verwendet werden, musst du auch beim Umwandeln besonders aufpassen. C# kann das ja nicht ahnen.

Aus den Namen schließe ich, dass du was mit LabVIEW zu tun hast.
Da musst du dich dann über die LabVIEW Datentypen schlau machen und das Standardmäßige Marschalling-Verhalten in .Net entsprechend anpassen.

Zu LVBoolean:

LVBoolean 	8-bit integer, 0 if FALSE, 1 if TRUE

erforder:

UnmanagedType.U1       1-Byte-Ganzzahl, wobei der Wert 1 für true und der Wert 0 für false steht.
LVBoolean 	8-bit integer, 0 if FALSE, 1 if TRUE

erforder:

UnmanagedType.U1       1-Byte-Ganzzahl, wobei der Wert 1 für true und der Wert 0 für false steht.

Zu LStrHandle:

typedef struct {

    int32 cnt;

    /* number of bytes that follow */

    uChar str[1];

    /* cnt bytes */

    } LStr, *LStrPtr, **LStrHandle;

Ist also im Prinzip ein char-Array mit vorgestellter Länge. Aber dein LStrHandle ist ein Pointer auf einen Pointer auf solch ein Objekt. Das kannst du nicht direkt marshallen lassen.
Dafür gibts nichts entsprechendes in .Net.
Entweder du schreibst dir selbe nen Marshaller, oder du machst das per Hand.
Du erwartest also nen IntPtr. Liest dort wieder den IntPtr aus und daraus dann die LStr-entsprechende Struktur.

Alles nicht so einfach 😃

beste Grüße
zommi

S
248 Beiträge seit 2008
vor 14 Jahren

Hallo,

auch den Datentyp long solltest du genauer anschauen, da dieser normalerweile 4 byte groß ist, welches ein int in .NET wäre.

Spooky

C
clever Themenstarter:in
5 Beiträge seit 2009
vor 14 Jahren

Danke für die Antworten.

Hast du ein Beispiel, dass zeigt wie man einen IntPtr ausliest?

Danke.

1.361 Beiträge seit 2007
vor 14 Jahren

Hi clever,

beispielsweise könntest du für deine TD1-Struktur folgendes nehmen:

    [StructLayout(LayoutKind.Sequential)]
    struct TD1
    {
        [MarshalAs(UnmanagedType.U1)]
	    public bool Error;

	    public int Code;

	    private IntPtr sourcePtr;

        public string Source
        {
            get
            {
                if (sourcePtr == IntPtr.Zero)
                    return null;

                IntPtr plstr = Marshal.ReadIntPtr(sourcePtr);
                if (plstr == IntPtr.Zero)
                    return null;

                int strLength = Marshal.ReadInt32(plstr);
                IntPtr strData = new IntPtr(plstr.ToInt32() + 4);
                return Marshal.PtrToStringAnsi(strData, strLength);
            }
        }
	}

Habs zwar jetzt nicht getestet, aber im Prinzip so.

beste Grüße
zommi

S
248 Beiträge seit 2008
vor 14 Jahren
IntPtr strData = new IntPtr(plstr.ToInt32() + 4);

sollte noch in

IntPtr strData = new IntPtr(unchecked(plstr.ToInt64() + 4));

geändert werden um auch unter 64bit zu funktionieren. Ggf könnte man die Offset Operation noch in eine sperate Methode mit Over-/Underflow Check auslagern.

Spooky

C
clever Themenstarter:in
5 Beiträge seit 2009
vor 14 Jahren

Danke. Ich werds mal ausprobieren und dann berichten obs ging.

C
clever Themenstarter:in
5 Beiträge seit 2009
vor 14 Jahren

Die Struktur TD1 funktioniert soweit in C#.

Nur werde ich nicht schlau, wie man die nächtsten Zeilen nach C# konvertiert.


typedef struct {
	LStrHandle sourceValue;
	LStrHandle measureValue;
	LStrHandle delayValueSec;
	LStrHandle timeValueSec;
	} TD3;

typedef struct {
	long dimSize;
	TD3 elt[1];
	} TD2;

typedef TD2 **TD2Hdl;


void __stdcall ReadMeasurement(char GPIBAddress[], TD1 *errorIn, 
	double *suppressionValue, char selectedFunction[], TD2Hdl *display, 
	TD1 *errorOut, long len);
[DllImport("Keith_236_Read_Measurement.dll", EntryPoint = "Keith236ReadMeasurement",
    ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
    public static extern int ReadMeasurement(string GPIBAddress, ref TD1 errorIn, 
	                                                 ref double suppressionValue, string selectedFunction, 
                                                     ref TD2 display, ref TD1 errorOut, int len);

[StructLayout(LayoutKind.Sequential)]
public struct TD3
{
    //public string sourceValue;


    private IntPtr sourcePtr;

    public string sourceValue
    {
        get
        {
            if (sourcePtr == IntPtr.Zero)
                return null;

            IntPtr plstr = Marshal.ReadIntPtr(sourcePtr);
            if (plstr == IntPtr.Zero)
                return null;

            int strLength = Marshal.ReadInt32(plstr);
            //IntPtr strData = new IntPtr(plstr.ToInt32() + 4);
            IntPtr strData = new IntPtr(unchecked(plstr.ToInt64() + 4));
            return Marshal.PtrToStringAnsi(strData, strLength);
        }
    }


    public string measureValue;
    public string delayValueSec;
    public string timeValueSec;
};

[StructLayout(LayoutKind.Sequential)]
public struct TD2
{
    public int dimSize;
    public TD3 elt;
};

Hab da mal reingeschaut
[http://msdn.microsoft.com/de-de/library/eshywdt7.aspx]Marshallen von Klassen, Strukturen und Unions](http://msdn.microsoft.com/de-de/library/eshywdt7.aspx]Marshallen%20von%20Klassen,%20Strukturen%20und%20Unions)

aber konnte nicht wirklich was ableiten.

Könnt ihr mir da weiterhelfen?

44 Beiträge seit 2008
vor 14 Jahren

Ich würde dir empfehlen, mal das PInovke Signature Toolkit anzuschauen:
http://www.codeplex.com/clrinterop/Release/ProjectReleases.aspx?ReleaseId=14120
Trage dort einfach deine C++ struct ein und erhältst als Output die struct in C#.

Probiers mal aus.
Vielleicht hilft es dir.

C
clever Themenstarter:in
5 Beiträge seit 2009
vor 14 Jahren

Funktinioniert nicht.

Das Tool kennt leider kein LStrHandle und andere Namen aus meinem Header file.

Beim Versuch eine Dll Datei zu laden, sagt das Tool

Could not load file or assemly ... The module was expected to contain an assembly manifest.

Die Dll Dateien habe ich aus LabView exportiert.

Vielleicht gibts eine Möglichkeit das assembly manifest nachträglich einzufügen?