I have a C++ dll that I am calling from C# code.
the dll takes in couple params and returns an int..
C++ code.
extern "C" __declspec(dllexport) int DoSomething(char* input1, char* buffer)
{
gss_buffer_desc token;
std::string encodedTokenStr = base64_encode((unsigned char *)token.value, token.length).c_str();
std::copy(encodedTokenStr.begin(), encodedTokenStr.end(), buffer);
return value;
}
C#
public sealed class MyClass
{
public int DoSomething(string input1, out StringBuilder buffer)
{
buffer = new StringBuilder(10000);
return DoSomething(input1, buffer)
}
[DllImport("mycppcode.dll")]
private static extern int DoSomething(string input1, StringBuilder buffer)
}
sometimes I see that a lot of memory is being used by this application and my first thought was about memory leaks.
Does garbage collector takes care of all the objects that are initialized in the C++ code?
does C++ code initialize some memory for the string builder ("buffer") even if it is initialized in C#. I cannot dispose this in the C++ because i need to collect the data from the string builder.
I have never worked on C++ but I see that few objects that were declared in C++ dll are being cleared.
I might be doing something wrong in the way that I am calling the C++ code.
can this string builder cause memory leaks??
C# doesn't free any memory allocated by C++ (with some small exceptions if you use COM allocators/BSTR).
In your specific case
I don't see any problem in your case. There is no memory allocation in C++ code, and the StringBuilder is "built" C#-side, and being 10000 characters big, I do hope it is enough (note that normally you would pass to C++ the size of the buffer, or you would first ask to another C++ method the size of the needed buffer).
On a tangential note, I would suggest using wchar_t in C++, to maintain full compatibility with C# strings/chars/...
Related
I am trying to call a function that converts sql queries between dialects. I am using an open-source project's DLL to use the functions for conversion in a C# university project. The problem i am facing is that i am getting a access violation reading location.
I've read some posts here on stack overflow which suggest that there might be a bad pointer somewhere, but i cannot find where. My pointers are not corrupt
The function for the conversion is this :
int ConvertSql(void *parser, const char *input, int64_t size, const char
**output, int64_t *out_size, int64_t *lines)
{
if(parser == NULL)
return -1;
SqlParser *sql_parser = (SqlParser*)parser;
// Run conversion
sql_parser->Convert(input, size, output, out_size, lines);
return 0;
}
I am calling the function in C#
char *parentInput;
fixed(char *input = &inputStr.ToCharArray()[0])
{
parentInput = input;
}
char** output = null;
Int64 out_size = 0;
Int64 lines = 0;
Int64 size = inputStr.Length;
Console.WriteLine(new IntPtr(&out_size)+" "+ new IntPtr(&lines)+" "+new IntPtr(&parserObj)+" "+new IntPtr(output));
int result = ConvertSql(&parserObj, intputStr, size, output, &out_size, &lines);
i get my parser object from this code which works without errors:
IntPtr parserObj = CreateParserObject();
the dllimport for the funtions are using this code:
[DllImport(dllName: "PATHTODLLFOLDER\\sqlparser.dll", EntryPoint = "CreateParserObject", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateParserObject();
[DllImport(dllName: "PATHTODLLFOLDER\\sqlparser.dll", EntryPoint = "ConvertSql", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern int ConvertSql(void *parser, String input, Int64 size, char **output, Int64 *out_size, Int64 *lines);
In .NET, calling an unmanaged method through P/invoke (which is what happens when you call an extern method) involves various type conversions on the parameters, which is known as "marshalling" and is done automatically by a part of the runtime known as the "marshaller".
In general, it's a terrible idea to marshal pointers. Instead, use the CLR marshaller's ability to convert certain types to pointers for you by changing the signature of your P/invoked method:
// split on multiple lines for readability
[DllImport("PATHTODLLFOLDER\\sqlparser.dll", EntryPoint = "ConvertSql", CallingConvention = CallingConvention.Cdecl)]
public static extern int ConvertSql(
IntPtr parser,
[MarshalAs(UnmanagedType.LPStr)] string input,
long size,
out IntPtr output, // see explanation below
out long out_size,
out long lines);
A few things about the above. First, I took the liberty of using the C# type aliases (string, long), because that's more idiomatic C#, but it doesn't change the behavior. Also, since there are no pointers anymore, no need for unsafe.
First, I declared parser as an IntPtr because those get converted to void* automatically if needed and that's what's already returned by CreateParserObject() anyway.
Second, out parameters can be converted to pointers to uninitialized objects, so by marking both out_size and lines as out, you fix that other problem.
The input is a string, which has a specific format in .NET. Since your function takes a const char*, you need to tell the marshaller how to convert the characters. That's where the MarshalAs attribute comes in: it's when the default conversion doesn't work for you. UnmanagedType.LPStr means char* in this case, so the string gets converted. The runtime will manage the memory for you.
But here we hit a big snag in the road: the output. When marshalling things, there's always questions about lifetime, or, specifically: who releases the memory? The fact that output is a char** implies that the parser allocates a block of memory and then returns it through that, which means the caller has to release it. From the get go, that reeks of bad C++ design because the caller doesn't know how the memory was allocated. Was it with malloc? new[]? Platform specific APIs like LocalAlloc? Is it a pointer to a block of static memory? Usually these APIs come with documentation telling precisely what to do with the pointer once you're done with it. Good C++ APIs return smart pointers, or ask that the caller pass a block of previously allocated memory that they can then play with.
But, this is the thing you're playing with, so here's how to make it work. First, you'd think that you could declare output as [MarshalAs(UnmanagedType.LPStr)] out string: the marshaller will copy the characters into a managed string and return it... but then the native string's (on the C++ side) memory will leak because the runtime doesn't know how the string was allocated, so it prefers to not do anything about it. Also, this assumes the string is null-terminated, which might not always be the case.
So, another option would be to instead declare output as out IntPtr. Then you can use Marshal.PtrToStringAnsi to convert your pointer into a string, and then release it... but you'll need to know how it was allocated first.
Putting it all together:
var parserObj = CreateParserObject();
var output = IntPtr.Zero;
try
{
long lines;
long out_size;
int result = ConvertSql(parserObj, inputStr, inputStr.Length, out output, out out_size, out lines);
var outputStr = Marshal.PtrToStringAnsi(output, (int)out_size);
// do what you want with outputStr here
}
finally
{
if (output != IntPtr.Zero)
{
// release output here
}
}
Oh, also, one final thought: whatever is returned by CreateParserObject() will probably have to be freed at one point. You're probably going to need another function for it, probably like:
[DllImport(/* ... */)]
public static extern void DestroyParserObject(IntPtr parserObject);
It might even already exist in your DLL.
Good luck!
I have the following function header in a native DLL:
unsigned char* Version_String()
I'm trying to call it from a C# project, I've tried the following call (as found on other similar questions here):
[DllImport("BSL430.dll", CharSet=CharSet.Ansi)]
public extern static UIntPtr Version_String();
And I keep getting the following exception:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
The next try was the following and I get the same exception:
[DllImport("BSL430.dll", CharSet=CharSet.Ansi)]
[return : MarshalAs(UnmanagedType.LPStr)]
public extern static string Version_String();
I can't seem to get around this issue. Any help would be greatly appreciated!
Edit:
I can't give the DLL code here, as it falls under an NDA, but the function I'm calling looks like this:
unsigned char versionString[50];
__declspec(dllexport) unsigned char* Version_String()
{
if(check_hardware_stuff())
{
strcpy((char *) versionString, "version_string_bla_bla");
versionString[5] = stuff;
}
else if (other_check())
{
//will return empty string, that should be filled with '\0'
}
else
{
strcpy( (char *) versionString, "ERROR" );
}
return versionString;
}
I'm not particularly fond of the DLL implementation, but I need to use it "as it is".
I get the exception thrown whenever I try to call VersionString(), regardless of what I do with the return value.
Update
Having seen the updated question, the various comments, and the code of your native function, it seems likely that the exception is raised when you call check_hardware_stuff(). It's simple enough to debug. I would replace your function with one like this:
unsigned char versionString[50];
__declspec(dllexport) unsigned char* Version_String()
{
strcpy(versionString, "testing");
return versionString;
}
If that still fails then my guess is that the error is raised in the DllMain of your DLL. Debug that by putting the above function into a plain vanilla DLL that does nothing else.
Original answer
Calling convention is the most obvious problem. Your native code most likely uses cdecl but the p/invoke default is stdcall. Change your p/invoke signature to be like this:
[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)]
public extern static IntPtr Version_String();
You can safely omit the CharSet parameter since none of the parameters have text because you are treating the return value as a pointer.
Edit: Hans correctly points out in the comments that since there are no parameters, the calling convention mis-match does not matter. So this isn't the problem.
Call Marshal.PtrToStringAnsi to convert to a .net string.
string version = Marshal.PtrToStringAnsi(Version_String());
Since PtrToStringAnsi is expecting an IntPtr parameter I would recommend that you use IntPtr as the return type of you p/invoke signature.
This all assumes that the memory returned from your native function is allocated and freed in the native DLL. If it is heap allocated and you expect the caller to deallocate it then you have a small problem. How do you deallocate the memory from C# since you don't have access to the native DLL's heap?
The simple solution is to use the shared COM heap to allocate the memory. Call CoTaskMemAlloc to allocate the buffer for the string. Then declare the return value to be of type string and the p/invoke marshaller will deallocate with the COM allocator.
[DllImport("BSL430.dll", CallingConvention=CallingConvention.Cdecl)]
public extern static string Version_String();
...
string version = Version_String();
Of course, this only applies if you are returning heap allocated memory that you expect the caller to deallocate.
Can someone explain what exactly is happening at a low level / memory management perspective on the 2 C# lines in "Main" in the following?
C++ Code (unmanaged):
#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT MyClass* MyClass_MyClass()
{
return new MyClass();
}
DLLEXPORT void MyClass_setName(MyClass* myClass, const char* name)
{
myClass->setName(name);
}
MyClass::MyClass()
{
_name.clear();
}
void MyClass::setName(const char* name)
{
_name.setCString(name, NAME_MAX_BYTES);
}
C# Code:
[DllImport(#"lib.dll")]
private static extern IntPtr MyClass_MyClass();
[DllImport(#"lib.dll")]
public static extern void MyClass_setName(
IntPtr myClass,
[System.Runtime.InteropServices.InAttribute()]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)]
string name);
public static void Main(string[] args)
{
var myClass = MyClass_MyClass();
MyClass_setName(myClass , "Test Name");
}
Specifically, I'm wondering how does .NET know much space to allocate for "myClass"? It's got to be doing some kind of "Marshal.AllocHGlobal(SIZE)" in the background, right? What happens if more space is needed (I set a name?)? Also, is there any risk of garbage collection coming around and moving memory around and messing up my "IntPtr myClass" ?
.NET knows nothing about MyClass type, it only stores a pointer to it. Size of the pointer is always known and fixed - 4 bytes for 32bit processes and 8 bytes for 64bit processes. All memory allocation and management in this particular case happens in unmanaged C++ code here:
return new MyClass();
and here:
myClass->setName(name);
It's up to this C++ DLL to decide how to allocate/free/manage memory, C# code will just call imported functions of this DLL.
No garbage collection will be performed on your unmanaged object and you'll need to provide additional (unmanaged) method to release it to avoid memory leak.
If the c++ code is not managed, .net isn't allocating anything beyond the IntPtr. It's being allocated by the c++ code.
This means that the only garbage collection will be done on that IntPtr. As that's small, it may take a long time before the garbage collector decides to clean it up.
What this means is that even if your C++ code is cleaning up well after itself, it may take a long time before it actually gets to do the cleanup. The C++ code may be using a ton of memory, but it's invisible to .net so it won't prioritize it for clean up over "larger" .net objects.
I have CString cs on C++ side and IntPtr ip on C# side which contains value of cs through marshaling mechanism.
Then, I simply get needed String as Marshal.PtrToStringAnsi(ip) and everything works fine, but I am wondering should I and if should, how can I delete unmanaged memory occupied by ip, i.e. cs?
You can't, you have no idea what allocator was used by the unmanaged code to create the CString instance. Moreover, you'd have to call the CString destructor, you can't get its address.
You are dead in the water if this CString object is returned as the function return value of a C++ function that you call from C#. It isn't clear from your question. You'll have an uncontrollable memory leak. A wrapper written in C++/CLI will be required to fix that problem. Strings returned as function return values must be allocated by CoTaskMemAlloc() to get properly cleaned up by the P/Invoke marshaller. No C++ code ever does that.
Unmanaged memory that was allocated by unmanaged code can only be freed by the unmanaged code. So you need to add another unmanaged function that will take a pointer to the allocated string and free the memory. This function then should be called from the managed code once it has finished working with the string.
Example:
class Program
{
[DllImport("test.dll")]
static extern IntPtr GetString();
[DllImport("test.dll")]
static extern IntPtr FreeString(IntPtr ptr);
static void Main()
{
IntPtr ptr = GetString();
try
{
var str = Marshal.PtrToStringAnsi(ptr);
// work with the string
}
finally
{
if (ptr != IntPtr.Zero)
{
FreeString(ptr);
}
}
}
}
I tried different things but i'm getting mad with Interop.
(here the word string is not referred to a variabile type but "a collection of char"):
I have an unmanaged C++ function, defined in a dll, that i'm trying to access from C#, this function has a string parameter and a string return value like this:
string myFunction(string inputString)
{
}
What should be string in C++ side? and C# one? and what parameters need DllImport for this?
What I've found to work best is to be more explicit about what's going on here. Having a string as return type is probably not recommended in this situation.
A common approach is to have the C++ side be passed the buffer and buffer size. If it's not big enough for what GetString has to put in it, the bufferSize variable is modified to indicate what an appropriate size would be. The calling program (C#) would then increase the size of the buffer to the appropriate size.
If this is your exported dll function (C++):
extern "C" __declspec void GetString( char* buffer, int* bufferSize );
Matching C# would be the following:
void GetString( StringBuilder buffer, ref int bufferSize );
So to use this in C# you would then do something like the following:
int bufferSize = 512;
StringBuilder buffer = new StringBuilder( bufferSize );
GetString( buffer, ref bufferSize );
The only good way that I know of doing this is to write a .NET C++ wrapper class using Managed C++ Extensions, and within the .NET C++ object call your native C++ code. There are functions in the managed extensions to convert a System.String to a char* or any other type of unmanaged string.
Basically you create a .NET class using C++ and expose it from an assembly, and internally to that assembly you can call your native C++ code. The other way is to add a pure C function to your C++ code using P/Invoke and then call your C code from C# and have your C function call your C++ code. This will work, but I tend to try to use managed code as much as possible.
The biggest problem with passing strings from C++ back to C# is the memory allocation. The GC should be able to know how to cleanup the memory allocated for this string. Since C# has extensive COm interop support, it does know about COM BSTRs and how to allocate and deallocate these. Thus the easiest way to do this would be to use BSTR on the C++ side and string on the C# side.
Note, using BSTRs does not imply that your function has to be expose through COM.
The "string" return value is the problem. The P/Invoke marshaller is going to call CoTaskMemFree() on the pointer you return. That's not going to work well unless you used CoTaskMemAlloc() in your C/C++ code to allocate the string buffer. Which is a fairly unusual thing to do.
The best solution is to allow the caller of your code to pass a pointer to a buffer and the buffer length to you as arguments. That way all memory allocation happens on one side. Scott showed you how to do this.
I had to convert a unicode C# string to a multibyte representation in order to convert to char* in c++ (this is partial one way solution)
I found the following very useful
string st;
IntPtr stPtr = Marshal.StringToHGlobalAnsi(st);
// Do your thing in C++
Marshal.FreeHGlobal(stPtr);
This may be inefficient and not in C# manner, I'm new to C#.