C# 使用C中基于vtable的本地DLL接口# P> >前不久,我在C++中编写了一个Windows DLL,以封装一些我想在不同C++和Delphi编译器之间共享的功能。我一直喜欢COM允许我处理对象的方式,而不是在DLL中使用扁平的C API。因为对于这个DLL,我不需要COM的全部功能,也不需要引用计数,所以我决定实现一个有点模仿COM的DLL接口。我有一个导出的C函数,它允许我创建一个对象并返回一个vtable接口指针。通过这个指针,我可以用C++中和Delphi中的编译器中立方法调用对象的方法。当我使用完接口指针后,我调用它的release

C# 使用C中基于vtable的本地DLL接口# P> >前不久,我在C++中编写了一个Windows DLL,以封装一些我想在不同C++和Delphi编译器之间共享的功能。我一直喜欢COM允许我处理对象的方式,而不是在DLL中使用扁平的C API。因为对于这个DLL,我不需要COM的全部功能,也不需要引用计数,所以我决定实现一个有点模仿COM的DLL接口。我有一个导出的C函数,它允许我创建一个对象并返回一个vtable接口指针。通过这个指针,我可以用C++中和Delphi中的编译器中立方法调用对象的方法。当我使用完接口指针后,我调用它的release,c#,com-interop,C#,Com Interop,现在我想从C#使用这个DLL,但我不知道如何导入接口。此外,我还有一个C#client必须实现的回调接口。下面是C++接口头文件(稍微有点短)。 您能告诉我如何从C语言中使用这个,特别是如何实现回调接口,如何处理“AytCCDL调用约定”以及如何处理回调中的char *参数,这些参数实际上是UTF8编码的C++字符串? < P>我写下了如下代码,这些代码似乎是我所需要的。它的灵感主要来自于这篇博文。该代码主要构建在Marshal.GetDelegateForFunctionPointer和Mar

现在我想从C#使用这个DLL,但我不知道如何导入接口。此外,我还有一个C#client必须实现的回调接口。下面是C++接口头文件(稍微有点短)。

您能告诉我如何从C语言中使用这个,特别是如何实现回调接口,如何处理“AytCCDL调用约定”以及如何处理回调中的char *参数,这些参数实际上是UTF8编码的C++字符串?

< P>我写下了如下代码,这些代码似乎是我所需要的。它的灵感主要来自于这篇博文。该代码主要构建在Marshal.GetDelegateForFunctionPointer和Marshal.GetFunctionPointerForDelegate上,它们从.NET 4.5开始就可用,这在我的情况下是可以接受的。总而言之,您需要知道C++对象的VTABLE,并且需要在非托管内存中为回调接口伪造一个VTABLE。 如果值得的话,我将留给读者。在我的情况下,它至少允许我重用我的DLL。下次我可能会坚持使用平面C API,或者首先使用COM

class CksAdapterCallback
{       
    private IntPtr instance;

    public CksAdapterCallback()
    {
        instance = Marshal.AllocHGlobal(IntPtr.Size * 6);

        IntPtr vtblPtr = IntPtr.Add(instance, IntPtr.Size);
        Marshal.WriteIntPtr(instance, vtblPtr);

        Marshal.WriteIntPtr(vtblPtr, IntPtr.Zero); //dummy entry for the destructor

        OnConnectedInternal = new OnConnectedDelegate(OnConnected);
        Marshal.WriteIntPtr(IntPtr.Add(vtblPtr, IntPtr.Size), Marshal.GetFunctionPointerForDelegate(OnConnectedInternal));

        OnDataInternal = new OnDataDelegate(OnData);
        Marshal.WriteIntPtr(IntPtr.Add(vtblPtr, 2*IntPtr.Size), Marshal.GetFunctionPointerForDelegate(OnDataInternal));

        ...
    }


    ~CksAdapterCallback()
    {
        Marshal.FreeHGlobal(instance);
    }


    public IntPtr Instance
    {
        get { return instance; }
    }


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void OnConnectedDelegate(IntPtr instance, Int32 clientID);
    OnConnectedDelegate OnConnectedInternal;

    void OnConnected(IntPtr instance, Int32 clientID)
    {
        ...
    }


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void OnDataDelegate(IntPtr instance, Int32 clientID, IntPtr data);
    OnDataDelegate OnDataInternal;

    void OnData(IntPtr instance, Int32 clientID, IntPtr data)
    {
        ...
    }

    ...
}


class CksAdapterProxy
{        
    private IntPtr instance;

    public CksAdapterProxy(IntPtr instance)
    {
        this.instance = instance;

        IntPtr vtblPtr = Marshal.ReadIntPtr(instance, 0);

        IntPtr funcPtr = Marshal.ReadIntPtr(vtblPtr, 1 * IntPtr.Size);            
        ReleaseInternal = Marshal.GetDelegateForFunctionPointer<ReleaseDelegate>(funcPtr);

        ...
    }


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate Int32 ReleaseDelegate(IntPtr instance);
    ReleaseDelegate ReleaseInternal;

    ...


    public Int32 Release()
    {
        return ReleaseInternal(instance);
    }

    ...
}


public class CksAdapterTest
{   
    [DllImport("CKS\\CksAdapter.dll")]
    [return: MarshalAs(UnmanagedType.I4)]
    static extern int CA_CreateAdapter(IntPtr adapterCallback, out IntPtr adapter);

    CksAdapterProxy adapter = null;
    CksAdapterCallback adapterCallback = new CksAdapterCallback();


    public CksAdapterTest()
    {            
        IntPtr nativeAdapter = IntPtr.Zero;
        int result = CA_CreateAdapter(adapterCallback.Instance, out nativeAdapter);

        if(result == 0)
        {
            adapter = new CksAdapterProxy(nativeAdapter);

            ...

            adapter.Release();
        }
    }
}
类CksAdapterCallback
{       
私有IntPtr实例;
公共CksAdapterCallback()
{
instance=Marshal.AllocHGlobal(IntPtr.Size*6);
IntPtr vtblPtr=IntPtr.Add(实例,IntPtr.Size);
Marshal.WriteIntPtr(实例,vtblPtr);
Marshal.WriteIntPtr(vtblPtr,IntPtr.Zero);//析构函数的伪条目
OnConnectedInternal=新的OnConnectedDelegate(未连接);
Marshal.WriteIntPtr(IntPtr.Add(vtblPtr,IntPtr.Size),Marshal.GetFunctionPointerForDelegate(OnConnectedInternal));
OnDataInternal=新的OnDataDelegate(OnData);
Marshal.WriteIntPtr(IntPtr.Add(vtblPtr,2*IntPtr.Size),Marshal.GetFunctionPointerForDelegate(OnDataInternal));
...
}
~CksAdapterCallback()
{
Marshal.FreeHGlobal(实例);
}
公共IntPtr实例
{
获取{return instance;}
}
[非托管函数指针(CallingConvention.Cdecl)]
委托void OnConnectedDelegate(IntPtr实例,Int32 clientID);
未连接的门未连接内部;
连接无效(IntPtr实例,Int32 clientID)
{
...
}
[非托管函数指针(CallingConvention.Cdecl)]
委托void OnDataDelegate(IntPtr实例、Int32 clientID、IntPtr数据);
OnDataDelegate OnDataInternal;
void OnData(IntPtr实例、Int32 clientID、IntPtr数据)
{
...
}
...
}
类cksadaperpoxy
{        
私有IntPtr实例;
公共cksadaperpoxy(IntPtr实例)
{
this.instance=instance;
IntPtr vtblPtr=Marshal.ReadIntPtr(实例,0);
IntPtr funcPtr=Marshal.ReadIntPtr(vtblPtr,1*IntPtr.Size);
ReleaseInternal=Marshal.GetDelegateForFunctionPointer(funcPtr);
...
}
[非托管函数指针(CallingConvention.Cdecl)]
委托Int32 ReleaseDelegate(IntPtr实例);
ReleaseDelegate ReleaseInternal;
...
公共Int32发行版()
{
返回ReleaseInternal(实例);
}
...
}
公共类CksAdapterTest
{   
[DllImport(“CKS\\CksAdapter.dll”)]
[返回:Marshallas(UnmanagedType.I4)]
静态外部int CA_CreateAdapter(IntPtr适配器回调,输出IntPtr适配器);
CksAdapterProxy适配器=null;
CksAdapterCallback adapterCallback=新的CksAdapterCallback();
公共CksAdapterTest()
{            
IntPtr nativeAdapter=IntPtr.Zero;
int result=CA_CreateAdapter(adapterCallback.Instance,out nativeAdapter);
如果(结果==0)
{
适配器=新的CksAdapterProxy(nativeAdapter);
...
适配器。释放();
}
}
}

你不能直接使用COM吗?对于你来说,仅仅为一个自定义DLL重新设计所有管道似乎是浪费。首先,这个DLL已经被其他软件使用,所以我别无选择。但正如我提到的,我不想重新发明所有的管道!我想要的只是一个最终可以发布的基于vtable的对象。从C++和Delphi这是非常简单的使用。如果你坚持尝试这个,我的建议是你去获取书,它是我见过的最全面的报道。牙齿有点长了,但应该和你想做的事情有关。不管它值多少钱,我相信你想在这里做的事情是可能的,但绝不是微不足道的。
OnConnected
OnData
OnLog
。。。看起来你正在连接到某个东西,并在处理你得到的数据。。当然,你可以在C#中以本机方式做一些事情。。在C#中编写功能不是更容易吗?@Sam:可能会,但在本机DLL中实现的设备通信协议相当复杂,需要大量指针算法。这就是为什么我认为能够重用它会很好。
class CksAdapterCallback
{       
    private IntPtr instance;

    public CksAdapterCallback()
    {
        instance = Marshal.AllocHGlobal(IntPtr.Size * 6);

        IntPtr vtblPtr = IntPtr.Add(instance, IntPtr.Size);
        Marshal.WriteIntPtr(instance, vtblPtr);

        Marshal.WriteIntPtr(vtblPtr, IntPtr.Zero); //dummy entry for the destructor

        OnConnectedInternal = new OnConnectedDelegate(OnConnected);
        Marshal.WriteIntPtr(IntPtr.Add(vtblPtr, IntPtr.Size), Marshal.GetFunctionPointerForDelegate(OnConnectedInternal));

        OnDataInternal = new OnDataDelegate(OnData);
        Marshal.WriteIntPtr(IntPtr.Add(vtblPtr, 2*IntPtr.Size), Marshal.GetFunctionPointerForDelegate(OnDataInternal));

        ...
    }


    ~CksAdapterCallback()
    {
        Marshal.FreeHGlobal(instance);
    }


    public IntPtr Instance
    {
        get { return instance; }
    }


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void OnConnectedDelegate(IntPtr instance, Int32 clientID);
    OnConnectedDelegate OnConnectedInternal;

    void OnConnected(IntPtr instance, Int32 clientID)
    {
        ...
    }


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void OnDataDelegate(IntPtr instance, Int32 clientID, IntPtr data);
    OnDataDelegate OnDataInternal;

    void OnData(IntPtr instance, Int32 clientID, IntPtr data)
    {
        ...
    }

    ...
}


class CksAdapterProxy
{        
    private IntPtr instance;

    public CksAdapterProxy(IntPtr instance)
    {
        this.instance = instance;

        IntPtr vtblPtr = Marshal.ReadIntPtr(instance, 0);

        IntPtr funcPtr = Marshal.ReadIntPtr(vtblPtr, 1 * IntPtr.Size);            
        ReleaseInternal = Marshal.GetDelegateForFunctionPointer<ReleaseDelegate>(funcPtr);

        ...
    }


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate Int32 ReleaseDelegate(IntPtr instance);
    ReleaseDelegate ReleaseInternal;

    ...


    public Int32 Release()
    {
        return ReleaseInternal(instance);
    }

    ...
}


public class CksAdapterTest
{   
    [DllImport("CKS\\CksAdapter.dll")]
    [return: MarshalAs(UnmanagedType.I4)]
    static extern int CA_CreateAdapter(IntPtr adapterCallback, out IntPtr adapter);

    CksAdapterProxy adapter = null;
    CksAdapterCallback adapterCallback = new CksAdapterCallback();


    public CksAdapterTest()
    {            
        IntPtr nativeAdapter = IntPtr.Zero;
        int result = CA_CreateAdapter(adapterCallback.Instance, out nativeAdapter);

        if(result == 0)
        {
            adapter = new CksAdapterProxy(nativeAdapter);

            ...

            adapter.Release();
        }
    }
}