Хорошо, вот сделка. Я пытаюсь связать C ++ dll с индикатором, написанным для платформы NinjaTrader (которая написана на ninjascript … по сути, на C # с некоторыми добавлениями кода для конкретной платформы). Чтобы моя dll работала так, как задумано, мне нужно иметь возможность передавать массив struct от индикатора к dll. В коде индикатора я передаю массив struct через ref. В DLL я пытаюсь принять массив структуры в качестве указателя. Это позволяет мне редактировать содержимое в dll, не пытаясь найти способ передать кучу информации обратно в NinjaTrader. По сути, DLL получает указатель массива структуры, который дает ей доступ к содержимому напрямую. Затем, когда функция dll возвращает флаг bool true для Ninja, она получает доступ к массиву структуры и отображает информацию на диаграмме. Звучит просто, правда? Я тоже так думал.
Здесь проблема. NinjaTrader не разрешает небезопасный код. Поэтому, когда я пытаюсь передать массив структуры в dll и получить его в качестве указателя, он немедленно приводит к краху платформы. Если я получу массив структуры как указатель на ссылку (*&), тогда это работает, но …. после того, как управление передано обратно в Ninja, все изменения, сделанные в массиве struct, не существуют.
Итак, чтобы ускорить этот процесс, я создал очень краткий индикатор и набор кодов dll, который демонстрирует, что я пытаюсь сделать.
Вот код индикатора ниндзя:
[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 );
}
А теперь код DLL C ++:
#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);
}
Пожалуйста, имейте в виду, что этот код, который я вставил выше, вызовет сбой NinjaTrader в тот момент, когда вы поместите индикатор на график, и он станет активным. Единственный способ, которым я смог сделать это НЕ сбой, — это изменить аргумент функции C ++ TestFunk на TestStruct *&testy
или же TestStruct **testy
отмечая, что .
операторы должны быть изменены на ->
также.
Теперь, когда я сказал все это, кто-нибудь знает, как обойти это ограничение и получить доступ к фактическому указателю, чтобы dll могла редактировать фактические значения, хранящиеся в массиве struct, которые будут отражены внутри NinjaTrader …. но не врезаться?
Ура! Я наконец понял это. Сначала позвольте опубликовать соответствующий код, затем я объясню.
Сначала 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 );
}
}
И код C ++:
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;
}
Во-первых, я должен был объявить struct
массив как static
, с помощью new
, давая ему фиксированную ячейку памяти в куче. Тогда я передаю это как ref
на мой C ++ DLL.
Внутри dll арг захватывается как void*
тип данных. Затем я типизирую arg в `TestStruct * и сохраняю его в другой переменной-указателе (чтобы сохранить нулевую ссылку на элемент без изменений).
С этого момента у меня есть указатель, который ссылается на нулевой элемент, который я могу использовать для редактирования значений нулевого элемента в массиве struct. Чтобы получить доступ к следующим элементам, все, что мне нужно было сделать, это увеличить указатель. Как только я это сделал, у меня был доступ к элементу один в массиве.
Ничто не было передано обратно в NinjaTrader, потому что DLL редактировал фактические значения в их исходных ячейках памяти. Нет необходимости передавать что-либо назад, таким образом, достигая уменьшенных циклов ЦП / операций с памятью, необходимых … что было моим первоначальным намерением.
Надеюсь, это поможет кому-то застрять в подобной ситуации.