挑战 - 带有C++ dll 的 Ninjascript C# 接口
Challenge - Ninjascript C# interface with a C++ dll
好吧,这是交易。 我正在尝试将一个C++ dll 与为 NinjaTrader 平台编写的指标(它是用忍者脚本编写的......本质上是 C#,添加了一些特定于平台的代码)。 为了使我的 dll 按预期方式工作,我需要能够将结构数组从指示器传递到 dll。 在指标代码中,我通过 ref 传递结构数组。 在 dll 中,我尝试接受结构数组作为指针。 这允许我编辑dll中的内容,而无需尝试找出将大量信息传递回NinjaTrader的方法。 基本上,dll 接收结构数组指针,这使其可以直接访问内容。 然后,当 dll 函数向 Ninja 返回布尔 true 标志时,它会访问结构数组并将信息呈现到图表中。 听起来很简单吧? 我也是这么想的。
这就是问题所在。 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 函数中的参数更改为 TestStruct *&testy
或 TestStruct **testy
,注意.
运算符也必须更改为 ->
。
现在我已经说了这么多,有没有人知道如何绕过这个限制并访问实际的指针,这样 dll 就可以编辑存储在结构数组中的实际值,这些值将反映在 NinjaTrader 中......但不是崩溃?
> Huzzah! 我终于想通了。 首先,让我发布相关代码,然后我将进行解释。
首先是 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* 中,并将其存储在另一个指针变量中(以保持我的零元素引用不变)。
有一个引用元素零的指针,我可以使用它来编辑结构数组中元素零处的值。 为了访问以下元素,我需要做的就是增加指针。 一旦我这样做了,我就可以访问数组中的元素 1。
没有任何东西被传回NinjaTrader,因为dll正在编辑其原始内存位置的实际值。 无需传递任何内容,从而减少了必要的 CPU 周期/内存操作......这是我的初衷。
希望这有助于陷入类似情况的人。
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- Visual C++GC接口如何启用它以及要包含哪个库
- Windows.h与GLFW.h的接口
- 当字段可以为null时,如何使用C++接口在Avro中写入数据
- 提供与TMP和SFINAE的通用接口
- 为重写std::exception的库生成swig接口时出错
- 内联如何影响模块接口中的成员函数
- COM 接口 c# 封送数组数组
- 如何在 SCIP C++ 接口中获取 MILP 约束矩阵中的系数值
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 如何绑定 C++ gRPC 客户端的网络接口
- 模板化接口 - 创建一个泛型模板类以返回任何容器
- 如何从实现接口的模板化类实例访问结构
- 带有进度表的 curl 多接口程序
- 设计帮助 - 为不同类型的消息处理通用接口的设计模式
- 我可以在具有一个标头和一个接口的 cpp 文件中有多个嵌入吗?
- 类接口,可以创建N个方法
- 类具有相同的接口,但参数的类型不同
- 如何与 Cheerp/js 中的 extern 变量接口?
- 挑战 - 带有C++ dll 的 Ninjascript C# 接口