Из C # как мне использовать собственный обратный вызов C с унаследованной структурой?

#c# #c #interop #callback

#c# #c #взаимодействие #обратный вызов

Вопрос:

Из C # мне нужно настроить обратный вызов на C , где данные, полученные с помощью обратного вызова, происходят из структур с наследованием (см. переработанный код ниже в разделе C ).

Я полагаю, что структуры в C сворачиваются компилятором, потому что они не содержат никаких виртуальных функций? Итак, использование свернутого класса данных ‘CollapsedCallbackInfo’ в C # фактически работает для этого конкретного класса данных. Но, имея много разных классов данных с несколькими уровнями наследования в реальной проблеме, есть ли способ решить эту проблему? Мне нужен какой-то общий тип для использования в моем определении делегата.

C#

Настройка обратного вызова

 [DllImport("NativeDLL", CallingConvention = CallingConvention.Cdecl)] 
public static extern void SetupCallback(CallbackFunc callback);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CallbackFunc(BaseCallbackInfo baseCallbackInfo);
  

Классы данных C #

 [StructLayout(LayoutKind.Sequential)]
public class BaseCallbackInfo 
{
    public uint base_data;
};

[StructLayout(LayoutKind.Sequential)]
public class ConcreteCallbackInfo : BaseCallbackInfo 
{
    public uint concrete_data;
};   
  

Свернутый класс данных

 [StructLayout(LayoutKind.Sequential)]
public class CollapsedCallbackInfo 
{
    public uint base_data;
    public uint concrete_data;
};
  

Тестовый класс, иллюстрирующий настройку обратного вызова

 public class TestClass
{
   public void RegisterCallback()
   {    
      SetupCallback(new CallbackFunc(OnCallback));
   }

   public void OnCallback(BaseCallbackInfo baseCallbackInfo)
   {
      // In the real problem I know this cast is valid
      ConcreteCallbackInfo ccbi = baseCallbackInfo as ConcreteCallbackInfo;
      Debug.Log(ccbi.concrete_data)
   }  
}
  

C

Классы данных

 struct BaseCallbackInfo
{
   uint base_data;
};

struct ConcreteCallbackInfo : public BaseCallbackInfo
{
   uint concrete_data;
};
  

Определение обратного вызова в стиле C и внешний интерфейс

 typedef void( *CallbackFunc )( BaseCallbackInfo* in_pCallbackInfo );

extern "C"
__declspec(dllexport) void SetupCallback(CallbackFunc callback);    
  

Ответ №1:

Просто определите несколько одинаковых DllImports, например

 [DllImport("NativeDLL", EntryPoint="SetupCallback", CallingConvention = CallingConvention.Cdecl)] 
public static extern void SetupCallback_Base(BaseCallbackFunc callback);

[DllImport("NativeDLL", EntryPoint="SetupCallback", CallingConvention = CallingConvention.Cdecl)] 
public static extern void SetupCallback_Concrete1(Concrete1CallbackFunc callback);

[DllImport("NativeDLL", EntryPoint="SetupCallback", CallingConvention = CallingConvention.Cdecl)] 
public static extern void SetupCallback_Concrete2(Concrete2CallbackFunc callback);
  

Я бы порекомендовал вам _stdcall вместо _cdecl.

Комментарии:

1. Что нужно сделать ‘SetupCallback’? И хорошо … функция обратного вызова имеет соглашение __stdcall, а «обычная функция» имеет соглашение __cdecl. Лучше ли использовать all __stdcall?