Help required to call a C++ DLL function that expects a double pointer as function argument in MQL4

Back to topics list To post a new topic, please log in or register
avatar
2
vikas_trl 2016.09.14 10:13
 

Hello All,

 

I have to call a function from a third party software DLL in MQL4. The DLL function definition is like this:

int func_1(double** X, int rows, int cols, double* A, double *B)

 

When I try to import the function using above definition, I am getting the error 'pointer cannot be used' 

Can someone please help me how to import this function from DLL and call this function from MQL4? 

 

Thanks in advance. 

avatar
12983
WHRoeder 2016.09.14 15:43 #
 
  1. There are no pointers in mql4. Perhaps you should read the manual. GetPointer - Common Functions - MQL4 Reference
    Only class objects have pointers. Instances of structures and simple-type variables can't have pointers.
  2. Pass the double as a reference, or pass an array (size of one) to the function by reference.
  3. You can't have a double**.  You're going to have to write a shim DLL for that.
avatar
2
vikas_trl 2016.09.14 21:12 #
 
WHRoeder:
  1. There are no pointers in mql4. Perhaps you should read the manual. GetPointer - Common Functions - MQL4 Reference
    Only class objects have pointers. Instances of structures and simple-type variables can't have pointers.
  2. Pass the double as a reference, or pass an array (size of one) to the function by reference.
  3. You can't have a double**.  You're going to have to write a shim DLL for that.
Thanks WHRoeder for your inputs.
avatar
1316
jjc 2016.09.16 18:37 #
 
You can't have a double**.  You're going to have to write a shim DLL for that.

I really rather doubt that there's a practical application of this, but it is possible to do from MQL4 without an intermediate DLL.

Taking the following DLL function as an example of what needs to be called with a double** parameter:

extern "C" double __stdcall Test(double ** x)
{
        return (**x) + 1; // i.e. get the value of the ultimate double value, and return its value plus 1
}

It's then possible to call that function from MQL4 as follows:

#import "Test.dll"
   double Test(int&x);
#import

#import "kernel32.dll"
   int MulDiv(double&,int,int);
#import

void OnStart()
{
   double v = 1234;
   int addr_v = MulDiv(v,1,1); // Get the address of the v variable
   double vplus1 = Test(addr_v); // Pass the address to the DLL by reference, i.e. a pointer to a pointer
   Print("vplus1: " , vplus1);
}

The trick here is an old one from the days of things like VB6: in a language which allows API calls but doesn't provide pointers (or pointers-to-pointers), you use the Win32 MulDiv() function as the way of getting the address of a variable. But you need to be very sure about the internal behaviour of the compiler/interpreter, because you're relying on it not shuffling its managed memory and variables about.

This all obviously also depends on why the DLL function needs and wants a double**. If it's expecting to be able to do pointer arithmetic, and access memory other than the original double value which you're passing in, then this isn't going to be useful. The most likely reason for wanting a double** parameter is that it's meant to be the address of an array containing pointers to doubles, i.e. double** = double*[], in which case I don't think there's a reliable way of doing it in MQL4 - anything involving arrays causes memory to shuffle around and the addresses collected via MulDiv to be unreliable.

avatar
1109
Ovo 2016.09.16 20:49 #
 
jjc:

I really rather doubt that there's a practical application of this, but it is possible to do from MQL4 without an intermediate DLL.

Taking the following DLL function as an example of what needs to be called with a double** parameter:

It's then possible to call that function from MQL4 as follows:

The trick here is an old one from the days of things like VB6: in a language which allows API calls but doesn't provide pointers (or pointers-to-pointers), you use the Win32 MulDiv() function as the way of getting the address of a variable. But you need to be very sure about the internal behaviour of the compiler/interpreter, because you're relying on it not shuffling its managed memory and variables about.

This all obviously also depends on why the DLL function needs and wants a double**. If it's expecting to be able to do pointer arithmetic, and access memory other than the original double value which you're passing in, then this isn't going to be useful. The most likely reason for wanting a double** parameter is that it's meant to be the address of an array containing pointers to doubles, i.e. double** = double*[], in which case I don't think there's a reliable way of doing it in MQL4 - anything involving arrays causes memory to shuffle around and the addresses collected via MulDiv to be unreliable.

I have no knowledge about c, but the wininet's HttpOpenRequest uses such a construction. So I believe it is quite common construction for passing a nul-terminated array of arrays, in the above case I would bet the OHLCV arrays are passed. If the OP knows the structure of the parameter, he can obtain the particular array pointers by some winapi call  (like you mentioned, or I use memcpy), and fill an int array with those pointers followed by zero.

 

avatar
1316
jjc 2016.09.16 21:15 #
 
Ovo:

I have no knowledge about c, but the wininet's HttpOpenRequest uses such a construction.  

This is obviously only a guess, but I'd assume that you are referring to lplpszAcceptTypes, and passing a two-item array: an entry such as text/*, and a terminating NULL. If so, that's infinitely easier and more reliable to handle in MQL4 than the probable use of a double* array. (In fact, an array of double* sounds so strange that I wonder if it's required for an area which I'm completely unfamiliar with such as GPU processing and optimisation.)

avatar
1316
jjc 2016.09.16 21:41 #
 
Ovo:

or I use memcpy

Now you mention it, I suppose it's possible to do something hideous such as the following.

Let's assume that the DLL wants a double** in order to do something very broadly like the following:

// Expect the address of an array containing two pointers to doubles, and return the total of the double values
extern "C" double __stdcall Test(double **x)
{
        return (*x[0] + *x[1]);
}

If so, we can assemble an array of the double values in MQL4; copy them into a contiguous block of memory; and then create a further block of memory which contains pointers to the address of each double.

Works, but not nice. I feel as though there must be a better way of doing it (not just the copy of the double array, which is deliberately not optimised), but it's a Friday evening...

#property strict

#import "Test.dll"
   // Example DLL function above...
   double Test(uint);
#import

#import "kernel32.dll"
   uint LocalAlloc(int,int);
   void RtlMoveMemory(int, double&, int);
   void RtlMoveMemory(int, uint&, int);
   int LocalFree(uint);
#import


void OnStart()
{
   // The array of double values which we want to turn into double*
   double example_array[] = {1,2};
   
   // Allocate a block of memory to hold each of the doubles, because
   // we don't trust MQL4's own array to remain static in memory while 
   // this code executes. There are better ways of doing this, but 
   // this is designed to be clear about the underlying mechanics...
   uint dmem = LocalAlloc(0x40 /* LPTR */, ArraySize(example_array) * sizeof(double));
   for (int i = 0; i < ArraySize(example_array); i++) {
      double value_to_copy = example_array[i];
      RtlMoveMemory(dmem + (i * sizeof(double)), value_to_copy, sizeof(double));
   }
   
   // We now create another block of memory which contains a pointer to 
   // each of the copied double values
   uint pmem = LocalAlloc(0x40, ArraySize(example_array) * sizeof(uint));
   for (int i = 0; i < ArraySize(example_array); i++) {
      uint value_to_copy = dmem + (i * sizeof(double));
      RtlMoveMemory(pmem + (i * sizeof(uint)), value_to_copy, sizeof(uint));
   }

   // Call the DLL function, giving it the address of the array of pointers
   Print("should be 3... " , Test(pmem));

   // Free the interim blocks of memory
   LocalFree(dmem);
   LocalFree(pmem);
}

 

avatar
1109
Ovo 2016.09.17 07:54 #
 
jjc:

Now you mention it, I suppose it's possible to do something hideous such as the following.

Let's assume that the DLL wants a double** in order to do something very broadly like the following:

If so, we can assemble an array of the double values in MQL4; copy them into a contiguous block of memory; and then create a further block of memory which contains pointers to the address of each double.

Works, but not nice. I feel as though there must be a better way of doing it (not just the copy of the double array, which is deliberately not optimised), but it's a Friday evening...

 

As I said, I cannot contribute to the c/c++ part, I am a Java guy and I don't have a bright idea about c.

But what I meant was simply passing an int[] to such a ** DLL function, as MQL4 can properly translate the array (parameter) to a pointer. The int[] must be filled by real pointers to the double arrays, obtained from a dummy call of memcpy.

Back to topics list  

To add comments, please log in or register