CLR can marshal managed types into unmanaged c/c++ types using marshal as attrubte
what I need is that what is the proper method to free this allocated memory.. for example::
in C/C++ code
char* dummy(char* text){
//do some thing
return text;//dummy way to return it to the caller to free it.
}
and in C#
[DllImport("test.dll",CallingConvention=CallingConvention.Cdecl)]
static extern IntPtr dummy([MarshalAs(UnmanagedType.LPStr)] string text);
and finally the test
C# Test
IntPtr marshaledData=dummy("any text");
//what is the proper way to free this memory location
Related
In C:
extern "C" __declspec(dllexport) int CfgGetVariableString(const char *Name, char **Value)
{
char StrValue[STR_MAX];
int RetValue = GetVariableToStrValue(Name, StrValue, STR_MAX);
if (RetValue == 0)
*Value = StrValue;
return RetValue;
}
C#
[DllImport(DllName, CallingConvention = DllCallingConvention)]
private static extern int CfgGetVariableString([MarshalAs(UnmanagedType.LPStr)]string name, [MarshalAs(UnmanagedType.LPStr)]out string value);
This does not work. I can make it work by calling CoTaskMemAlloc, but then I guess I should free it with separate managed to native call?
So what is the cleanest way to do it?
I can make it work by calling CoTaskMemAlloc.
That is the only way that it can work. Because the marshaller will call CoTaskMemFree to deallocate the memory returned from the native function.
I guess I should free it with separate managed to native call?
As mentioned above, that's not necessary because the framework already does so.
What is the cleanest way to do it?
In my opinion the cleanest approach here, since you are already using a string length determined at compile time, is to have the caller allocate the memory. Change the type from char** to char*. Add an extra argument containing the length of the allocated string. Use StringBuilder on the C# side. There are countless examples of this pattern available here and indeed in other places so I don't feel any need to add to the canon.
// --------------------------- C# Code ------------------------------
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void PassStringOut([MarshalAs(UnmanagedType.BStr)] out String str);
[DllImport("MarshallStringsWin32.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void FreeString([MarshalAs(UnmanagedType.BStr)] String str);
static void Main(string[] args)
{
String str;
PassStringOut(out str);
FreeString(str);
}
// --------------------------- C+ Code ------------------------------
void PassStringOut(__out BSTR* str)
{
const std::string stdStr = "The quick brown fox jumps over the lazy dog";
_bstr_t bstrStr = stdStr.c_str();
*str = bstrStr.copy();
}
void FreeString(BSTR str)
{
SysFreeString(str);
}
The value of 'str' pointer in PassStringOut() and FreeString() are different, and I am getting a heap corruption error when calling SysFreeString(). Should I pass 'str' by reference to FreeString()? If so, what is the syntax I should use in C# and C++?
The marshaling layer will allocate a copy of the string in managed memory. That copy will be freed by the garbage collector. You do not have to SysFreeString a String in C#, and in fact, attempting to do so is a great way to corrupt the heap, as you have discovered.
should I take it that there will be 2 copies performed on the string? The *str = bstrStr.copy(); and then by the marshalling layer?
Let me describe in more detail what is happening here.
Your Main method invokes the unmanaged code, passing the managed address of a local variable of type String. The marshaling layer creates its own storage of suitable size to hold a BSTR and passes a reference to that storage to your unmanaged code.
The unmanaged code allocates a string object that refers to the storage associated with the literal, then allocates a BSTR and makes the first copy of the original string into the heap-allocated BSTR. Then it makes a second copy of that BSTR and fills in the out parameter with a reference to that storage. The bstrStr object goes out of scope and its destructor frees the original BSTR.
The marshaling layer then makes a managed string of the appropriate size, copying the string for a third time. It then frees the BSTR that was passed to it. Control returns to your C# code, which now has a managed string.
That string is passed to FreeString. The marshaling layer allocates a BSTR and for the fourth time makes a copy of the string into the BSTR, which is passed to your unmanaged code. It then frees a BSTR which it does not own and returns. The marshaling layer frees the BSTR that it allocated, corrupting the heap.
The managed heap remains uncorrupted; the managed string will be freed by the garbage collector at a time of the garbage collector's choosing.
Should I pass 'str' by reference to FreeString()?
No. Rather, you should stop writing interop code until you have a thorough and deep understanding of how all aspects of marshalling work.
Marshalling data between managed and unmanaged code is difficult even for experts to get correct. My advice is that you take a large step back and obtain the services of an expert who can teach you how to write interop code safely and correctly, should you need to do so.
This doesn't work the way you think it works. The pinvoke marshaller already released the BSTR automatically. It happened when you called PassStringOut(), the marshaller converted it to a System.String and released the BSTR. This is the normal and necessary protocol for passing BSTRs between native and managed code.
What goes wrong in FreeString() is that a new BSTR got allocated by the pinvoke marshaller. And it got released twice. First by your native code, again by the pinvoke marshaller. Kaboom from the debug heap that is used when you run your code with a debugger attached.
You are just helping too much, don't call FreeString().
You can get the pinvoke marshaller to handle ANSI strings for you, it is in fact the default behavior since they are so common in legacy C code. Your C++ function could look like this:
extern "C" __declspec(dllexport)
void __stdcall PassStringOut(char* buffer, size_t bufferLen)
{
const std::string stdStr = "The quick brown fox jumps over the lazy dog";
strcpy_s(buffer, bufferLen, stdStr.c_str());
}
With matching C# code:
class Program {
static void Main(string[] args) {
var buffer = new StringBuilder(666);
PassStringOut(buffer, buffer.Capacity);
Console.WriteLine(buffer.ToString());
Console.ReadLine();
}
[DllImport("Example.dll")]
private static extern bool PassStringOut(StringBuilder buffer, int capacity);
}
Having to guess the proper size of the buffer is however the fugly detail.
I have a third party unmanaged C++ dll that I need to call from C#. The C++ function returns a char*. I've figured out how to convert that to a managed string in C#. But I don't know if I need to deallocate the memory. The following code works except that Marshal.FreeHGlobal(p) throws "The handle is invalid.". So do I need to deallocate the memory, and if so, how?
[DllImport("abc.dll", EntryPoint = "?GetVersion#ABC##QEAAPEADXZ", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
private static extern unsafe char* GetVersionRaw();
public static unsafe string GetVersion()
{
char* x = Abc.GetVersionRaw(); // call the function in the unmanaged DLL
IntPtr p = new IntPtr(x);
string s = Marshal.PtrToStringAnsi(p);
Marshal.FreeHGlobal(p);
return s;
}
It is impossible to say in general how to free an arbitrary pointer returned from a C++ function. There are many ways the pointer could have been produced (new, malloc, VirtualAlloc when you're on Windows, etc). There is no way to determine by inspection which one the pointer came from, and using the wrong deallocation function is an error (which may crash your process). Also, even if you know which function to call, your code may be linked against a different version of the language runtime library - so you might be correctly calling free or delete, but an incompatible version.
Normal procedure is for libraries that perform allocation to provide a corresponding deallocation function that frees the allocated memory appropriately. If the library you're using doesn't have this, you'll just have to try what they've told you and follow up if it doesn't work.
I have an unmanaged DLL on C with func:
char* My_Func(char* data, int input_length, int output_length);
In this func i have
result = (char*)malloc(output_lenght);
strcpy(result,test_char);
return(result);
In C# i import it
[DllImport(#"libsmev.DLL", CharSet = CharSet.Ansi)]
public static extern IntPtr My_Func([MarshalAs(UnmanagedType.LPStr)]string data, int input_length, out int output_length);
And call it
IntPtr result = My_Func (n1, n1.Lenght,n2);
How to free char* or IntPtr?
Marshal.FreeHGlobal(IntPtr) and Marshal.FreeCoTaskMem(IntPtr) doesn`t work.
You need to export a function that can free the pointer using free. As you have noticed Marshal.FreeHGlobal and Marshal.FreeCoTaskMem doesn't work because these functions expect pointer that have allocated using a different allocation function.
If you create an API that allocates memory and returns a pointer to this memory you have to also add a function in the API to free the memory. Otherwise your API will not be available outside the library that was used to allocate the memory (in this case the C standard library).
In C, something like this:
void Free_Obj(char* obj) {
free(obj);
}
In C#, something like this:
[DllImport("libsmev.DLL")]
public static extern void Free_Obj(IntPtr obj);
And call it:
IntPtr result = My_Func (n1, n1.Lenght,n2);
Free_Obj(result);
Instread of creating a custom free function in your DLL, you could also use CoTaskMemAlloc instead of malloc to allocate the memory.
You can then use Marshal.FreeCoTaskMem to free it.
I'm calling C# method from C++ and passing char** as argument. It has to be char** because I need to return value through parameter.
C# code:
[ExportDll("test", System.Runtime.InteropServices.CallingConvention.StdCall)]
public static int test([MarshalAs(UnmanagedType.AnsiBStr)] ref string p)
{
Console.WriteLine(p);
}
C++ code to invoke function:
typedef int (__stdcall *MYPROC)(char **);
VOID main(VOID)
{
HINSTANCE hinstLib;
MYPROC MyProc;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
hinstLib = LoadLibrary(TEXT("mydll.dll"));
if (hinstLib != NULL)
{
ProcAdd = (MYPROC) GetProcAddress(hinstLib, "test");
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
char s1[] = "test";
char *s2 = s1;
char **s3 = &s2;
(MyProc) (s3);
cout << s3;
}
fFreeResult = FreeLibrary(hinstLib);
}
}
It's simple to pass char* (remove ref in c#, and use char* in c++), but when trying to pass char** i get a runtime error on line where I call the function :(
in c#, Console.WriteLine prints out correct value, but after that, I get an error:
Windows has triggered a breakpoint in COMDynamicLoad.exe.
This may be due to a corruption of the heap, which indicates a bug in COMDynamicLoad.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while COMDynamicLoad.exe has focus.
The output window may have more diagnostic information.
How should I do this?
You declare ref UnmanagedType.AnsiBStr but you expect a char**. This cannot work, since a ref to a BSTR is not a char**. See Default Marshaling for Strings for examples of marshaling declarations. These are possible declarations for an input-output string:
PassStringRef2([in, out] BSTR *s);
PassStringRef3([in, out] LPStr *s);
PassStringRef4([in, out] LPWStr *s);
and the equivalent C# marshaling declarations are:
PassStringRef2([MarshalAs(UnmanagedType.BStr)]ref String s);
PassStringRef3([MarshalAs(UnmanagedType.LPStr)]ref String s);
PassStringRef4([MarshalAs(UnmanagedType.LPWStr)]ref String s);
Your char** declaration is the equivalent of LPStr *s, so the correct marshaling is [MarshalAs(UnmanagedType.LPStr)]ref String s. But a better option is to use BSTR because of the explicit length declaration, and manipulate it in C++ with the BSTR helpers.
This is likely because you took a char* pointing to a string literal- which is bad because modifying that memory is undefined behaviour.
Would this work?
public static int test([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] p)
In many cases and as seems to be the situation here (see accepted answer by Remus Rusanu), using the proper marshaling attributes in the API declarations is all that is needed to have the interop "glue" on both ends of the interface start "playing nice with one another" and result in...
the proper data values being sent/delivered [good!]
Windows not triggering automatic breakpoint upon various suspected ... corruption of the heap ... [better! ;-)]
I am adding this answer, 5 months after the original post, because this question and its responses were very useful in fixing a recent interop bug of mine, but failed to mention directly information about the very plausible cause of many interop issues, namely:
Mismatch in the Memory Ownership conventions
(and/or in the memory allocation and freeing methods).
The January 2008 article on MSDN Magazine titled Marshaling between Managed and Unmanaged Code provided me with the info I needed in regards to memory ownership conventions. Indeed this article provides a complete overview of the marshaling process. It covers in specific details and examples the issues of
[InAttribute] and [OutAttribute] aka [In] and [Out]
Keywords Out and Ref and passing by reference
Return values
StringBuilder and marshaling
Copying and pinning
(Pinning = optiimization by CLR when it deems it safe to lock data area in the CLR heap and pass corresponding pointers to unmanaged code)
Memory Ownership:
No change allowed vs. In-place change vs. Reference change
Also, on the need of using CoTaskMemFree() and CoTaskMemAlloc() to free or allocate memory respectively received from or sent to the Managed code.
Reverse P/Invoke and delegate lifetime
P/Invoke Interop Assistant
This article is very useful because it gathers in a single document and in an accessible fashion information otherwise disseminated across dozen of technically authoritative but dry [and oft' confusing] reference documents (cf. the old but on-point joke about Microsoft's documentation at large).
In short, it makes it a good primer or refresher for casual implementers of interop solutions.