可以说新手使用P-INVOKE最开始的头疼就是C#和C++的字符串传递,因为这里涉及到两个问题。
第一:C#的string和C++的字符串首指针如何对应。
第二:字符串还有ANSI和UNICODE(宽字符串)之分。
本文分三部分阐述:
第一:字符串指针当输入参数,
第二:字符串指针作为返回值,
第三:字符串指针作为输入输出参数。
C++部分的测试代码很简单这里就全部贴出来了:
1 #include "stdafx.h" 2 #include "TestDll.h" 3 #include <stdio.h> 4 #include <string.h> 5 #include <tchar.h> 6 7 8 static char * _hello = "Hello,World!!"; 9 static TCHAR * _helloW = TEXT("Hello,World!!"); 10 11 void __stdcall PrintString(char * hello) 12 { 13 printf("%s\n",hello); 14 } 15 16 void __stdcall PrintStringW(TCHAR * hello) 17 { 18 _tprintf(TEXT("%s\n"),hello); 19 } 20 21 22 char * __stdcall GetStringReturn() 23 { 24 return _hello; 25 } 26 27 TCHAR * __stdcall GetStringReturnW() 28 { 29 return _helloW; 30 } 31 32 33 void __stdcall GetStringParam(char * outHello,int len) 34 { //output "aaaaaaaa" 35 for(int i= 0; i< len -1 ;i++) outHello[i] = 'a'; 36 outHello[len - 1] = '\0'; 37 } 38 39 void __stdcall GetStringParamW(TCHAR * outHello,int len) 40 { //output "aaaaaaaa" unicode version. 41 for(int i= 0; i< len -1 ;i++) outHello[i] = TEXT('a'); 42 outHello[len - 1] = TEXT('\0'); 43 }
下面看C#如何调用。
第一:字符串指针作为输入参数,可以使用byte[] 和MarshalAs来解决。(注意四个P-INVOKE,两个ANSI版本,和两个UNICODE版本),推荐使用MarshalAs方法简单明了。
1 [DllImport("TestDll", EntryPoint = "PrintString")] 2 public static extern void PrintStringByBytes(byte[] hello); 3 4 [DllImport("TestDll", EntryPoint = "PrintString")] 5 public static extern void PrintStringByMarshal([MarshalAs(UnmanagedType.LPStr)]string hello); 6 7 [DllImport("TestDll", EntryPoint = "PrintStringW")] 8 public static extern void PrintStringByBytesW(byte[] hello); 9 10 [DllImport("TestDll", EntryPoint = "PrintStringW")] 11 public static extern void PrintStringByMarshalW([MarshalAs(UnmanagedType.LPWStr)]string hello); 12 13 14 public void Run() 15 { 16 PrintStringByBytes(Encoding.ASCII.GetBytes("use byte[]")); 17 PrintStringByMarshal("use MarshalAs"); 18 PrintStringByBytesW(Encoding.Unicode.GetBytes("use byte[]")); 19 PrintStringByMarshalW("use MarshalAs"); 20 }
第二:字符串指针作为返回值,和上面一样也有两种声明方法,同样也包含两个版本。注意:Marshal.PtrToStringAnsi()函数的使用,把字符串指针转变为C#的string.推荐使用MarshalAs方法简单明了。
1 [DllImport("TestDll", EntryPoint = "GetStringReturn")] 2 public static extern IntPtr GetStringReturnByBytes(); 3 4 [DllImport("TestDll", EntryPoint = "GetStringReturn")] 5 [return:MarshalAs(UnmanagedType.LPStr)] 6 public static extern string GetStringReturnByMarshal(); 7 8 [DllImport("TestDll", EntryPoint = "GetStringReturnW")] 9 public static extern IntPtr GetStringReturnByBytesW(); 10 11 [DllImport("TestDll", EntryPoint = "GetStringReturnW")] 12 [return: MarshalAs(UnmanagedType.LPWStr)] 13 public static extern string GetStringReturnByMarshalW(); 14 15 16 public void Run() 17 { //Marshal.PtrToStringAuto(GetStringReturnByBytes()); 自动判断类型不错。 18 Console.WriteLine(Marshal.PtrToStringAnsi(GetStringReturnByBytes())); 19 Console.WriteLine(GetStringReturnByMarshal()); 20 Console.WriteLine(Marshal.PtrToStringUni(GetStringReturnByBytesW())); 21 Console.WriteLine(GetStringReturnByMarshalW()); 22 }
第三:字符串指针作为输入输出参数时,因为要求有固定的容量,所以这里使用的是StringBuilder,大家仔细看了,当然也有byte[]版本。这个看大家喜欢那个版本就是用那个.
1 [DllImport("TestDll", EntryPoint = "GetStringParam")] 2 public static extern void GetStringParamByBytes(byte[] outHello, int len); 3 4 [DllImport("TestDll", EntryPoint = "GetStringParam")] 5 public static extern void GetStringParamByMarshal([Out, MarshalAs(UnmanagedType.LPStr)]StringBuilder outHello, int len); 6 7 [DllImport("TestDll", EntryPoint = "GetStringParamW")] 8 public static extern void GetStringParamByBytesW(byte[] outHello, int len); 9 10 [DllImport("TestDll", EntryPoint = "GetStringParamW")] 11 public static extern void GetStringParamByMarshalW([Out, MarshalAs(UnmanagedType.LPWStr)]StringBuilder outHello, int len); 12 13 14 public byte[] _outHello = new byte[10]; 15 public byte[] _outHelloW = new byte[20]; 16 public StringBuilder _builder = new StringBuilder(10); //很重要设定string的容量。 17 18 public void Run() 19 { 20 // 21 GetStringParamByBytes(_outHello, _outHello.Length); 22 GetStringParamByMarshal(_builder, _builder.Capacity); 23 GetStringParamByBytesW(_outHelloW, _outHelloW.Length / 2); 24 GetStringParamByMarshalW(_builder, _builder.Capacity); 25 26 // 27 Console.WriteLine(Encoding.ASCII.GetString(_outHello)); 28 Console.WriteLine(_builder.ToString()); 29 Console.WriteLine(Encoding.Unicode.GetString(_outHelloW)); 30 Console.WriteLine(_builder.ToString()); 31 } 32
下载:代码
|
请发表评论