Reputation: 109
First, I am new to C++ (nearly a week into it), so forgive me if this is obvious. Also, I have hunted through many posts with similar issues. Either my understanding is just not developed enough, or none had relevant info to help me understand this issue.
In Metatrader 4, I am trying to figure out how to pass a structure variable to a dll, and modify variables stored in said structure. So far, I have had great success, even when dealing with structure arrays. Then I encountered an issue.
I have narrowed the problem down to the use of strings. If you will, please have a look at the following code, which I have used to focus on solving this problem, and help me understand why I keep getting this 'Access violation write to 0x00000000' error whenever I try and run the script in mt4.
The mql4 code:
struct Naming
{
string word;
} name;
#import "SampleDLLtest.dll"
bool NameTest(Naming &name);
#import
int init() { return(0); }
int start()
{
Print("original name: ", name.word);
if( NameTest( name ) )
{
Print("new name: ", name.word);
}
//---
return(0);
}
This is the relevant dll code:
#define WIN32_LEAN_AND_MEAN
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
//---
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
//---
return(TRUE);
}
struct Naming
{
std::string n_name;
};
bool __stdcall NameTest(Naming *name)
{
name->n_name = "Captain Success";
return true;
}
Upvotes: 3
Views: 2755
Reputation: 109
I know this is a bit odd, but I am answering my own question because I figured out what was going on....mostly at least.
So, here is the deal. Technically speaking, you can pass a structure that contains a string. What you cannot do is edit the string. There is no automatic conversion of a string to a char[] in the structure. So, when the dll attempts to edit the string, it throws up the access violation because a string is not really a string in C++, but is a char array disguised as a string.
That said, I did resolve how to pass a structure containing a string, and modify the value in the dll. Here is how I did it.
---Starting with the mql4 code--- First, I declared the struct with a char[] instead of a string.
struct Naming
{
char word[65];
} name;
Then I initialized the char[] with null value, checked it, passed the struct, and checked to see if the value was set correctly.
ArrayInitialize(name.word, '\0');
Print("original name: ", CharArrayToString(name.word));
if( NameTest( name ) )
{
Print("new name: ", CharArrayToString(name.word));
}
---now to the C++ code--- I declared the same struct.
struct Naming
{
char n_name[65];
};
Then the function. I first had to capture the string literal in a temporary char[]. The I cycled a for loop to distribute the elements to the char[] in the struct. The problem is, the char[] from the struct is not a const, and the char temp[] is. I got around this by capturing each char to a char variable, and then storing that variable value in the struct char[].
bool __stdcall NameTest(Naming *name)
{
char temp[] = "Captain Success";
for (int i = 0; temp[i] != '\0'; i++)
{
char t = temp[i];
name->n_name[i] = t;
}
return true;
}
This code works beautifully.
Upvotes: 1
Reputation: 555
From the documentation of mql4: http://docs.mql4.com/basis/preprosessor/import
The following can't be used for parameters in imported functions:
- pointers (*);
- links to objects that contain dynamic arrays and/or pointers.
Classes, string arrays or complex objects that contain strings and/or dynamic arrays of any types cannot be passed as a parameter to functions imported from DLL.
The imported function takes a pointer and that is apparently not supported by mql4.
You should probably use a fixed size array of characters to pass data to and from the dll:
like:
struct Naming {
char m_name[255];
}
The function would need to accept a reference to this struct (but this is probably not supported either) or accept the struct directly and return the struct.
Naming NameTest(Naming name) {
strncpy(name.m_name, "New Content", sizeof(name.m_name) -1);
if (sizeof(name.m_name) > 0) {
name.m_name[sizeof(name)-1] = 0;
}
return name;
}
Calling it would then look like this:
name = NameTest(name);
Upvotes: 2