2012-04-12 5 views
6

Я пытаюсь создать обертку в C dll, и я пытаюсь вызвать функцию, которая принимает функцию обратного вызова, получает объект как указатель, который передается обратно.Как мне вызвать функцию в C++ Dll из C#, которая имеет void * callback и параметр объекта

заголовочных файл delares

extern int SetErrorHandler(void (*handler) (int, const char*, void*), 
           void *data_ptr); 

Обработчик является функцией обратного вызова, которая вызывается, когда происходит ошибка и data_ptr любой предмет (состояние), которое передается обратно к вам, в случае моего приложение, которое будет именно этим (текущий объект).

Я могу вызвать функции в dll, которая использует marshalled константные типы, такие как простые типы строк, ints и т. Д. Но я не могу понять, как просто маршировать указатель на объект C#, являющийся состоянием.

Чтобы передать объектную ссылку на функцию C из того, что я нашел, выполнив поиск здесь, и в противном случае кажется, что мне нужен тип структуры, чтобы иметь возможность маршаллировать функцию, поэтому я создал структуру для хранения объекта :

[StructLayout(LayoutKind.Sequential)] 
struct MyObjectState 
{ 
    public object state; 
} 

EDIT: Я пытался поставить атрибут: [MarshalAs(UnmanagedType.Struct, SizeConst = 4)] на public object state собственности, но это дает ту же ошибку, так что я удалил его, не кажется, что это будет работать в любом случае.

Структура содержит одно свойство объекта для хранения любого объекта для функции обратного вызова.

Я заявил делегат в C# следующим образом:

delegate void ErrorHandler(int errorCode, IntPtr name, IntPtr data); 

Тогда я объявил функцию импорта в C# следующим образом:

[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)] 
static extern int SetErrorHandler handler, IntPtr data); 

Затем я создал функцию обратного вызова в моей C# код:

void MyErrorHandler(int errorCode, IntPtr name, IntPtr data) 
{ 
    var strName = Marshal.PtrToStringAnsi(name); 
    var state = new MyObjectState(); 
    Marshal.PtrToStructure(data, state); 
    Console.WriteLine(strName); 
} 

Я могу вызвать библиотечную функцию следующим образом:

var state = new MyObjectState() 
{ 
    state = this 
}; 
IntPtr pStruct = Marshal.AllocHGlobal(Marshal.SizeOf(state)); 
Marshal.StructureToPtr(state, pStruct, true); 
int ret = SetErrorHandler(MyErrorHandler, pStruct); 

на вызов работы и функция обратного вызова вызывается, но я не могу получить доступ к данным в функции обратного вызова, и когда я пытаюсь Marshal.PtrToStructure я получаю сообщение об ошибке:

The structure must not be a value class.

Я сделал много поиска здесь и нашел различные вещи на Маршалле и пустоте *, но ничто не помогло мне заставить это работать

Спасибо.

ответ

3

Вы делаете это более сложным, чем это должно быть. Ваш клиент C# не должен использовать параметр data_ptr, потому что делегат C# уже имеет встроенный механизм для поддержания указателя this.

Таким образом, вы можете просто передать IntPtr.Zero делегату. Внутри делегата обработчика ошибок вы просто игнорируете значение data_ptr, так как будет доступно this.

Если вы не следуете этому описанию, вот короткая программа, иллюстрирующая, что я имею в виду. Обратите внимание, как MyErrorHandler - это метод экземпляра, который действует как обработчик ошибок и может изменять данные экземпляра.

class Wrapper 
{ 
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    delegate void ErrorHandler(int errorCode, string name, IntPtr data); 

    [DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)] 
    static extern int SetErrorHandler(ErrorHandler handler, IntPtr data); 

    void MyErrorHandler(int errorCode, string name, IntPtr data) 
    { 
     lastError = errorCode; 
     lastErrorName = name; 
    } 

    public Wrapper() 
    { 
     SetErrorHandler(MyErrorHandler, IntPtr.Zero); 
    }    

    public int lastError { get; set; } 
    public string lastErrorName { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Wrapper wrapper = new Wrapper(); 
    } 
} 
+0

В этом случае это правда. Действительно, мне действительно не нужно передавать это данные, поскольку он будет доступен в моем обратном вызове объекта, но я хотел бы предоставить правильный вызов функции, когда пользователь хочет обеспечить контекст. Я думаю, управление данными может обрабатываться объектом C# для поддержания состояния/контекста. Поэтому я предполагаю, что это решает мою проблему, но я предполагаю, что исходный вопрос о том, как или можно передать объект, остается без ответа. – Andre

+0

Вы действительно не хотите передавать ссылку на объект C# на мой взгляд. Помните, что управляемые объекты могут быть перемещены GC. Собираетесь ли вы закрепить такие объекты? И что может сделать нативный код с такой штукой? Это абсолютно непрозрачно. Вы можете использовать 'ObjectIDGenerator' для получения уникального идентификатора для каждого объекта, но лично я думаю, что ваш вопрос лучше всего решать, пошагово его! –

+1

То, что вы говорите, имеет смысл, и я думаю, что ответ - это не делать этого/избегать, это может быть не невозможно, но вы не должны, спасибо – Andre

2

Возможно, это может быть способ сделать это, но я сдался давно. Решение, которое я придумал немного хаком, но это очень эффективно и работает со всем, что я бросил на него:

C# -> Управляемый C++ -> Native называет

Делая это таким образом Вы заканчиваете написав небольшую обертку в управляемом C++, что немного больно, но я нашел более способным и менее болезненным, чем весь этот код маршалинга.

Честно говоря, хотя я надеюсь, что кто-то даст не уклончивый ответ, я долгое время боролся с этим.

+0

Спасибо Крис, это полезно, но у меня нет инструментов или знаний на этот момент, чтобы написать управляемую библиотеку C++, возможно, я смогу это выяснить в будущем. Я не предполагаю, что вы можете предоставить ссылку на общий с документацией, как ее использовать? Наконец, если это все не работает для меня, решение состоит в том, чтобы убедиться, что любые данные, которые мне нужны в обратном вызове, должны быть добавлены в структуру и отсортированы как простые значения? Спасибо – Andre

+0

PS: im также имеет дополнительную проблему с моим обратным вызовом, он отлично работает, но даже если я удалю весь код в теле функции обратного вызова, в конце функции я получаю нарушение доступа (Попытка чтения или записи защищенной памяти) – Andre

+0

На самом деле я только что понял: мне не хватало атрибута [UnmanagedFunctionPointer (CallingConvention.Cdecl)] на моем делетете: http://stackoverflow.com/questions/4906931/nullreferenceexception-during-c-callback-to-c- sharp-function – Andre

Смежные вопросы