2015-06-20 118 views
0

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

这是问题所在。 NinjaTrader不允许使用不安全的代码。所以,当我尝试将结构数组传递给dll并将其作为指针接收时,它立即崩溃了平台。如果我将结构数组作为指向ref(* &)的指针接收,那么它可以工作,但是......一旦控制传回忍者,在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函数中的参数更改为TestStruct *&testyTestStruct **testy,并指出.运算符也必须更改为->

现在我已经说过了,有没有人知道如何解决这个限制并访问实际的指针,所以dll可以编辑存储在struct数组中的实际值,这将在NinjaTrader中反映出来。 ...但不会崩溃?

+0

不使用不安全的代码。这是托管(C#)和非托管(C++)代码(即使用指针)之间最大的区别之一。 – SteveFerg

+0

到目前为止,这是我的结论。我不能动摇在C++端可以完成的任何事情来捕获内存地址。 – MehZhure

+0

根据您的约束,您可以始终传递数组或结构,并以格式返回字符串,您可以轻松地执行类似returnString.split(',')的操作来获取所需内容。 – SteveFerg

回答

1

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

首先,我不得不把struct阵列声明为static,使用new,上给它一个固定的存储器位置堆。然后我将它作为ref传递给我的C++ dll。

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

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

没有东西传回给NinjaTrader,因为dll正在编辑其原始内存位置的实际值。不需要传回任何东西,从而达到减少CPU周期/内存操作所需的......这是我的原意。

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