现在还在做桌面这一块的,可能非常少了。昨天在调用封装的加密狗模块时,遇到了一些问题。查了一些资料,这里做一些总结。
C#与C++进行互操作时,字符串这一块需要注意几个地方。
使用char*(多字节)的情况
1、不需要修改字符串的情况,直接用string类型即可(不需要提前分配空间),这里提供一段示例代码。
先用C++封装一个导出函数 PrintName(char* strName) (C#与C++交互可参考C#与C++互操作 - zhaotianff - 博客园)
1 #include "stdafx.h"
2 #include<iostream>
3
4 using namespace std;
5
6 void PrintName(char* strName)
7 {
8 cout << strName<< endl;
9 }
在C#中调用
1 using System.Text;
2 using System.Runtime.InteropServices;
3
4 namespace ConsoleApplication1
5 {
6 class Program
7 {
8 [DllImport("Win32Project2.dll")]
9 public static extern void PrintName(string name);
10
11 static void Main(string[] args)
12 {
13 PrintName("Jack");
14 }
15 }
16 }
运行结果如下
2、需要修改字符串的情况,使用StringBuilder(需要提前分配空间),这里提供一段示例代码。
使用C++封装一个导出函数ModifyName(char* strName),这个函数会对传入的字符串进行修改
1 void ModifyName(char* strName)
2 {
3 char* name = "David";
4
5 strcpy_s(strName, sizeof(strName), name);
6 }
C#调用
如果使用string 类型,会发现执行ModifyName后并不会修改字符串里的内容
改成StringBuilder类型,如下
1 using System;
2 using System.Text;
3 using System.Runtime.InteropServices;
4
5 namespace ConsoleApplication1
6 {
7 class Program
8 {
9 [DllImport("Win32Project2.dll")]
10 public static extern void ModifyName(StringBuilder name);
11
12 static void Main(string[] args)
13 {
14 var name = new StringBuilder(100);
15
16 ModifyName(name);
17
18 Console.WriteLine(name);
19 }
20 }
21 }
运行结果如下,运行结果正常
这里还发现一件有趣的事,就是当我把StringBuilder 的容量写小一点,当这个容量不足以容纳字符串时,CLR会改变StringBuider的容量,使它刚好能装下这些字符串。如果我直接不分配容量,CLR会分配一个稍微大一点的容量。为啥会这样我也没有去深入研究了。
分配Capacity=3的情况
不指定Capacity的情况
使用wchar_t*[LPWSTR、LPCWSTR、PWSTR、PCWSTR ](Unicode)的情况
这种情况跟上面所述一致,不修改字符串内容的,用string ,需要修改并传出的,用StringBuilder ,但是需要使用 MarshalAsAttribute 特性修饰或使用 DllImportAttribute 特性时指定 CharSet = CharSet.Unicode
这里不封装导出函数了,直接用API函数GetCurrentDirectory为例
GetCurrentDirectoryA 是多字节字符集版本,GetCurrentDirectoryW是Unicode字符集版本
1 using System;
2 using System.Text;
3 using System.Runtime.InteropServices;
4
5 namespace ConsoleApplication1
6 {
7 class Program
8 {
9 [DllImport("Kernel32.dll",CharSet = CharSet.Unicode)]
10 public static extern int GetCurrentDirectoryW(int nBufferLength,StringBuilder lpBuffer);
11
12 //或者声明成下面这种
13 //[DllImport("Kernel32.dll")]
14 //public static extern int GetCurrentDirectoryW(int nBufferLength,
[MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpBuffer);
15
16
17 [DllImport("Kernel32.dll", CharSet = CharSet.Ansi)]
18 public static extern int GetCurrentDirectoryA(int nBufferLength, StringBuilder lpBuffer);
19
20 private const int MAX_PATH = 260;
21
22 static void Main(string[] args)
23 {
24
25 StringBuilder sb1 = new StringBuilder(MAX_PATH);
26 StringBuilder sb2 = new StringBuilder(MAX_PATH);
27
28 GetCurrentDirectoryA(MAX_PATH,sb1);
29 GetCurrentDirectoryW(MAX_PATH,sb2);
30
31 Console.WriteLine(sb1);
32 Console.WriteLine(sb2);
33
34 }
35 }
36 }
运行结果如下: