Файловые операции в MQL4 без ограничений

 

В окне "Инструментарий" редактора MetaEditor для функции FileOpen читаем:
... файлы могут открываться только в папке каталог_терминала\experts\files (каталог_терминала\tester\files в случае тестирования эксперта) или ее подпапках...
И нет режима совместного доступа к открытому файлу. По крайней мере не указан явно.

А мне вот хочется писать куда хочу и читать откуда хочу. Да еще и передавать данные из одного работающего советника в другой работающий советник через файл.

Я тут уже просил поковырять системные библиотеки на предмет использования файловых операций в советнике. Да видимо некому. Хотя функцию SendMessage из библиотеки Shell32.dll вроде как обсудили, правда с дельфийским уклоном.

Так вот, функции файловых операций используются в системной библиотеке kernel32.dll и её то мы импортируем в советник. Код приведен ниже. Создайте файл "C:\Text.txt", запишите в него немного текста. Запустите приведенный ниже скрипт. Скрипт считывает все содержимое файла. Добавить немного проверки на символы разделители и на символы конца строки, немного вишневого сиропа - и готова функция ReadLine ...

// File Read Write.mq4
// Скрипт. Положить в папку experts\scripts
 
#property copyright "mandorr@gmail.com"
#include <WinUser32.mqh>
#import "kernel32.dll"
   int _lopen  (string path, int of);
   int _lcreat (string path, int attrib);
   int _llseek (int handle, int offset, int origin);
   int _lread  (int handle, string buffer, int bytes);
   int _lwrite (int handle, string buffer, int bytes);
   int _lclose (int handle);
#import
 
void start() 
  {
    string buffer=ReadFile("C:\Text.txt");
    int count=StringLen(buffer);
    string title="Чтение из файла";
    string msg="Число считанных байт: "+count+"    \n"
               +"Cодержимое файла:\n\n"+buffer;
    MessageBox(msg,title,MB_OK|MB_ICONINFORMATION);
    WriteFile("C:\Text2.txt",buffer);
  }
 
string ReadFile (string path) 
  {
    int handle=_lopen (path,0);           
    if(handle<0) 
      {
        Print("Ошибка открытия файла ",path); 
        return ("");
      }
    int result=_llseek (handle,0,0);      
    if(result<0) 
      {
        Print("Ошибка установки указателя" ); 
        return ("");
      }
    string buffer="";
    string char="x";
    int count=0;
    result=_lread (handle,char,1);
    while(result>0) 
      {
        buffer=buffer+char;
        char="x";
        count++;
        result=_lread (handle,char,1);
     }
    result=_lclose (handle);              
    if(result<0)  
      Print("Ошибка закрытия файла ",path);
    return (buffer);
  }
 
void WriteFile (string path, string buffer) 
  {
    int count=StringLen (buffer); 
    int result;
    int handle=_lopen (path,1);
    if(handle<0) 
      {
        handle=_lcreat (path,0);
        if(handle<0) 
          {
            Print ("Ошибка создания файла ",path);
            return;
          }
        result=_lclose (handle);
     }
    handle=_lopen (path,1);               
    if(handle<0) 
      {
        Print("Ошибка открытия файла ",path); 
        return;
      }
    result=_llseek (handle,0,0);          
    if(result<0) 
      {
        Print("Ошибка установки указателя"); 
        return;
      }
    result=_lwrite (handle,buffer,count); 
    if(result<0)  
        Print("Ошибка записи в файл ",path," ",count," байт");
    result=_lclose (handle);              
    if(result<0)  
        Print("Ошибка закрытия файла ",path);
  }

Дальше для тех, кому интересно, как это работает:

// _lopen : Откpывает указанный файл. Возвpащает: описатель файла.
// _lcreat : Создает указанный файл. Возвpащает: описатель файла.
// _llseek : Устанавливает указатель в откpытом файле. Возвpащает: новое смещение указателя.
// _lread : Считывает из откpытого файла указанное число байт. Возвpащает: число считанных байт; 0 - если конец файла.
// _lwrite : Записывает данные из буфеpа в указанный файл. Возвpащает: число записанных байт.
// _lclose : Закpывает указанный файл. Возвpащает: 0.
// В случае неуспешного завеpшения все функции возвращают значение HFILE_ERROR=-1.

// path : Стpока, опpеделяющая путь и имя файла.
// of : Способ открытия.
// attrib : 0 - чтение или запись; 1 - только чтение; 2 - невидимый или 3 - системный.
// handle : Файловый описатель.
// offset : Число байт, на котоpое пеpемещается указатель.
// origin : Указывает начальную точку и напpавление пеpемещения: 0 - впеpед от начала; 1 - с текущей позиции; 2 - назад от конца файла.
// buffer : Пpинимающий/записываемый буфеp.
// bytes : Число считываемых байт.

// Способы открытия (параметр of):
// int OF_READ = 0; // Открыть файл только для чтения
// int OF_WRITE = 1; // Открыть файл только для записи
// int OF_READWRITE = 2; // Открыть файл в режиме запись/чтение
// int OF_SHARE_COMPAT = ; // Открывает файл в режиме общего совместного доступа. В этом режиме любой процесс может открыть данный файл любое количество раз. При попытке открыть этот файл в любом другом режиме, функция возвращает HFILE_ERROR.
// int OF_SHARE_EXCLUSIVE =16; // Запрет текущему и другим процессам на доступ к этому файлу в режимах чтения/записи. Файл в этом режиме можно открыть только один раз (текущим процессом). Все остальные попытки открытия файла будут провалены.
// int OF_SHARE_DENY_WRITE=32; // Открывает файл в режиме общего доступа с запретом на запись другим процессам. При попытке открытия данного файла с флагами OF_SHARE_COMPAT и/или OF_WRITE или OF_READWRITE, функция возвращает HFILE_ERROR.
// int OF_SHARE_DENY_READ =48; // Открывает файл в режиме общего доступа с запретом на чтение другим процессам. При попытке открытия данного файла с флагами OF_SHARE_COMPAT и/или OF_READ или OF_READWRITE, функция возвращает HFILE_ERROR.
// int OF_SHARE_DENY_NONE =64; // Открывает файл в режиме общего доступа без запрета на чтение/запись другим процессам. При попытке открытия данного файла в режиме OF_SHARE_COMPAT, функция возвращает HFILE_ERROR.

 
// File Read Write.mq4
// Скрипт. Положить в папку experts\script
 
#property copyright "mandorr@gmail.com"
#include <WinUser32.mqh>
#import "kernel32.dll"
   int _lopen  (string path, int of);
   int _lcreat (string path, int attrib);
   int _llseek (int handle, int offset, int origin);
   int _lread  (int handle, string buffer, int bytes);
   int _lwrite (int handle, string buffer, int bytes);
   int _lclose (int handle);
#import
 
void start() {
   string path="C:\Text.txt";
   string title="Чтение из файла";
   string msg;
   int result;
   int handle=_lopen(path,4);
   if (handle<0) {
      msg="Ошибка открытия файла";
      MessageBox(msg,title,MB_OK|MB_ICONERROR);
      return;
   }
   result=_llseek (handle,0,0);
   string buffer="";
   string char="x";
   int count=0;
   result=_lread (handle,char,1);
   while (result>0) {
      buffer=buffer+char;
      char="x";
      count++;
      result=_lread (handle,char,1);
   }
   result=_lclose (handle);
   msg="Число считанных байт: "+count+"    \n"
   +"Все содержимое файла:\n\n"+StringTrimRight(buffer);
   MessageBox(msg,title,MB_OK|MB_ICONINFORMATION);
}
[/code]
 
Дальше для тех, кому интересно, как это работает:
 
// _lopen  : Откpывает указанный файл. Возвpащает: описатель файла.
// _lcreat : Создает указанный файл.   Возвpащает: описатель файла.
// _llseek : Устанавливает указатель в откpытом файле. Возвpащает: 
// новое смещение указателя.
// _lread  : Считывает из откpытого файла указанное число байт. 
// Возвpащает: число считанных байт; 0 - если конец файла.
// _lwrite : Записывает данные из буфеpа в указанный файл. Возвpащает: 
// число записанных байт.
// _lclose : Закpывает указанный файл. Возвpащает: 0.
// В случае неуспешного завеpшения все функции возвращают значение 
// HFILE_ERROR=-1.
 
// path   : Стpока, опpеделяющая путь и имя файла.
// of     : Способ открытия.
// attrib : 0 - чтение или запись; 1 - только чтение; 2 - невидимый или 
// 3 - системный.
// handle : Файловый описатель.
// offset : Число байт, на котоpое пеpемещается указатель.
// origin : Указывает начальную точку и напpавление пеpемещения: 0 - 
// впеpед от начала; 1 - с текущей позиции; 2 - назад от конца файла.
// buffer : Пpинимающий/записываемый буфеp.
// bytes  : Число считываемых байт.
 
// Способы открытия (параметр of):
// int OF_READ            =0; // Открыть файл только для чтения
// int OF_WRITE           =1; // Открыть файл только для записи
// int OF_READWRITE       =2; // Открыть файл в режиме запись/чтение
// int OF_SHARE_COMPAT    =3; // Открывает файл в режиме общего 
// совместного доступа. В этом режиме любой процесс может открыть данный 
// файл любое количество раз. При попытке открыть этот файл в любом другом
// режиме, функция возвращает HFILE_ERROR.
// int OF_SHARE_DENY_NONE =4; // Открывает файл в режиме общего доступа 
// без запрета на чтение/запись другим процессам. При попытке открытия 
// данного файла в режиме OF_SHARE_COMPAT, функция возвращает HFILE_ERROR.
// int OF_SHARE_DENY_READ =5; // Открывает файл в режиме общего доступа с 
// запретом на чтение другим процессам. При попытке открытия данного файла 
// с флагами OF_SHARE_COMPAT и/или OF_READ или OF_READWRITE, функция 
// возвращает HFILE_ERROR.
// int OF_SHARE_DENY_WRITE=6; // Тоже самое, только с запретом на запись.
// int OF_SHARE_EXCLUSIVE =7; // Запрет текущему и другим процессам на 
// доступ к этому файлу в режимах чтения/записи. Файл в этом режиме можно 
// открыть только один раз (текущим процессом). Все остальные попытки 
// открытия файла будут провалены.
Сделал покрасивше.
 
В функциях WriteFile и ReadFile есть грубые ошибки, но тем не менее фунции отлично работают. Видимо это глюк метатрейдера. Не исключено, что начиная с некоторого билда, использование этих функций будет приводить к зависанию терминала. Для устойчивости, надо считывать/записывать блоками не более 250 символов. Тогда всё будет гут.

Примечание. В словаре MetaEditor читаем: "Длина строковой константы - от 0 до 255 символов. Если длина строковой константы превосходит максимальную, лишние символы справа отбрасываются, и компилятор выдает соответствующее предупреждение".
 
mandor:
В функциях WriteFile и ReadFile есть грубые ошибки, но тем не менее фунции отлично работают. Видимо это глюк метатрейдера. Не исключено, что начиная с некоторого билда, использование этих функций будет приводить к зависанию терминала. Для устойчивости, надо считывать/записывать блоками не более 250 символов. Тогда всё будет гут.

Примечание. В словаре MetaEditor читаем: "Длина строковой константы - от 0 до 255 символов. Если длина строковой константы превосходит максимальную, лишние символы справа отбрасываются, и компилятор выдает соответствующее предупреждение".

пожалуй лучше использовать API
 
YuraZ писал (а):
пожалуй лучше использовать API

Испугался? Всё не так уж и страшно. Лекарство я описал. Зато через импорт быстрее и надежнее. И красивее (если кому-то существенно).
А что собственно понимается под загадочной абревиатурой API? Особенно в сочетании со словами "лучше использовать" .. .
Нужто MetaTrader 4 API?
 
mandor:
YuraZ писал (а):
пожалуй лучше использовать API

Испугался? Всё не так уж и страшно. Лекарство я описал. Зато через импорт быстрее и надежнее. И красивее (если кому-то существенно).
А что собственно понимается под загадочной абревиатурой API? Особенно в сочетании со словами "лучше использовать" .. .
Чего ж тут пугаться! :-)
по поводу API вы шутите ? поисковые системы могут помочь! - но посылать к поисковику это грубо :)


API это Aplication Programm Interface - стандарт WINDOWS

в двух словах набор функций, которые реализованы через вызовы в DLL - фактически обращение к ядру операционной системы
причем не обязательно к "kernel32.dll" :)

думаю вы знаете что можно вызывать и собственные DLL это фактически аналогично только уже вызов не к ядру
а к вашему модулю!

#import "kernel32.dll"
int _lopen (string path, int of);
...
int _lclose (int handle);
#import

собственно это и есть вызовы через API

Правда многие программисты даже не знают что такое API , просто пишут на более высокоуровневых языках
например вызываете WriteFile и не паритесть знаниями API правда рано или поздно
эта функция обратиться чеез API к WINDOWS

все приличные языки в реде WINOWS поддерживают вызовы DLL

можно и блоками по 250 байт :) но обычно лень дробить выходные данные на блоки
а если у функции еще и глюки :) , можно более прямую дорогу найти и уже без глюков
 

Вопрос, близкий к теме:

Каким образом можно получить то, что в документации по MQL называется

"Директория, из которой запущен клиентский терминал terminal_dir"


Казалось, бы в документации столько ссылок на имя этой директории,

что получить его- раз плюнуть.

Но не нашел. Или не там искал?

 
string TerminalPath( )

Например прочитать файл из папки символсетов:

string text=ReadFile(TerminalPath()+"\symbolsets\05052008.set");
 
kombat писал (а) >>
string TerminalPath( )

Например прочитать файл из папки символсетов:

Спасибо. Ну точно, плохо искал.

Кстати, обратите внимание на терминологический разнобой.

"Директория, из которой запущен клиентский терминал terminal_dir"

и

"string TerminalPath( )
Возвращает директорий, из которого запущен клиентский терминал"

Причем это единственный случай в хэлпе по MQL, где это слово применяется в мужском роде.

Но это уже придирка к авторам МТ :-)

 

В новых билдах эти функции уже не работают.

Кто нибудь знает как это исправить?

 
Использовать CreateFile и иже с ними. А вообще работают, переделать просто надо
Причина обращения: