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!
Related
I'm writting C wrapper to C++ class to expose it to C# code using PInvoke. I found some interesting thing and I would like to know why this code behaves like this. Question is why c_str return empty value?
// c_++ class
class Foo
{
public:
method returns m_fullName
std::string fullName() const;
private:
const std::string m_fullName;
};
extern "C" const char* fullNameFoo(Foo* object)
{
if (object != NULL)
{
// this return empty string - but why? class exists so fullName exists too
return object->fullName().c_str();
// returns proper value
return strcpy(new char[object->fullName().size()], object->fullName().c_str());
}
return NULL;
}
// C# code
[DllImport("mylib.dll", EntryPoint = "fullNameFoo")]
static public extern IntPtr FullNameFoo(IntPtr foo);
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.
I have an unmanaged DLL that exports only a C style factory method that returns a new instance of a class (simplified here to look simple).
hello.h
#if defined(HWLIBRARY_EXPORT) // inside DLL
# define HWAPI __declspec(dllexport)
#else // outside DLL
# define HWAPI __declspec(dllimport)
#endif
struct HelloWorld{
public:
virtual void sayHello() = 0;
virtual void release() = 0;
};
extern "C" HWAPI HelloWorld* GetHW();
hello.cpp
#include "hello.h"
struct HelloWorldImpl : HelloWorld
{
void sayHello(){
int triv;
std::cout<<"Hello World!";
std::cin>>triv;
};
void release(){
this->HelloWorldImpl::~HelloWorldImpl();
};
HelloWorld* GetHW(){
HelloWorld* ptr = new HelloWorldImpl();
return ptr;
};
Now, I can use dllimport to access GetHW() but is there a way to access the member functions of the returned 'struct'... ie, sayHello and release?
I was also stuck with the same problem. This question was asked a while before. I commented to it for any better solution but didn't get any reply yet. So, reposting it.
When i googled, able to find out two solutions.
Solution1: Expose all the member functions in the C-style for the existing dll. Which i cant do, as it is a 3rd party dll.
Solution2: Write a managed C++ dll exposing the functionality of native C++ dll, which later can be used in your C# dll. Here many classes/functions are present. So, creating would take most of the time.
i got the above solutions from the link below.
How To Marshall
Please let me know if there is any better solution other than the above two solutions?
i have the source code for C++ solution. But what i though was not to touch C++ dll. If there is any possibility to do it in C#, it would be great.
If there is no alternative, i need to follow any one of the specified two solutions.
The C++ code is using the way abstract classes are implemented by the Visual C++ compiler. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx. This memory layout is "fixed" because it is used for implementing COM interfaces. The first member of the struct in memory will be a pointer to a vtable containing the function pointers of your methods. So for a
struct HelloWorldImpl : public HelloWorld
{
public:
int value1;
int value2;
}
the "real" layout in memory would be:
struct HelloWorldImpl
{
HelloWorldVtbl *vtbl;
int value1;
int value2;
}
where vtbl would be:
struct HelloWorldVtbl
{
void *sayHello;
void *release;
}
Just for the sake of doing a complete response, I'm writing the example for this signatures:
struct HelloWorld {
public:
virtual int sayHello(int v1, int v2, int v3) = 0;
virtual void release() = 0;
};
C# code:
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetHW();
[StructLayout(LayoutKind.Sequential)]
struct HelloWorldVtbl
{
public IntPtr sayHello;
public IntPtr release;
}
Your functions are void Func(void) or int Func(int, int, int), but in truth they have a hidden parameter, this, so you can write them as:
int sayHello(HelloWorld*, int, int, int);
void release(HelloWorld*);
so in C# the delegate is
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int Int32MethodInt32Int32Int32(IntPtr ptr, int v1, int v2, int v3);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void VoidMethodVoid(IntPtr ptr);
Then you can use
IntPtr ptr = GetHW();
IntPtr vtbl = Marshal.ReadIntPtr(ptr, 0);
HelloWorldVtblhw = (HelloWorldVtbl)Marshal.PtrToStructure(vtbl, typeof(HelloWorldVtbl));
Int32MethodInt32Int32Int32 sayHello = (Int32MethodInt32Int32Int32)Marshal.GetDelegateForFunctionPointer(hw.sayHello, typeof(Int32MethodInt32Int32Int32));
int res = sayHello(ptr, 1, 2, 3);
Console.WriteLine(res);
VoidMethodVoid release = (VoidMethodVoid)Marshal.GetDelegateForFunctionPointer(hw.release, typeof(VoidMethodVoid));
release(ptr);
I'm trying to import some functions from 32 bit and 64 bit DLLs written in unmanaged C++ into my C# project. As a sample, I did this:
C++ DLL function
long mult(int a, int b) {
return ((long) a)*((long) b);
}
C# code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication2
{
class DynamicDLLImport
{
private IntPtr ptrToDll;
private IntPtr ptrToFunctionToCall;
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int Multiply(int a, int b);
private Multiply multiply;
public DynamicDLLImport(string dllName)
{
ptrToDll = LoadLibrary(dllName);
// TODO: Error handling.
ptrToFunctionToCall = GetProcAddress(ptrToDll, "mult");
// TODO: Error handling.
// HERE ARGUMENTNULLEXCEPTION
multiply = (Multiply)Marshal.GetDelegateForFunctionPointer(ptrToFunctionToCall, typeof(Multiply));
}
public int mult_func(int a, int b)
{
return multiply(a, b);
}
~DynamicDLLImport()
{
FreeLibrary(ptrToDll);
}
}
class DLLWrapper
{
private const string Sixtyfour = "c:\\Users\\Hattenn\\Documents\\Visual Studio 2010\\Projects\\ConsoleApplication2\\ConsoleApplication2\\easyDLL0_64.dll";
private const string Thirtytwo = "c:\\Users\\Hattenn\\Documents\\Visual Studio 2010\\Projects\\ConsoleApplication2\\ConsoleApplication2\\easyDLL0.dll";
// [DllImport(Sixtyfour)]
// public static extern int mult(int a, int b);
[DllImport(Thirtytwo)]
public static extern int mult(int a, int b);
}
class Program
{
static void Main(string[] args)
{
int a = 5;
int b = 4;
DynamicDLLImport dllimp = new DynamicDLLImport("easyDLL0.dll");
Console.WriteLine(DLLWrapper.mult(a, b));
//Console.WriteLine(dllimp.mult_func(a, b));
Console.ReadKey();
}
}
}
I can't seem to get it to work. Here are the error messages that I get:
When I use the DLLWrapper class with the 32 bit DLL file I get "DLLNotFoundException", but the DLL file is exactly in that path.
When I use the DLLWrapper class with the 64 bit DLL file and change the "Platform Target" property to "x64" I get the same "DLLNotFoundException", if I try to build with "x86" then I get "BadImageException".
When I use the DynamicDLLImport class, I always get "ArgumentNullException" at the line commented with "HERE ARGUMENTNULLEXCEPTION" in the code.
What am I doing wrong?
How did you export your function from the DLL? Windows DLL's do not automatically export all functions, and C++ will decorate the names such as to tell apart function overloads for example unless you tell it not to, but exactly how is compiler specific, and other languages definitely don't understand it.
You can check by starting the visual studio command prompt, and using the command
dumpbin /EXPORTS "your library.dll"
The problem turned out to be that the dll was deployed in debug mode and not in a correct way. When it was deployed in release mode, everything seemed to work fine. Anyone having a similar problem should know that compiling the dll in debug mode and just copying it to another computer is not the right way to do it.
Windows needs to be able to located the DLL at runtime, not just compile time. Put the DLL in your binary folder, or somewhere on the PATH and the static import will work.
Your C# method declaration does not match the C++ one:
long mult(int a, int b)
private delegate int Multiply(int a, int b);
Try changing the return type to long?
I've been working on a prototype code application that runs in C# and uses classes and functions from older C++ code (in the form of an imported DLL). The code requirement is to pass in a class object to the unmanaged C++ DLL (from C#) and have it be stored/modified for retrieval later by the C# application. Here's the code I have so far...
Simple C++ DLL Class:
class CClass : public CObject
{
public:
int intTest1
};
C++ DLL Functions:
CClass *Holder = new CClass;
extern "C"
{
// obj always comes in with a 0 value.
__declspec(dllexport) void SetDLLObj(CClass* obj)
{
Holder = obj;
}
// obj should leave with value of Holder (from SetDLLObj).
__declspec(dllexport) void GetDLLObj(__out CClass* &obj)
{
obj = Holder;
}
}
C# Class and Wrapper:
[StructureLayout(LayoutKind.Sequential)]
public class CSObject
{
public int intTest2;
}
class LibWrapper
{
[DLLImport("CPPDLL.dll")]
public static extern void SetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
CSObject csObj);
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
ref CSObject csObj);
}
C# Function Call to DLL:
class TestCall
{
public static void CallDLL()
{
...
CSObject objIn = new CSObject();
objIn.intTest2 = 1234; // Just so it contains something.
LibWrapper.SetDLLObj(objIn);
CSObject objOut = new CSObject();
LibWrapper.GetDLLObj(ref objOut);
MessageBox.Show(objOut.intTest2.ToString()); // This only outputs "0".
...
}
}
Nothing but junk values appear to be available within the DLL (coming from the passed in C# object). I believe I am missing something with the class marshalling or a memory/pointer issue. What am I missing?
Edit:
I changed the above code to reflect changes to the method/function definitions, in C#/C++, suggested by Bond. The value (1234) being passed in is retrieved by the C# code correctly now. This has exposed another issue in the C++ DLL. The 1234 value is not available to the C++ code. Instead the object has a value of 0 inside the DLL. I would like to use predefined C++ functions to edit the object from within the DLL. Any more help is greatly appreciated. Thanks!
Bond was correct, I can't pass an object between managed and unmanaged code and still have it retain its stored information.
I ended up just calling C++ functions to create an object and pass the pointer back into C#'s IntPtr type. I can then pass the pointer around to any C++ function I need (provided it's extern) from C#. This wasn't excatly what we wanted to do, but it will serve its purpose to the extent we need it.
Here's C# the wrapper I'm using for example/reference. (Note: I'm using StringBuilder instead of the 'int intTest' from my example above. This is what we wanted for our prototype. I just used an integer in the class object for simplicity.):
class LibWrapper
{
[DllImport("CPPDLL.dll")]
public static extern IntPtr CreateObject();
[DllImport("CPPDLL.dll")]
public static extern void SetObjectData(IntPtr ptrObj, StringBuilder strInput);
[DllImport("CPPDLL.dll")]
public static extern StringBuilder GetObjectData(IntPtr ptrObj);
[DllImport("CPPDLL.dll")]
public static extern void DisposeObject(IntPtr ptrObj);
}
public static void CallDLL()
{
try
{
IntPtr ptrObj = Marshal.AllocHGlobal(4);
ptrObj = LibWrapper.CreateObject();
StringBuilder strInput = new StringBuilder();
strInput.Append("DLL Test");
MessageBox.Show("Before DLL Call: " + strInput.ToString());
LibWrapper.SetObjectData(ptrObj, strInput);
StringBuilder strOutput = new StringBuilder();
strOutput = LibWrapper.GetObjectData(ptrObj);
MessageBox.Show("After DLL Call: " + strOutput.ToString());
LibWrapper.DisposeObject(ptrObj);
}
...
}
Of course the C++ performs all the needed modifications and the only way for C# to access the contents is, more or less, by requesting the desired contents through C++. The C# code does not have access to the unmanged class contents in this way, making it a little longer to code on both ends. But, it works for me.
This is the references I used to come up with the basis of my solution:
http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
Hopefully this can help some others save more time than I did trying to figure it out!
I believe you should declare your returning method like this
__declspec(dllexport) void getDLLObj(__out CClass* &obj)
and respectively the C# prototype
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
ref CSObject csObj);
the inbound method should take a pointer to CClass too, the C# prototype is ok.
If you using the class just from C# ,you should use GCHandle for that http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28v=vs.110%29.aspx
edit:
CClass *Holder;
extern "C"
{
// obj always comes in with a 0 value.
__declspec(dllexport) void SetDLLObj(CClass* obj)
{
(*Holder) = (*obj);
}
// obj should leave with value of Holder (from SetDLLObj).
__declspec(dllexport) void GetDLLObj(__out CClass** &obj)
{
(**obj) = (*Holder);
}
}