能夠說新手使用P-INVOKE最開始的頭疼就是C#和C++的字符串傳遞,由於這裏涉及到兩個問題。c#
第一:C#的string和C++的字符串首指針如何對應。ide
第二:字符串還有ANSI和UNICODE(寬字符串)之分。函數
本文分三部分闡述:測試
第一:字符串指針當輸入參數,ui
第二:字符串指針做爲返回值,指針
第三:字符串指針做爲輸入輸出參數。code
C++部分的測試代碼很簡單這裏就所有貼出來了:orm
#include "stdafx.h" #include "TestDll.h" #include <stdio.h> #include <string.h> #include <tchar.h> staticchar* _hello ="Hello,World!!"; static TCHAR * _helloW = TEXT("Hello,World!!"); void __stdcall PrintString(char* hello) { printf("%s\n",hello); } void __stdcall PrintStringW(TCHAR * hello) { _tprintf(TEXT("%s\n"),hello); } char* __stdcall GetStringReturn() { return _hello; } TCHAR * __stdcall GetStringReturnW() { return _helloW; } void __stdcall GetStringParam(char* outHello,int len) { //output "aaaaaaaa" for(int i=1; i< len -1 ;i++) outHello[i] ='a'; outHello[len -] ='\'; } void __stdcall GetStringParamW(TCHAR * outHello,int len) { //output "aaaaaaaa" unicode version. for(int i=1; i< len -1 ;i++) outHello[i] = TEXT('a'); outHello[len -] = TEXT('\'); }
下面看C#如何調用。ci
第一:字符串指針做爲輸入參數,能夠使用byte[] 和MarshalAs來解決。(注意四個P-INVOKE,兩個ANSI版本,和兩個UNICODE版本),推薦使用MarshalAs方法簡單明瞭。unicode
[DllImport("TestDll", EntryPoint ="PrintString")] publicstaticexternvoid PrintStringByBytes(byte[] hello); [DllImport("TestDll", EntryPoint ="PrintString")] publicstaticexternvoid PrintStringByMarshal([MarshalAs(UnmanagedType.LPStr)]string hello); [DllImport("TestDll", EntryPoint ="PrintStringW")] publicstaticexternvoid PrintStringByBytesW(byte[] hello); [DllImport("TestDll", EntryPoint ="PrintStringW")] publicstaticexternvoid PrintStringByMarshalW([MarshalAs(UnmanagedType.LPWStr)]string hello); publicvoid Run() { PrintStringByBytes(Encoding.ASCII.GetBytes("use byte[]")); PrintStringByMarshal("use MarshalAs"); PrintStringByBytesW(Encoding.Unicode.GetBytes("use byte[]")); PrintStringByMarshalW("use MarshalAs"); }
第二:字符串指針做爲返回值,和上面同樣也有兩種聲明方法,一樣也包含兩個版本。注意:Marshal.PtrToStringAnsi()函數的使用,把字符串指針轉變爲C#的string.推薦使用MarshalAs方法簡單明瞭。
[DllImport("TestDll", EntryPoint ="GetStringReturn")] publicstaticextern IntPtr GetStringReturnByBytes(); [DllImport("TestDll", EntryPoint ="GetStringReturn")] [return:MarshalAs(UnmanagedType.LPStr)] publicstaticexternstring GetStringReturnByMarshal(); [DllImport("TestDll", EntryPoint ="GetStringReturnW")] publicstaticextern IntPtr GetStringReturnByBytesW(); [DllImport("TestDll", EntryPoint ="GetStringReturnW")] [return: MarshalAs(UnmanagedType.LPWStr)] publicstaticexternstring GetStringReturnByMarshalW(); publicvoid Run() { //Marshal.PtrToStringAuto(GetStringReturnByBytes()); 自動判斷類型不錯。 Console.WriteLine(Marshal.PtrToStringAnsi(GetStringReturnByBytes())); Console.WriteLine(GetStringReturnByMarshal()); Console.WriteLine(Marshal.PtrToStringUni(GetStringReturnByBytesW())); Console.WriteLine(GetStringReturnByMarshalW()); }
第三:字符串指針做爲輸入輸出參數時,由於要求有固定的容量,因此這裏使用的是StringBuilder,你們仔細看了,固然也有byte[]版本。這個看你們喜歡那個版本就是用那個.
[DllImport("TestDll", EntryPoint ="GetStringParam")] publicstaticexternvoid GetStringParamByBytes(byte[] outHello, int len); [DllImport("TestDll", EntryPoint ="GetStringParam")] publicstaticexternvoid GetStringParamByMarshal([Out, MarshalAs(UnmanagedType.LPStr)]StringBuilder outHello, int len); [DllImport("TestDll", EntryPoint ="GetStringParamW")] publicstaticexternvoid GetStringParamByBytesW(byte[] outHello, int len); [DllImport("TestDll", EntryPoint ="GetStringParamW")] publicstaticexternvoid GetStringParamByMarshalW([Out, MarshalAs(UnmanagedType.LPWStr)]StringBuilder outHello, int len); publicbyte[] _outHello =newbyte[]; publicbyte[] _outHelloW =newbyte[]; public StringBuilder _builder =new StringBuilder(); //很重要設定string的容量。 publicvoid Run() { // GetStringParamByBytes(_outHello, _outHello.Length); GetStringParamByMarshal(_builder, _builder.Capacity); GetStringParamByBytesW(_outHelloW, _outHelloW.Length /); GetStringParamByMarshalW(_builder, _builder.Capacity); // Console.WriteLine(Encoding.ASCII.GetString(_outHello)); Console.WriteLine(_builder.ToString()); Console.WriteLine(Encoding.Unicode.GetString(_outHelloW)); Console.WriteLine(_builder.ToString()); }