I've this C-C# code that works:
.h
typedef struct {
float a;
float b;
} MyStruct;
extern MyStruct mystruct;
__declspec(dllexport) void GetMyStruct (MyStruct* s);
__declspec(dllexport) void SetMyStruct (MyStruct* s);
.c
MyStruct mystruct;
void GetMyStruct (MyStruct* s)
{
*s = AeroLink_IOPkt;
}
void SetMyStruct (MyStruct* s)
{
AeroLink_IOPkt = *s;
}
void test()
{
// some code that update element in struct
// mystruct.a = 0.4;
// mystruct.a = 0.1;
}
.cs
public struct MyStruct
{
public float a;
public float b;
}
[DllImport(DLL_NAME, EntryPoint = "GetMyStruct")]
protected static extern void GetMyStruct(ref MyStruct s);
[DllImport(DLL_NAME, EntryPoint = "SetMyStruct")]
protected static extern void SetMyStruct(ref MyStruct s);
This way, every time I need to set data from C# to C, I must call void SetMyStruct and vice-versa if I want to get data from C (updated from void test) to C# I must call GetMyStruct. I must do this 50 times per seconds.
Is there a way to avoid calling SetMyStruct and GetMyStruct every time? I would like to use SetMyStruct one time and then have all changes be reflected, from and to. I do not know if this is possible.
You can do this with unsafe and pointers.
You need to compile your C# program with "Unsafe" enabled.
EDIT: A better way:
Add a following function to the library:
__declspec(dllexport) void GetMyStructRef (MyStruct** s);
void GetMyStructRef(MyStruct** s)
{
*s = &mystruct;
}
In C#:
[DllImport(DLL_NAME, EntryPoint = "GetMyStructRef")]
protected static extern void GetMyStructRef(ref MyStruct* s);
MyStruct* data;
GetMyStructRef(ref data);
Console.WriteLine($"{data->a} {data->b}");
Old answer:
unsafe class MyClass : IDisposable
{
[DllImport(DLL_NAME, EntryPoint = "GetMyStruct")]
protected static extern void GetMyStruct(MyStruct* s);
[DllImport(DLL_NAME, EntryPoint = "SetMyStruct")]
protected static extern void SetMyStruct(MyStruct* s);
GCHandle handle;
MyStruct* structRef;
public void MyClass()
{
//we need to get a pinned reference to your struct
handle = GCHandle.Alloc(new MyStruct(), GCHandleType.Pinned);
structRef = (MyStruct*)handle.AddrOfPinnedObject().ToPointer();
SetMyStruct(structRef);
}
public void Dispose()
{
//We need to free the handle to release memory
//GC will not collect it without this
handle.Free();
}
}
Related
I have a DLL developed in C++ that performs some computations. I am trying to link this DLL into my C# application.
I have a struct within a struct which contains a char array (C string). I have a pointer to the object created by the C++ DLL within my C# application.
namespace Test
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Child
{
public float number1;
public float number2;
[MarshalAs(UnmanagedType.LPArray)]
public char[] name;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct DataStructure
{
public Child child;
...
}
.
.
.
public unsafe partial class Form1:Form {
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern IntPtr createInstance();
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern DataStructure* processData(IntPtr source); // Setup of data
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int calculate(IntPtr calc); // Perform calculation
.
.
public Form2()
{
private void button1_Click_1(object sender, EventArgs e)
{
// test functions
unsafe
{
IntPtr ptr1 = createInstance(); // Returns pointer to the instance
DataStructure* data = processData(ptr1); // Returns pointer to DataStructure object created through DllImport
data->child.number1 = 1.234F; // Works
data->child.number1 = 9.876F; // Works
data->child.name = "......" // This doesn't work!!
int result = calculate(ptr1); // Returns value when name commented
}
}
}
}
If I don't comment name, I get the Error - Cannot take the address of managed object.
If I comment name, I am able to run the calculation without issue.
I wish to update the value of the char array name but can't seem to figure out the solution. Below is my usual C solution. Thanks!
#include "DataStructure.h"
#define DLLNAME "Calculation.dll"
int main(int argc, char** argv) {
HINSTANCE dllHandle = LoadLibraryA(fileNameDll);
const void* ptr1 = 0;
int result = 0;
DataStructure *data;
ptr1 = createInstance();
data = processData(ptr1);
data->child.number1 = 1.234;
data->child.number2 = 9.876;
sprintf(data->child.name, "3104-03");
result = calculate(ptr1);
}
I need to return the necessary information about an object as a struct with callbacks and other data.
This is what it looks like on the C# side:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ChartAddDataCallback(IntPtr data, int size);
[StructLayout(LayoutKind.Sequential)]
public struct ChartAccessors
{
public IntPtr HWnd;
public ChartAddDataCallback addDataCallback;
}
[DllImport("CppPart.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetAccessors")]
public static extern ChartAccessors GetAccessors();
The C++ "mirrored" version looks like this:
typedef void(__cdecl *AddDataCallback) (int * data, int size);
struct ChartAccessors
{
HWND m_chartHWnd;
AddDataCallback m_addDataCallback;
};
extern "C" __declspec(dllexport) ChartAccessors GetAccessors();
Usage:
static void Main(string[] args)
{
ChartAccessors accessors = GetAccessors();
}
However, when i start up the program i get the exception "Method's type signature is not PInvoke compatible."
It works if i use any other return type (like int or float) instead of the struct.
Marshal.PtrToStructure was actually solving this issue, as Pavel pointed out.
void RegisterCallbacks(IntPtr callbackPtr)
{
ChartAccessors accessors = (ChartAccessors)Marshal.PtrToStructure(callbackPtr, typeof(ChartAccessors));
// do stuff with the struct
}
Is it possible to call a method in c# from c++ DLL ?
I have good communication c# -> C++ , but I would like to be able to initiate a call C++ -> c# to poll some data
my code looks something like this...
host.h
/*
define the exporter for the C API
*/
#ifdef DLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
class myClass
{
public:
myCLass(){
//do some initialising
}
~myCLass(){
//do some tidying
}
int getInt(int target) {
switch (target)
{
case 0:
return someIntValue;
case 1:
return someOtherIntValue;
default:
return 0;
}
}
std::string getString(int target) {
switch (target)
{
case 0:
return someStringValue;
case 1:
return someOtherStringValue;
default:
return "";
}
}
}
extern "C" {
DLL_EXPORT myClass* myClassConstructor();
DLL_EXPORT void DestroySpatialser(const myClass* _pContext);
DLL_EXPORT int getInt(myClass* _pContext, int target);
DLL_EXPORT void getString(myClass* _pContext, int target, __out BSTR* returnStr);
}
host.cpp
extern "C" {
myClass* myClassConstructor()
{
return new myClass();
}
void myClassDestructor(const myClass* _pContext)
{
if (_pContext != nullptr)
{
_pContext->~myClass();
delete _pContext;
}
}
//example
int getInt(myClass* _pContext, int target)
{
if (_pContext == nullptr)
{
return K_ERR_INT;
}
return _pContext->getInt(target);
}
void getString(myClass* _pContext, int target, __out BSTR* returnStr)
{
std::string str;
if (_pContext == nullptr)
{
str = K_ERR_CHAR;
}
else {
str = _pContext->getString(target);
}
const std::string stdStr = str;
_bstr_t bstrStr = stdStr.c_str();
*returnStr = bstrStr.copy();
}
}
csharp.cs
private const string dllname = "myhost";
[DllImport(dllname)]
private static extern IntPtr myClassConstructor();
[DllImport(dllname)]
private static extern void myClassDestructor(IntPtr _pContext);
[DllImport(dllname)]
private static extern int getInt(IntPtr _pContext, int target);
[DllImport(dllname)]
private static extern float getFloat(IntPtr _pContext, int target);
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
extern static void getString(IntPtr _pContext, int target, [MarshalAs(UnmanagedType.BStr)] out String str);
private static IntPtr _pContext;
public static IntPtr Create()
{
_pContext = myClassConstructor();
Debug.Log("_pContextcreated");
return _pContext;
}
public static void Destroy()
{
myClassDestructor(_pContext );
}
private static int _getInt(int target)
{
return getInt(_pContext , target);
}
private static string _getString(int target)
{
String str;
getString(_pContext , target, out str);
return str;
}
This all works fine.
I'd like to add the ability to get a value from the .cs app (it will be a struct of 3 floats) which should be called from the instance of myClass.
I don't want to initiate this on c# side (i know how to do that).
Any advice?
EDIT
I implemented the solution found here
https://forum.unity.com/threads/communicating-c-with-c.89930/#post-586885
with two changes (see comments below for why)
public int whatever = 0;
public delegate int myCallbackDelegate( int n, int m );
private myCallbackDelegate myCallback; // <-- add this line
[DllImport ("DLLImport_CProj")]
private static extern int TakesCallback( myCallbackDelegate fp, int n, int m );
void Awake()
{
myCallback = new myCallbackDelegate( this.myCallback ) // <-- add this
int resp = TakesCallback(myCallback, 10, 20 ); // <-- change to this
Debug.Log( resp );
}
Well, One way I can think of is to pass a JSON string as char array from C++ to C# and then parse it on C# and get the data. it's a communication method both languages are familiar with.
Also, you will need to pass a callback from C# to C++ to allow this like explained in this question.
Let me know if this helps :)
Make your C# types COMVisible, which means you will be able to call them from COM aware C++ code, e.g., using ATL.
I have this code in a c++ file, with compiles to a dll.
#include "stdafx.h"
#include "WHUU.h"
#include "stdafx.h"
typedef int (__stdcall * Callback)(const int text);
static int x , y= 0;
Callback Handler = 0;
int getX()
{
return x;
}
void setX(int i)
{
x = i;
}
void setY(int i)
{
y = i;
}
int getY()
{
return y;
}
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall addX(int x) {
setX(x);
}
extern "C" __declspec(dllexport)
void __stdcall addY(int y) {
setY(y);
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int z = getX() + getY();
int retval = Handler(z);
}
My c# application now has to load this dll on runtime. Add to the callback and call the functions. I dont want to use a class. I could load the class and with
Type[] types = moduleAssembly.GetTypes();
But this overkill! Also c++ is not managed.
I mean its so tiny! (and yes this is an example , but the "real" is just as big as this example).
How do i do that?
Thanks you for your help!
add:
i dont like frameworks (like pinvoke / assembly)
the function names / types are fixed and will never change (think of a driver.dll read write)
this dll is written by customers so it should be as easy as possible!
You could also do it over p/invoke, as example:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
private extern static int yourFunction(int var1, int var2);
i dont like frameworks (like pinvoke / assembly)
I'll suggest P/Invoke anyway. I don't think there are any reasonable alternatives. Perhaps writing a managed wrapper in managed C++, but really?
When you use P/Invoke the .NET runtime will dynamically load the DLL's that you have specified (and this is done when you first call the function). There is no reason to use P/Invoke to call LoadLibrary first.
You can either use P/Invoke to call the dll directly, or you can create a C++/CLI wrapper for it.
Here's how you can do it using P/Invoke:
Add the compiled dll to your C# project, set its 'Copy to Output Directory' property to 'Copy Always'. and call it like this:
class Program
{
[DllImport("cppdll.dll")]
private static extern void addX(int x);
[DllImport("cppdll.dll")]
private static extern void addY(int y);
static void Main(string[] args)
{
addX(5);
addY(7);
}
}
One way to do this would be through API. Find below an example for the setX and getX method from your sample above.
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr LoadLibraryEx(string libraryPath, IntPtr fileHandle, int actionFlag);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern bool FreeLibrary(IntPtr libraryHandle);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetProcAddress(IntPtr dllPointer, string functionName);
IntPtr ptr = IntPtr.Zero;
private delegate void setX (int i);
private delegate int getX();
private setX setXDel;
private getX getXDel;
public Constructor()
{
loadLib();
}
public void YourMethod()
{
setXDel(100);
int y = getXDel();
Console.WriteLine(y.ToString());
}
private void loadLib()
{
string path = "your dll path";
ptr = LoadLibraryEx(path, IntPtr.Zero, 0);
if (ptr == IntPtr.Zero)
throw new Exception("Cannot load dll.");
IntPtr addressPtr = GetProcAddress(ptr, "setX");
setXDel = (setX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(setX));
addressPtr = GetProcAddress(unrarPtr, "getX");
getXDel = (getX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(getX));
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
FreeLibrary(ptr);
}
}
I used P/Invoke, post my own cause the callback was not included by the others. But they were good answers! The C# Applicationhas use this GUI.
The Code for the Application is this : (mind it is a prototype to answer a technical question)
public partial class Form1 : Form
{
private delegate int Callback(int text);
private Callback mInstance; // Ensure it doesn't get garbage collected
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addX(int x);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addY(int y);
[DllImport(#"C:\\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void TestCallback();
private int Handler(int text)
{
textBox3.Text = text.ToString();
return 42;
}
private void button1_Click(object sender, System.EventArgs e)
{
mInstance = new Callback(Handler); // to set the callback in lib
SetCallback(mInstance); // could also be withhin constructor!
addX(int.Parse(textBox1.Text)); // other people in this thread posted this correct answer
addY(int.Parse(textBox2.Text));
TestCallback();
}
public Form1()
{
InitializeComponent();
}
So if you want to use a c++ lib with functions and callbacks (like in the question posted) you can use this.
But how to change the lib?
The Path to the lib is hardcoded (see [dllImport ......] ). But you
can swop the lib in filesystem. This can happen AFTER you build this
application.
If the lib is not on the given path an exception is thrown. So if
your program wants to use this lib first check the lib is present on the filesystem.
(not included in this simple prototype)
So to switch functionality (swop a driver lib ect) you copy a different lib with the
same name into the given path. (Copy paste - maybe change name)
This solution has the least overhead and is good if you never change the interface of an dll after the application was build!
I am trying to do 2 things: get a return value from a C dll function, and have that same function modify 1 of the members of a structure that is passed to it. After much experimentation I was able to get the function to return a value, but I am still unable to get it to return a modified value to the C# code; the value remains (unmodified) as 0.
I've tried lots of variations (ref, [In,Out], etc) to no avail
using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
namespace Vexing.Problem{
public class myIdx : VexingObject {
public myIdx(object _ctx) : base(_ctx) { }
private IPlotObject plot1;
[StructLayout(LayoutKind.Sequential)]
public class PLEX { public int yowser; }
[DllImport("my.dll", CharSet = CharSet.Unicode)]
public static extern int cFunction(
[MarshalAs(UnmanagedType.LPStruct)] PLEX mPlex);
PLEX a;
protected override void Create() { a = new PLEX(); }
protected override void CalcBar() {
int mf = cFunction(a);
plot1.Set(a.yowser); }
}}
// pertinent c dll code
typedef struct s_plex { int yowser;} cplex;
extern "C" __declspec( dllexport )
int cFunction(cplex *Cplex){ Cplex->yowser = 44; return 1;}
Your import declaration is wrong.
Setting the CharSet in your case doesn't make any sense (there are no string parameters in the native function declaration).
If you want to pass class instance, ref/out also must be thrown away (classes being passed by reference).
And the main point: extern "C" __declspec( dllexport ) means CallingConvention.Cdecl.
UPDATE. Here's complete working code sample:
C++ (header):
struct CStruct
{
int myField;
};
extern "C" __declspec( dllexport ) int MyFunction(CStruct *pStruct);
C++ (code):
int MyFunction(CStruct *pStruct)
{
pStruct->myField = 100;
return 1;
}
C#:
[StructLayout(LayoutKind.Sequential)]
class MyStruct
{
public int myField;
};
class Program
{
MyStruct myStruct = new MyStruct();
[DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int MyFunction(MyStruct pStruct);
static void Main(string[] args)
{
var p = new Program();
var result = MyFunction(p.myStruct);
Console.WriteLine("Result: {0}, MyStruct.myField = {1}", result, p.myStruct.myField);
}
}
Prints:
Result: 1, MyStruct.myField = 100