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

How do I implement a C++ virtual pure class in C#?

本文关键字:虚拟 C++ 实现      更新时间:2023-10-16

你好堆栈溢出社区!

我正在尝试使用用C++编写的非常旧的 dll 在 C# 程序中互操作,我面临着一个非常大的问题:

  • 我的C++ DLL公开了一个具有以下签名的方法getPlugin:

    extern "C" Plugin* getPlugin();
    
  • 使用插件的定义:

    virtual void onLoad(Service* service) =  0;
    virtual const char *getDescription()const = 0;
    
  • 类服务的定义:

    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();
      }
      
    • 插件类的实现,其中包含与 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;
      }
      }
      
    • 服务类的实现

      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指针的地址不同。

有人有解决方案吗?

编辑:堆栈跟踪
无效主(字符串参数[](。
Server.Load(table(.
.func(servicePointer(
...void MyClass::onLoad(Service* service(CPP Side
...._service->getLongValueConf("FOO", "BAR",(long*( &_value(Access Violation

谢谢!

奥利维尔。

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

有人遇到过这个问题吗?