2016-10-11 5 views
0

Я пишу обертку C# для родной библиотеки. Она содержит эту функцию обратного вызова:C# native callback с указателем на параметр struct

typedef application_event_result(*application_event_ptr)(application_request* request); 

Параметр определяется как, например:

typedef struct { 
    uint32_t query; 
    const char* client; 
    bool  isAuthenticated; 
    bool  isGuest; 
} application_request; 

я определил C# обратного вызова делегата, как это:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate application_event_result application_event([MarshalAs(UnmanagedType.Struct)] 
     ref application_request request); 

Структура в C#:

[StructLayout(LayoutKind.Sequential)] 
public struct application_request 
{ 
    public UInt32 query; 

    [MarshalAs(UnmanagedType.LPStr)] 
    public string client; 

    [MarshalAs(UnmanagedType.I1)] 
    public bool isAuthenticated; 

    [MarshalAs(UnmanagedType.I1)] 
    public bool isGuest; 
} 

Это все кажется для работы. Обратный вызов в C# запускается, а члены структуры имеют ожидаемые значения.

Но после возврата к собственному коду инициируется исключение кучи (0xc0000374).

Очевидно, я хотел бы избежать этого.

Если я изменяю сигнатуру обратного вызова C#, чтобы использовать IntPtr вместо параметра ref ref_request, а затем маршал его вручную, используя следующий код, который он работает.

var request = Marshal.PtrToStructure<application_request>(requestptr); 

Но я хотел бы, чтобы подпись была максимально точной и не должна была использовать Маршалер.

Есть ли у меня возможность изменить подпись делегата callback, так что .net может автоматически преобразовывать структуру?

+0

Ошибка возникает из-за несоответствия размера возвращаемого параметра. IntPtr имеет четыре байта, поэтому вы должны объявить в C# возвращаемый параметр как четыре байта. Расположение указателя также должно быть в статическом пространстве памяти для работы кода c и C#. IntPtr будет работать, поскольку он находится в неуправляемом статическом пространстве памяти. Управляется объект C#, который даст ошибку, если он помещен в неуправляемое пространство. – jdweng

+0

Проблема заключается не в возвращаемом значении, а в параметре ввода указателя на структуру. Есть ли способ отметить это, поэтому он помещается в неуправляемое пространство? – RasmusW

+0

@jdweng Возвращаемое значение выглядит как перечисление нет? –

ответ

1

Ваша проблема: char* член struct. Маршрутизатор C# предполагает, что он отвечает за освобождение этой памяти. Он делает это, вызывая CoTaskMemFree. Я думаю, что довольно ясно, что память не предназначена для уничтожения кодом C#.

Маршал, который состоит в IntPtr вместо:

[StructLayout(LayoutKind.Sequential)] 
public struct application_request 
{ 
    public UInt32 query; 

    public IntPtr client; 

    [MarshalAs(UnmanagedType.I1)] 
    public bool isAuthenticated; 

    [MarshalAs(UnmanagedType.I1)] 
    public bool isGuest; 
} 

Внутри вашего метода обратного вызова вы можете прочитать значение строки с помощью вызова Marshal.PtrToStringAnsi.

Вы оберните это, сделав элементы закрытыми и разоблачая их через свойства. Это позволит вам инкапсулировать преобразование из указателя в строку.

+0

Удивительный, это работает. И спасибо за хорошую идею обернуть IntPtr в свойстве, которое делает преобразование: public string Client => Маршал.PtrToStringAnsi (клиент); Я не знал, что свойства структуры могут быть частными, а маршалер C# все еще может их найти. – RasmusW