您的位置:首页 > 其它

平台调用P-INVOKE(二)--(封送字符串)

2012-11-13 09:02 471 查看
可以说新手使用P-INVOKE最开始的头疼就是C#和C++的字符串传递,由于不同编程语言对字符串处理的机制不同,因此导致托管代码的平台调用必须对字符串进行特殊的封送处理。本节将阐述以下几个问题:

(1)、C#的string和C++的字符串首指针如何对应

(2)、字符串还有ANSI和UNICODE(宽字符串)之分

(3)、封送字符串数组

1、通过CharSet字段控制字符串封送行为:

C++:
void __cdecl TestString1(
char* hello
);

void __cdecl TestString2(const wchar_t* str,wchar_t* outStr,int size);

MSDN上给出C/C++字符串类型与C#字符串类型的对应关系

Wtypes.h 中的非托管类型
非托管C/C++
语言类型
托管类名
说明
CHAR
char
System.Char
用 ANSI 修饰。
LPSTR
char*
System.String 或 System.Text.StringBuilder
用 ANSI 修饰。
LPCSTR
Const char*
System.String 或 System.Text.StringBuilder
用 ANSI 修饰。
LPWSTR
wchar_t*
System.String 或 System.Text.StringBuilder
用 Unicode 修饰。
LPCWSTR
Const wchar_t*
System.String 或 System.Text.StringBuilder
用 Unicode 修饰。
C#
[DllImport("test.dll", EntryPoint = "TestString1", CharSet =CharSet.Ansi)]
public static extern voidTestString1(string
 hello
);
[DllImport("test.dll", EntryPoint = "TestString1", CharSet =CharSet.Unicode)]
public static extern voidTestString2(stringstr,StringBuilder
outStr,int size)

2、使用MarshalAs属性控制字符串封送行为:

CharSet字段影响的是整个函数过程的字符串封送行为,MarshalAs属性只影响其作用的字符串参数。因此,当一个非托管函数的参数即由ANSI字符串,又有Unicode字符串时,就只能用MarshalAs属性来控制封送行为。

C++:
void __cdecl TestString3(const char* str1,const wchar_t* str2,wchar_t*
outStr,int size);
MSDN给出MarshalAs属性控制字符串封送行为:

枚举类型
非托管格式说明
UnmanagedType.AnsiBStr
长度前缀为双字节的 Unicode字符的COM样式的BSTR。。
UnmanagedType.LPStr
单字节、null空终止的 ANSI 字符数组的指针。(默认值)
UnmanagedType.LPTStr
null空终止与平台相关的字符数组的指针。
UnmanagedType.LPWStr
null空终止与Unicode的字符数组的指针。
UnmanagedType.TBStr
一个有长度前缀的与平台相关的 COM样式的BSTR。
需要注意的是:此表只适用于string类型,对于StringBuilder而言,能够允许的选项只有:LPStr、LPTStr、LPWStr。

C#:
[DllImport("test.dll",
EntryPoint = "TestString3")]
public static extern voidTestString3(
[MarshalAs(UnmanagedType.LPStr)]stringstr1,
[MarshalAs(UnmanagedType.LPWStr)]string
str2,
[MarshalAs(UnmanagedType.LPWStr)]stringoutStr,
int size);

3、封送作为返回值的字符串:

C++:

char*   __cdecl GetStringReturn1()

wchar_t*
__cdecl 
GetStringReturn2();

这里,有两种声明方法:

(1)、直接用string类型对应:

[DllImport("test.dll", EntryPoint = "[code]GetStringReturn1
",
CharSet = CharSet.Ansi)]
public static extern
string
GetStringReturn1
();
[DllImport("test.dll", EntryPoint = "
GetStringReturn2
",
CharSet = CharSet.Unicode)]
public
static extern
string
GetStringReturn2
();
(2)、用IntPtr指针对应:

[DllImport("test.dll", EntryPoint = "
GetStringReturn1
",
CharSet = CharSet.Ansi)]
public static extern
IntPtr
GetStringReturn1
();
[DllImport("test.dll", EntryPoint = "
GetStringReturn2
",
CharSet = CharSet.Unicode)]
public
static extern
IntPtr
GetStringReturn2[/code]();

GetStringReturn2[/code]为例,给出C#如何使用:

string
ret="";
IntPtr
strPtr=
GetStringReturn2[/code]();
ret=Marshal.PtrToStringUni(strPtr);
//对于IntPtr传递的变量,需要手工释放非托管内存

Marshal.FreeCoTaskMem(strPtr);
//释放非托管内存是互操作的一个难题,将在后面的章节做专门的介绍

4、封送字符串数组

C++:

int TestArrayOfStrings(char* ppStrArray[], int size);
C#:
[ DllImport( "test.dll" )]
public static extern int TestArrayOfStrings( [In, Out]   String[] ppStrArray, int size );
使用:
String[] strArray = { "one", "two", "three", "four", "five" };
int lenSum = LibWrap.TestArrayOfStrings( strArray, strArray.Length );
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: