本文介绍一个C#调用C++写的DLL的完整例子,其中DLL的定义中涉及结构体、数组、指针、回调函数,首先做一下大概的介绍:
1、数组,C#向DLL传数组实际是传入数组的首地址,即传址(ref)。
2、指针,C#提供专门用于处理指针的类型及Ptr类型,比如用于传递Void * 类型的IntPtr类型。
3、结构体,结构体的传递一般要把结构体进行序列化处理,即在结构体的定义前加上:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
4、回调函数,C#提供委托的功能,所以回调函数一般我们用委托来实现,以下是个例子。
以下是DLL中各函数、结构体、回调函数的定义:
typedef struct _VOCLINK_PARAM {
TCHAR sVocIp[16];
UINT nPort;
} VOCLINK_PARAM, *PVOCLINK_PARAM;
typedef struct _VMCCB_EVT {
UINT EvtCode;
UINT RetCode;
UINT Channel;
UINT uBufferLen; // pBuffer 's length
void* pBuffer; // Buffer for holding extra info
} VMCCB_EVT, *PVMCCB_EVT;
typedef void (*VLVMC_CB)( long VocHandle, const PVMCCB_EVT pVmcEvt, LPARAM lpData );
_DLLIMP long CALL_CONVENT
VLVmcOpen( const VLVMC_CB pVmcCB, LPARAM lpData, long *pVmcHandle );
_DLLIMP long CALL_CONVENT
VLVmcClose( long VmcHandle );
// Maximum 30 voice associated with each VMC
_DLLIMP long CALL_CONVENT
VLVmcConnectVoc( long VmcHandle, const PVOCLINK_PARAM pVocSrv, long *pVocHandle );
_DLLIMP long CALL_CONVENT
VLVmcDisconnectVoc( long VocHandle );
C#结构体、委托、函数定义代码:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct VOCLINK_PARAM
{
/// TCHAR[16]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string sVocIp;
/// UINT->unsigned int
public uint nPort;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct VMCCB_EVT
{
/// UINT->unsigned int
public uint EvtCode;
/// UINT->unsigned int
public uint RetCode;
/// UINT->unsigned int
public uint Channel;
/// UINT->unsigned int
public uint uBufferLen;
/// void*
public IntPtr pBuffer;
}
/// Return Type: void
///VocHandle: int
///pVmcEvt: PVMCCB_EVT->_VMCCB_EVT*
///lpData: LPARAM->LONG_PTR->int
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void VLVMC_CB(int VocHandle, ref VMCCB_EVT pVmcEvt, int lpData);
public class VMCOperator
{
/// Return Type: int
///pVmcCB: VLVMC_CB
///lpData: LPARAM->LONG_PTR->int
///pVmcHandle: int*
[DllImportAttribute("VLVmc.dll", EntryPoint = "VLVmcOpen", CallingConvention = CallingConvention.StdCall)]
public static extern int VLVmcOpen(VLVMC_CB pVmcCB, int lpData, ref int pVmcHandle);
/// Return Type: int
///VmcHandle: int
[DllImportAttribute("VLVmc.dll", EntryPoint = "VLVmcClose", CallingConvention = CallingConvention.StdCall)]
public static extern int VLVmcClose(int VmcHandle);
/// Return Type: int
///VmcHandle: int
///pVocSrv: PVOCLINK_PARAM->_VOCLINK_PARAM*
///pVocHandle: int*
[DllImportAttribute("VLVmc.dll", EntryPoint = "VLVmcConnectVoc", CallingConvention = CallingConvention.StdCall)]
public static extern int VLVmcConnectVoc(int VmcHandle, ref VOCLINK_PARAM pVocSrv, ref int pVocHandle);
/// Return Type: int
///VocHandle: int
[DllImportAttribute("VLVmc.dll", EntryPoint = "VLVmcDisconnectVoc", CallingConvention = CallingConvention.StdCall)]
public static extern int VLVmcDisconnectVoc(int VocHandle);
}
C#调用代码
private int vmc_handler;
private int voc_handler;
private VLVMCInterop.VOCLINK_PARAM voc_link_param;
private VLVMCInterop.VLVMC_CB vmc_cb;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
vmc_cb = new VLVMCInterop.VLVMC_CB(OnVLVMC_CB);
voc_link_param = new VLVMCInterop.VOCLINK_PARAM();
}
private void btnOpenVMC_Click(object sender, RoutedEventArgs e)
{
if (checkInput())
{
vmc_handler = 0;
int lpdata = 0;
if (VLVMCInterop.VMCOperator.VLVmcOpen(vmc_cb, lpdata, ref vmc_handler) == VLVMCInterop.VMCCodeDefine.VLVMC_ERR_SUCCESS)
{
string vocip = txtVocIP.Text;
voc_link_param.sVocIp = vocip;
voc_link_param.nPort = Convert.ToUInt32(txtVocPort.Text);
voc_handler = 0;
if (VLVMCInterop.VMCOperator.VLVmcConnectVoc(vmc_handler, ref voc_link_param, ref voc_handler) == VLVMCInterop.VMCCodeDefine.VLVMC_ERR_SUCCESS)
{
}
}
}
}
private void OnVLVMC_CB(int voc_handler, ref VLVMCInterop.VMCCB_EVT vmccb_evt, int lpdata)
{
uint code = vmccb_evt.EvtCode;
uint channel = vmccb_evt.Channel;
switch (code)
{
.......
}
}