1
votes

I'm coding an Indicator which calls a function from C++ DLL. I need to pass only Close rates to DLL and return a string from DLL.

MQL4 code:

#import "PythonZones.dll"
void CalculateZones(double &data[], double quantile, int arraySize,char&[]);
#import

#property indicator_chart_window
char buffer[4096];
input color M5color = clrTurquoise;

input int WINDOW = 446;
input double PARAM_SHIFT = 0.14;

int start()
  {
    if (IsNewBar()){
         double data[];
         ArrayResize(data,WINDOW);
         Rate(data,WINDOW);
         Print(data[0]);
         CalculateZones(data,PARAM_SHIFT ,WINDOW,buffer);
         string zones = CharArrayToString(buffer);
         Print(zones);
  }
   return(0);
}

void Rate(double &out[],int len){
        for(int i=(len - 1);i>-1;i--){
         double close = iClose(NULL,0,i+1);
         out[i] = close;
      }   
 }

and C++ DLL code:

#define WIN32_LEAN_AND_MEAN  // Exclude rarely-used stuff from Windows headers
//#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <windows.h>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include "Python.h"
#include "numpy/arrayobject.h"
//----
#define MT4_EXPFUNC __declspec(dllexport)

PyObject *Amanda;
PyObject *AmandaZones;


BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
  {
//----
   switch(ul_reason_for_call)
     {
      case DLL_PROCESS_ATTACH:
          Py_Initialize();
          import_array1(-1);
          PyObject *pName;
          pName = PyString_FromString("Amanda");
                  Amanda = PyImport_Import(pName);
          Py_DECREF(pName);
                  if (Amanda != NULL){
                          AmandaZones = PyObject_GetAttrString(Amanda, "CalculateZones");
                          if (AmandaZones == NULL){
                 OutputDebugString("Failed to get desired func.");
             }
          }else{
              OutputDebugString("Failed to load Amanda.");
          }
          OutputDebugString("Attached.");
          break;
      case DLL_THREAD_ATTACH:
          break;
      case DLL_THREAD_DETACH:

          break;
      case DLL_PROCESS_DETACH:
                  Py_DECREF(Amanda);
                  Py_DECREF(AmandaZones);
          Py_Finalize();
          break;
     }
//----
   return(TRUE);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MT4_EXPFUNC void __stdcall CalculateZones(double *data, double quantile, int arraySize,char *buffer)
  {

        if (arraySize < 0) return;


        npy_intp npy_arraysize;

        npy_arraysize = arraySize;
        PyObject *q = PyFloat_FromDouble(quantile);
        PyObject *np_value = PyArray_SimpleNewFromData(1, &npy_arraysize, NPY_DOUBLE, data);
        Py_INCREF(np_value);;
        Py_INCREF(q);;
        PyObject *pArgs = PyTuple_New(2);
        PyTuple_SetItem(pArgs, 0, np_value);
        PyTuple_SetItem(pArgs, 1, q);


        PyObject *pResult = PyObject_CallObject(AmandaZones, pArgs);
   Py_DECREF(pArgs);
   Py_DECREF(np_value);
   Py_DECREF(q);

   char* res = PyString_AsString(pResult);

   Py_DECREF(pResult);


   strcpy(buffer,res);


  }

Indicator works perfectly, but when I remove the indicator from the chart and add it again, It doesn't work anymore and returns an error "Access violation read" .

And also when I use it with Strategy Tester, MetaTrader crashes without logging any error.

I think the problem is with the array referencing. I attempted to try it with using struct instead of arrays, but I couldn't compile it any way because I don't know MQL4 or C++ well.

How can I solve the problem?

2

2 Answers

1
votes

Q: How can I solve the problem?

MQL4 / DLL integration which uses string-s is a wild ride ...

Yet the first step ought be to repair the #import call-signature, where interfacing details are provided for the MQL4-compiler to use:

#import "PythonZones.dll"
void CalculateZones( double &data[],
                     double  quantile,
                     int     arraySize,
                     char   &buffer[]   // this line is a suspect for mystifying compiler
                     );
#import

If this repair of the call-signature, so as to meet the DLL-published call-interface, does not help ( which we cannot test without the copy of the DLL-Under-Test ) you need rather an expert for such dual system integration ( having a dual-domain expertise, C++ is not enough for making the MQL4-orchestrations work ).

I pray for you to have the problem fixed just with the repaired #import call-signature. A few dozens man*years of troubleshooting this dual-domain tell me the reasons for such a prayer.

0
votes

The problem was with numpy package. It should not be initialized twice when using in C++. Py_finilize() not works correctly and does not remove numpy module objects. I solve it from here https://github.com/numpy/numpy/issues/8097