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

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.

Related

Pass an array to a C# DLL from C++ DLL

I am trying to handle a C++ DLL which in turn connects to a C# DLL.
My problem is when I want to send or receive arrays between C++ and C# DLLs.
I have created the C# DLL and C++ DLL, so I can modified all the files. Within the C# DLL, I must necessarily have an array of string an another of double because that is where I make use of other functions unrelated to this question. (The declaration of the function that I have to use in C# DLL is Error SetMultipleChannelValues(string[] names,double[] values).
I am using CLI in Visual Studio 2015 to compile and generate all DLLs and projects.
Here it is the C# code that I have:
public static Error SetMultipleSignals(string[] signals, double[] values)
{
Error error = null;
error = workspace.SetMultipleChannelValues(signals, values);
if (error.IsError)
Console.WriteLine("[DLL] Error in SetMultipleChannelValues(). Code: " + error.Code + ". Message: " + error.Message);
return error;
}
Here it is the C++ code that I have:
bool setMultipleSignals(double* setSignal_values)
{
array<double, DEFAULT_SETSIGNAL_SIZE> values;
for (int index = 0 ; index < DEFAULT_SETSIGNAL_SIZE ;index++)
{
values[index] = *(setSignal_values + index);
}
if (!veristand_wrapper_cs::VeriStand_dll::SetMultipleSignals(setSignals_path, values)) // This is the call to C# function
return true;
else
return false;
}
Here it is the C++ header that I have:
#pragma once
#define VERISTAND_WRAPPER_CPP __declspec(dllexport)
#include <string>
#include <iostream>
#include <windows.h>
#include <array>
using namespace std;
using namespace System;
using System::Runtime::InteropServices::Marshal;
#using "veristand_wrapper_cs.dll" // This is the C# DLL
#define DEFAULT_SETSIGNAL_SIZE 100
extern "C" VERISTAND_WRAPPER_CPP bool setMultipleSignals(double* setSignal_values); // This function is called from API C++
I pass the double array with a pointer to double as a parameter from my C++ application, but I have to pass to C# DLL an array, so I have tried to build a whole array of double before pass it.
The array setSignals_path is created as a global array in this C++ DLL as array<String^, DEFAULT_SETSIGNAL_SIZE> setSignals_path;.
The problem is that the call of C# function provides me an error that says that I can not call the function with these arguments. It expects array<System::String^> ^signals, array<double> ^values and I am passing std::array<System::String^, 100Ui64>, std::array<double, 100Ui64>
The concept of my idea is simple. From the C++ API, pass a pointer to the array of doubles, so that the function of my DLL in C++ passes the array of doubles and strings to the DLL of C#, and it returns the array of modified doubles to be able to indicate to the API from C++ that the values ​​have been modified from that memory address.
Would anyone know how to approach this problem or how to do it in some other way to make it work?
It looks like you are using c++/cli. So you can actually use .Net types in native code.
In C++/cli you need to explicitly declare if a variable is a reference, that is what the ^ is for. Your arrays is declared without this, but your method needs a reference. You will also need to allocate your array on the managed heap. So your array should probably be declared as
array<double>^ values = gcnew array< double>(DEFAULT_SETSIGNAL_SIZE);
But it was a while ago I coded c++/cli, so I may have missed something. See How to use arrays in c++/cli. It also looks like you may be able to use ref new instead of gcnew
Use following. The size of array must be 100, or add another parameter before the string array indicating length. The string in code is a byte array terminating with '\0'. c++ must use a method that will read the null terminated string. A double is 8 bytes so the 100 double can be consecutive memory locations (800 bytes).
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
public struct DoubleArray
{
public double[] values;
}
public static Error SetMultipleSignals(IntPtr signals, IntPtr values)
{
const int DEFAULT_SETSIGNAL_SIZE = 100;
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[DEFAULT_SETSIGNAL_SIZE];
Marshal.PtrToStructure(signals, stringStruct);
string[] strArray = new string[DEFAULT_SETSIGNAL_SIZE];
for(int i = 0; i < DEFAULT_SETSIGNAL_SIZE; i++)
{
strArray[i] = Marshal.PtrToStringAnsi(stringStruct.unmanagedArray[i]);
}
DoubleArray dArray = new DoubleArray();
dArray.values = new double[DEFAULT_SETSIGNAL_SIZE];
dArray = Marshal.PtrToStructure<DoubleArray>(values);
return null;
Here is going the other way.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StringArray
{
public IntPtr[] unmanagedArray;
}
static void Main(string[] args)
{
string[] strArray = { "Message 1", "Message 2", "Message 3"};
StringArray stringStruct = new StringArray();
stringStruct.unmanagedArray = new IntPtr[strArray.Length];
for (int i = 0; i < strArray.Length; i++)
{
stringStruct.unmanagedArray[i] = Marshal.StringToBSTR(strArray[i]);
}
IntPtr unmanagedArray = Marshal.AllocHGlobal(Marshal.SizeOf(stringStruct));
Marshal.StructureToPtr(stringStruct, unmanagedArray, false);
}

type conversion problem for marshaling datatypes from C# to C++

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.

Referencing an object array in C# PInvoke

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

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