您的位置:首页 > 编程语言 > C语言/C++

C#调用C++的DLL的解决方案

2010-09-15 22:36 495 查看
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++调用初探,像结构的传入与传出将在后续中作进一步探讨。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: