How to pass a string to vc++ dll from a csharp programme - c#

We want to pass a string to the vc++ from a csharp programme.
Following is the code :
In C#
[DllImport("ConsoleApplication2.dll")]
public static extern int main_c(StringBuilder IpAddr, int p);
public string[] tcp()
{
StringBuilder buffer = new StringBuilder("192.168.1.100");
int i = main_c(buffer, 34318);
In vc++
extern __declspec( dllexport ) int main_c(char *peer,int port)
{
This gives a error as ":main_c' has unbalanced the stack." How can this be done ?

Personnally, I'd try declaring it so:
[DllImport("ConsoleApplication2.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int main_c([MarshalAs(UnmanagedType.LPStr)] String IpAddr, int port);
And declare the pointer const in the VC++ function, since it's not supposed to write there. You don't even need a StringBuilder.

Related

correctly convert C++ long to C# int

I'm currently working on a .NET Framework 4.7.2 application using a business logic library written in unmanaged C++. I need to use unmanaged C++.
I need to use the logic from the C++ project, unfortunately I cannot correctly convert the input or output parameters of my program.
When I input 42, and simply want to return that value, I get 17582022 as a result. Which should actually be 42.
My C++ code looks like that:
MYCore header file:
#ifdef MYCORE_EXPORTS
#define MYCORE_API __declspec(dllexport)
#endif
#pragma once
#include <string>
using namespace std;
extern "C"
{
class MYCORE_API TestClass
{
private:
string name;
public:
TestClass(char*);
long Iterate(long &n);
};
MYCORE_API TestClass* TestClass_Create(char* name);
}
MYCore source file:
#include "stdafx.h"
#include "MYCore.h"
TestClass::TestClass(char* n)
{
name = n;
}
long TestClass::Iterate(long &n)
{
return n;
}
extern "C"
{
MYCORE_API TestClass * TestClass_Create(char* name)
{
return new TestClass(name);
}
}
I'm using a .NET 4.7.2 Framework Interface project to export the C++ library functionality:
namespace MYCore.Interface
{
public static class MYProxy
{
private const string coreDLL = "my.core.dll";
[DllImport(coreDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TestClass_Create(string name);
[DllImport(coreDLL, EntryPoint = "?Iterate#TestClass##XXXXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int Iterate(int n);
}
}
In my actual application I further import the dll and use the logic like that:
public static void Initialize()
{
var test = MYProxy.WrapperIterator_Create("test");
var result = MYProxy.Iterate(42); // as a result I'm getting sth. like 17582022 instead of 42
}
Do you know how to correctly convert an int input from C# to C++ and vice versa?
Thank you!
What you're doing in C# does not work in C++ either:
auto result = Iterate(42l);
results in the compiler error
Cannot convert argument 1 from 'long' to 'long &'
I see two solutions:
a) Change the C++ code
long TestClass::Iterate(long n)
(without the reference)
b) Change the C# code
static extern int Iterate(ref int n);
(pass a reference) and call it like
int n = 42;
Console.WriteLine(Iterate(ref n));
The problem is actually called "Marshal an unmanaged C++ Class to C#".
In my Proxy class I created a method to call an actual instance method:
[DllImport(coreDLL, EntryPoint = "?Iterate#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int CallIterate(IntPtr instance, int n);
and the method in my C++ looks like that:
MYCORE_API int CallIterate(TestClass * instance, int n)
{
if (instance!= NULL)
{
return instance->Iterate(n);
}
}
For further reading on how to marshal unmanaged C++ classes, I can suggest the following article:
https://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
My solution works fine now. Thanks for all the good input!

How to free the memory of a string returned from Rust in C#?

I have these two functions exposed from Rust
extern crate libc;
use std::mem;
use std::ffi::{CString, CStr};
use libc::c_char;
pub static FFI_LIB_VERSION: &'static str = env!("CARGO_PKG_VERSION"); // '
#[no_mangle]
pub extern "C" fn rustffi_get_version() -> *const c_char {
let s = CString::new(FFI_LIB_VERSION).unwrap();
let p = s.as_ptr();
mem::forget(s);
p as *const _
}
#[no_mangle]
pub extern "C" fn rustffi_get_version_free(s: *mut c_char) {
unsafe {
if s.is_null() {
return;
}
let c_str: &CStr = CStr::from_ptr(s);
let bytes_len: usize = c_str.to_bytes_with_nul().len();
let temp_vec: Vec<c_char> = Vec::from_raw_parts(s, bytes_len, bytes_len);
}
}
fn main() {}
They are imported by C# as below
namespace rustFfiLibrary
{
public class RustFfiApi
{
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version")]
public static extern string rustffi_get_version();
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version_free")]
public static extern void rustffi_get_version_free(string s);
}
}
The memory of the string returned from rustffi_get_version is not managed by Rust anymore as mem::forget has been called. In C#, I want to call the get version function, get the string, and then pass it back to Rust for memory deallocation like below.
public class RustService
{
public static string GetVersion()
{
string temp = RustFfiApi.rustffi_get_version();
string ver = (string)temp.Clone();
RustFfiApi.rustffi_get_version_free(temp);
return ver ;
}
}
But the C# program crashes when it runs rustffi_get_version_free(temp). How to free the forgotten string memory in C#? What should be passed back to Rust for deallocation?
Instead of defining string as the argument in the C# extern, I changed it to pointer.
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version")]
public static extern System.IntPtr rustffi_get_version();
[DllImport("rustffilib.dll", EntryPoint = "rustffi_get_version_free")]
public static extern void rustffi_get_version_free(System.IntPtr s);
public static string GetVersion()
{
System.IntPtr tempPointer = RustFfiApi.rustffi_get_version();
string tempString = Marshal.PtrToStringAnsi(tempPointer);
string ver = (string)tempString.Clone();
RustFfiApi.rustffi_get_version_free(tempPointer);
return ver ;
}
The IntPtr from rustffi_get_version can be successfully converted to a C# managed string type. tempString and ver are good.
When rustffi_get_version_free(tempPointer) runs, it throws an exception saying stack unbalanced:
A call to PInvoke function 'rustFfiLibrary!rustFfiLibrary.RustFfiApi::rustffi_get_version_free' 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.
sizeof(IntPtr) and sizeof(char *) are both 4 on my system. Plus, IntPtr works for return value; why doesn't it work as an input parameter?
extern "C" fn in Rust means the function uses the C calling convention.
C# expects P/Invoke functions to use the stdcall calling convention by default.
You can tell C# to use the C calling convention:
[DllImport("rustffilib.dll", CallingConvention = CallingConvention.Cdecl)]
Alternatively, you could use extern "stdcall" fn on the Rust side.

Function takes pointer to structure

I have this structure in C
struct system_info
{
const char *name;
const char *version;
const char *extensions;
bool path;
};
And this function signature
void info(struct system_info *info);
I'm trying to use this function like this:
[DllImport("...")]
unsafe public static extern void info(info *test);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public unsafe struct info
{
public char *name;
public char *version;
public char *extensions;
public bool path;
}
And on my main:
info x = new info();
info(&x);
I'm getting an error, pointers cannot reference to marshaled structures, how can I manage this?
There's no need at all to use unsafe here. I would do it like this:
public struct info
{
public IntPtr name;
public IntPtr version;
public IntPtr extensions;
public bool path;
}
And then the function is:
[DllImport("...")]
public static extern void getinfo(out info value);
You may need to specify the Cdecl calling convention, depending on the native code.
Call the function like this:
info value;
getinfo(out value);
string name = Marshal.PtrToStringAnsi(value.name);
// similarly for the other two strings fields
Since there is no mention of string length in the native code you posted, I'm assuming that the strings are allocated by the native code and that you don't need to to anything to deallocate them.
Solved by using ref instead of *test like Hans Passant mentioned
[DllImport("...")]
unsafe public static extern void info(ref system_info test);

Problem invoking C DLL in C#

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();

DLLImport c++ function with default parameters

I am trying to import a function from an unmanaged code c++ dll into my c# application. The c++ prototype is
int somefunction (int param1, int *param2 = NULL);
How do I declare this in c# to take advantage of the default nature of param2? The following code does not work. param2 gets initialized with garbage.
DllImportAttribute("mydll.dll", EntryPoint = "somefunction")]
public static extern int somefunction(int param1);
If you are using C# 4.0 then dtb`s answer is the right approach. C# 4.0 added optional parameter support and they work just as well with PInvoke functions.
Prior to C# 4.0 there is no way to take advantage of the optional parameter. The closest equivalent is to define one function that forwards into the other.
[DllImport("mydll.dll", EntryPoint = "somefunction")]
static extern int somefunction(int param1, IntPtr param2);
static int somefunction(int param1) {
someFunction(param1, IntPtr.Zero);
}
Try
[DllImport("mydll.dll", EntryPoint = "somefunction")]
static unsafe extern int somefunction(int param1, int* param2 = null);
or
[DllImport("mydll.dll", EntryPoint = "somefunction")]
static extern int somefunction(int param1, IntPtr param2 = default(IntPtr));

Categories