Пишем библиотеку DLL для Metastock с нуля. Часть 3
В этой статье я объясню, как подключить к нашим индикаторам аргументы (часть1, часть2). За аргументы в динамической библиотеке MSX DLL программы Metastock отвечают две служебные функции: MSXNthArg и MSXNthCustomString.Аргументы MSXNthArg вызывается во время инициализации для каждого аргумента, каждой внешней функции, имеющей аргументы. BOOL __stdcall MSXNthArg (int a_iNthFunc, int a_iNthArg, MSXFuncArgDef *a_psFuncArgDef) , где• a_iNthFunc — индекс внешней функции.• a_iNthArg — индекс аргумента этой функции.• a_psFuncArgDef — Указатель на структуру данных MSXFuncArgDef, используемой для заполнения пользователем информацией об аргументе внешней функции.Функция возвращает: • MSX_SUCCESS если все правильно и• MSX_ERROR в случае ошибки.Все аргументы подразделяются на четыре типа: • MSXDataArray — массив данных• MSXNumeric — число, • MSXString — строка, • MSXCustom — custom.При использовании аргумента пользователь должен указать его тип и имя. Аргумент типа MSXCustom представляет собой определенный набор, члены которого я буду называть custom-аргументами. Если тип аргумент MSXCustom, тогда надо еще указать количество custom-аргументов. То есть при описании аргумента мы пишем в MSXNthArg функциидля не MSXCustom типа: a_psFuncArgDef→iArgType = MSXDataArray; // или MSXNumeric или MSXString strcpy (a_psFuncArgDef→szArgName, «Имя аргумента»); для MSXCustom типа: a_psFuncArgDef→iArgType = MSXCustom; a_psFuncArgDef→iNCustomStrings = 8; // количество custom-аргументов strcpy (a_psFuncArgDef→szArgName, «Имя аргумента»); MSXNthCustomString отвечает за custom-аргументы.
BOOL __stdcall MSXNthCustomString (int a_iNthFunc, int a_iNthArg, int a_iNthString, MSXFuncCustomString *a_psCustomString) , где• a_iNthFunc — индекс внешней функции.• a_iNthArg — индекс аргумента этой функции.• a_iNthString — индекс custom-аргумента.• a_psFuncArgDef — Указатель на структуру данных MSXFuncCustomString, используемой для заполнения пользователем информацией о custom-аргументах внешней функции.Функция возвращает те же значения, что и предыдущая.Custom-аргумент представляет из себя пару: строка-идентификатор.Функция MSXNthCustomString устанавливает соответствие строки и числового идентификатора (ID). Во внешней функции custom-аргументы вызываются по ID. Строки, используемые для определения custom-аргументов, должны состоять только из алфавитно-цифровых символов. Пробелы и специальные символы не допускаются. Custom-аргументы не чувствительны к регистру.Количество аргументов каждого типа не может быть больше десяти. Не забывайте, что функции и аргументы нумеруются с 0.Исключения Чтобы избежать аварийного завершения работы программы Metastock, необходимо добавить в каждую внешнюю функцию обработку исключительных ситуаций. Исключения могут быть следующими: • Неверное количество аргументов, • Входящий (используемый для расчетов) массив не соответствует стандартам Metastock (см. часть2), • Аргумент не получен, • Аргумент не верен, • Выходящий (результат расчетов) массив не соответствует стандартам Metastock.Каждую из этих ситуаций мы будем обрабатывать в следующем общем виде: if (проверка на исключение, если да, тогда) { strncpy (a_psResult→szExtendedError, «Error: Описание ошибки», sizeof (a_psResult→szExtendedError)-1); // возвращаем пустой массив a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } Для обработки исключений я добавил несколько дополнительных функций.Пример В следующем ниже примере я создал четыре индикатора, которые занимаются сложением. У каждого индикатора два аргумента — один массив, а второй один из четырех типов. Как и обещал в первой части, расписал последовательность действий по шагам.Создаем в Visual Studio проект с тремя файлами: MSXStruc.h, Add.cpp, Add.def. Наша библиотека будет иметь имя Add.dll.Шаг 1 — Заголовки и экспорт
Код
#include
#define DLL_EXPORT extern «C» __declspec (dllexport) Шаг 2 — Изменяемые параметры
Здесь прописываются все изменяемые пользователем параметры функций и аргументов.
Код // Информация об автовстве const char *szMayCopyright =«VS 2010 C++ MSX DLL, Copyright © andrzwet, 2014»;
const int MyFuncs = 4; // количество моих функций (fan1, fan2, fan3, fan4) // Имена функций const char *szNfan1 = «fan1»; const char *szNfan2 = «fan2»; const char *szNfan3 = «fan3»; const char *szNfan4 = «fan4»; // Дескрипторы функций const char *szDfan1 = «Add- (DA1 + DA2)»; const char *szDfan2 = «Add- (DA1 + Number)»; const char *szDfan3 = «Add- (DA1 + DAStr)»; const char *szDfan4 = «Add- (DA1 + DACust)»;
const int fan1Args = 2; // 2 аргумента у функции fan1 — 2 массива // Имена аргументов функции fan1 const char *szNAfan1Arg1 = «DA1»; const char *szNAfan1Arg2 = «DA2»;
const int fan2Args = 2; // 2 аргумента у функции fan2 — 1 массив ,1 число // Имена аргументов функции fan2 const char *szNAfan2Arg1 = «DA1»; const char *szNNfan2Arg2 = «Numeric»;
const int fan3Args = 2; // 2 аргумента у функции fan3 — 1 массив ,1 строка // Имена аргументов функции fan3 const char *szNAfan3Arg1 = «DA1»; const char *szNSfan3Arg2 = «DAStr»;
const int fan4Args = 2; // 2 аргумента у функции fan4 — 1 массив ,1 custom // Имена аргументов функции fan4 const char *szNAfan4Arg1 = «DA1»; const char *szNCfan4Arg2 = «DACust»;
const int fan4ArgsCust = 8; // один из двух аргументов функции fan4 является Custom, // который в свою очередь состоит из 8 аргументов (строк) // Имена custom-аргументов и идентификаторы char *szCust1 = «Open»; char *szCust5 = «O»; int id1 = 0; char *szCust2 = «High»; char *szCust6 = «H»; int id2 = 1; char *szCust3 = «Low»; char *szCust7 = «L»; int id3 = 2; char *szCust4 = «Close»; char *szCust8 = «C»; int id4 = 3; Шаг 3 — Функции инициализации
Здесь описываются четыре функции: MSXInfo, MSXNthFunction, MSXNthArg, MSXNthCustomString.
Код // ---------------------------------------------------- DLL_EXPORT BOOL __stdcall MSXInfo (MSXDLLDef *a_psDLLDef) { strncpy (a_psDLLDef→szCopyright, szMayCopyright, sizeof (a_psDLLDef→szCopyright)-1); a_psDLLDef→iNFuncs = MyFuncs; // число функций. a_psDLLDef→iVersion = MSX_VERSION; return MSX_SUCCESS; } // ---------------------------------------------------- DLL_EXPORT BOOL __stdcall MSXNthFunction (int a_iNthFunc, MSXFuncDef *a_psFuncDef) { BOOL l_bRtrn = MSX_SUCCESS;
switch (a_iNthFunc) { case 0: // --- fan1 --- strcpy (a_psFuncDef→szFunctionName, szNfan1); // имя strcpy (a_psFuncDef→szFunctionDescription, szDfan1); // дескриптор a_psFuncDef→iNArguments = fan1Args; // число аргументов break; case 1: // --- fan2 --- strcpy (a_psFuncDef→szFunctionName, szNfan2); // имя strcpy (a_psFuncDef→szFunctionDescription, szDfan2); // дескриптор a_psFuncDef→iNArguments = fan2Args; // число аргументов break; case 2: // --- fan3 --- strcpy (a_psFuncDef→szFunctionName, szNfan3); // имя strcpy (a_psFuncDef→szFunctionDescription, szDfan3); // дескриптор a_psFuncDef→iNArguments = fan3Args; // число аргументов break; case 3: // --- fan4 --- strcpy (a_psFuncDef→szFunctionName, szNfan4); // имя strcpy (a_psFuncDef→szFunctionDescription, szDfan4); // дескриптор a_psFuncDef→iNArguments = fan4Args; // число аргументов. break; default: l_bRtrn = MSX_ERROR; break; } return l_bRtrn; } // ---------------------------------------------------- DLL_EXPORT BOOL __stdcall MSXNthArg (int a_iNthFunc, int a_iNthArg, MSXFuncArgDef *a_psFuncArgDef) { BOOL l_bRtrn = MSX_SUCCESS; // задаем количество Custom-аргументов = 0 (инициализация) a_psFuncArgDef→iNCustomStrings = 0;
switch (a_iNthFunc) { case 0: // --- аргументы fan1 --- switch (a_iNthArg) { case 0: a_psFuncArgDef→iArgType = MSXDataArray; // тип strcpy (a_psFuncArgDef→szArgName, szNAfan1Arg1); // имя break; case 1: a_psFuncArgDef→iArgType = MSXDataArray; // тип strcpy (a_psFuncArgDef→szArgName, szNAfan1Arg2); // имя break; default: l_bRtrn = MSX_ERROR; break; } break; case 1: // --- аргументы fan2 --- switch (a_iNthArg) { case 0: a_psFuncArgDef→iArgType = MSXDataArray; // тип strcpy (a_psFuncArgDef→szArgName, szNAfan2Arg1); // имя break; case 1: a_psFuncArgDef→iArgType = MSXNumeric; // тип strcpy (a_psFuncArgDef→szArgName, szNNfan2Arg2); // имя break; default: l_bRtrn = MSX_ERROR; break; } break; case 2: // --- аргументы fan3 --- switch (a_iNthArg) { case 0: a_psFuncArgDef→iArgType = MSXDataArray; // тип strcpy (a_psFuncArgDef→szArgName, szNAfan3Arg1); // имя break; case 1: a_psFuncArgDef→iArgType = MSXString; // тип strcpy (a_psFuncArgDef→szArgName, szNSfan3Arg2); // имя break; default: l_bRtrn = MSX_ERROR; break; } break; case 3: // --- аргументы fan4 --- switch (a_iNthArg) { case 0: a_psFuncArgDef→iArgType = MSXDataArray; // тип strcpy (a_psFuncArgDef→szArgName, szNAfan4Arg1); // имя break; case 1: a_psFuncArgDef→iArgType = MSXCustom; // тип a_psFuncArgDef→iNCustomStrings = fan4ArgsCust; // кол-во custom-аргументов strcpy (a_psFuncArgDef→szArgName, szNCfan4Arg2); // имя break; default: l_bRtrn = MSX_ERROR; break; } break; default: l_bRtrn = MSX_ERROR; break; } return l_bRtrn; } // ---------------------------------------------------- DLL_EXPORT BOOL __stdcall MSXNthCustomString (int a_iNthFunc, int a_iNthArg, int a_iNthString, MSXFuncCustomString *a_psCustomString) { BOOL l_bRtrn = MSX_SUCCESS; // иницилизация a_psCustomString→szString[0] = '\0'; a_psCustomString→iID = -1; // создаем структуру typedef struct { char *szString; int iID; } LocalStringElement; // инициализация массива с пользовательскими строками и идентификаторами LocalStringElement l_sTheStrings[] = { {szCust1, id1}, {szCust5, id1}, {szCust2, id2}, {szCust6, id2}, {szCust3, id3}, {szCust7, id3}, {szCust4, id4}, {szCust8, id4} };
switch (a_iNthFunc) { case 3: // Custom-аргумент у нас используется в четвертой функции (fan4) switch (a_iNthArg) { case 1: // Custom-аргумент у нас является вторым аргументом fan4 // проверка количества Custom-аргументов. if (a_iNthString >= 0 && a_iNthString < fan4ArgsCust) { // устанавливаем соответствие строк и ID strncpy (a_psCustomString->szString, l_sTheStrings[a_iNthString].szString, sizeof (a_psCustomString→szString)-1); a_psCustomString→iID = l_sTheStrings[a_iNthString].iID; } break; default: l_bRtrn = MSX_ERROR; break; } break; default: l_bRtrn = MSX_ERROR; break; } return l_bRtrn; } Шаг 4 — Дополнительные функии
Здесь пропишем функции нужные для обработки исключений.
Код /* ForceFloatRange — эта локальная функция проверяет не выходят ли за пределы допустимого передаваемые нашими функциями float значения. • FLT_MAX — Максимальное положительное значение, которое может быть представлено типом float (3.402823466e+38F), • FLT_MIN — Минимальное положительное значение, которое может быть представлено типом float (1.175494351e-38F). */ #define MSXMax (a, b) (((a) > (b)) ? (a) : (b)) #define MSXMin (a, b) (((a) < (b)) ? (a) : (b))
double ForceFloatRange (double a_lfDbl) { if (a_lfDbl > 0.0) { a_lfDbl = MSXMin (a_lfDbl, double (FLT_MAX)); a_lfDbl = MSXMax (a_lfDbl, double (FLT_MIN)); } else { if (a_lfDbl < 0.0) { a_lfDbl = MSXMax (a_lfDbl, double(-FLT_MAX)); a_lfDbl = MSXMin (a_lfDbl, double(-FLT_MIN)); } }
return a_lfDbl; } /* MSXArray — используется для проверки аргументов входящего массива данных и исходящего массива результатов на соответствие стандартам MetaStock. Функция вызывается с единственным указателем на MSXDataRec в качестве первого аргумента. Вторым аргументом может быть входной массив, массив результата или одним из суб членов массива структуры MSXDataRec. Эта функция используется, чтобы убедиться, что действительно индексы массива аргументов не выходят за рамки цен закрытия в массиве Metastock BasicData, который доступен для всех функций.А также, что нижняя и верхняя границы всех индексов выше нуля, что соответствует требованиям MetaStock’а. */ BOOL MSXArray (const MSXDataRec *BasicData, const MSXDataInfoRec *ArgData) { if (ArgData→iFirstValid < 0) return FALSE; if (ArgData->iLastValid < 0) return FALSE; if (ArgData->iLastValid < ArgData->iFirstValid) return FALSE; if (ArgData→iFirstValid < BasicData->sClose.iFirstValid) return FALSE; if (ArgData→iLastValid > BasicData→sClose.iLastValid) return FALSE;
return TRUE; } Шаг 5 — Внешние функции
fan1 суммирует два массива данных (ExtFml («Add.fan1», DA1, DA2)).Функция имеет два аргемента, оба — массив данных.
fan1 DLL_EXPORT BOOL __stdcall fan1 (const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult) { int i = 0; //задаем начальный и конечный индекс (максимальный действительный диапазон) int iMinRecords = a_psBasic→sClose.iFirstValid; int iMaxRecords = a_psBasic→sClose.iLastValid; //инициализируем наши аргументы const MSXDataInfoRec *l_psInput1 = a_psArrayArgs→psDataInfoRecs[0]; const MSXDataInfoRec *l_psInput2 = a_psArrayArgs→psDataInfoRecs[1]; int iLvi; int iFvi; // убеждаемся, что количество аргементов правильное if (a_psArrayArgs→iNRecs == 2 && a_psNumericArgs→iNRecs == 0 && a_psStringArgs→iNRecs == 0 && a_psCustomArgs→iNRecs == 0) { if (!(MSXArray (a_psBasic, l_psInput1) && MSXArray (a_psBasic, l_psInput2))) { // если входящие массивы повреждены, возвращаем пустой массив strncpy (a_psResult→szExtendedError, «Error: Corrupted Input Array», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } // убеждаемся что аргументы действительно переданы if (l_psInput1 && l_psInput2) { // вычисление значений функии fan1 for (i=iMinRecords; i<=iMaxRecords; i++) a_psResult->psResultArray→pfValue[i] = float (ForceFloatRange (l_psInput1→pfValue[i] + l_psInput2→pfValue[i])); //задаем начальный и конечный индекс для исходящего массива iFvi = MSXMax (l_psInput1→iFirstValid, l_psInput2→iFirstValid); iLvi = MSXMin (l_psInput1→iLastValid, l_psInput2→iLastValid); a_psResult→psResultArray→iFirstValid = iFvi; a_psResult→psResultArray→iLastValid = iLvi; } else { // аргументы отсутствуют! strncpy (a_psResult→szExtendedError, «Error: Data array argument missing», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } } else { // неверное количество аргументов! strncpy (a_psResult→szExtendedError, «Error: Wrong number of arguments», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } if (! MSXArray (a_psBasic, a_psResult→psResultArray)) { // если исходящий массив поврежден, возвращаем пустой массив strncpy (a_psResult→szExtendedError, «Error: Corrupted Result Array.», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } return MSX_SUCCESS; } fan2 суммирует массив данных и число (ExtFml («Add.fan2», DA1, Numeric)).Функция имеет два аргемента, один — массив данных, другой — число.
fan2 DLL_EXPORT BOOL __stdcall fan2 (const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult) { int i = 0; //инициализируем наши аргументы const MSXDataInfoRec *l_psInput = a_psArrayArgs→psDataInfoRecs[0]; //массив float l_fNumber = a_psNumericArgs→fNumerics[0]; //число // убеждаемся, что количество аргементов правильное if (a_psArrayArgs→iNRecs == 1 && a_psNumericArgs→iNRecs == 1 && a_psStringArgs→iNRecs == 0 && a_psCustomArgs→iNRecs == 0) { if (! MSXArray (a_psBasic, l_psInput)) { // недействительный диапазон входящего массива strncpy (a_psResult→szExtendedError, «Error: Corrupted Input Array», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } // если входящий массив существует if (l_psInput) { // расчет значений индикатора fan2 for (i=l_psInput→iFirstValid; i<=l_psInput->iLastValid; i++) a_psResult→psResultArray→pfValue[i] = float (ForceFloatRange (l_psInput→pfValue[i] + l_fNumber)); //задаем начальный и конечный индекс для исходящего массива a_psResult→psResultArray→iFirstValid = l_psInput→iFirstValid; a_psResult→psResultArray→iLastValid = l_psInput→iLastValid; } else { // аргумент массива отсутствует! strncpy (a_psResult→szExtendedError, «Error: Array argument missing», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } } else { // неверное количество аргументов! strncpy (a_psResult→szExtendedError, «Error: Wrong number of arguments», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; }
if (! MSXArray (a_psBasic, a_psResult→psResultArray)) { // если исходящий массив поврежден, возвращаем пустой массив strncpy (a_psResult→szExtendedError, «Error: Corrupted Result Array», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } return MSX_SUCCESS; } fan3 суммирует массив данных и массив данных, определяемый строковым аргементом (ExtFml («Add.fan3», DA1, DAStr)).Функция имеет два аргемента, один — массив данных, другой — строка.
fan3 DLL_EXPORT BOOL __stdcall fan3 (const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult) { int i = 0; int iMinRecords = a_psBasic→sClose.iFirstValid; int iMaxRecords = a_psBasic→sClose.iLastValid; const MSXDataInfoRec *l_psInput1; l_psInput1 = a_psArrayArgs→psDataInfoRecs[0]; // массив const char *l_pszInput2 =a_psStringArgs→pszStrings[0]; // строка const MSXDataInfoRec *l_psData; int iLvi; int iFvi; // Убеждаемся , что количество аргументов правильное if (a_psArrayArgs→iNRecs == 1 && a_psNumericArgs→iNRecs == 0 && a_psStringArgs→iNRecs == 1 && a_psCustomArgs→iNRecs == 0) { if (! MSXArray (a_psBasic, l_psInput1)) { // недействительный диапазон входящего массива strncpy (a_psResult→szExtendedError, «Error: Corrupted Input Array», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } // Убеждаемся что аргумент массива передан if (l_psInput1) { // Убеждаемся что строковый аргумент передан if (l_pszInput2) { while (*l_pszInput2) { // получаем адреса массивов switch (*l_pszInput2++) { case 'O': case 'o': l_psData = &a_psBasic→sOpen; break; case 'H': case 'h': l_psData = &a_psBasic→sHigh; break; case 'L': case 'l': l_psData = &a_psBasic→sLow; break; case 'C': case 'c': l_psData = &a_psBasic→sClose; break; default: // не верный строковый аргумент! strncpy (a_psResult→szExtendedError, «Error: Wrong String argument», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; break; } // расчет значений индикатора fan3 for (i=iMinRecords; i<=iMaxRecords; i++) a_psResult->psResultArray→pfValue[i] = float (ForceFloatRange ( l_psData→pfValue[i] + l_psInput1→pfValue[i])); iFvi = MSXMax (l_psInput1→iFirstValid, l_psData→iFirstValid); iLvi = MSXMin (l_psInput1→iLastValid, l_psData→iLastValid); a_psResult→psResultArray→iFirstValid = iFvi; a_psResult→psResultArray→iLastValid = iLvi; }
} else { // нет строкового аргумента! strncpy (a_psResult→szExtendedError, «Error: String argument missing», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } } else { // аргумент массива отсутствует! strncpy (a_psResult→szExtendedError, «Error: Array argument missing», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } } else { // неверное количество аргументов! strncpy (a_psResult→szExtendedError, «Error: Wrong number of arguments», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } if (! MSXArray (a_psBasic, a_psResult→psResultArray)) { strncpy (a_psResult→szExtendedError, «Error: Corrupted Result Array», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } return MSX_SUCCESS; } fan4 суммирует массив данных и массив данных определяемый custom-аргементом (ExtFml («Add.fan4», DA1, DACust)).Функция имеет два аргемента, один — массив данных, другой — custom-аргемент.
fan4 DLL_EXPORT BOOL __stdcall fan4 (const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult)
{ int i = 0; int iMinRecords = a_psBasic→sClose.iFirstValid; int iMaxRecords = a_psBasic→sClose.iLastValid; const MSXDataInfoRec *l_psInput1; l_psInput1 = a_psArrayArgs→psDataInfoRecs[0]; // массив int l_psInput2 = a_psCustomArgs→iCustomIDs[0]; // custom int iLvi; int iFvi; // убеждаемся, что количество аргементов правильное if (a_psArrayArgs→iNRecs == 1 && a_psNumericArgs→iNRecs == 0 && a_psStringArgs→iNRecs == 0 && a_psCustomArgs→iNRecs == 1) { if (!(MSXArray (a_psBasic, l_psInput1))) { strncpy (a_psResult→szExtendedError, «Error: Corrupted Input Array», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } // массив существует if (l_psInput1) { switch (l_psInput2) { case 0: // Open { for (i=iMinRecords; i<=iMaxRecords; i++) a_psResult->psResultArray→pfValue[i] = float (ForceFloatRange (a_psBasic→sOpen.pfValue[ i ] + l_psInput1→pfValue[i])); iLvi = MSXMin (l_psInput1→iLastValid, iMaxRecords); iFvi = MSXMax (l_psInput1→iFirstValid, iMinRecords); a_psResult→psResultArray→iFirstValid = iFvi; a_psResult→psResultArray→iLastValid = iLvi; } break; case 1: // High { for (i=iMinRecords; i<=iMaxRecords; i++) a_psResult->psResultArray→pfValue[i] = float (ForceFloatRange (a_psBasic→sHigh.pfValue[ i ] + l_psInput1→pfValue[i])); iLvi = MSXMin (l_psInput1→iLastValid, iMaxRecords); iFvi = MSXMax (l_psInput1→iFirstValid, iMinRecords); a_psResult→psResultArray→iFirstValid = iFvi; a_psResult→psResultArray→iLastValid = iLvi; } break; case 2: // Low { for (i=iMinRecords; i<=iMaxRecords; i++) a_psResult->psResultArray→pfValue[i] = float (ForceFloatRange (a_psBasic→sLow.pfValue[ i ] + l_psInput1→pfValue[i])); iLvi = MSXMin (l_psInput1→iLastValid, iMaxRecords); iFvi = MSXMax (l_psInput1→iFirstValid, iMinRecords); a_psResult→psResultArray→iFirstValid = iFvi; a_psResult→psResultArray→iLastValid = iLvi; } break; case 3: // Close { for (i=iMinRecords; i<=iMaxRecords; i++) a_psResult->psResultArray→pfValue[i] = float (ForceFloatRange (a_psBasic→sClose.pfValue[ i ] + l_psInput1→pfValue[i])); iLvi = MSXMin (l_psInput1→iLastValid, iMaxRecords); iFvi = MSXMax (l_psInput1→iFirstValid, iMinRecords); a_psResult→psResultArray→iFirstValid = iFvi; a_psResult→psResultArray→iLastValid = iLvi; } break; default: { // не верный custom-аргумент! strncpy (a_psResult→szExtendedError, «Error: Invalid Custom argument», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } break; } } else { // аргумент массива отсутствует! strncpy (a_psResult→szExtendedError, «Error: Array argument missing», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; }
} else { // неверное количество аргументов! strncpy (a_psResult→szExtendedError, «Error: Wrong number of arguments», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } if (! MSXArray (a_psBasic, a_psResult→psResultArray)) { strncpy (a_psResult→szExtendedError, «Error: Corrupted Result Array», sizeof (a_psResult→szExtendedError)-1); a_psResult→psResultArray→iFirstValid = 0; a_psResult→psResultArray→iLastValid = -1; return MSX_ERROR; } return MSX_SUCCESS; } Шаг 6 — DEF файл
Ниже приведен пример файла DEF для моего проекта.Файл DEF отдельный текстовый файл, и должен иметь то же имя, что и ваш проект.
Add.def LIBRARY Add EXPORTS MSXInfo MSXNthFunction MSXNthArg MSXNthCustomString fan1 fan2 fan3 fan4 Надеюсь, что в моих статьях мне удалось доступно изложить принципы построения динамической библиотеки MSX DLL для программы Metastock.