如何从外部DLL加载包含函数指针的结构?

时间:2018-06-06 15:16:21

标签: c# dllimport

我已经获得了一个外部c ++ dll,我需要在我的C#项目中加载和使用它。 dll附带一个头文件,这是(简化/匿名):

typedef struct
{
    int (*GetVersion)();
    int (*StartServer)(const char *ip, int port);
    void (*OnRemoteError)(void *caller, int error);
} RemoteServerPluginI;

extern "C" __declspec(dllexport) RemoteServerPluginI* GetServerPluginInterface();

关于如何在我的C#项目中使用它,我有几个问题:

  • 我用对象翻译“void *”吗?
  • 我将char *数组转换为字符串或char []?
  • OnRemoteError应该是一个回调;注册我的回调,我应该简单地将我的回调函数分配给这个字段吗?

非常感谢任何相关文档的链接。

1 个答案:

答案 0 :(得分:0)

BIG DISCLAIMER :此时我无法与实际系统接口,因此这可能是错误的。但是我已经成功加载了dll并阅读了版本,这让我觉得我可能已经解决了它。如果有任何问题,我会更新答案。

首先要声明struct将C ++ struct映射到我们的C#代码中。 我们可以使用MarshalAs属性告诉编组人员这些委托实际上只是函数指针:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetVersionT();

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int StartServerT(string ip, int port);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnRemoteErrorT(object caller, int error);

public struct RemoteServerPluginI
{
    [MarshalAs(UnmanagedType.FunctionPtr)] public GetVersionT GetVersion;
    [MarshalAs(UnmanagedType.FunctionPtr)] public StartServerT StartServer;
    [MarshalAs(UnmanagedType.FunctionPtr)] public OnRemoteErrorT OnRemoteError;
    // a lot of other methods not shown
}

然后我们创建一个帮助器类,使用DLLImport加载DLL并调用dll中定义的方法。

使用extern方法可以轻松完成:

[DllImport("data/remoteplugin.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetServerPluginInterface();

请注意,我们必须指定调用约定。 另一个需要注意的重要事项是:此方法返回IntPtr个对象。 幸运的是我们现在已经完成了,我们只需要将其转换为正确的类型:

var ptr = GetServerPluginInterface();
var server = (RemoteServerPluginI)Marshal.PtrToStructure(ptr, typeof(RemoteServerPluginI));

此时我只是将所有内容都包含在便利类中以管理访问权限,并且瞧! 这是最终的代码:

public static class IntPtrExtensions
{
    // I'm lazy
    public static T ToStruct<T>(this IntPtr ptr)
        => (T)Marshal.PtrToStructure(ptr, typeof(T));
}

public static class RemoteControlPlugin
{
    [DllImport("path/to/remoteplugin.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr GetServerPluginInterface();

    private static RemoteServerPluginI? _instance = null;

    public static RemoteServerPluginI Instance =>
        (RemoteServerPluginI)(
            _instance ??
            (_instance = GetServerPluginInterface().ToStruct<RemoteServerPluginI>())
        );
}

internal static class Program
{
    private static void Main(string[] args)
    {
        var remoteServer = RemoteControlPlugin.Instance;
        Console.WriteLine(remoteServer.GetVersion());  // Easy!
    }
}