DLL-Import mit Zeiger auf Funktion als Parameter

sTEk

Erfahrenes Mitglied
Wie der Titel schon verrät, ist es etwas kniffelig.
Ich habe eine C-DLL, die mir bis dato auch kaum Schwierigkeiten gemacht hat. Leider habe ich jetzt eine Funktion zu importieren, die wiederum eine Funktion als Parameter hat.
Die zu extrahierende Funktion reagiert auf ein vorher festgelegtes Event und ruft eine - selbst definierte - Funktion auf. Letztere wird als Zeiger übergeben.

Dummerweise habe ich keine Ahnung, wie ich das in C# realisiere.


Das sieht im C-Header so aus:
Code:
typedef void (*FUNCTION)(_int32 /*Eventmaske*/, _uint8 * /*Daten*/);

_uint32 Event(
_uint32 hdl,
FUNCTION callback
);

In einem C-Beispiel wird das folgendermaßen gelöst:
Code:
  result = Event(Handle, &myCallBack); 
void myCallBack(_uint32 eventMask, _uint8 * data) {...}


Bei mir hakts beim Import:
Code:
        [DllImport("beispiel.dll", EntryPoint = "Event")]
        public static extern UInt32 RegisterEvent(UInt32 handle, /*was kommt hier rein*/);

Nebenbei habe ich die Funktionen in einer eigenen Bibliothek hinterlegt, damit man darauf auch aus späteren C#-Projekten zugreifen kann. Somit ist die zu übergebende Funktion bzw. der Pointer darauf ja noch nicht fest...
 
Zuletzt bearbeitet:
.NET bietet Hilfe beim Wechsel zwischen managed und unmanaged (welcher die C Bibliothek ist) Code, genannt Marshalling. Dabei werden u.a. managed Datentypen (z.B. string) in die am ehesten entsprechenden C Datentypen umgewandelt (z.B. NULL terminiertes char array). Welche Variable in welchen Typ umgewandelt werden soll teilt man dem .NET-Framework mittels des MarshalAs-Attributs mit, das beim Funktionsimport aus der nativen DLL verwendet wird.

Code:
[DllImport("library.dll")]
void sampleFunction(int Arg1,
    [MarshalAs(UnmanagedType.LPStr)]
    string Arg2);

/** C Definition:
 * void sampleFunction(int Arg1, char *Arg2);
 */

Um einen Funktionspointer zu uebergeben, kann man einen delegate benutzen, das Argument wird dann mit MarshalAs(UnmanagedType.FunctionPtr) gekennzeichnet.
Mehr Informationen findest du in der MSDN:
http://msdn2.microsoft.com/en-us/library/system.runtime.interopservices.unmanagedtype.aspx

Allerdings gibt es bei der Verwendung von Funktionszeigern noch viel mehr Fallen als es beim normalen Interop eh schon gibt. Eine Alternative waere z.B das Argument mit dem Zeiger zum IntPtr zu machen (nichts anderes als ein Pointer), der dann auf eine unmanaged Funktion zeigt (den Zeiger holst du dir mit Hilfe einer anderen Funktion):

Code:
[DllImport("library.dll")]
IntPtr getFnPtr();

[DllImport("library.dll")]
void callbackFunction(int arg1, IntPtr fn);

void Main()
{
    callbackFunction(123, getFnPtr());
}

Ich hoffe, das war einigermassen verstaendlich ;).
 
Hi - danke für Deine Antwort. Inzwischen konnte ich es (abschließend heut morgen) mit etwas Hilfe lösen.
Das problem war zusätzlich, dass das data in der Übergabefunktion wiederum ein Zeiger auf eine Struktur ist, die dazu verschiedene Strukturtypen annehmen kann.
Das ganze war kniffliger als gedacht - so hats dann aber geklappt:

C#:
public delegate void uebergabefunk(UInt32 hdl, IntPtr data);
[DllImport("iBeanAPI.dll", EntryPoint = "Event")]
public extern static UInt32 RegisterEvent(UInt32 handle, uebergabefunk data);

Main {
       Uebergabe = new uebergabefunk(Auswertung);
       RegisterEvent(handle, Uebergabe);
}

private void Auswertung(UInt32 events, IntPtr data)
{
       //DataStructures.DEVICE_EVENT_SERIELRECEIVE ist eine mit der in der C-DLL übereinstimmende Struktur
       //anstatt dieser Struktur kann bei data aber auch andere hinterlegt sein, dass wird durch events angezeigt (hier aber nicht ausgewertet)
       DataStructures.DEVICE_EVENT_SERIELRECEIVE daten;
       daten = (DataStructures.DEVICE_EVENT_SERIELRECEIVE)System.Runtime.InteropServices.Marshal.PtrToStructure(
                     data,
                     typeof(DataStructures.DEVICE_EVENT_SERIELRECEIVE)
                     );
...
}
 
Zuletzt bearbeitet von einem Moderator:

Neue Beiträge

Zurück