Answered by:
What is the best method to P/Invoke a native function which takes an array of TCHAR* as an input and returns the first TCHAR* element of the array?

Question
-
What is the best method to P/Invoke a native function which takes an array of TCHAR* as an input and returns the first TCHAR* element of the array?
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.- Edited by Xiaoyun Li – MSFT Tuesday, April 7, 2009 10:05 AM
Tuesday, April 7, 2009 7:26 AM
Answers
-
Suppose that a native function takes an array of TCHAR* as an input and returns the first TCHAR* element of the array. When using P/Invoke to call such a native function, we can refer to the following codes:
NativeDll.cpp:
#include "stdafx.h" #include <tchar.h> #include <strsafe.h> #include <objbase.h> extern "C" __declspec(dllexport) TCHAR* test(TCHAR** strarr) { int length = _tcslen(strarr[0]) + 1; // '+1' for the terminating null character STRSAFE_LPWSTR result = (STRSAFE_LPWSTR)CoTaskMemAlloc(length * sizeof(TCHAR)); // CoTaskMemAlloc accepts the size in bytes StringCchCopy(result, length, strarr[0]); return (TCHAR*) result; }
CSharpCaller.cs:using System.Runtime.InteropServices; class Program { [DllImport("NativeDll.dll", CharSet = CharSet.Auto)] static extern IntPtr test(string[] array); static void Main(string[] args) { string[] array = new string[2]; array[0] = "hello"; array[1] = "world"; IntPtr ptr = test(array); string result = Marshal.PtrToStringAuto(ptr); Marshal.FreeCoTaskMem(ptr); System.Console.WriteLine(result); } }
Regarding the CLR interop feature, on the native side we use CoTaskMemAlloc to allocate a block of native memory to hold the return value and copy the characters in the input to the memory, instead of directly returning the first element of the input array. (The reason will be discussed in the following content)On the managed side, we don’t declare the return type as String in the P/Invoke signature of native function test. Actually the memory allocated by CoTaskMemAlloc for the return value is automatically freed by the CLR interop layer. Although declaring the return type as a String won’t cause any memory leaks in .NET Framework runtime v2 of x86 platform, the marshalling staff changes significantly in runtime v4, which will be released soon. For instance, the interop layer does not free the memory when returning a String for P/Invoke. It causes a memory leak if we do not manually free the memory chunk returned from the native code. There are at least two options to fix it:
1. Return an IntPtr and call Marshal.FreeCoTaskMem to free the memory addressed by the pointer, as the above code sample suggested.
2. Provide a function in the native DLL to specifically free the memory and P/Invoke it in the managed code. Using this option hides the implementation of the memory allocation/clearing logic. The benefit is that you can use different heap-operation pairs, such as malloc – free, new – delete, new[] – delete[], instead of being forced to allocate memory with CoTaskMemAlloc and use CoTaskMemFree to release it.
Forum Thread Analysis:
Thread URL:
http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/ccbf1e8c-c5de-4903-8d8d-d9b2bcae18ff/In native functions, if we directly return the first array element, as shown in the following code, the return String in the managed side will receive garbage characters. (.NET Framework runtime v2 in x86 platform)
NativeDLL.cpp:
extern "C" __declspec(dllexport) TCHAR* test(TCHAR** strarr) { return strarr[0]; }
CSharpCaller.cs:
class Program { [DllImport("NativeDll.dll", CharSet = CharSet.Auto)] static extern string test(string[] array); static void Main(string[] args) { string[] array = new string[2]; array[0] = "hello"; array[1] = "world"; string result = test(array); Console.WriteLine(result); } }
Cause of this issue: (applied to .NET Framework runtime v2 on x86 platform)
When the managed code calls into the native function, it cannot pass the string array directly to the native side. Instead, CLR allocates the memory (CoTaskMemAlloc) and constructs the native TCHAR* array according to the contents in the .NET object. From the perspective of resource management, it is CLR’s responsibility to allocate and clean the memory. The interop layer does the following for P/Invoke:
1. Allocates the native TCHAR* array.
2. Fills in the TCHAR* array according to the managed string array.
3. Invokes the native method with the newly allocated TCHAR* array.
4. Frees the memory allocated in step 1.
5. Allocates a .NET String object to hold the return value (the first element in the input TCHAR* array).
6. Converts the characters in the TCHAR* return value to the newly created .NET String object.
7. Frees the native memory that holds the return value.
Because the native memory was freed in step 4, the remaining steps (step 5, 6, 7) are accessing the freed memory and copying its corrupted values into the .NET String object as the return.
We can use the code sample in the first part of this section to resolve this issue.
Other related thread:
http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/daf8d0d6-1e0d-4f25-9ae9-d7b317946c16
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/af2d0101-0bf4-49c9-bc2a-10a40074674e
For more FAQ about Visual C# General, please see Visual C# General FAQ
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.- Marked as answer by Xiaoyun Li – MSFT Tuesday, April 7, 2009 7:51 AM
- Unmarked as answer by Xiaoyun Li – MSFT Tuesday, April 7, 2009 10:18 AM
- Marked as answer by Xiaoyun Li – MSFT Tuesday, April 7, 2009 10:24 AM
Tuesday, April 7, 2009 7:34 AM
All replies
-
Introduction and Reference:
Platform Invocation Service (P/Invoke) makes it easy to call Win32 DLLs like Windows API from .NET managed applications. For detail information and a code sample, please refer to Calling Win32 DLLs in C# with P/Invoke by Jason Clark.
Here is an introduction to P/Invoke and Marshaling on the .NET Framework:
http://msdn.microsoft.com/en-us/library/aa446536.aspxFor P/Invoke signatures of most Windows APIs in Visual C# and Visual Basic .NET, please refer to:
http://www.codeplex.com/clrinterop/Release/ProjectReleases.aspx?ReleaseId=14120
http://www.pinvoke.netHere is an article about the DLLs search order when using P/Invoke:
http://msdn.microsoft.com/en-us/library/ms682586.aspx
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.Tuesday, April 7, 2009 7:27 AM -
Suppose that a native function takes an array of TCHAR* as an input and returns the first TCHAR* element of the array. When using P/Invoke to call such a native function, we can refer to the following codes:
NativeDll.cpp:
#include "stdafx.h" #include <tchar.h> #include <strsafe.h> #include <objbase.h> extern "C" __declspec(dllexport) TCHAR* test(TCHAR** strarr) { int length = _tcslen(strarr[0]) + 1; // '+1' for the terminating null character STRSAFE_LPWSTR result = (STRSAFE_LPWSTR)CoTaskMemAlloc(length * sizeof(TCHAR)); // CoTaskMemAlloc accepts the size in bytes StringCchCopy(result, length, strarr[0]); return (TCHAR*) result; }
CSharpCaller.cs:using System.Runtime.InteropServices; class Program { [DllImport("NativeDll.dll", CharSet = CharSet.Auto)] static extern IntPtr test(string[] array); static void Main(string[] args) { string[] array = new string[2]; array[0] = "hello"; array[1] = "world"; IntPtr ptr = test(array); string result = Marshal.PtrToStringAuto(ptr); Marshal.FreeCoTaskMem(ptr); System.Console.WriteLine(result); } }
Regarding the CLR interop feature, on the native side we use CoTaskMemAlloc to allocate a block of native memory to hold the return value and copy the characters in the input to the memory, instead of directly returning the first element of the input array. (The reason will be discussed in the following content)On the managed side, we don’t declare the return type as String in the P/Invoke signature of native function test. Actually the memory allocated by CoTaskMemAlloc for the return value is automatically freed by the CLR interop layer. Although declaring the return type as a String won’t cause any memory leaks in .NET Framework runtime v2 of x86 platform, the marshalling staff changes significantly in runtime v4, which will be released soon. For instance, the interop layer does not free the memory when returning a String for P/Invoke. It causes a memory leak if we do not manually free the memory chunk returned from the native code. There are at least two options to fix it:
1. Return an IntPtr and call Marshal.FreeCoTaskMem to free the memory addressed by the pointer, as the above code sample suggested.
2. Provide a function in the native DLL to specifically free the memory and P/Invoke it in the managed code. Using this option hides the implementation of the memory allocation/clearing logic. The benefit is that you can use different heap-operation pairs, such as malloc – free, new – delete, new[] – delete[], instead of being forced to allocate memory with CoTaskMemAlloc and use CoTaskMemFree to release it.
Forum Thread Analysis:
Thread URL:
http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/ccbf1e8c-c5de-4903-8d8d-d9b2bcae18ff/In native functions, if we directly return the first array element, as shown in the following code, the return String in the managed side will receive garbage characters. (.NET Framework runtime v2 in x86 platform)
NativeDLL.cpp:
extern "C" __declspec(dllexport) TCHAR* test(TCHAR** strarr) { return strarr[0]; }
CSharpCaller.cs:
class Program { [DllImport("NativeDll.dll", CharSet = CharSet.Auto)] static extern string test(string[] array); static void Main(string[] args) { string[] array = new string[2]; array[0] = "hello"; array[1] = "world"; string result = test(array); Console.WriteLine(result); } }
Cause of this issue: (applied to .NET Framework runtime v2 on x86 platform)
When the managed code calls into the native function, it cannot pass the string array directly to the native side. Instead, CLR allocates the memory (CoTaskMemAlloc) and constructs the native TCHAR* array according to the contents in the .NET object. From the perspective of resource management, it is CLR’s responsibility to allocate and clean the memory. The interop layer does the following for P/Invoke:
1. Allocates the native TCHAR* array.
2. Fills in the TCHAR* array according to the managed string array.
3. Invokes the native method with the newly allocated TCHAR* array.
4. Frees the memory allocated in step 1.
5. Allocates a .NET String object to hold the return value (the first element in the input TCHAR* array).
6. Converts the characters in the TCHAR* return value to the newly created .NET String object.
7. Frees the native memory that holds the return value.
Because the native memory was freed in step 4, the remaining steps (step 5, 6, 7) are accessing the freed memory and copying its corrupted values into the .NET String object as the return.
We can use the code sample in the first part of this section to resolve this issue.
Other related thread:
http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/daf8d0d6-1e0d-4f25-9ae9-d7b317946c16
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/af2d0101-0bf4-49c9-bc2a-10a40074674e
For more FAQ about Visual C# General, please see Visual C# General FAQ
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.- Marked as answer by Xiaoyun Li – MSFT Tuesday, April 7, 2009 7:51 AM
- Unmarked as answer by Xiaoyun Li – MSFT Tuesday, April 7, 2009 10:18 AM
- Marked as answer by Xiaoyun Li – MSFT Tuesday, April 7, 2009 10:24 AM
Tuesday, April 7, 2009 7:34 AM