📜 ⬆️ ⬇️

Writing a DLL for Metastock from scratch. Part 3

DLL  Metastock

In this article I will explain how to connect arguments to our indicators ( part 1 , part 2 ). Two service functions are responsible for the arguments in the MSX DLL dynamic library of the Metastock program: MSXNthArg and MSXNthCustomString.

Arguments


MSXNthArg is called during initialization for each argument, each external function that has arguments.

BOOL __stdcall MSXNthArg (int a_iNthFunc, int a_iNthArg, MSXFuncArgDef *a_psFuncArgDef) 

where
• a_iNthFunc - index of the external function.
• a_iNthArg - index of the argument of this function.
• a_psFuncArgDef - Pointer to the MSXFuncArgDef data structure used to fill the user with information about the argument of the external function.
The function returns:
• MSX_SUCCESS if everything is correct and
• MSX_ERROR in case of an error.
All arguments are divided into four types:
• MSXDataArray - data array
• MSXNumeric - number,
• MSXString - string,
• MSXCustom - custom.
When using an argument, the user must specify its type and name. An argument of type MSXCustom is a specific set, the members of which I will call custom-arguments. If the argument type is MSXCustom, then you must also specify the number of custom arguments. That is, when describing an argument, we write functions in the MSXNthArg
for non-MSXCustom type:
')
 a_psFuncArgDef->iArgType = MSXDataArray; //  MSXNumeric  MSXString strcpy (a_psFuncArgDef->szArgName, " "); 

for MSXCustom type:

 a_psFuncArgDef->iArgType = MSXCustom; a_psFuncArgDef->iNCustomStrings = 8; //  custom- strcpy (a_psFuncArgDef->szArgName, " "); 


MSXNthCustomString is responsible for custom arguments.

 BOOL __stdcall MSXNthCustomString (int a_iNthFunc, int a_iNthArg, int a_iNthString, MSXFuncCustomString *a_psCustomString) 

where
• a_iNthFunc - index of the external function.
• a_iNthArg - index of the argument of this function.
• a_iNthString - index of the custom argument.
• a_psFuncArgDef - Pointer to the MSXFuncCustomString data structure used to fill the user with information about the custom arguments of the external function.
The function returns the same values ​​as the previous one.
A custom argument is a pair: a string-identifier. The MSXNthCustomString function matches a string with a numeric identifier (ID). In the external function, custom arguments are called by ID. Strings used to define custom arguments must consist of alphanumeric characters only. Spaces and special characters are not allowed. Custom arguments are not case sensitive.
The number of arguments of each type cannot be more than ten. Do not forget that functions and arguments are numbered from 0.

Exceptions


To avoid the crash of the Metastock program, you must add exception handling to each external function. Exceptions may be as follows:
• Incorrect number of arguments.
• Incoming (used for calculations) array does not meet Metastock standards (see part 2),
• Argument not received,
• The argument is not correct,
• The outgoing (calculation result) array does not meet Metastock standards.
Each of these situations we will handle in the following general form:

 if (  ,  , ) { strncpy (a_psResult->szExtendedError, "Error:  ", sizeof(a_psResult->szExtendedError)-1); //    a_psResult->psResultArray->iFirstValid = 0; a_psResult->psResultArray->iLastValid = -1; return MSX_ERROR; } 

For exception handling, I added a few extra features.

Example


In the following example, I created four indicators that deal with addition. Each indicator has two arguments - one array, and the second one of four types. As promised in the first part, I painted a sequence of actions in steps.
Create a project in Visual Studio with three files: MSXStruc.h, Add.cpp, Add.def. Our library will be named Add.dll.

Step 1 - Headers and Export
Code

 #include <string.h> #include <stdlib.h> #include <math.h> #include <float.h> #include <tchar.h> #include "MSXStruc.h" #define DLL_EXPORT extern "C" __declspec(dllexport) 


Step 2 - Variable Parameters

All the parameters of functions and arguments that are changed by the user are written here.
Code

 //    const char *szMayCopyright ="VS 2010 C++ MSX DLL, Copyright (c) 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 CustomType //    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; 


Step 3 - Initialization Functions

Four functions are described here: MSXInfo, MSXNthFunction, MSXNthArg, MSXNthCustomString.
Code

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


Step 4 - Additional Functions

Here we write the functions needed to handle exceptions.
Code

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


Step 5 - External Functions

Fan1 summarizes two data arrays ( ExtFml ("Add.fan1", DA1, DA2) ).
The function has two arguments, both are data arrays.
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 summarizes the data array and the number ( ExtFml ("Add.fan2", DA1, Numeric) ).
The function has two arguments, one is the data array, the other is the number.
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 summarizes the data array and data array, defined by the string argument
( ExtFml ("Add.fan3", DA1, DAStr) ).
The function has two arguments, one is the data array, the other is a string.
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 summarizes the data array and data array defined by the custom argument
( ExtFml ("Add.fan4", DA1, DACust) ).
The function has two arguments, one is the data array, the other is an argument of the MSXCustom type.
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; } 


Step 6 - DEF File

Below is an example DEF file for my project.
The DEF file is a separate text file, and must have the same name as your project.
Add.def

 LIBRARY Add EXPORTS MSXInfo MSXNthFunction MSXNthArg MSXNthCustomString fan1 fan2 fan3 fan4 


I hope that in my articles I was able to explain the principles for constructing the MSX DLL dynamic library for the Metastock program.

Source: https://habr.com/ru/post/220111/


All Articles