2015-06-20 52 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週期/內存操作所需的......這是我的原意。

希望這可以幫助有人陷入類似的情況。