I have a C++ dll that has a function that returns a c string and I have a C# program that calls this function and returns the data to a C# string. Here's what I mean
__declspec(dllexport) const char* function (const char* str) {
std::string stdString( str );
std::cout << stdString.c_str() << std::endl; // this prints fine, no data loss
return stdString.c_str();
}
And here's the C# code
[DllImport("MyDLL.dll")]
public static extern string function(string data);
string blah = function("blah");
Console.WriteLine(blah); // doesn't print anything...
When I look into the locals it says variable 'blah' is equal to "".
What happened to the data?
Your C++ code is broken. You are returning a pointer to a local variable. It no longer exists after the function returns. This tends to work by accident in a C++ program but is strong Undefined Behavior. It cannot possibly work in an interop scenario, the pinvoke marshaler's use of the stack will overwrite the string.
A declaration that could work:
void function (const char* str, char* output, size_t outputLength)
Use a StringBuilder in the [DllImport] declaration for the output argument and pass an initialized one with sufficient Capacity.
Related
I am working on C#.Net application which uses C-based library. As it needs C# Wrapper I am implementing code to marshal functions from C-based library to be accessible in C# and map data type correctly.
I have a function in library which Takes A char array by reference, The function contains logic to update array with values in it. So, At function call we can pass null array and access result value after its been called.
Objective: to pass null char array and return value by reference
The C-dll function:
int function_call(char ** var);
The C function call code:
char *name = NULL;
int val = function_Call(&name);
The C# wrapper code:
[DllImport("mylibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function_call([In] string[] val);
The C# function call code:
string[] name = null;
int ret = function_call(name);
I came across similar queries :
How to get char** using C#? [duplicate]
How do I marshal a pointer to an array of pointers to structures?
marshaling char**
C# equivalent to C const char**
Tried all of them but did not work. returns null value.
Is there a simple working way to marshal Char ** ?
You can simply marshal it as IntPtr :
[DllImport("mylibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function_call(IntPtr val);
Just that on function call side, allocate mem to pointer and to access the data held by pointer : Marshal data from an unmanaged block of memory to the type specified by a generic type parameter.
I have a large library written in C that I would like to use as a DLL in a C# program. Most of the C code will be used by the libraries own functions, but I do need one function to be able to be called from the C# project.
So there's an example C function below
__declspec(dllexport) char* test(char* a){
char* b = "World";
char* result = malloc(strlen(a) + strlen(b) + 1);
strcpy(result, a);
strcpy(result, b);
return result;
}
Now in the C# code I have got using System.Running.InteropServices;
and also [DllImport("mydll.dll")] but I'm not sure how to declared the function.
public static extern char* test(char* a); obviously doesn't work because C# doesn't support pointers like C does.
So how should I pass a string to this C function and have it return a string as well?
You're looking for a MarshalAs attribute:
[DllImport("mydll.dll")]
static public int test([MarshalAs(UnmanagedType.LPStr)]String a);
As for returning a dynamically allocated C string, bad idea. How will you reliably de-allocate the string? It's a recipe for a memory leak. Allocate a byte array in C#, pin it and pass it to the C code along with its size so the C code can copy the result into it. Then convert to a string using System.Text.Encoding.ASCII.GetString().
I have two c++ functions that I want to DllImport:
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize);
There are a lot of articles that write how to DllImport strings.
Alas quite often I see different replies to the same question.
For instance some say if a c++ function returns a a char* and an int* strLen, then some people say I should use a StringBuilder in my dllImport statement and others say return byte[], some have a marshall statement in the dllImport, some don't. Some answers seem needed because of old C# / .net versions.
So the question is: If the dll call from c++ is fairly straightforward, without strange calling conventions, or other strange items, what should the corresponding DllImport functions be if you have output char* and size or input char * and size?
c++ .h
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize);
What are the corresponding DllImports? replace the instr and outstr with string? stringbuilder? char[]? byte[]? Is any marshal statement needed?
bool SendString(const char* pSendStr, long strSize);
This function is the easy one. The text is sent from the caller to the callee. The p/invoke is declared like this:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr, int Len);
Note that I'm assuming the cdecl calling convention since that is the default for C++ code. And also do note that long in C++ on Windows is 32 bits wide. So it matches int in C#.
When you call the function you need to pass the string and its length. However, the normal convention is for null-terminated strings to be used so the length parameter is not needed. I'd declare the unmanaged function like this:
bool SendString(const char* pSendStr);
And the p/invoke like this:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr);
The other function is a little more complex. You've declared it like this:
bool ReadString(char* pReadStr, long& readStrSize);
Here the caller allocates the buffer which is populated by the callee. You can use StringBuilder for the text and let the marshaler do the work for you. The p/invoke is:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool ReadString(StringBuilder ReadStr, ref int Len);
The convention is that you supply the length of the provided buffer. In turn the function will let you know how many characters it wrote. You'd call the function like this:
int len = 256;
StringBuilder ReadStr = new StringBuilder(len);
bool succeeded = ReadString(ReadStr, ref len);
if (succeeded)
{
string str = ReadStr.ToString();
}
As leppie wrote, what you usually want is:
[DllImport(my.dll)]
static extern bool SendString(string sendString, int stringSize);
[DllImport(my.dll)]
static extern bool ReadString(StringBuilder readString, ref int readStringSize);
This would do automatic conversion to Unicode (and back) for you.
If you want precise access to your char*, you would use byte[]. This way no conversion is done and you have more control on what is going on. Usually you won't need that. One use case might by when your char* can include \0 chars.
I am having C++ code like this just to try out to get the string to C# application from c++ dll.
int GetValue(void *pBuffer)
{
int x = 0;
String^ temp;
temp = "TestStringtest1test2";
memcpy(pBuffer, &temp,sizeof(temp));
Console::WriteLine(temp);
return x;
}
on c# side all I am doing is
[DllImport("Cplusplusapp.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int GetValue(StringBuilder pBuffer);
StringBuilder Buffer = new StringBuilder(100);
int x = GetValue(Buffer);
Console.WriteLine(Buffer);
I have tried marshalling and various other suggestions but I am not able to understand why I am getting garbage values.Its fairly simple but what is that I am missing.
Address of temp IS NOT string itself then memcpy won't work as expected. Moreover sizeof(String) will return size of System.String object (4 bytes on 32 bit machine), not string length.
If you need to copy a value from a managed string to an unmanaged pointer you have to follow next steps.
First of all System.String is always UNICODE then you won't have char* but wchar_t* (but you can convert it to what you need with usual helper macros). To convert a string from System.String to wchar_t* call Marshal::StringToBSTR():
String^ temp = L"My string";
BSTR bstr = Marhshal::StringToBSTR(temp);
memcpy(pBuffer, bstr, SysStringLength(bstr));
Marshal::FreeBSTR(bstr);
Of course if you do not need to use a System.String you can skip all of these simply not assigning your string to System.String. Change it to wchar_t* or char* according to how you'll import that function (CharSet.Ansi or not), take a look to other Marshal::StringToXYZ functions, I would create e temporary string from System::String and then I would strcpy to destination buffer (check functions with Ansi suffix for char* versions). See this post here on SO for an example of that.
I am currently developing an Excel add-in in C#, and I am using interop with a C++ library. Both are developed by me. The C++ library is not a COM component, but just a DLL exporting some functions.
I have one C# function, that I want to pass a number and two strings to the C++ function, and the C++ function returns a string. First I tried to return a string directly, so the prototypes look something like
//C#
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string Function([MarshalAs(UnmanagedType.I4)] int number,
[MarshalAs(UnmanagedType.LPStr)]string str1,
[MarshalAs(UnmanagedType.LPStr)] string str2);
//C++
extern "C" __declspec(dllexport) string Function(int, char*, char*);
(Btw, I set already the calling convention in C# for this function as Cdecl)
Then when I call the C# version, and stepped into the C++ (when debugging), I found that the parameters are shifted, so the integer is the value of the pointer to the first string, and the first string is the second string, and the second string points to somewhere else. I also checked the memory, and found that the parameters were not passed according to the _cdecl convention, the integer was the last one pushed into the stack (which is ok), but the positions of the two strings were switched. There are many other information that the compiler added, so I don't want to go further deep into this.
However, later I changed the return string as a parameter, and the prototypes look like
//C#
private static extern void Function([MarshalAs(UnmanagedType.I4)]int number,
[MarshalAs(UnmanagedType.LPStr)] string str1,
[MarshalAs(UnmanagedType.LPStr)] string str2,
[MarshalAs(UnmanagedType.LPStr), Out] StringBuilder returnStr);
//C++
extern "C" __declspec(dllexport) void Function(int, char*, char*, string&);
And now the parameters are passed correctly.
So my first question is, how does this happen?
My second question is, even using the second method, I could not get the program run correctly, the program crashed when returning from the C++ function (actually in the C++ function, the only thing being done is calling another member function of a class, so the function is just like a wrapper of the member function, and the program crashed exactly when returning in the member function. Since the warpper function just calls the member function, so I cannot tell where the problem is).
Does anyone have an idea how to do this? So the main point of the function is to take a number and two strings and return another string.
Thanks in advanced.
look at this article about the usage of stringbuilder in unmanaged code
try something like this:
//C#
public string Function(int number, string str1, string str2)
{
StringBuilder sbStr1 = new StringBuilder(str1);
StringBuilder sbStr2 = new StringBuilder(str2);
StringBuilder sbReturnStr = new StringBuilder(1024);
Function(number, sbStr1 , sbStr2 , sbReturnStr , sbReturnStr.Capacity);
return sbReturnStr.ToString();
}
//C# p/invoke
private static extern void Function(int number, StringBuilder str1, StringBuilder str2, StringBuilder returnStrBuffer, int size);
//C++
extern "C" __declspec (dllexport) void __stdcall Function(int number, const char* str1, const char* str2, char* returnStrBuffer, int size)
{
//fill the returnStrBuffer
strncpy(returnStrBuffer, "blablalb", size); //example!!
}