在 MultiCharts 使用函數、指標、策略呼叫DLL,執行狀態設為on,MultiCharts 會將 DLL函數Lock並且將資料Keep於記憶體中,直到使用狀態改為off,才將DLL函數UnLock與釋放記憶體空間。
這樣在執行單一函數、指標、策略下可正常運作看不出任何問題,但是同時執行2個以上的函數、指標、策略就會發生資料覆蓋的問題,對 MultiCharts 來說DLL是單一資源,其所使用CPU、IO、Memory、Network等資源都是單一,如果多個函數、指標、策略同時執行存取單一資源,會發生資源使用權互搶,看誰先搶到誰就先執行,就這是所謂的競賽現象,後執行的就會覆蓋先執行的資料,這樣導致MultiCharts 發生未知的錯誤。
為了解決以上資源共用的問題,筆者在DLL中加了一個小技巧來處理這樣的問題,就是將每次DLL產生的資料存在一個專屬空間中,並給於一個ID,後續要存取這部分資料時,只要帶入這個ID即可取得對應的完整資料,如此一來解決了多個函數、指標、策略執行時資料會被覆蓋的問題。
這裡舉一個例子做說明,使用VC++ 2010實作一個DLL,以讀取外部檔案資料並傳給MultiCharts為主。
架構圖如下:
建立一個名為 FileInfo 專案的DLL,在 FileInfo.cpp 中輸入以下程式碼。
// FileInfo.cpp : Defines the initialization routines for the DLL. // #include "stdafx.h" #include "FileInfo.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CFileInfoApp BEGIN_MESSAGE_MAP(CFileInfoApp, CWinApp) END_MESSAGE_MAP() // CFileInfoApp construction OI g_OI; CFileInfoApp::CFileInfoApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } // The one and only CFileInfoApp object CFileInfoApp theApp; // CFileInfoApp initialization BOOL CFileInfoApp::InitInstance() { CWinApp::InitInstance(); return TRUE; } int APIENTRY ReadFileText(CHAR * FilebName) { return g_OI.Init(FilebName); } int APIENTRY GetTextCount(int nIndex) { return g_OI.GetCount(nIndex); } char* APIENTRY GetDataText(int nIndex, int nNum) { return g_OI.GetFileData(nIndex, nNum); } int APIENTRY ClearSpace(int nIndex) { return g_OI.Release(nIndex); }在FileInfo.h中輸入以下程式碼。
// FileInfo.h : main header file for the FileInfo DLL // #pragma once #ifndef __AFXWIN_H__ #error "include 'stdafx.h' before including this file for PCH" #endif #include "resource.h" // main symbols #include "OI.h" // CFileInfoApp // See FileInfo.cpp for the implementation of this class // class CFileInfoApp : public CWinApp { public: CFileInfoApp(); // Overrides public: virtual BOOL InitInstance(); DECLARE_MESSAGE_MAP() };在FileInfo.def輸入以下程式碼。
; FileInfo.def : Declares the module parameters for the DLL. LIBRARY EXPORTS ; Explicit exports can go here ReadFileText @1 GetTextCount @2 GetDataText @3 ClearSpace @4建立OI class,在OI.cpp中輸入以下程式碼。
// OI.cpp : implementation file // #include "stdafx.h" #include "FileInfo.h" #include "OI.h" static HANDLE g_hMutex = CreateMutex(NULL, FALSE, NULL); static vector <INFO*> g_List; static int g_nIndex = 0; OI::OI() { } OI::~OI() { } // OI message handlers INFO *OI::FindData(int index) { INFO *info = NULL; if(index == 0) return NULL; for (int i = 0 ; i < (int) g_List.size() ; i++) { info = g_List.at(i); if (info && info->index == index) break; } return info; } BOOL OI::ReadText(CHAR * FilebName, int nIndex) { CHAR line[MAX_SIZE]; fstream fin; fin.open(FilebName, ios::in); if(!fin) return FALSE; INFO * info = FindData(nIndex); if(!info) return FALSE; while(fin.getline(line, sizeof(line), '\n')) info->Data.Add(line); fin.close(); return TRUE; } int OI::Init(CHAR * FilebName) { DLLMutexLock(); int nIndex; //建立專屬空間與給予ID INFO * Info = new INFO(); Info->index = ++g_nIndex; nIndex = Info->index; //將空間存入一個ID序列 g_List.push_back(Info); //取得資料存入該空間 ReadText(FilebName, nIndex); DLLMutexUnLock(); return nIndex; } int OI::GetCount(int nIndex) { DLLMutexLock(); INFO * info = FindData(nIndex); if(!info) { DLLMutexUnLock(); return 0; } DLLMutexUnLock(); return info->Data.GetUpperBound() + 1; } CHAR * OI::GetFileData(int nIndex, int nNum) { DLLMutexLock(); CString csTemp, cs; INFO * info = FindData(nIndex); if(!info) { DLLMutexUnLock(); return 0; } csTemp = info->Data.GetAt(nNum); cs.Format("nIndex = %d, nNum = %d, %s", nIndex, nNum, csTemp); OutputDebugString(cs); csTemp.Trim(); DLLMutexUnLock(); return (char*)(LPCTSTR)csTemp; } int OI::Release(int index) { int ret = 0; DLLMutexLock(); for (int i = 0 ; i < g_List.size() ; i++) { INFO *info = g_List.at(i); if (info && info->index == index) { g_List.erase(g_List.begin() + i); delete info; ret = 1; break; } } DLLMutexUnLock(); return ret; } void OI::DLLMutexLock() { WaitForSingleObject(g_hMutex, INFINITE); } void OI::DLLMutexUnLock() { ReleaseMutex(g_hMutex); }在OI.h輸入以下程式碼。
#pragma once #include <fstream> #include <iostream> #include <vector> using namespace std; #define MAX_SIZE 100 typedef struct{ UINT index; CArray <CString, CString> Data; }INFO; class OI : public CWnd { public: OI(); virtual ~OI(); int Init(CHAR * FilebName); int GetCount(int nIndex); CHAR * GetFileData(int nIndex, int nNum); int Release(int index); private: BOOL ReadText(CHAR * FilebName, int nIndex); INFO *FindData(int index); void DLLMutexLock(); void DLLMutexUnLock(); };在MultiCharts 的PLE中建立一個名為Readfile函數,回傳數值資料。
Inputs : istrFileName(StringSimple), iastrData[x,y](StringArrayRef); var: ii(0), vIndex(0), vCount(0), vRecord(Spaces(100)); DefineDLLFunc: "C:\Code\FileInfo\Release\FileInfo.dll", int, "ReadFileText", lpstr; DefineDLLFunc: "C:\Code\FileInfo\Release\FileInfo.dll", int, "GetTextCount", int; DefineDLLFunc: "C:\Code\FileInfo\Release\FileInfo.dll", lpstr, "GetDataText", int, int; DefineDLLFunc: "C:\Code\FileInfo\Release\FileInfo.dll", int, "ClearSpace", int; vIndex = ReadFileText(istrFileName); vCount = GetTextCount(vIndex); for ii = 0 to vCount - 1 begin vRecord = GetDataText(vIndex, ii); iastrData[ii, 1] = Midstr(vRecord, 0, InStr(vRecord, ",") - 1); vRecord = Midstr(vRecord, InStr(vRecord, ",") + 1, strlen(vRecord)); iastrData[ii, 2] = Midstr(vRecord, 0, InStr(vRecord, ",") - 1); vRecord = Midstr(vRecord, InStr(vRecord, ",") + 1, strlen(vRecord)); iastrData[ii, 3] = vRecord; end; ClearSpace(vIndex); ReadFile = vCount;建立名為Amin_Test6的指標。
input:iName("TXF"); vars:ii(0); array:aData[800,3](""); once cleardebug; if currentbar = 1 then begin value1 = ReadFile("C:\Users\Amin\Desktop\TXF.csv", aData); end; for ii = 0 to value1 begin if d = JulianToDate(StringToDate(aData[ii, 1])) Then begin value2=StrToNum(aData[ii, 3]); break; end; end; plot1(value2, "TXF", iff(value2> 0, red, green));建立名為Amin_Test7的指標。
input:iName("MXF"); vars:ii(0); array:aData[800,3](""); once cleardebug; if currentbar = 1 then begin value1 = ReadFile("C:\Users\Amin\Desktop\MTX.csv", aData); end; for ii = 0 to value1 begin if d = JulianToDate(StringToDate(aData[ii, 1])) Then begin value2=StrToNum(aData[ii, 3]); break; end; end; plot1(value2, "MTX", iff(value2> 0, red, green));資料格式:
執行結果:
透過Dbgview來看看DLL內執行的狀態。