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));
}
Related
The following example illustrates what I am looking to do. The operation function receives a value and a function as a parameter, then returns the execution of the function. So far everything is perfect. Now I would like to use that function operation in a library to consume in C#.
fn operation(number: i32, f: &dyn Fn(i32) -> i32) -> i32 {
f(number)
}
fn square(number: i32) -> i32 {
number * number
}
fn cube(number: i32) -> i32 {
number * number * number
}
fn test() {// passing a function as parameter, OK :)
println!("{}", operation(5, &square));
println!("{}", operation(7, &square));
println!("{}", operation(3, &cube));
}
// Tried without success :_(
/*
#[no_mangle]
pub extern "C" c_operation(number: i32, f: &dyn Fn(i32) -> i32) -> *mut i32 {
unsafe {
&mut f(number)
}
}
*/
What should the right signature of the function c_operation?
I think that the following C# code should work when the signature is well defined in the library.
const string RSTLIB = "../../PathOfDLL";
public delegate int Fn(int number);
[DllImport(RSTLIB)] static extern int c_operation(int x, Fn fn);
int Square(int number) => number * number;
int Cube(int number) => number * number * number;
void Test()
{
Console.WriteLine("{0}", c_operation(5, Square));
Console.WriteLine("{0}", c_operation(7, Square));
Console.WriteLine("{0}", c_operation(3, Cube));
}
I appreciate your attention
For use from C, one way to declare c_operation and define it in terms of operation would be something like:
#[no_mangle]
pub extern "C" fn c_operation(
number: i32,
f: unsafe extern "C" fn(i32) -> i32
) -> i32 {
operation(number, &|n| unsafe { f(n) })
}
dyn pointers are not FFI safe, so they cannot be passed through extern "C" barriers. You actually get a warning about this:
warning: extern fn uses type dyn Fn(i32) -> i32, which is not FFI-safe.
However you can pass a plain function pointer, as long as it is also extern "C", of course):
#[no_mangle]
pub extern "C" fn c_operation(number: i32, f: extern "C" fn(i32) -> i32) -> *mut i32 {
unsafe {
&mut f(number)
}
}
(You are returning a dangling pointer to a local temporary, but I guess that is just because you are writing dummy code.)
Then, from C, you can use plain functions, but there is no place for the closure data. That would be an issue for C or C++, but not for C#, because it does some magic that creates raw plain function pointers from any delegate, as long as you decorate the delegate type properly:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int Fn(int number)
Then, your c_operation() declaration should just work, except that your return type in C# int while the Rust version returns a pointer. That corresponds to IntPtr:
[DllImport(RSTLIB)] static extern IntPtr c_operation(int x, Fn fn);
Also, note that if the c_operation() stores the pointer fn to call it later, then in C# you must keep the delegate object alive, either holding it in a variable, or allocating a GCHandle to it.
Fn fSquare = Square;
//keep fSquare somewhere
c_operation(5, fSquare);
If you fail to do that, and the Garbage Collector collects the automatic temporary delgate (that the compiler created for you), and then the Rust code tries to call the stored function pointer... well, it will not be nice.
User4815162342's answer was exactly what I intended. I would like to add that it is surprising that you can pass a C# function as a parameter to a Rust function, and get a result. Likewise, we can from C# pass a Rust function to the same Rust library in a callback. To round off the matter I am going to show the result.
// user4815162342 answer:
#[no_mangle]
pub extern "C" fn c_operation(number: i32, f: unsafe extern "C" fn(i32) -> i32) -> i32 {
operation(number, &|n| unsafe { f(n) })
}
// by example
#[no_mangle]
fn cube(number: i32) -> i32 {
number * number * number
}
The C# code:
const string RSTLIB = "...\release\rstlib.dll";
public delegate int Fn(int number);
[DllImport(RSTLIB)] static extern int c_operation(int number, Fn fn);
[DllImport(RSTLIB)] static extern int cube(int number);
int Square(int number) => number * number;
int Cube(int number) => number * number * number;
public void Test()
{
// passing a C# function as parameter of Rust function
Console.WriteLine("c_operation(5, Square) : {0}", c_operation(5, Square));
Console.WriteLine("c_operation(3, Cube) : {0}", c_operation(3, Cube));
// passing a Rust function as callback inside the same Rust function
Console.WriteLine("c_operation(5, cube) : {0}", c_operation(5, cube));
// Output
// c_operation(5, Square) : 25
// c_operation(3, Cube) : 27
// c_operation(5, cube) : 125
}
Thanks for your attention.
I am trying to execute a c++ function (32 bit) from c# code (console application).
C++ function signature:
EngError GUIFN eUpdateRecord(
Order::X * odrX, //array of objects
Order::Y * odrY, //array of objects
Order::P * odrP, //array of objects
Order::Q * odrQ, //single objects
ulong A,
ulong B,
int & C)
where Order is the C++ namespace containing the below classes:
class X
{
public:
pb::Uint XID; //Uint is unsigned short
pb::String No; //String is char *
pb::String Msg;
};
class Y
{
public:
pb::Uint YID;
pb::String Code;
pb::Uint OrderId;
pb::String Msg;
pb::Char Status;
pb::Long Capacity;
};
Classes P & Q are very similar to above classes with just change in property names (excluding them for now).
I am invoking the C++ function from C# as below
C# Method
[DllImport("x3iface.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public extern unsafe EngErr eUpdateRecord([In,Out] X[] odrX, [In, Out] Y[] odrY, [In, Out] P[] odrP, Q* odrQ, uint A, uint B, ref int C);
Sample C# Class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public unsafe struct Y
{
public UInt16 YID;
public char* Code;
public UInt16 OrderId;
public char* Msg;
public byte Status;
public Int32 Capacity;
}
All arrays passed to the C++ function have pre-assigned values and the C++ function will update values of each array object based on some internal logic.
When i run the code and step into the C++ function, the values are mapped correctly for arrays X and Y, but i am getting the error 'Unable to read memory' for the array P and object Q. All the other values A,B and C are mapped correctly.
If i swap the position of inputs X and Y with P and Q in C++( the parameter positions are changed accordingly in C# method too), then P and Q starts working fine, but then i get the error 'Unable to read memory' for the arrays X and Y.
public extern unsafe EngErr eUpdateRecord([In,Out] P[] odrX, Q* odrQ,[In, Out] X[] odrX, [In, Out] Y[] odrY, uint A, uint B, ref int C);
Do we have any limitation in the number of [In,Out] object arrays we can marshal and pass to a C++ function? How can i best trouble shoot this error?
I do not have a provision to alter the functionality in C++ code as it is being reused in other applications. Kindly suggest possible solutions.
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 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!!
}
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)]