C# 使用C中基于vtable的本地DLL接口# P> >前不久,我在C++中编写了一个Windows DLL,以封装一些我想在不同C++和Delphi编译器之间共享的功能。我一直喜欢COM允许我处理对象的方式,而不是在DLL中使用扁平的C API。因为对于这个DLL,我不需要COM的全部功能,也不需要引用计数,所以我决定实现一个有点模仿COM的DLL接口。我有一个导出的C函数,它允许我创建一个对象并返回一个vtable接口指针。通过这个指针,我可以用C++中和Delphi中的编译器中立方法调用对象的方法。当我使用完接口指针后,我调用它的release
现在我想从C#使用这个DLL,但我不知道如何导入接口。此外,我还有一个C#client必须实现的回调接口。下面是C++接口头文件(稍微有点短)。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语言中使用这个,特别是如何实现回调接口,如何处理“AytCCDL调用约定”以及如何处理回调中的char *参数,这些参数实际上是UTF8编码的C++字符串?
< P>我写下了如下代码,这些代码似乎是我所需要的。它的灵感主要来自于这篇博文。该代码主要构建在Marshal.GetDelegateForFunctionPointer和Marshal.GetFunctionPointerForDelegate上,它们从.NET 4.5开始就可用,这在我的情况下是可以接受的。总而言之,您需要知道C++对象的VTABLE,并且需要在非托管内存中为回调接口伪造一个VTABLE。 如果值得的话,我将留给读者。在我的情况下,它至少允许我重用我的DLL。下次我可能会坚持使用平面C API,或者首先使用COMclass 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();
}
}
}