I have to call an unmanaged function from C# and must provide an array of coordinates (doubles) to it. How does the marshalling work for this case correctly?
On the unmanaged side:
typedef struct dPoint3dTag
{
double x, y, z;
} dPoint3d;
void UnmanagedModifyGeometry(char *strFeaId, dPoint3d *pnts, int iNumPnts);
I defined a managed Structure for DPoint3d on the managed side:
[StructLayout(LayoutKind.Sequential)]
public struct DPoint3d
{
// Constructor
public DPoint3d(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public double x, y, z;
}
I'm trying to call the unmanaged function from C# in this way:
// Import of the unmanaged function
[DllImport("Unmanaged.dll")]
public static extern void UnmanagedModifyGeometry([MarshalAs(UnmanagedType.LPStr)] string strFeaId, DPoint3d[] pnts, int iNumPnts);
// Using the unmanaged function from C#
// Allocating points
DPoint3d[] pnts = new DPoint3d[iPntCnt];
String strFeaId = "4711";
// After filling in the points call the unmanaged function
UnmanagedModifyGeometry(strFeaId, pnts, iPntCnt);
Is this workflow correct?
Regards
tomtorell
First of all on the unmanaged side, char* is a modifiable string. You should use const here to indicate that the data flows from caller to callee. And it makes sense to do the same for the other parameters:
void UnmanagedModifyGeometry(
const char *strFeaId,
const dPoint3d *pnts,
const int iNumPnts
);
Now it is clear to all how the data flows.
On the managed side, there is one obvious problem with the declaration which is that you don't specify the calling convention. The default is stdcall, but your unmanaged code will be cdecl, assuming that the declaration in the question is accurate.
The struct declarations that you show match perfectly. There is nothing more to say on that subject.
You can also make use of the default marshalling to simplify the p/invoke. I'd write it like this:
[DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void UnmanagedModifyGeometry(
string strFeaId,
[In] DPoint3d[] pnts,
int iNumPnts
);
And call it like this:
DPoint3d[] pnts = new DPoint3d[...]; // supply appropriate value for array length
// populate pnts
UnmanagedModifyGeometry("4711", pnts, pnts.Length);
Related
I have a c++ function with the method signature as
MyMethod(std::vector<double> tissueData, std::vector<double> BGData, std::vector<double> TFData, std::vector<double> colMeans, std::vector<double> colStds, std::vector<double> model)
I wish to call this c++ function in c# via dllimport. While creating the dll library I have defined the function from the c++ side as
extern "C" __declspec(dllexport) int MyMethod(double *tissue, double *bg, double *tf, double *colMeans, double *colStds, double* model);
I plan to pass an array of doubles from the c# side to the c++ dll function.
However, I am not sure how I should define the DllImport from the c# side and also how should i convert the double array when i parse it into the dllImport function ?
I read a little about marshalling but i still dont quite get it and Im not sure if it can be applied here ?
You cannot interop with C++ classes (such as std::vector), only with basic C-style data types and pointers. (As a side note) that was one of the problems Microsoft attempted to solve when it invented COM.
To make it work, you should export a different function, which receives plain C arrays and their respective lengths:
C++ side
extern "C" __declspec(dllexport) int MyExternMethod(
double *tissue, int tissueLen,
double *bg, int bgLen,
/* ... the rest ... */
);
// implementation
int MyExternMethod(
double* tissue, int tissueLen,
double* bg, int bgLen,
/* ... the rest ... */ )
{
// call your original method from here:
std::vector<double> tissueData(tissue, tissue + tissueLen);
std::vector<double> bgData(bg, bg + bgLen);
/* ... the rest ... */
return MyMethod(tissueData, bgData, /* ...the rest... */);
}
The interop import on C# side would be:
C# side
public static class MyLibMethods
{
[DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MyExternMethod(
double[] tissue, int tissueLen,
double[] bg, int bgLen,
/*...the rest...*/
);
}
And you can call it in C# like this:
C# side
public int CallMyExternMethod(double[] tissue, double[] bg, /*... the rest ...*/)
{
return MyLibMethods.MyExternMethod(
tissue, tissue.Length,
bg, bg.Length,
/*...the rest...*/
);
}
I'm currently working on an C# (.NET Framework 4.7.2) application using some business logic from an unmanaged C++ library. I try to pass data (interop) back and forth from C# to C++. I may not use C++/CLI, no common language runtime allowed in my project.
It works fine for int. Unfortunately as soon as I try to send another datatype I'm getting an conversion error e.g. float 4.2f becomes 1 and string "fourtytwo" turns into -1529101360.
My C# code looks like this:
// works fine, creates an instance of TestClass
var test = TestProxy.Wrapper_Create("test");
// int, works fine, a = 42
var a = TestProxy.TryInt(test, 42);
// float, problem, b = 1
var b = TestProxy.TryFloat(test, 4.2f);
// string, problem, c = -159101360
var c = TestProxy.TryString(test, "fourtytwo");
My C# Interop Proxy class to call the native (unmanaged) C++ code looks like this:
public static class TestProxy
{
private const string coreDLL = "test.core.dll";
[DllImport(coreDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Wrapper_Create(string name);
[DllImport(coreDLL, EntryPoint = "?TryInt#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryInt(IntPtr instance, int n);
[DllImport(coreDLL, EntryPoint = "?TryFloat#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryFloat(IntPtr instance, float n);
[DllImport(coreDLL, EntryPoint = "?TryString#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int TryString(IntPtr instance, string n);
}
My native (unmanaged) C++ looks like that:
the header file:
#ifdef TESTCORE_EXPORTS
#define TESTCORE_API __declspec(dllexport)
#endif
#pragma once
extern "C"
{
class TESTCORE_API TestClass
{
private:
char* name;
public:
TestClass(char*);
int TryInt(int);
float TryFloat(float);
char* TryString(char*);
};
TESTCORE_API TestClass* Wrapper_Create(char* name);
}
the implementation file:
#include "stdafx.h"
#include "TESTCore.h"
TestClass::TestClass(char* n)
{
name = n;
}
int TestClass::TryInt(int n)
{
return n; // works fine
}
float TestClass::TryFloat(float n)
{
return n; // something goes wrong here
}
char* TestClass::TryString(char* n)
{
return n; // something goes wrong here
}
extern "C"
{
TESTCORE_API TestClass * Wrapper_Create(char* name)
{
return new TestClass(name);
}
TESTCORE_API int TryInt(TestClass * instance, int n)
{
if (instance != NULL)
{
return instance->TryInt(n);
}
}
TESTCORE_API float TryFloat(TestClass * instance, float n)
{
if (instance != NULL)
{
return instance->TryFloat(n);
}
}
TESTCORE_API char* TryString(TestClass * instance, char* n)
{
if (instance != NULL)
{
return instance->TryString(n);
}
}
}
Do you know how to correctly marshal float, string from C# to C++ and back?
Thank you!
C++ doesn't have standard ABI. It's rarely a good idea to use C++ classes across DLLs, even when you have same language on both sides.
There're better ways.
Replace your __thiscall class methods with global functions, cdecl or stdcall whichever you like (but note C# and C++ have different defaults, if you'll do nothing C++ will use cdecl, C# will import as stdcall). You can pass "this" pointer of the class in the first argument, IntPtr in C#, just like you're doing now. Also if you'll write extern "C" or use a module definition file, they will have human-readable names.
If you want objects, use COM. Declare an interface that inherits from IUnknown, implement it in C++ (I usually use ATL), and export a global function to create an instance of that object (2 lines in ATL, CComObject<T>::CreateInstance followed by AddRef). No need to register, type libraries, you just need to implement IUnknown (but see this if you want to use them from multiple threads)
Update: strings are indeed harder. Apply [MarshalAs(UnmanagedType.LPTStr)] to the argument. Apply [return: MarshalAs(UnmanagedType.LPTStr)] to the function. Specify PreserveSig=true in your DllImport. Finally, modify the C++ code to return a copy of the string, i.e. call strlen then CoTaskMemAlloc (don't forget about the '\0') then strcpy.
Easier way to deal with strings is like this:
HRESULT TryString( TestClass *instance, BSTR i, BSTR *o )
At least there're CComBSTR and _bstr_t built-in classes to deal with memory management.
In the below example, the dataArray defined in C++ works if defined as an array, but not as a pointer (Just turns out garbage data). Is there another way to marshal the C# array so that it reads the pointer in as an array?
C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CSharpFoo{
int alpha;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5]
public int[] dataArray;
int beta;
}
C++
struct CPPFoo{
int alpha;
//int* dataArray; //Doesn't work, even though initialized to an array elsewhere
int dataArray[5];
int beta;
}
Being passed through a function like this
C#
[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public extern static bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)] ResultCallback callbackPointer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ResultCallback(CSharpFoo value);
C++
//Callback
typedef void(__stdcall * ResultCallback)(CPPFoo);
__declspec(dllexport) bool InitializeDLL(ResultCallback callback);
Thanks in advance!
Edit::
Because "initialized to an array elsewhere" wasn't clear:
CPPFoo(int dummy){ //Constructor
alpha = 32;
dataArray = new int[5];
for (int i = 0; i < 5; i++){
dataArray[i] = i;
}
beta = 13;
}
//dataArray C++ {0,1,2,3,4}
//alpha C# 32
//dataArray C# {Total random garbage} (dataArray[3] is 13!)
//beta C# 0
PS, the CPPFoo struct is a complex struct that comes from a DLL, so I cannot change it. For now, to get things working, I copy it to a more appropriate array like in NonCreature0714's answer, but this results in all of the data being copied--twice. It's this double copy that I'm trying to avoid.
Another edit:
While it seems that for a struct containing a single array the values get passed properly, for a complex struct, garbage gets thrown in.
I have updated the code to reflect a more complex struct!
I did a couple of tests on my local machine, as far as I understood your code. I had no problems with the following code
c# side
struct Foo
{
public int alpha;
public IntPtr Data;
public int beta;
public void GetData(ref int[] buffer,int length)
{
Marshal.Copy(Data,buffer,0,length);
}
}
class Program
{
[DllImport("MyPtr.dll",EntryPoint = "InitializeDLL", CallingConvention = CallingConvention.Cdecl)]
public static extern bool InitializeDLL([MarshalAs(UnmanagedType.FunctionPtr)]ResultCallback callbackPointer);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ResultCallback(ref Foo value);
static void CallBackMe(ref Foo value)
{
var buffer = new int[5];
value.GetData(ref buffer,buffer.Length);
}
static void Main(string[] args)
{
InitializeDLL(CallBackMe);
}
}
C++ side
struct CPPFoo {
int* dataArrayPtr;
CPPFoo()
{
dataArrayPtr = new int[5];
for (int i = 0; i < 5; i++) {
dataArrayPtr[i] = i;
}
}
};
typedef void(__stdcall * ResultCallback)(CPPFoo);
extern "C" __declspec(dllexport) bool InitializeDLL(ResultCallback callback)
{
CPPFoo f;
callback(f);
return true;
}
Code in action
My experience in C# is very limited but it seems that it works with int dataArray[5]; because the code agrees to the type of CSharpFoo and CPPFoo. The size of both is five ints. When you change the type to a pointer then one is the size of five ints and the other is a single pointer which would be less then five ints.
Most likely passing a dynamically sized array will need a size and to get a pointer you may need to do something with unsafe code but I don't write in C# so that is just a guess.
struct CPPFoo {
int* dataArray = nullptr;
int size = 0;
}
Use a vector.
#include <vector>
struct CPPFoo {
std::vector<int> dataArray;
};
Vector is more readable and more safe. It only slighty increases the size of the code, and is only slightly slower than using a primitive array. You also don’t need to worry about keeping around a variable for size, and updating it every time it’s changes.
I'm building a spectrometry application which uses a C# GUI and a native C++ logical dll. I'm trying to make the dll fill an array of simple C++ structs passed by reference from the C# side. However, when I try to print the [supposed to be filled] array elements, I'm getting System.NullReferenceExceptions and the array elements are marked as null in memory.
Here is the C++ struct definition and method implementation:
typedef struct intensitytype {
unsigned short usEchelleOrder; // echelle order
unsigned short usPixel; // horizontal camera pixel index (unbinned !!)
double dIntensity; // intensity
double dWaveLen; // wave length [nm]
} intensitytype;
void CameraControl::getResults(intensitytype* graphData)
{
graphData = _spectroData; // _spectroData is a pointer to a dynamic intensitytype array.
}
Here are the C# class definition and signature
[StructLayout(LayoutKind.Sequential)]
public class intensitytype
{
public ushort usEchelleOrder = 0; // echelle order
public ushort usPixel = 0; // horizontal camera pixel index (unbinned !!)
public double dIntensity = 0; // intensity
public double dWaveLen = 0; // wave length [nm]
}
[DllImport(#"Elemission.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void getResults(bool freeData, out intensitytype[] graphData);
I'm not sure what type of C# reference identifier is needed in this instance, or even if manual pointer marshalling is required. If one of you guys can point me in the right direction, I'd be forever grateful.
I finally found the solution to my problems, and am posting this for anyone who would need some clarification:
First, as David pointed out, simply treating the referenced argument as a pointer and assigning it the array's address does not work. You have to copy the entire array contents into the referenced array. Easily fixed.
The second mistake was in the C# method signature; the descripton that was needed here was "[Out]", with the brackets, compared to simply "out" (Once again, thanks David).
So, the end result:
The structs do not change, but the function signature in C# does:
[DllImport(#"Elemission.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void getResults([Out] intensityType[] graphData);
and here is a snippet of the function call:
int arraySize;
DllMethods.executeAcquisition(out arraySize); // arraySize is the Struct array length
intensityType[] resultData = new intensityType[arraySize];
DllMethods.getResults(resultData);
Meanwhile in C++, the C# call is received by the wrapper and passed to the member function...
__declspec(dllexport) void getResults(intensitytype* graphData)
{
MainControl::getInstance()->getCamera()->getResults(graphData);
}
...and lo and behold, the struct array is filled.
void CameraControl::getResults(intensitytype* graphData)
{
for (int i = 0; i < _spectroDataLength; i++)
graphData[i] = _spectroData[i];
}
Alright, here is the deal. I am trying to interface a C++ dll with an indicator written for the NinjaTrader platform (which is written in ninjascript...essentially C# with some platform specific code additions). TO make my dll work the way it is intended, I need to be able to pass a struct array from the indicator to the dll. In the indicator code, I am passing the struct array via ref. In the dll, I am trying to accept the struct array as a pointer. This allows me to edit the contents in the dll, without trying to figure out a way to pass a horde of information back to NinjaTrader. Basically, the dll receives the struct array pointer, which gives it access to the contents directly. Then, when the dll function returns a bool true flag to Ninja, it accesses the struct array and renders the information to the chart. Sounds simple right? I thought so too.
Here is the problem. NinjaTrader does not allow unsafe code. So, when I try to pass the struct array to the dll, and receive it as a pointer, it immediately crashes the platform. If I receive the struct array as a pointer to a ref (*&), then it works, but....once control is passed back to Ninja, all the edits done in the struct array are non-existent.
So, to speed up this process, I have created a very brief indicator, and dll code set which demonstrates what I am trying to do.
Here is the ninja indicator code:
[StructLayout(LayoutKind.Sequential)]
public struct TestStruct
{
public int x, y;
}
TestStruct[] test = new TestStruct[2];
protected override void OnBarUpdate()
{
if(CurrentBar < Count - 2) {return;}
test[0].x = 10;
test[0].y = 2;
test[1].x = 0;
test[1].y = 0;
Print(GetDLL.TestFunk(ref test));
Print("X0: " + test[0].x.ToString() + " Y0: " + test[0].y.ToString());
Print("X1: " + test[1].x.ToString() + " Y1: " + test[1].y.ToString());
}
class GetDLL
{
GetDLL() {}
~GetDLL() {}
[DllImport("testdll.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "TestFunk")]
public static extern int TestFunk(
[In,MarshalAs(UnmanagedType.LPArray)] ref TestStruct[] test );
}
And now the C++ dll code:
#define WIN32_LEAN_AND_MEAN
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
struct TestStruct
{
int x, y;
};
extern "C" __declspec(dllexport) int __stdcall TestFunk( TestStruct *testy )
{
testy[1].x = 20;
testy[1].y = 9;
int one = testy[1].x;
int two = testy[1].y;
return (one + two);
}
Now, please keep in mind, this code I have pasted in above WILL cause NinjaTrader to crash the moment you place the indicator on a chart and it becomes active. The only way I have been able to make it NOT crash is to change the arg in the C++ TestFunk function to either TestStruct *&testy or TestStruct **testy, noting that the . operators will have to be changed to -> also.
Now that I have said all that, does anyone know how to get around this limitation and get access to the actual pointer, so the dll can edit the actual values stored in the struct array, which will be reflected inside NinjaTrader....but not crash?
Huzzah! I finally figured it. First, let me post the relevant code, then I will explain.
First the C#/Ninjascript
public class TestIndicator : Indicator
{
[StructLayout(LayoutKind.Sequential)]
public struct TestStruct { public int x, y; }
static TestStruct[] testy = new TestStruct[2];
protected override void OnBarUpdate()
{
if(CurrentBar < Count - 2) {return;}
GetDLL.TestFunk( ref testy[0] );
Print("X0: " + testy[0].x.ToString() + " Y0: " + testy[0].y.ToString());
Print("X1: " + testy[1].x.ToString() + " Y1: " + testy[1].y.ToString());
}
class GetDLL
{
GetDLL() {}
~GetDLL() {}
[DllImport("testdll.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "TestFunk")]
public static extern void TestFunk( ref TestStruct testy );
}
}
And the C++ code:
struct TestStruct { int x, y; };
extern "C" __declspec(dllexport) void __stdcall TestFunk( void *t)
{
TestStruct* ptE = (TestStruct*)t;
ptE->x = 10; ptE->y = 2;
ptE++;
ptE->x = 20; ptE->y = 9;
}
First, I had to declare the struct array as static, using new, giving it a fixed memory location on the heap. Then I pass it as a ref to my C++ dll.
Inside the dll, the arg is captured as a void* data type. Next, I typecast the arg into a `TestStruct* and store it in another pointer variable (to keep my zero element reference intact).
From that point, I have a pointer that references element zero, which I can use to edit the values at element zero in the struct array. To gain access to the following elements, all I needed to do was increment the pointer. Once I did that, I had access to element one in the array.
Nothing was passed back to NinjaTrader because the dll was editing the actual values in their original memory locations. No need to pass anything back, thus attaining the reduced cpu cycles/memory operations necessary...which was my original intent.
Hopefully this helps someone stuck in a similar situation.