【问题标题】:Challenge - Ninjascript C# interface with a C++ dll挑战 - Ninjascript C# 接口与 C++ dll
【发布时间】:2015-06-20 20:16:17
【问题描述】:

好的,这就是交易。我正在尝试将 C++ dll 与为 NinjaTrader 平台编写的指标接口(它是用 ninjascript 编写的......本质上是 C# 并添加了一些特定于平台的代码)。为了使我的 dll 按预期方式工作,我需要能够将结构数组从指标传递给 dll。在指标代码中,我通过 ref 传递结构数组。在 dll 中,我试图接受 struct 数组作为指针。这使我可以编辑 dll 中的内容,而无需设法将大量信息传回 NinjaTrader。基本上,dll 接收结构数组指针,这使它可以直接访问内容。然后,当 dll 函数向 Ninja 返回一个 bool true 标志时,它会访问 struct 数组并将信息呈现给图表。听起来很简单吧?我也是这么想的。

这就是问题所在。 NinjaTrader 不允许不安全的代码。因此,当我尝试将结构数组传递给 dll 并将其作为指针接收时,它会立即使平台崩溃。如果我接收到 struct 数组作为指向 ref (*&) 的指针,那么它可以工作,但是....一旦将控制权传递回 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 );
}

现在是 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 *&amp;testyTestStruct **testy,注意 . 运算符也必须更改为 -&gt;

现在我已经说了这么多,有谁知道如何绕过这个限制并访问实际指针,因此 dll 可以编辑存储在结构数组中的实际值,这将反映在 NinjaTrader 内部。 ..但不会崩溃?

【问题讨论】:

  • 必须使用不安全的代码。这是托管 (C#) 和非托管 (C++) 代码之间最大的区别之一,即指针的使用。
  • 到目前为止,这就是我的结论。我无法摆脱在 C++ 端可以做一些事情来捕获内存地址的感觉。
  • 根据您的约束,您始终可以传递数组或结构,并以您可以轻松执行诸如 returnString.split(',') 之类的格式传回字符串以取回什么你想要/需要。
  • 通过该方法返回的数据过多。系统会将其编译为字符串,然后将字符串解码为可用数据,这将对性能造成重大影响。这将破坏用 C++ 对系统进行编码的整个要点,而这几乎完全是为了性能。
  • 传递结构体并返回结构体呢?

标签: c# c++ arrays dll ninja


【解决方案1】:

嘘!我终于想通了。先贴一下相关代码,再解释一下。

首先是 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;
}

首先,我必须使用newstruct 数组声明为static,并为其在堆上提供一个固定的内存位置。然后我将它作为ref 传递给我的C++ dll。

在 dll 中,arg 被捕获为 void* 数据类型。接下来,我将 arg 类型转换为 `TestStruct* 并将其存储在另一个指针变量中(以保持我的零元素引用完好无损)。

从那时起,我有了一个引用零元素的指针,我可以用它来编辑结构数组中零元素的值。要访问以下元素,我需要做的就是增加指针。一旦我这样做了,我就可以访问数组中的元素一。

没有任何东西回传给 NinjaTrader,因为 dll 正在编辑其原始内存位置中的实际值。无需传回任何东西,从而减少必要的 cpu 周期/内存操作......这是我的初衷。

希望这可以帮助陷入类似情况的人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-12
    • 1970-01-01
    • 2022-06-14
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多