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!!
}
Related
I need to use a library of C functions in a DLL from a C# application. I am having trouble calling DLL functions with char * arguments:
The C DLL:
extern "C" __declspec(dllexport) int CopyFunc(char *, char *);
int CopyFunc(char *dest, char *src)
{
strcpy(dest, src);
return(strlen(src));
}
the C# app needs to look something like this:
[DllImport("dork.dll")]
public static extern int CopyFunc(string dst, string src);
int GetFuncVal(string source, string dest)
{
return(CopyFunc(dest,source));
}
I've seen examples using string or StringBuilder, or IntPtr as replacements for the char * required by the DLL function prototype, but I've not been able to get any of them to work. The most common exception I get is that PInvoke is unbalancing the stack because the function call does not match the prototype.
Is there a simple solution to this?
Update your P/Invoke declaration of your external function as such:
[DllImport("dork.dll")]
public static extern int CopyFunc([MarshalAs( UnmanagedType.LPStr )]string a, [MarshalAs( UnmanagedType.LPStr )] string b);
int GetFuncVal(string src, string dest)
{
return(CopyFunc(dest,src));
}
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 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.
I've created a DLL function for using inside C# using DLLImport but having troubles in calling the method, as I'm getting memory corruption problems;
[DllImport("mydll.dll", EntryPoint = "callinmydll")]
public static extern int testdllcall(double *firstinput, long firstcount, double *secondoutput, long secondcount);
Here's part of the C++ library header;
extern "C" {
mydll_API int callinmydll(double *in, long firstcount, double *out, long secondcount);
}
Implementation.
mydll_API int callinmydll(double *in, long firstcount, double *out, long secondcount)
{
for( int i =0 ; i < 10 ; i++ )
{
*(out + i) = (*(in + i) + 10 );
}
return 0;
}
Now when my DLLImport function calls the callinmydll function and passes valid data to it, this is where things get interesting. The pointer in contains data, as does firstcount. Although everything beyond this point is corrupted. Why? Curiously I rearrange my function to be double, double*, long, long now the corruption happens after third parameter. I'm curious as to what's going on, as I'm passing valid data; two valid pointers, and int cast to int64.
Help!
In Win32 C, a long is still 32-bits. Your C# signature is using a long which is 64-bits. Your second and forth parameter should be an int in the C# signature.
See this table for more information.
So your signature looks like so:
[DllImport("mydll.dll", EntryPoint = "callinmydll")]
public static extern int testdllcall(double *firstinput, int firstcount, double *secondoutput, int secondcount);
Additionally, make sure your calling convention is correct as Ramhound pointed out in the comments. Your C function looks like it is using the CDecl convention, and .NET defaults to StdCall. You can set the calling convention in the attribute:
[DllImport("mydll.dll", EntryPoint = "callinmydll", CallingConvention = CallingConvention.Cdecl)]