Как реализовать виртуальный чистый класс C ++ в C #? - PullRequest
0 голосов
/ 11 сентября 2018

Здравствуйте, сообщество Stackoverflow!

Я пытаюсь взаимодействовать в программе на C # с очень старой DLL, написанной на C ++, и сталкиваюсь с очень большой проблемой:

  • Моя C ++ DLL предоставляет метод getPlugin со следующей подписью:

    extern "C" Plugin* getPlugin();
    
  • с определением Plugin:

    virtual void onLoad(Service* service) =  0;
    virtual const char *getDescription()const = 0;
    
  • Определение класса Service:

    virtual void getLongValueConf(const char *section, const char*key,long *output_value ) = 0;
    
  • реализация метода getDescription:

    const char* MyClass::getDescription() const
    {
        static char  buffer[512];
        sprintf(buffer, "FOO");
        return loc_tc_buffer;
    }
    
  • реализацияметод onLoad, вызывающий метод getLongValueConf:

    void MyClass::onLoad(Service* service)
    {
        _service = service;
        _service->getLongValueConf("FOO", "BAR",(long*) &_value);
    }
    
  • Итак, я реализую в своей программе на C #:

    • СтатическийОболочка, содержащая точку входа:

      public static class Wrapper
      {
          [DllImport("MyCppDll.dll", EntryPoint= "getPlugin", CallingConvention= CallingConvention.StdCall, CharSet= CharSet.Ansi, BestFitMapping= false, ThrowOnUnmappableChar= true)]
          public static extern IntPtr getPlugin();
      }
      
    • Реализация класса Plugin, который содержит структуру, соответствующую vtable:

      public class Plugin
      {
          [UnmanagedFunctionPointer(CallingConvention.StdCall)]
          public delegate void onLoadCb(IntPtr service);
      
          [UnmanagedFunctionPointer(CallingConvention.StdCall)]
          public delegate IntPtr getDescriptionCb();
      
          [StructLayout(LayoutKind.Sequential)]
          public struct PluginVtbl
          {
              public IntPtr onLoad;
              public IntPtr getDescription;
          }
      }
      
    • Реализация класса Service

      public class Service
      {
          [UnmanagedFunctionPointer(CallingConvention.StdCall)]
          public delegate void getLongValueConf(char[] section, char[] key, ref int output_value);
          [StructLayout(LayoutKind.Sequential)]
          public struct ServiceVtbl
          {
              public IntPtr getLongValueConf;
          }
      }
      
    • Внутренний класс для вызова методов onLoad и getDescription:

      public static class Loader
      {
          private static IntPtr serverPointer;
          private static IntPtr vtable;
          private static PluginVtbl handle;
          private static IntPtr servicePointer;
          public static void Load(ServiceVtbl service)
          {
              serverPointer = SSI_getServerPlugin();
              vtable = Marshal.ReadIntPtr(serverPointer, 0);
              handle = (PluginVtbl)Marshal.PtrToStructure(vtable, typeof(PluginVtbl));
              onLoadCb func = (onLoadCb)Marshal.GetDelegateForFunctionPointer(handle.onLoad, typeof(onLoadCb));
              servicePointer = new IntPtr();
              int size = Marshal.SizeOf(typeof(ServiceVtbl));
              servicePointer = Marshal.AllocHGlobal(size);
              Marshal.StructureToPtr(service, servicePointer, false);
              func(servicePointer);
          }
          public static string GetDescription()
          {
              serverPointer = SSI_getServerPlugin();
              vtable = Marshal.ReadIntPtr(serverPointer, 0);
              handle = (PluginVtbl)Marshal.PtrToStructure(vtable, typeof(PluginVtbl));
              getDescriptionCb func = (getDescriptionCb)Marshal.GetDelegateForFunctionPointer(handle.getDescription, typeof(getDescriptionCb));
              IntPtr pointerTemp = func();
              return Marshal.PtrToStringAnsi(pointerTemp);
          }
      }
      
    • И затем, моя программная реализация:

      class Program 
      {
          static void Main(string[] args)
          {
              Console.WriteLine(Server.GetDescription());
              ServiceVtbl table = new ServiceVtbl();
              getLongValueConf delgetLongValueConf = new getLongValueConf(getLongValueConf);
      
              table.getLongValueConf = Marshal.GetFunctionPointerForDelegate(delgetLongValueConf);
      
              Server.Load(table);
          }
      
          static void getLongValueConf(char[] section, char[] key, ref int output_value)
          {
              Console.WriteLine("{0} {1}", section, key);
          }
      }
      

Когда я вызываю Console.WriteLine(Server.GetDescription());, мой вывод 'FOO'.Но у меня есть AccesViolationException, когда я вызываю Server.onLoad метод.Поэтому я решил проверить адреса разных указателей и заметил, что servicePointer в моем коде C # не имеет тот же адрес, что и указатель service в коде C ++.

Есть ли у кого-нибудьрешение?

РЕДАКТИРОВАТЬ: трассировка стека
void Main (строковые аргументы [])
.Server.Load (таблица)
..func (servicePointer)
... void MyClass :: onLoad (Сервис * сервис) Сторона CPP
...._ service-> getLongValueConf ("FOO", "BAR", (long *) & _value) Нарушение доступа

Спасибо!

Оливье.

1 Ответ

0 голосов
/ 18 сентября 2018

Я до сих пор не нашел, почему я не могу разделить память между моей Программой C # и моей C ++ DLL ... Я, вероятно, напишу оболочку C ++, которая явно отображает мои обратные вызовы.

Кто-нибудь когда-либо имелсталкиваться с этой проблемой?

...