Referencing an object array in C# PInvoke - c#

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];
}

Related

Can a pointer be marshalled to an array when passing a struct?

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.

P/Invoke C++ DLL for filling a managed buffer

Basically I have a DLL and a header file (C++) for a data acquisition device that I use for my research. The device has 8 sensors and each sensor has x,y,z,etc data points. I'd like to create a UI program in WPF for this device. So far I've successfully been able to communicate with the device in .NET via an unsafe code implementation and read data into an array of structs (where each struct corresponds to one of the sensors). However I'd like to see if I can get it working with a strictly safe code implementation, but I've just been hitting brick walls for a while now. This is my first time working with unmanaged vs. managed code so please bear with me. I've searched countless threads online and the rabbit hole just keeps getting deeper so I'd like some advice from someone with experience. Basically the API header file has a function with the following definition:
int GetSynchronousRecord(USHORT sensorID, void *pRecord, int recordSize);
Essentially we pass the function a buffer by reference, and it fills it up. I have the option of getting either a single sensor's data, or all sensors at once depending on the sensorID argument I pass. What HAS worked for me so far (in the managed world) is if I do the following:
[StructLayout(LayoutKind.Sequential)]
public struct DOUBLE_POSITION_ANGLES_TIME_Q_RECORD
{
public double x;
public double y;
public double z;
public double a;
public double e;
public double r;
public double time;
public ushort quality;
};
...
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD record, int recordSize);
...
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD record = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD();
// Get the data from SENSOR_1 only
errorCode = GetSynchronousRecord(1, ref record, Marshal.SizeOf(record));
So this implementation works fine, I can get all the coordinate data and at a really good speed. However, I'd like to get ALL the sensors at once. In the C++ API code samples, they pass the GetSynchronousRecord function an ARRAY of STRUCTS, one struct for each sensor. I tried to do the same in C# as follows:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record, int recordSize);
// Define Array of Struct for each sensor
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[8];
while(recording) {
...
// Get data from ALL sensors (0xffff)
errorCode = GetSynchronousRecord(0xffff, ref record, Marshal.SizeOf(record)*8);
...
}
But this straight up crashes my program with an System.ExecutionEngineException error. I've read that since my function is expecting a void* pointer, that I should use an IntPtr argument, but this approach seemed quite confusing to be honest. Another thing I tried is to actually loop over each sensor and call the function for the sensor, but this dropped the speed INSANELY almost to 1 record/second (instead of 100 records/second). Many other similar threads on stackexchange say to use out parameter, or to use [In, Out] attribute on the function definition, but none of these suggestions worked.
TL;DR: If I understand my situation correctly, I have a MANAGED array of structs that I need to correctly pass to a C++ function as an argument (pass by reference), and then the function will fill my structs with data from a data acquisition device.
I apologize for the wall of text/code, any information for me from someone with experience would be much appreciated.
EDIT: Just to clarify, the GetSynchronousRecord function is INSIDE a while loop where on each iteration I'm getting new data points for each struct.
Your second p/invoke declaration is wrong. You had
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(
ushort sensorID,
ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record,
int recordSize
);
The problem is the array parameter. Because you pass that array by ref that actually makes it a double pointer. Instead you want to simply remove the ref and declare the import like so:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(
ushort sensorID,
[Out] DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record,
int recordSize
);
The [Out] attribute tells the marshaler that the data is flowing out of the function. Without it the default assumption is that the data flows in.
When you call the function do so like this:
errorCode = GetSynchronousRecord(0xffff, record, Marshal.SizeOf(record)*record.Length);
The best way is probably using IntPtr rather than ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD.
I'd change the P/Invoke signature to:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, IntPtr record, int recordSize);
Create an IntPtr that points to the needed memory space:
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>() * 8);
Call the P/Invoke function (which should fill the memory with the structs):
errorCode = GetSynchronousRecord(0xffff, ptr, Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>() * 8);
Get the structures from the memory block:
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] records = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[8];
for (i = 0; i < 8; i++) {
records[i] = Marshal.PtrToStructure<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>(IntPtr.Add(ptr, i * Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>()));
}
Marshal.FreeHGlobal(ptr);

Challenge - Ninjascript C# interface with a C++ dll

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.

Marshalling an array of strucs to call an unmanaged function from C#

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

Passing an array of unknown size of structs from c# to c++ dll and back

I asked a similar question yesterday, but this is slightly different. I am having problems passing arrays of struct from c# to c++, and getting this back again.
Here is the c++ code. Firstly, the struct:
struct Tri
{
public:
int v1, v2, v3;
}
and now the c++ dll part:
extern "C" __declspec(dllexport) void Dll_TriArray(Tri *tri)
{
int num = 10;
tri = new Tri[num];
for (int i = 0; i < num; i++)
{
tri[i].v1 = i + 5;
tri[i].v2 = i + 10;
tri[i].v3 = i + 25;
}
}
and here's the c# code, again starting with the struct:
[StructLayout(LayoutKind.Sequential)]
public struct Tri
{
public int v1, v2, v3;
}
public class Testing
{
[DllImport("testing.dll")]
static extern void Dll_TriArray(out Tri[] tryArray);
public GetTriArray()
{
Tri[] triArray;
Dll_TriArray(out triArray);
}
}
So the triArray i get when calling the GetTriArray method will come back as null. I have thought about passing an IntPtr in as the argument, but then how does one marshal an intptr into/from an array of struct?
BTW - at this stage, i'm not interested in memory leaks.
I'm not an expert (by any means) in C# but the C++ part gets passed a pointer to Tri-struct, which in C++ can be used like an dynamic array you allocate and fill that correctly but you don't have a way to get it back, because from C-perspective you'd need to modify the caller's (C#) pointer but you only get a copy and not a reference to the original.
In C++ the closest thing to what you are tying to do, would be to change the prototype to void Dll_TriArray(Tri *&tri) (call by ref, not call by copy) but I'm not sure how to interface that with C# (probably Dll_TriArray(ref triArray); ).

Categories

Resources