Как лучше передать данные эксперту из своей программы?

 
Имеется эксперт, в который нужно передавать несколько переменных из самодельной программы. Сначала оформил программу в DLL и пытался передать переменные по указателю, выяснилось, что это невозможно. Потом пытался передать указатель на массив double, сообщило об ошибке 126. Что есть «ошибка 126» найти не смог.

С DLL решил завязать т. к. моя программа вызывает ещё 3 другие DLL, работает с внешними файлами и вообще неизвестно как там с путями всё сделать. Решил попробовать такой подход:
  1. Эксперт запускает exe-программу (с помощью WinExec).
  2. Программа выполняется и создаёт файл с передаваемыми данными.
  3. Эксперт читает файл с данными.
Проблема в том, что программа выполняется несколько секунд, и непонятно как заставить эксперта подождать не только появления файла данных, но и его корректного заполнения. Пока единственное, что приходит в голову, это то, что программа после заполнения файла данных и его закрытия должна создать файл семафора для эксперта, а эксперт соответственно после запуска программы должен в цикле ждать появления файла семафора, и при появлении читать данные и удалять семафор. Как следствие непонятно как сказать эксперту «ждать появления файла семафора».

Я новичок в MQL, посоветуйте, пожалуйста, правильно ли я иду или же есть более грамотные решения для передачи нескольких переменных из своей программы эксперту?

P. S. С DLL указатель на массив пытался получить так:
double A[9];
 
#import "Pred.dll"
void    Pred(double& A[]);
#import
 
int init() {
   Pred(A);
   Alert(A[0]);
}
 
Лучше работать через DLL. Посмотри статью 'Автоматизированный выбор ДЦ для эффективной работы экспертов', там есть исходники и внешней программы и dll, в которых есть примеры передачи данных.

Либо вот еще примерчик :
описание на с++ в dll __declspec(dllexport) bool __stdcall GetAction(char *isInstrument, double bid, double ask, int *action, double *lots, int *magik)
описание в mql4 bool GetAction(string isInstrument, double bid, double ask, int &action[], double &lots[], int &magik[]);

Что касается твоей dll, то возможно неправильное объявление функции в DLL.

Идея с файлом - семафором тоже вполне рабочая. Только для решения проблемы в файл надо что-то писать и программе и эксперту, выработать протокол обмена данными. Например, программа пишет в файл-семафор 1 и закрывает хэндл. Эксперт ждет 1 в семафоре, потом считывает данные из файла, записывает 0 в семафор и закрывает хэндл.
 
polecat:
Пока единственное, что приходит в голову, это то, что программа после заполнения файла данных и его закрытия должна создать файл семафора для эксперта, а эксперт соответственно после запуска программы должен в цикле ждать появления файла семафора, и при появлении читать данные и удалять семафор. Как следствие непонятно как сказать эксперту «ждать появления файла семафора».

int start()
{
   while( SemaforIsBusy() && !IsStopped() ) Sleep(100);
 
   // работа с файлом
 
   while( !SemaforOFF() && !IsStopped() ) Sleep(100);
   return(0);
}
 
bool SemaforIsBusy()
{
     //---- Проверяем состояние семафора, и, если он свободен, занимаем его.
    //---- Открываем файл
    int file_handle = FileOpen( "semafor.csv", FILE_READ | FILE_WRITE | FILE_CSV ), _GetLastError;
 
    //---- Если произошла ошибка,
    if ( file_handle < 0 )
    {
        //---- Выводим сообщение и выходим
        _GetLastError = GetLastError();
        Print( "SemaforIsBusy: FileOpen error #", _GetLastError );
        return(true);
    }
 
    //---- Если размер файла больше 10 байт, значит семафор занят
    if ( FileSize( file_handle ) > 10 )
    {
        FileClose( file_handle );
        return(true);
    }
 
 
    //---- Записываем в файл фразу "семафор занят!"
    //---- Если возникла ошибка,
    if ( FileWrite( file_handle, "семафор занят!" ) < 0 )
    {
        //---- Выводим сообщение, закрываем файл и выходим
        _GetLastError = GetLastError();
        Print( "SemaforIsBusy: FileWrite error #", _GetLastError );
        FileClose( file_handle );
        return(true);
    }
 
    //---- Закрываем файл
    FileClose( file_handle );
    return(false);
}
 
bool SemaforOFF()
{
     //---- Обнуляем файл-семафор готовности
    //---- Открываем файл только для записи (содержимое файла теряется)
    int file_handle = FileOpen( "semafor.csv", FILE_WRITE | FILE_CSV ), _GetLastError;
    
    //---- Если произошла ошибка,
    if ( file_handle < 0 )
    {
        //---- Выводим сообщение и выходим
        _GetLastError = GetLastError();
        Print( "SemaforOFF: FileOpen error #", _GetLastError );
        return(false);
    }
 
    //---- Закрываем файл
    FileClose( file_handle );
    return(true);
}
 
polecat:
и непонятно как заставить эксперта подождать не только появления файла данных, но и его корректного заполнения.

Я делаю это в два прохода, сохраняя коллекцию имён найденных файлов и их размеров в байтах. Если файл найден первый раз, он запоминается в коллекции со своим размером. Если найден, и он уже есть в коллекции, то это его второй (третий, 4-й, ..) мониторинг. Если размер в байтах совпадает, то запись в файл завершена, и он готов к обработке, иначе ждать неизменного размера (можно и дату LastUpdated мониторить).
 

Спасибо большое за советы, буду разбираться!

 
polecat:
и непонятно как заставить эксперта подождать не только появления файла данных, но и его корректного заполнения.
пишите в первой строке файла метку начала, а в последней строке метку конца файла. Читая метки, делаете вывод о корректной заполненности файла.
 
У меня были друлие проблермы с dll: в dll использовался managed code, и dllне освобождала память после вызова в МТ. Следовательно, после некоторого времени работы сьедалась вся память в компютере. Я решил проблему таким образом, что я написал простенькую, НЕ managed, dll, которая:
  1. пишет данные в файл
  2. формирует коммандную строку
  3. запускает внешнюю программу и ждет ее завершения

По пункту 1:
а) Для создания файла я использовал функцию GetTempFileName, так как песочница МТ не очень удобна в связи с изменяющимися путями в модусе тестирования и работы
б) Явное обьявление locale в dll и внешней программе, у меня были проблемы при читании double в managed и unmanaged: setlocale( LC_NUMERIC, "en_US" );

По пункту 3:
Дла запуска и ожидания завершения используйте CreateProcess и WaitForSingleObject. В интернете полно информации и примеров как ими пользоваться.

Замечания:
а) не забудьте стереть temp-файл после использования
б) можете использовать RAM-диск для повышения быстродействия. Значение системной переменной %TEMP% должно тогда показывать на RAM-диск.

Удачи!
 
Огромное спасибо всем за советы. С DLL конечно более красивый подход, но за неимением времени на разбирательства сделал таким образом: Эксперт запускает программу, и ждёт появления файла семафора. Программа сначала пишет данные в файл, затем когда закрывает его, создаёт файл семафора. Эксперт, дождавшись семафора, удалят его, и спокойно читает данные.

Это дело реализовал так:
//Удаляем файл семафора, если он есть
if (FileExists(SemaFile)) {FileDelete(SemaFile);}
 
//Запускаем программу
WinExec(PredProg, 0);
 
//Ждём появления семафора
while (!DataReady()) {
    Sleep(WaitLimit);
}
 
//Получаем данные из программы
ReadDataFile();

Где функции FileExists и DataReady имеют следующую реализацию:
//Проверяет существование файла FileName
bool FileExists(string FileName) {
    handle = FileOpen(FileName, FILE_CSV|FILE_READ, FileDelimiter);
    if (handle >= 1) {
        FileClose(handle);
        return(true);
    } else {
        return(false);
    }
}
 
//Проверяем выставлен ли семафор о том, что можно принять данные
bool DataReady() {
    if (FileExists(SemaFile)) {
        FileDelete(SemaFile);
        return(true);
    } else {
        return(false);   
    }
}
 
olexij:
Дла запуска и ожидания завершения используйте CreateProcess и WaitForSingleObject. В интернете полно информации и примеров как ими пользоваться.
Сначала хотел сделать именно так, без использования семафоров, но угробив день на разбирательства так и не понял как это можно сделать в MQL. :( Примеров действительно много... буду разбираться.
 
polecat:
olexij:
Дла запуска и ожидания завершения используйте CreateProcess и WaitForSingleObject. В интернете полно информации и примеров как ими пользоваться.
Сначала хотел сделать именно так, без использования семафоров, но угробив день на разбирательства так и не понял как это можно сделать в MQL. :( Примеров действительно много... буду разбираться.
В MQL вы делаете только вызов вашей dll. В дистрибуции МТ есть пример, как это сделать. Остальное - создание файлов, вызов программы и т.д. делается вашей dll средствами среды программирования. Вот вам кусочек кода, как я сам делаю, VC++:

MT4_EXPFUNC int __stdcall Recommendation(const RateInfo rates[], const int numberRates /*, ваши параметри */)                                                  
{
    int recommendation = RECOMMENDED_NOACTION;
    STARTUPINFO si;
    memset(&si, 0, sizeof(si));
    si.cb=sizeof(si);
    // Do not show the window
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
 
    // open pipe
    SECURITY_ATTRIBUTES sa;  
    sa.nLength=sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle=TRUE;
    sa.lpSecurityDescriptor=NULL;
    HANDLE readPipe, writePipe;
 
    char szCommandLine[BUFSIZE];
 
    // set US locale
    char *locale = setlocale( LC_NUMERIC, "en_US" );

        // ваш код создания командной строки и записи файла
        ....
    if(!CreatePipe(&readPipe, &writePipe, &sa, 0))
    {
	MessageBox(NULL, "Unable to create input/output pipes", "Your program name", NULL);
    }
    else

    {
        si.dwFlags|=STARTF_USESTDHANDLES;
        si.hStdOutput=writePipe;
        si.hStdInput=readPipe;
        si.hStdError=writePipe;
        PROCESS_INFORMATION pi;
 
        // start prozess
        if(!CreateProcess(0, szCommandLine, 0, 0, TRUE, 0, 0, 0, &si, &pi ))
        {
            MessageBox(NULL, "Unable to start child process", "Your program name", NULL);
        }    
        else
        {
             if (WaitForSingleObject(pi.hProcess, WAITING_TIMEOUT) != WAIT_FAILED) 
             { 
                DWORD exitCode;
                GetExitCodeProcess(pi.hProcess, &exitCode);
 
                DWORD NumberOfBytesRead;
                char buffer[BUFSIZE];
                std::string output;
                while(ReadFile(readPipe, buffer, BUFSIZE, &NumberOfBytesRead, 0))
                {
                    buffer[NumberOfBytesRead]=0;
                    output+=buffer;
                    if(NumberOfBytesRead!=BUFSIZE)        
                        break;
                }
                if(exitCode == 0)
                {
                    // parse output on success
                    recommendation = atol(buffer);
                }
                else
                {
                    // processing error, ignore output
                }
                CloseHandle (pi.hThread);
                CloseHandle (pi.hProcess);
            }
        }
        CloseHandle(writePipe);
        CloseHandle(readPipe);          
    } 
 
        return recommendation;
}
Моя внешняя программа пишет рекомендацию как целоe число на stdout, вариаций может быть много. Если будете делать в VS, не забудьте отключить поддержку managed code.
Что еще можна усовершенствовать? Например передачу данных через stdin программы. Или еще лучше - работать с веб-сервисом...
Причина обращения: