在 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內執行的狀態。




