如何在C#中实现C ++虚拟纯类?

时间:2018-09-11 09:23:02

标签: c++ interop pinvoke marshalling

你好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”。 但是,当我调用Server.onLoad方法时,我有一个AccesViolationException。因此,我决定检查我的不同指针的地址,我注意到C#代码中的servicePointer与C ++代码中的service指针没有相同的地址。

有人可以解决吗?

编辑:堆栈跟踪
void Main(string args [])
.Server.Load(表)
..func(servicePointer)
... void MyClass :: onLoad(Service *服务) CPP端
...._ service-> getLongValueConf(“ FOO”,“ BAR”,(long *)&_value)访问冲突

谢谢!

奥利维尔。

1 个答案:

答案 0 :(得分:0)

我仍然没有找到为什么我不能在C#程序和C ++ DLL之间共享内存...我可能会写一个C ++包装程序,显式地公开我的回调。

有人遇到过这个问题吗?