Pointer in C# - aber Vermeidung von unsafe Code



  • Hallo zusammen ich stehe jetzt schon geraume Zeit vor einem kleinen Problem.
    Wie so oft schon besprochen importiere ich Funktionen per DllImport aus C-Dlls und das klappt auch teilweise.
    Jetzt habe ich aber eine Funktion vor mir die vom Hersteller so deklariert ist:

    <Return-Wert> = i_APCI1710_SetBoardInformation(BYTE b_SlotNumber,PBYTE pb_BoardHandle)

    Meine bisherige Funktion und der AUfruf sehen so aus, leider verursache ich damit nen Laufzeitfehler da der Befehl "written" auf irgendwelche Speichersegmente nicht ausgeführt werden kann.

    [DllImport("APCI1710.dll")]
    public static extern int i_APCI1710_SetBoardInformation (byte b_SlotNumberArray, byte b_BoardHandleArray);
    
    public static int APCI1710_SetBoardInformation(byte slota, byte slotb)
    {
       return i_APCI1710_SetBoardInformation(slota, slotb);
    }
    

    Mein Aufruf dazu:

    i_ReturnValue = apci1710.APCI1710_SetBoardInformation(b_SlotNumberArray[i_Count], b_BoardHandleArray[i_Count]);
    

    Ich habe dato schon Hinweise auf Ref/Out oder Marshalling gesammelt, schaffe es aber momentan nicht in Kontext zu bringen. Hat da jemand ne Idee wie ich das ohne Unsafe Code zu benutzen da den geforderten Parameter übergeben kann?



  • Dir ist schon klar, daß "PBYTE" etwas anderes ist als "byte", oder?

    Der Linker hat keine Ahnung von Parameterlisten und Typkonzepten - er sucht nur nach Funktionen mit dem richtigen Namen und fügt sie zu einem fertigen Programm zusammen. Im Endeffekt hast sieht der C-Compiler eine Funktion, die einen BYTE und einen Zeiger entgegennimmt (und intern wird der Zeiger vermutlich dereferenziert) und der C#-Compiler eine Funktion, die zwei BYTE per Wert entgegennimmt - im jeweiligen Kontext passt alles und da beide Funktionen den selben Namen haben, ist auch der Linker glücklich. Aber zur Laufzeit kracht es dann, weil du an die C-Funktion zwei BYTE übergibst und sie dann versucht, das zweite zu dereferenzieren.



  • Nun die Sache ist diese:

    Ich habe die Funktion auch schon mal mit char aufgerufen, mit selbigem Ergebnis da das mitgelieferte Sample des Herstellers so aussieht:

    [cpp]
    int _tmain(int argc, _TCHAR* argv[])
    {
    unsigned char b_BoardHandleArray [8]; // APCI-1710 Board Handle
    .
    .
    i_NbrOfBoardsFound = Initialisation (b_BoardHandleArray);
    .
    .
    }[cpp]

    int Initialisation (unsigned char * pb_BoardHandleArray)
    {
    .
    .
    i_ReturnValue = i_APCI1710_SetBoardInformation (b_SlotNumberArray [i_Count],
    &pb_BoardHandleArray [i_Count]);
    .
    .
    }

    Vorab, war ich mir dessen nicht bewusst ich habe das p einfach als hinweis auf Pointer interpretiert. Wenn man aber das Sample ansieht ist es doch ein gewöhnliches char/byte Array...



  • C kennt keine Referenzen, deswegen wird "call by reference" üblicherweise mit Pointern implementiert - d.h. du übergibst nicht die Variable selber (das würde nur den Wert kopieren und Änderungen in der Funktion haben keine Wirkung nach außen), sondern ihre Adresse. (C++ hat Referenzen eingeführt, die da einiges automatisieren können - die genaue Syntax in C# kenne ich nicht)

    Btw, für dein Problem ist es eigentlich unerheblich, daß die Werte in einem Array stehen - du könntest genausogut zwei Einzelvariablen verwenden:

    BYTE i,j;int ret;
    ret = i_APCI1710_SetBoardInformation (i/*Übergabe als Wert*/,&j/*Übergabe als Adresse*/);
    


  • Ja da hast du recht, ich habe auch schon mal versucht die Werte vorher einfach aus dem Array auszulesen und einzeln zu übergeben.

    Das Problem bei deiner Lösung ist das C# keine & in einer solchen Methode zulässt mit folgender Begründung:

    error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
    error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer

    Womit wir wieder bei dem Problem wären ob man da ohne Unsafe (wobei ich das unterdessen auchs chon erfolglos veruscht habe) überhaupt weiterkommt



  • Ja, der obige Code war ja auch für die C-Seite gedacht.

    Randfrage: Wie unterscheidet denn C# zwischen Wert- und Referenz-Parametern? Vielleicht reicht es ja aus, den zweiten Parameter als call-by-reference anzugeben.



  • CStoll schrieb:

    Randfrage: Wie unterscheidet denn C# zwischen Wert- und Referenz-Parametern? Vielleicht reicht es ja aus, den zweiten Parameter als call-by-reference anzugeben.

    Ja, sollte. Einfach den Parameter als 'ref' oder 'out' deklarieren. (Unterschied: 'ref' erwartet einen gültigen Zeiger, 'out' nicht, sondern nimmt an, dass dieser in der Methode erzeugt wird).


Anmelden zum Antworten