挑战 - Ninjascript C# 接口与 C++ dll
Challenge - Ninjascript C# interface with a C++ dll
好的,这就是交易。我正在尝试将 C++ dll 与为 NinjaTrader 平台编写的指标(用 ninjascript 编写...本质上是 C# 并添加了一些特定于平台的代码)连接起来。为了使我的 dll 按预期方式工作,我需要能够将结构数组从指标传递到 dll。在指标代码中,我通过 ref 传递结构数组。在 dll 中,我试图接受结构数组作为指针。这使我能够编辑 dll 中的内容,而无需尝试找出将大量信息传回 NinjaTrader 的方法。基本上,dll 接收结构数组指针,这使它可以直接访问内容。然后,当 dll 函数 returns 一个 bool true 标志给 Ninja 时,它访问结构数组并将信息呈现给图表。听起来很简单吧?我也是这么想的
问题来了。 NinjaTrader 不允许不安全的代码。因此,当我尝试将结构数组传递给 dll 并将其作为指针接收时,它会立即使平台崩溃。如果我收到结构数组作为指向 ref (*&) 的指针,那么它可以工作,但是....一旦控制权传回 Ninja,在结构数组中完成的所有编辑都不存在。
因此,为了加快这个过程,我创建了一个非常简短的指标和 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 );
}
现在是 C++ dll 代码:
#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 函数中的 arg 更改为 TestStruct *&testy
或 TestStruct **testy
,注意必须更改 .
运算符到 ->
也。
既然我已经说了这么多,有没有人知道如何绕过这个限制并访问实际的指针,这样 dll 就可以编辑存储在结构数组中的实际值,这将反映在 NinjaTrader 中....但没有崩溃?
万岁!我终于想通了。先让我post相关代码,然后我会解释。
首先是 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;
}
首先,我必须使用 new
将 struct
数组声明为 static
,为其在堆上指定一个固定的内存位置。然后我将它作为 ref
传递给我的 C++ dll。
在 dll 内部,arg 被捕获为 void*
数据类型。接下来,我将 arg 类型转换为 `TestStruct* 并将其存储在另一个指针变量中(以保持我的零元素引用完好无损)。
从那时起,我有了一个引用元素零的指针,我可以使用它来编辑结构数组中元素零处的值。要访问以下元素,我需要做的就是增加指针。一旦我这样做了,我就可以访问数组中的第一个元素。
没有任何内容传回 NinjaTrader,因为 dll 正在编辑原始内存位置中的实际值。不需要传回任何东西,从而减少了必要的 cpu cycles/memory 操作...这是我的初衷。
希望这对遇到类似情况的人有所帮助。
好的,这就是交易。我正在尝试将 C++ dll 与为 NinjaTrader 平台编写的指标(用 ninjascript 编写...本质上是 C# 并添加了一些特定于平台的代码)连接起来。为了使我的 dll 按预期方式工作,我需要能够将结构数组从指标传递到 dll。在指标代码中,我通过 ref 传递结构数组。在 dll 中,我试图接受结构数组作为指针。这使我能够编辑 dll 中的内容,而无需尝试找出将大量信息传回 NinjaTrader 的方法。基本上,dll 接收结构数组指针,这使它可以直接访问内容。然后,当 dll 函数 returns 一个 bool true 标志给 Ninja 时,它访问结构数组并将信息呈现给图表。听起来很简单吧?我也是这么想的
问题来了。 NinjaTrader 不允许不安全的代码。因此,当我尝试将结构数组传递给 dll 并将其作为指针接收时,它会立即使平台崩溃。如果我收到结构数组作为指向 ref (*&) 的指针,那么它可以工作,但是....一旦控制权传回 Ninja,在结构数组中完成的所有编辑都不存在。
因此,为了加快这个过程,我创建了一个非常简短的指标和 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 );
}
现在是 C++ dll 代码:
#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 函数中的 arg 更改为 TestStruct *&testy
或 TestStruct **testy
,注意必须更改 .
运算符到 ->
也。
既然我已经说了这么多,有没有人知道如何绕过这个限制并访问实际的指针,这样 dll 就可以编辑存储在结构数组中的实际值,这将反映在 NinjaTrader 中....但没有崩溃?
万岁!我终于想通了。先让我post相关代码,然后我会解释。
首先是 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;
}
首先,我必须使用 new
将 struct
数组声明为 static
,为其在堆上指定一个固定的内存位置。然后我将它作为 ref
传递给我的 C++ dll。
在 dll 内部,arg 被捕获为 void*
数据类型。接下来,我将 arg 类型转换为 `TestStruct* 并将其存储在另一个指针变量中(以保持我的零元素引用完好无损)。
从那时起,我有了一个引用元素零的指针,我可以使用它来编辑结构数组中元素零处的值。要访问以下元素,我需要做的就是增加指针。一旦我这样做了,我就可以访问数组中的第一个元素。
没有任何内容传回 NinjaTrader,因为 dll 正在编辑原始内存位置中的实际值。不需要传回任何东西,从而减少了必要的 cpu cycles/memory 操作...这是我的初衷。
希望这对遇到类似情况的人有所帮助。