Ein Struktur-Feld fester Größe in einer Struktur.



  • Hallo Profis,

    wie kann ich folgendes Problem lösen:

    public unsafe struct Struktur1
    {
        fixed byte feld[2];
    }
    
    public unsafe struct Struktur2
    {
        ushort a;
        fixed Struktur1 feld[10];
    }
    

    Struktur 1 ist ja kein Problem, aber die Struktur2, weil

    fixed
    

    auf keine Strukturen anwendbar ist.

    Danke für Eure Hilfe!!! 👍

    VG

    Paul



  • public struct Struktur
    { 
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public byte[] feld]; 
    } 
    
    public struct Struktur2 
    { 
        public ushort a;
    
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        public Struktur1[] feld; 
    }
    

    ...könnte vielleicht gehen, die struct-Elemente sollte man vermutlich vorher einmal initialisieren, kp 😉

    (Ich nehme mal an du machst da Interop-Zeugs?, StructLayout-Attribut mit LayoutKind.Sequential und evtl. Pack=1 würde ich noch angeben...)



  • Hi geeky,

    Klasse! Es hat gefunzt, DAANKE. Auch StructLayout-Attribut mit LayoutKind.Sequential und Pack=1 habe ich angegeben.

    Soweit so gut. Beim Übersetzen meckert VS nicht mehr. Nun bin ich an ein weiteres Problem gestoßen. So wie es aussieht sind die verschachtelten Strukturen für C# zu komplex (wie kann es eigentlich sein?). Die Fehlermeldung lautet:

    *"Eine nicht benahdelte Ausnahme des Typs "System.Runtime.InteropServices.MarshalDirectiveException" ist in CSharp.exe aufgetreten.

    Zusätzliche Informationen: "parameter #3" kann nicht gemarshallt werden: Interne Einschränkung: Die Struktur ist zu komplex oder zu groß.."*

    Wie kann ich es umgehen?



  • Was immer funktioniert aber umständlich ist, ist statt der Struktur einfach IntPtr anzugeben und sich den Kram via Marshal.ReadInt32() etc. selbst in managed-Objekte zu kloppen...



  • Probier mal beides über UnmanagedType.ByValArray zu machen, statt 1x fixed und 1x UnmanagedType.ByValArray . Nur so ne Idee, keine Ahnung ob es was bringt.



  • Hallo,

    @hustbaer: das habe ich bereits gemacht.

    @geeky: kannst du mir bitte anhand eines Beispiels zeigen, wie du es meinst?

    Zusatzinfo (vielleicht hilft es weiter):

    die Funktion, die ich ansprechen möchte hat folgende Form (Beschreibung in der Dokumentation):

    DPR_DWORD DP_get_pointer(
        DPR_WORD user_handle,             //in
        DPR_WORD timeout,                 //in
        DPR_CP5613_DP_T volatile **dpr,   //out
        DP_ERROR_T *error);               //out
    

    in C# sieht der Code folgendermaßen aus:

    //Import aus DLL
    [DllImport("dp_base.dll", CharSet = CharSet.Ansi)]
    private static extern uint DP_get_pointer(DPR_DWORD user_handle, // in 
                                              DPR_DWORD timeout, // in 
                                              out DPR_CP5613_DP_T dpr, // out 
                                              out DP_ERROR_T  error ); // out
    
    //Deklaration
    DPR_DWORD Antwort;
    DPR_DWORD user_hand;
    uint timeout_get_pointer = 5000;
    DPR_CP5613_DP_T dpr;
    DP_ERROR_T err = new DP_ERROR_T();
    
    //Aufruf der Funktion
    Antwort = DP_get_pointer(user_hand, timeout_get_pointer, out dpr, out err);
    

    Die Meldung kommt beim dritten Parameter (dpr). 😞



  • Hallo zusammen,

    ich habe es nun so gelöst

    public struct Struktur
    {
        public static byte[] feld = new byte[2];
    }
    
    public struct Struktur2
    {
        public ushort a;
        public static Struktur1[] feld = new Struktur1[10];
    }
    

    Es scheint zu funktionieren. Sagt mir bitte, ob es durch CLR zu Problemen im Fall einer Optimierung geben kann? Da kenne ich mich leider viel zu schlecht aus.


  • Administrator

    Wie sieht denn die Struktur in C aus? Was du da gemacht hat, kann fast nicht funktionieren, da du statische Felder verwendet hast. Die sind in einem Objekt gar nicht vorhanden! Ein Objekt vom Typ Struktur hat somit eine Grösse von 0, bzw. aus technischen Gründen wahrscheinlich 1.

    Die Struktur kann nicht übersetzt werden, weil du wahrscheinlich ein Array von nicht blittable Typen hast. Sowas ist wirklich verdammt kompliziert, um es richtig an die Funktion zu übergeben und zurückzuerhalten. Da wären einige Schritte nötig, um das ganze korrekt rumzukopieren.

    Grundsätzlich kann dies aber ganz einfach anpassen. Wenn du ein Array mit konstanter Grösse von 2 hast, dann mach doch einfach 2 einzelne Bytes in die Struktur. Also sowas:

    [StructLayout(LayoutKind.Sequential)]
    struct Inner
    {
      public byte data0;
      public byte data1;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    struct Outer
    {
      public ushort data;
    
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
      public Inner[] inner;
    }
    

    Was mich allerdings bei deinem präsentierten C Code etwas stutzig macht, ist der doppelte Zeiger bei der C Funktion. Was macht die Funktion? Befüllt sie die Struktur oder erstellt sie die Struktur und gibt dir einen Zeiger darauf zurück? Bei solchen doppelten Zeiger Zeug muss man bei P/Invoke verdammt vorsichtig sein, da P/Invoke der COM-Speicherverwaltung folgt und die ist etwas gewöhnungsbedürftig. Wenn Zeiger auf Speicher zurückgegeben werden (gilt auch, wenn sie per Referenz zurückkommen), dann wird der Speicher darauf automatisch per CoTaskMemFree freigegeben.

    Grüssli



  • Hallo Dravere,

    vielen Dank für deine Mühe. Leider funktioniert dieser Ansatz nicht, weil die Strukturen recht komplex sind. Beim Übersetzen meckert der Compiler nicht, aber bei der Übergabe der Struktur an die DLL-Funktion: "Eine nicht benahdelte Ausnahme des Typs "System.Runtime.InteropServices.MarshalDirectiveException" ist in CSharp.exe aufgetreten. Zusätzliche Informationen: "parameter #3" kann nicht gemarshallt werden: Interne Einschränkung: Die Struktur ist zu komplex oder zu groß.."
    Da bin ich echt am Verzweifeln... 😞 Wenn es nur daran scheitern soll, war die ganze Arbeit für die Katz!

    Zu deiner Frage zum Thema "was macht die Funktion":

    in der Doku wird dieser Parameter so beschrieben:
    "... Adresse eines vom DP-Anwenderprogramm
    bereitgestellten Zeigers. Im Erfolgsfall wird hier die
    Zugriffsadresse auf das Prozessabbild des
    CP 5613/CP 5614 eingetragen - Mit Hilfe dieses Zeigers
    kann das DP-Anwenderprogramm anschließend direkt auf
    das Datenabbild des CP 5613/CP 5614 zugreifen."


  • Administrator

    C(esar) schrieb:

    Da bin ich echt am Verzweifeln... 😞 Wenn es nur daran scheitern soll, war die ganze Arbeit für die Katz!

    Ich habe bis jetzt alles durch den Marshaler bekommen, daran wird es schon nicht scheitern. Es ist nur eine Frage der nötigen Information und Zeit und letzteres habe ich aktuell leider nicht gerade im Überfluss 🙂

    C(esar) schrieb:

    Zu deiner Frage zum Thema "was macht die Funktion":

    in der Doku wird dieser Parameter so beschrieben:
    "... Adresse eines vom DP-Anwenderprogramm
    bereitgestellten Zeigers. Im Erfolgsfall wird hier die
    Zugriffsadresse auf das Prozessabbild des
    CP 5613/CP 5614 eingetragen - Mit Hilfe dieses Zeigers
    kann das DP-Anwenderprogramm anschließend direkt auf
    das Datenabbild des CP 5613/CP 5614 zugreifen."

    Das klingt aber so, also ob du nur einen Zeiger auf die Struktur zurückbekommst und daher nicht eine Struktur übergeben muss. Aus dem zurückerhaltenen Zeiger musst du dann die Struktur auslesen. Dann machst du bereits etwas grundlegendes falsch.

    [DllImport("dp_base.dll", CharSet = CharSet.Ansi)]
    private static extern uint DP_get_pointer(
      DPR_DWORD user_handle, // in
      DPR_DWORD timeout, // in 
      out IntPtr dpr, // out 
      out DP_ERROR_T  error ); // out
    

    Aus dem Zeiger müsste man nun die Struktur auslesen, was dann wie folgt geht:

    object obj = Marshal.PtrToStructure(ptr, typeid(...));
    

    Desweiteren was mir auch noch auffällt, verwendest du eine normale C DLL? Wie sind die C Funktionen definiert? Sind das normale exportierte C Funktionen oder wurden sie mit __stdcall versehen? Wenn sie nicht mit __stdcall versehen wurden, dann hast du hier eine weitere Fehlerquelle, da du beim Import angeben müsstest, dass sie über __cdecl aufgerufen werden.

    Und wie gesagt, ich wäre noch recht froh, wenn du die tatsächlich C Struktur angeben würdest, welche du da marschalen möchtest. Dann könnte ich dies allenfalls auch in einem Testprojekt schnell durchprobieren. Allerdings vielleicht nicht gerade heute oder morgen 😉

    Grüssli



  • Hi Dravere,

    vielen Dank für deine Mühe,

    ich glaube mittlerweile es wird einfach zu viel Aufwand all diese Definitionen usw. in C# umzuschreiben.

    Ich versuche jetzt ein Wrappermodul in C++ zu schreiben und im C# dann die vereinfachte FUnktionen über DllImport einzubinden. Ich denke insgesamt wird es die bessere Lösung. Oder was denkst du?

    VG

    Paul


  • Administrator

    Ist eine Möglichkeit.
    Eine weitere wäre es, die Vereinfachung gleich in C++/CLI zu schreiben. Dadurch musst du gar kein P/Invoke mehr einsetzen.

    Grüssli



  • Hallo,

    ich wollte nur berichten, welchen Ansatz ich im Moment verfolge, der eigentlich auch ganz gut funktioniert.

    In C++/CLI werden zwei Klassen definiert: managed und unmanaged. Die Managed-Klasse stellt vereinfachte Methoden mit C#-konformen Parameter dem C# zur Verfügung. In der Managed-Klasse wird ein Objekt der Unmanaged-Klasse erzeugt. In den Managed-Methoden, die eine Referenz an die Unmanaged-Methode weiterleiten müssen, wird vorher die Referenz mit pin_ptr festgenagelt.

    Noch habe ich es nicht mit den Strukturen ausprobiert (wie Dravere es vorgeschlagen hat). Da müsste ich die StructLayout-Attribute verwenden, wenn ich es recht verstanden habe. Aber ich denke, es sollte an sich auch kein Problem sein.

    In diesem Sinne: DANKE an alle Beteiligten!

    VG

    Paul


Anmelden zum Antworten