.net 互操作之p/invoke- 数据封送之字符串(2)
2010-08-26 23:45 Clingingboy 阅读(758) 评论(0) 编辑 收藏 举报
使用Unicode传递
一.定义托管函数
// void __cdecl TestStringArgumentsInOut(const wchar_t* inString, wchar_t* outString, int bufferSize); [DllImport(_dllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private extern static void TestStringArgumentsFixLength(string inString, StringBuilder outString, int bufferSize);
第一个参数是传入的参数,第二个是输出的.
注意
1.String是不可变类型,const wchar_t* inString
2.wchar_t*为Unicode,所以指定为CharSet = CharSet.Unicode,设置为Unicode则整个函数的参数均为以Unicode传递
3.第二个参数是输出参数,所以设置为StringBuilder类型
下面示例功能为拷贝字符串,托管代码分配内存
二.非托管代码示例
void __cdecl TestStringArgumentsFixLength( const wchar_t* inString, wchar_t* outString, int bufferSize) { if(NULL != inString) { wcscpy_s(outString, bufferSize, inString); } }
三.托管代码
private static void TestStringArgumentsFixLength() { string inString = "This is a input string."; int bufferSize = inString.Length; StringBuilder sb = new StringBuilder(bufferSize); TestStringArgumentsFixLength(inString, sb, bufferSize + 1); Console.WriteLine("Original: {0}", inString); Console.WriteLine("Copied: {0}", sb.ToString()); }
以MarshalAs的方式传递
以MarshalAs标签定义的话,可以细化函数参数是否以Unicode形式传递,可能非托管函数既有ANSI字符串也有Unicode字符串。
MarshalAs以指定UnmanagedType参数来设置要传递参数的类型,UnmanagedType定义了很多非托管类型
1.定义托管函数
// void __cdecl TestStringMarshalArguments(const char* inAnsiString, const wchar_t* inUnicodeString, wchar_t* outUnicodeString, int outBufferSize) [DllImport(_dllName, CallingConvention = CallingConvention.Cdecl)] private extern static void TestStringMarshalArguments( [MarshalAs(UnmanagedType.LPStr)] string inAnsiString, [MarshalAs(UnmanagedType.LPWStr)] string inUnicodeString, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder outStringBuffer, int outBufferSize);
第一个是ANSI字符串,第二传入的Unicode字符串和第三个是传出的Unicode字符串
2.定义非托管代码
void __cdecl TestStringMarshalArguments(const char* inAnsiString, const wchar_t* inUnicodeString, wchar_t* outUnicodeString, int outBufferSize) { size_t ansiStrLength = strlen(inAnsiString); size_t uniStrLength = wcslen(inUnicodeString); size_t totalSize = ansiStrLength + uniStrLength + 2; wchar_t* tempBuffer = new(std::nothrow) wchar_t[totalSize]; if(NULL == tempBuffer) { return; } wmemset(tempBuffer, 0, totalSize); mbstowcs(tempBuffer, inAnsiString, totalSize); wcscat_s(tempBuffer, totalSize, L" "); wcscat_s(tempBuffer, totalSize, inUnicodeString); wcscpy_s(outUnicodeString, outBufferSize, tempBuffer); delete[] tempBuffer; }
3.托管代码测试
private static void TestMarshalArguments() { string string1 = "Hello"; string string2 = "世界!?"; int outBufferSize = string1.Length + string2.Length + 2; StringBuilder outBuffer = new StringBuilder(outBufferSize); TestStringMarshalArguments(string1, string2, outBuffer, outBufferSize); Console.WriteLine("{0}", outBuffer.ToString()); }
释放由非托管函数分配的内存
当在函数中传递引用参数时,非托管函数则需要分配内存
一.自动释放
1.非托管函数
void __cdecl TestStringArgumentOut(int id, wchar_t** ppString) { if(NULL != ppString) { int bufferSize = 128; *ppString = (wchar_t*)CoTaskMemAlloc(bufferSize); swprintf_s(*ppString, bufferSize/sizeof(wchar_t), L"Out string of ID: %d", id); } }
使用CoTaskMemAlloc方法来申请内存,则会自动调用CoTaskMemFree来释放非托管内存,这就意味了托管代码无需处理内存问题,减轻了托管代码的的复杂度.
2.定义托管函数
[DllImport(_dllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private extern static void TestStringArgumentOut(int id, ref string outString);
需要以ref 关键字来制定字符串是输出的,以表示此参数是引用类型
3.测试
string strResult = ""; TestStringArgumentOut(2, ref strResult); Console.WriteLine("Return string value: {0}", strResult);
输出
二.手动控制
非托管代码不变,重新定义托管函数,以IntPtr 类型代替String类型
// NATIVELIB_API void __cdecl TestStringArgumentOut(int id, wchar_t** ppString); [DllImport(_dllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TestStringArgumentOut")] private extern static void TestStringArgumentOutIntPtr(int id, ref IntPtr outString);
测试代码
string strResult; IntPtr strIntPtr = IntPtr.Zero; TestStringArgumentOutIntPtr(1, ref strIntPtr);
//数据类型转换,复制数据
strResult = Marshal.PtrToStringUni(strIntPtr); //手动释放内存
Marshal.FreeCoTaskMem(strIntPtr); Console.WriteLine("Return string IntPtr: {0}", strResult);
封送作为返回值的字符串
看以下托管函数定义,同上返回IntPtr 则需要手动释放内存,直接返回String的则会自动处理
// void wchar_t* __cdecl TestStringAsResult() [DllImport(_dllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, EntryPoint = "TestStringAsResult")] private extern static IntPtr TestStringAsResultIntPtr(int id); [DllImport(_dllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private extern static string TestStringAsResult(int id);
测试代码
// 1. IntPtr string result; IntPtr strPtr = TestStringAsResultIntPtr(1); result = Marshal.PtrToStringUni(strPtr); // Marshal.FreeCoTaskMem(strPtr); Console.WriteLine("Return string IntPtr: {0}", result); // 2. String result = TestStringAsResult(2); Console.WriteLine("Return string value: {0}", result);
特殊的BSTR
必须采用手动释放,不可以自动释放