I want to pass a structure to C function and I write the following code.
When I run it, the first function - Foo1 is working and then function Foo gets an exception. Can you help me to understand what the problem is?...
The C code:
typedef struct
{
int Size;
//char *Array;
}TTest;
__declspec(dllexport) void Foo(void *Test);
__declspec(dllexport) int Foo1();
void Foo(void *Test)
{
TTest *X = (TTest *)Test;
int i = X->Size;
/*for(int i=0;i<Test->Size;Test++)
{
Test->Array[i] = 127;
}*/
}
int Foo1()
{
return 10;
}
The C# code:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential)]
public class TTest
{
public int Size;
}
class Program
{
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern void Foo(
[MarshalAs(UnmanagedType.LPStruct)]
TTest lplf // characteristics
);
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern int Foo1();
static void Main(string[] args)
{
TTest Test = new TTest();
Test.Size = 25;
int XX = Program.Foo1();
Program.Foo(Test);
}
}
}
To the downvoters: This answer solves two issues: the immediate issue of the calling convention/the MarhsalAs attribute, and the issue he will soon find where his TTest parameter won't work if he takes my suggestion of turning TTest into a struct.
Your native code is asking for a void*, which in C# is an IntPtr. First you should define TTest as a struct and not a class. Second, you should change the declaration of Foo to:
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(IntPtr lplf);
And third, you should pin the TTest using the fixed keyword and pass it's pointer to Foo. If you're using a class, you can use Marhsal.StructureToPtr to get an IntPtr from your TTest.
This provides the same functionality on both sides, where a pointer to any type can be passed in. You can also write overloads with all the class types that you want to use since they all equate to void* on the native side. With a struct, your parameters would be prepended with a ref.
What I'm curious about is why your native code wants a void* instead of a TTest* when the first thing you do in the unmanaged code is cast to a TTest*. If you switched the parameter to a TTest*, then providing identical functionality becomes simpler. You declaration would become:
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(ref TTest lplf);
And you would call the function as Program.Foo(ref Test);
If you're using the class, the ref isn't necessary as classes are reference types.
You are using C call so you need to specify CallingConvention.Cdecl
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
By default its stdcall in C# pinvoke as i remember; You can also do change C code instead and leave your C# code as is like in below
__declspec(dllexport) void __stdcall Foo(void *Test);
But for me best is to both declare __cdecl (or stdcall) in your C export and CallingConvention.Cdecl (or stdcall) in your C# code to keep convenience. You can check https://learn.microsoft.com/en-gb/cpp/cpp/argument-passing-and-naming-conventions?view=vs-2017 and https://learn.microsoft.com/en-gb/dotnet/api/system.runtime.interopservices.callingconvention?view=netframework-4.7.2 for further info
Related
I try to pass a string as a function argument to a Rust library (cdylib) as described in the Rust FFI Omnibus.
I tried to however omit the libc dependency, because I think it should not be necessary anymore.
I am using Rust 1.50.0 and .net 5.0.103.
From the the documentation it seems to me as if the CStr::from_ptr() function constructs a CStr from the pointer by reading all bytes until the null-termination. And that C# strings are automatically marshalled to C compatible strings (and are therefore null-terminated). My problem however is, that I do not get the full string that I supply as the function argument, instead I only get the first character as the string.
This is my lib.rs:
use std::os::raw::c_char;
use std::ffi::CStr;
#[no_mangle]
pub extern fn print_string(text_pointer: *const c_char) {
unsafe {
let text: String = CStr::from_ptr(text_pointer).to_str().expect("Can not read string argument.").to_string();
println!("{}", text);
}
}
and my Cargo.toml:
[package]
name = "mylib"
version = "0.1.0"
authors = ["FrankenApps"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
And this is my C# code:
using System;
using System.Runtime.InteropServices;
namespace dotnet
{
class Program
{
[DllImport("mylib.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern void print_string(string text);
static void Main(string[] args)
{
print_string("Hello World.");
}
}
}
In this case the output when I run the program is:
H
When I run the linked sample, I get an error:
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Utf8Error { valid_up_to: 1, error_len: Some(1) }', src\lib.rs:12:32
However when I only use ASCII characters and modify the code like that:
Rust:
use libc::c_char;
use std::ffi::CStr;
#[no_mangle]
pub extern "C" fn how_many_characters(s: *const c_char) -> u32 {
let c_str = unsafe {
assert!(!s.is_null());
CStr::from_ptr(s)
};
let r_str = c_str.to_str().unwrap();
println!("{}", r_str.to_string());
r_str.chars().count() as u32
}
C#
using System;
using System.Runtime.InteropServices;
class StringArguments
{
[DllImport("mylib", EntryPoint="how_many_characters")]
public static extern uint HowManyCharacters(string s);
static public void Main()
{
var count = StringArguments.HowManyCharacters("Hello World.");
Console.WriteLine(count);
}
}
I do get the desired output:
Hello World.
12
My question is what did I do wrong in my own sample, where I tried to not use libc? Is there any difference between c_char in libc and the standard library, that makes them behave differently?
My guess is that I missed something simple, because I do expect this to work...
Since .NET 4.7 you can use MarshalAs(UnmanagedType.LPUTF8Str) so the following should work fine:
using System.Runtime.InteropServices;
namespace dotnet
{
class Program
{
[DllImport("mylib.dll")]
public static extern void print_string([MarshalAs(UnmanagedType.LPUTF8Str)] string utf8Text);
static void Main(string[] args)
{
print_string("göes to élevên");
}
}
}
You need to use CharSet = CharSet.Ansi which does seem to be the default.
When I replace
[DllImport("mylib.dll", CharSet = CharSet.Unicode, SetLastError = true)]
with
[DllImport("mylib.dll", CharSet = CharSet.Ansi, SetLastError = true)]
I do get the output:
Hello World.
Still it would have been nice, if unicode strings could be supported somehow.
Edit
I figured out how to use UTF-8 strings. I did not change anything in the rust implementation, but instead of Marshalling the string automatically in C#, a UTF-8 encoded byte array is used as the function parameter in C# like that:
using System;
using System.Runtime.InteropServices;
namespace dotnet
{
class Program
{
[DllImport("mylib.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern void print_string(byte[] utf8Text);
static void Main(string[] args)
{
print_string(Encoding.UTF8.GetBytes("göes to élevên"));
}
}
}
This works perfectly and prints:
göes to élevên
I'm injecting my C# dll into an unmanaged process written in C. How would I go about calling a function in this process? I know the address of the function and the function signature. The function uses fastcall calling convention. I'm not sure if this complicates things.
I have tried using delegates like so:
[UnmanagedFunctionPointer(CallingConvention.FastCall, CharSet = CharSet.Unicode)]
public delegate IntPtr _PrintText(string msg, int color);
Which raises a warning about FastCall not being supported.
Here's a more complete, simple example of what I've tried:
C++ function:
void __fastcall PrintText (wchar_t * wMessage, int nColor)
Address: 0x31E3A0
C# code:
public class Example
{
[UnmanagedFunctionPointer(CallingConvention.FastCall, CharSet = CharSet.Unicode)]
public delegate IntPtr _PrintText(string msg, int color);
public static int EntryPoint(string pwzArgument)
{
var ptr = new IntPtr(0x31E3A0);
_PrintText MyPrint = Marshal.GetDelegateForFunctionPointer<_PrintText>(ptr);
MyPrint("TESTING", 0);
}
}
How do I properly define and call a FastCall C function using C#?
I'm trying to build a wrapper to use cpp code inside c# code, and I want to return custom struct (Class) as output for method2, and std::string for method1, and this is my code in cpp
extern "C" __declspec(dllexport) std::string method1()
{
std::string s;
//Some Code/////////
return s;
}
and this is the method that should return custom struct (or class)
extern "C" __declspec(dllexport) MyStruct method2()
{
MyStruct s;
//Some Code//////
return s;
}
and I tried to write c# code for both of these methods and here is my c# code
[DllImport("<dllPath>", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string method1(); //Exception
[DllImport("<DllPath>")]
public static extern MyStruct method2(); //Compile Error
Now when I'm trying to run the c# code I get MarshalDirectiveException for method1, and compiling error for method2?
In the first PInvoke, the C++ method should return const char* (s.c_str()). You should remove "[return [...]]" and replace string by IntPtr. Then, you can convert the IntPtr to string with Marshal.PtrToStringAnsi(ptr). http://msdn.microsoft.com/en-US/library/s9ts558h(v=vs.110).aspx can help you.
In the second PInvoke, you should define in C# the MyStruct (but I can't be more precise because I have no information about MyStruct). This link can help you : http://www.codeproject.com/Articles/66243/Marshaling-with-C-Chapter-Marshaling-Compound-Ty
EDIT : Thanks to hvd's comment, it is better sometimes to use BSTR, because the use of char* can be dangerous !
I have the following PInvoke:(C to C#)
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([MarshalAs(UnmanagedType.LPArray, SizeConst = 64)]sPiece[] board);
On C:
__declspec(dllexport) void InitBoard(sPiece board[8][8]);
In InitBoard function, the values of the matrix changing, but after a call to PInvoke I do not see the change.
sPiece[] board = new sPiece[64];
InitBoard(board);
//Here the values of the board is still initialized (as before the function call) at default values
I tried to change the variable to ref (although it already reference) but it stuck the program when the function was called, so I do not think it's the solution.
It took me a while to get here (I'm new to the subject) I'd love to help!
EDIT:
sPiece On C:
typedef struct Piece
{
ePieceType PieceType; //enum
ePlayer Player; //enum
int IsFirstMove;
} sPiece;
sPiece On C#:
[StructLayout(LayoutKind.Sequential)]
public struct sPiece
{
public ePieceType PieceType;
public ePlayer Player;
public int IsFirstMove;
}
Possibly you are failing to allocate memory before calling the function.
sPiece[] board = new sPiece[64];
InitBoard(board);
Declare the function like this:
[DllImport("chess_api.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void InitBoard([Out] sPiece[] board);
The default marshalling is [In]. Although since your struct is blittable, the array you pass is pinned and the call behaves as though it was [In,Out]. So I think you could omit [Out] if you wished, but it is clearer as written above.
You can add the UnmanagedType.LPArray option if you wish but it's not needed.
I'm currently trying to invoke a method made in C from C#
C code looks like this:
extern "C" int addSum(int a, int b)
{
return a*b;
}
extern "C" int getCount()
{
return 12;
}
and C# code looks like this:
[DllImport("mydll.dll", SetLastError=true)]
private static extern int addSum(IntPtr a, IntPtr b);
[DllImport("mydll.dll", SetLastError = true)]
private static extern int getCount();
public static int mySum(int a, int b)
{
return suma(a, b);
}
public static int getMyCount()
{
return getCount();
}
The code returns the right values but i'm getting the following error:
addSum' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
Any sugestion regarding this issue ?
Thanks
In addtion to the datatype which might or might not be a problem depending on the target platform, you might also need to look at the calling convention. It is the calling convention that determines amoung other thing who is responsible for the the stack clean-up and the order that arguments are pushed on the stack or assigned to registers etc.
It is common for C code to use the cdecl calling convention.
[DllImport("mydll.dll",
SetLastError=true,
CallingConvention=CallingConvention.Cdecl)]
You don't need to use IntPtr as argument. You could directly use integer values when defining the method signature:
[DllImport("mydll.dll", SetLastError = true)]
public static extern int addSum(int a, int b);
[DllImport("mydll.dll", SetLastError = true)]
public static extern int getCount();
And then directly invoke the function:
int result = SomeClass.addSum(3, 4);
int count = SomeClass.getCount();