I'm having memory leak issues with a third party c++ dll. For certain calls, the dll allocates memory for the string, passes it out as a char* and then expects to receive that pointer back so that it can de-allocate the memory.
Here are some comments from the header file, a couple of examples of where the char* get returned, and the signature of the "Release" method.
(The dll is called SW_API, it's from a trade clearing house - if anyone has perhaps wrapped this already I'd love to talk to them!).
/* Strings returned by the API are similarly normal nul-terminated C strings.
* The user should not attempt to change any of the bytes or read past the
* terminating nul of any returned string. All returned strings must be
* released using SW_ReleaseString() once the user is finished with the
* result. Failure to do this will result in memory leaks.
*/
/**
* @typedef const char* SW_XML
* @brief A string containing an XML documents text.
* @note As with all output strings, returned XML must be freed
* by the user. See @ref resource.
* @sa ErrorCodes
*/
typedef const char* SW_XML;
const char* STDAPICALLTYPE SW_GetLastErrorSpecifics();
SW_ErrCode STDAPICALLTYPE SW_DealGetSWML(SW_LoginID lh,
const char* swmlVersion,
SW_DealVersionHandle dealVersionHandle,
SW_XML* resultXML_out);
void STDAPICALLTYPE SW_ReleaseString(const char* buffer);
Attempting to read up from various sources, I have tried the following:
// Extern declarations
[DllImport(sw_api_dll, EntryPoint = "_SW_GetLastErrorSpecifics@0", CharSet = CharSet.Ansi)]
public static extern IntPtr SW_GetLastErrorSpecifics();
[DllImport(sw_api_dll, EntryPoint = "_SW_DealGetSWML@16", CharSet = CharSet.Ansi)]
public static extern int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, [Out] out IntPtr outputSWML);
[DllImport(sw_api_dll, EntryPoint = "_SW_ReleaseString@4", CharSet=CharSet.Ansi)]
public static extern void SW_ReleaseString(IntPtr buffer);
// Using the externs.
private static string GetIntPtrStringAndRelease(IntPtr ptr)
{
string result = Marshal.PtrToStringAnsi(ptr);
API.SW_ReleaseString(ptr);
return result;
}
public static int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, ref string outputSWML)
{
IntPtr outputSWML_out = new IntPtr();
int result = API.SW_DealGetSWML(lh, swmlVersion, dealVersionHandle, out outputSWML_out);
outputSWML = GetIntPtrStringAndRelease(outputSWML_out);
return result;
}
public static string SW_GetLastErrorSpecifics()
{
IntPtr ptr = API.SW_GetLastErrorSpecifics();
return GetIntPtrStringAndRelease(ptr);
}
It seems I just can't get the API to release the strings.
Now, it's possible that this is just a bug in the API, but I doubt it. More likely is I'm doing something funamentally wrong.
All I know is that my working set just keeps on growing.
The company in question provide a Java wrapper but won't stretch to a .Net wrapper.
Any help most gratefully received.
Brett.
My best guess is that the IntPtr is not equivalent to the char* of your string. So when you call SW_ReleaseString, you're not providing the same pointer.
What you can do, is throw together a little C++CLI intermediary. In C++CLI, and you will have access to the char*
directly, as well as being able to use Marshal::PtrToString and managed string pointers, with String^
.
Here's what I think that would look like:
C++/CLI:
String^ GetStringAndRelease(char* ptr)
{
string result = Marshal::PtrToStringAnsi(ptr);
SW_ReleaseString(ptr);
return result;
}
int SW_DealGetSWML(int lh, const char* swmlVersion, const char* dealVersionHandle, String% outputSWML)
{
char* outputSWML_out;
int result = SW_DealGetSWML(lh, swmlVersion, dealVersionHandle, outputSWML_out);
outputSWML = GetStringAndRelease(outputSWML_out);
return result;
}
String^ SW_GetLastErrorSpecifics()
{
char* ptr = SW_GetLastErrorSpecifics();
return GetStringAndRelease(ptr);
}
and then in C#:
[DllImport(your_wrapper_dll, EntryPoint = "_SW_DealGetSWML@16", CharSet = CharSet.Ansi)]
public static extern int SW_DealGetSWML(int lh, string swmlVersion, string dealVersionHandle, [Out] out string outputSWML);
[DllImport(your_wrapper_dll, EntryPoint = "_SW_GetLastErrorSpecifics@0", CharSet = CharSet.Ansi)]
public static extern string SW_GetLastErrorSpecifics();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With