1、 引言
为了缩短界面开发成本,加上.NET集成开发环境与现代智能手机(windows mobile)系统的无缝整合,于是决定新的项目使用C#进行开发。以前的dll一直使用C++进行开发,如果重新使用C#改装,势必增加人力、时间成本,C# 提 供了调用P/Invoke机制,通过[DllImport]声明要调用的dll,可以调用C++的dll,于是复用以前的API无疑是最节省成本的方案。 本文只是通过解决具体的某类调用问题展开,给出可行的解决方案,至于结构的传出等其他类型调用、C#的非托管非安全指针的调用等, 有待进一步深入。
2、 问题描述
C++里API定义:HRESULT WINAPI XMFunc ( DWORD cbInput , BYTE * pInput , DWORD * cbOutput , BYTE ** ppOutBufferput , IRAPIStream * pStream );
其中:输入参数cbInput,pInput;
输出参数cbOutput,ppOutBufferput,pStream(为null);
* pcbOutput = ( DWORD ) SysStri ngByteLen ( bstrXML );
* ppOutBufferput = ( BYTE *) LocalAlloc ( LPTR , (* pcbOutput )+1);
memcpy ( * ppOutBufferput , ( const void *) bstrXML , * pcbOutput );
也就是说,cbOutput返回 unicode 字符 所占byte 个数 ;ppOutBufferput指针指向返回字符串的一块内存。
3、 解决方案1
C#中声明: [ DllImport ( "// XMDLL .dll" , CharSet = CharSet . Unicode , SetLastError = true )]
p ublic static extern int XMFunc(int cbInput, IntPtr pInput, ref int pcbOutput,ref string pOutBuffer,IntPtr pStream);
调用:
i nt cbInput = 0;
IntPtr pInput = IntPtr.Zero;
i nt cbOutput = 0;
String pOutBuffer = “” ;
i nt n Ret = XMFunc(cbInput,pInput,ref cbOutput,ref pOutBuffer,IntPtr.Zero);
结果测试:cbOutput、pOutBuffer都返回正确结果。不过dll中 * ppOutBufferput 指针没有释放,会引起内存泄露。可以在dll中定义一个释放内存的API, 调用该API进行内存释放。
4、 解决方案2
C++中增加一个API:int WINAPI XMFuncEx(DWORD cbInput,BYTE* pInput,DWORD *pcbOutput, w char_t ** pOutBuffer , IRAPIStream * pStream )
{
BYTE *pbtOutput = NULL;
int nRet = XMFunc(cbInput,pInput,pcbOutput,&pbtOutput,NULL);
if (pbtOutput)
* pOutBuffer = ( wchar_t *) pbtOut ;
return nRet;
}
C#中声明: [ DllImport ( "// XMDLL .dll" , CharSet = CharSet . Unicode , Set LastError = true )]
P ublic static extern int XMFunc Ex (int cbInput, IntPtr pInput, ref int pcbOutput,ref IntPtr pOutBuffer,IntPtr pStream);
调用:
int cbInput = 0;
IntPtr pInput = IntPtr.Zero;
i nt cbOutput = 0;
IntPtr pOutBuffer = new IntPtr();
int nRet = X MFuncEx( cbInput,pInput,ref cbOutput,ref pOutBuffer,IntPtr.Zero );
Marshal.FreeHGlobal(pOutBuffer);
通过把dll中的内存传递出来,Marshal进行内存释放。这样的话,内存声明与释放分开,不是一种良好的编程风格,使用时需谨慎。
5、 未通过测试的方案
C++中增加一个API:int WINAPI XMFuncEx(DWORD cbInput,BYTE* pInput,DWORD *pcbOutput, wchar_t ** pOutBuffer , IRAPIStream * pStream )
{
BYTE *pbtOutput = NULL;
int nRet = XMFunc(cbInput,pInput,pcbOutput,&pbtOutput,NULL);
if (pbtOutput)
{
wsprintf(*pOutBuffer, TEXT( “ %s ” ), (LPCTSTR)pbtOutput);
LocalFree( pbtOut ) ;
}
return nRet;
}
C#中声明: [ DllImport ( "// XMDLL .dll" , CharSet = CharSet . Unicode , SetLastError = true )]
public static extern int XMFunc(int cbInput, IntPtr pInput, ref int pcbOutput,ref string pOutBuffer,IntPtr pStream);
调用:
i nt cbInput = 0;
IntPtr pInput = IntPtr.Zero;
i nt cbOutput = 0;
String pOutBuffer = “” ;
i nt nRet = XMFunc(cbInput,pInput,ref cbOutput,ref pOutBuffer,IntPtr.Zero);
随后 System.Diagnostics.Debug.WriteLine(pOutBuffer); 打印完后就会链接断开,退出。
6、 结论
C++中定义的类型可以使用C#中 对应的类型进行替代声明,常用类型对应关系参考其他相关资料,本文不再祥述。如果遇到C++指针,C#中可以使用ref用来按址传递或接收返回。总之,本文只是提供C#和C++调用初探,像结构的传入与传出将在后续中作进一步探讨。