Неадекватное поведение при импорте функций из файла "kernel32.dll"

 

Немного предыстории. Нужен вариант с передачей данных от терминала к терминалу. Нашёл на форуме, Класс с реализацией того, что мне нужно. Который реализует это на базе импорта функций из системной библиотеки kernel32.ddl. Но при подключении Класса, всё пошло не так. Сам Класс написан в 2010 году. Уж кто знает на каком билде он работал?! Но сейчас - не работает.  Может быть кто-то из тех. поддержки обратит внимание и передадут на исправление... 

Внизу - прикреплённый файл с мой реализацией, вне Класса. На котором разбирался.

У меня: Операционная система Виндовс 11, MT5 билд 4092.

Ну и далее...

Создание именованного канала при помощи 

kernel32.dll 

и импортируемой функции

CreateNamedPipeW()

При запуске кода ниже:

#import "kernel32.dll"
int CreateNamedPipeW(string lpName, 
                     uint dwOpenMode, 
                     uint dwPipeMode, 
                     uint nMaxInstances, 
                     uint nOutBufferSize, 
                     uint nInBufferSize, 
                     uint nDefaultTimeOut, 
                     uint lpSecurityAttributes);
int CloseHandle(int fileHandle);
#import
//+------------------------------------------------------------------+
//| Service program start function                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
  //Print("Произвольны текст 1");
  string S ="\\\\.\\pipe\\WQ"; 
  string s ="\\\\.\\pipe\\";
  string ss = "WQ";
  string sss = s+ss;
  //Print("Произвольный текст 2");
  //Print(sss);
  //Print(S);
  int i = CreateNamedPipeW(S,3,0,1,10024,10024,0,0);
  Print(i); 
  CloseHandle(i);
  //Print("Произвольный текст 3");
  //Print(sss);
  //Print(S);
  
  }

В панель экспертов выводится сообщение с номером хэндла созданного именованного канала - всё работает!

В коде выше, в импортируемую функцию передаётся строковая переменная  с содержанием, которое не меняли после присвоения значения переменной.

Строка должна быть передана в функцию именно в таком формате, где, в данном случае  "WQ", это имя самого созданного канала. Которое может быть любым.  


Но стоит изменить код следующим образом:

#import "kernel32.dll"
int CreateNamedPipeW(string lpName, 
                     uint dwOpenMode, 
                     uint dwPipeMode, 
                     uint nMaxInstances, 
                     uint nOutBufferSize, 
                     uint nInBufferSize, 
                     uint nDefaultTimeOut, 
                     uint lpSecurityAttributes);
int CloseHandle(int fileHandle);
#import
//+------------------------------------------------------------------+
//| Service program start function                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
  //Print("Произвольны текст 1");
  string S ="\\\\.\\pipe\\WQ"; 
  string s ="\\\\.\\pipe\\";
  string ss = "WQ";
  string sss = s+ss;
  //Print("Произвольный текст 2");
  //Print(sss);
  //Print(S);
  int i = CreateNamedPipeW(sss,3,0,1,10024,10024,0,0);
  Print(i); 
  CloseHandle(i);
  //Print("Произвольный текст 3");
  //Print(sss);
  //Print(S);
  
  }

И в импортируемую функцию поставить строковую переменную сформированную через конкатенацию,

в журнале экспертов выводится сообщение с, как я понимаю, ошибкой прав доступа:

2023.12.12 21:15:08.468 Сервер  Access violation at 0x00007FFBFC3DE67C read to 0x00007FF600000010
2023.12.12 21:15:08.468 Сервер     crash -->  00007FFBFC3DE67C 837A1000          cmp        dword ptr [rdx+0x10], 0x00
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE680 448D4102          lea        r8d, [rcx+0x02]
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE684 488B4208          mov        rax, [rdx+0x08]
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE688 410F45C8          cmovnz     ecx, r8d
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE68C 488945B8          mov        [rbp-0x48], rax
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE690 894DB0            mov        [rbp-0x50], ecx
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE693 4885C0            test       rax, rax
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE696 0F848C010000      jz         0x00007FFBFC3DE828
2023.12.12 21:15:08.468 Сервер  
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE69C 8BC3              mov        eax, ebx
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE69E 448BD3            mov        r10d, ebx
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6A1 41C1EA19          shr        r10d, 0x19
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6A5 C1F81F            sar        eax, 0x1F
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6A8 41F7D2            not        r10d
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6AB 83E002            and        eax, 0x02
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6AE 4183E220          and        r10d, 0x20
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6B2 440BD0            or         r10d, eax
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6B5 8B4560            mov        eax, [rbp+0x60]
2023.12.12 21:15:08.468 Сервер                00007FFBFC3DE6B8 85C0              test       eax, eax
2023.12.12 21:15:08.468 Сервер  
2023.12.12 21:15:08.468 Сервер  00: 0x00007FFBFC3DE67C
2023.12.12 21:15:08.468 Сервер  01: 0x0000022C34CA00C2
2023.12.12 21:15:08.468 Сервер  02: 0x0000000000000001
2023.12.12 21:15:08.468 Сервер  03: 0x0000022C3FE20020
2023.12.12 21:15:08.468 Сервер  04: 0x0000022C34CA0000
2023.12.12 21:15:08.468 Сервер  05: 0xFFFFFFFF9C963AA8
2023.12.12 21:15:08.468 Сервер  06: 0x00007FF600002728
2023.12.12 21:15:08.468 Сервер  07: 0x0000000000002728

Более того, это сообщение об ошибке выводится и в случае, если я передаю в функцию переменную "S", которую не меняли. Но перед этим вывожу её значение  через функцию Print(), следующим образом:

#import "kernel32.dll"
int CreateNamedPipeW(string lpName, 
                     uint dwOpenMode, 
                     uint dwPipeMode, 
                     uint nMaxInstances, 
                     uint nOutBufferSize, 
                     uint nInBufferSize, 
                     uint nDefaultTimeOut, 
                     uint lpSecurityAttributes);
int CloseHandle(int fileHandle);
#import
//+------------------------------------------------------------------+
//| Service program start function                                   |
//+------------------------------------------------------------------+
void OnStart()
  {
  //Print("Произвольны текст 1");
  string S ="\\\\.\\pipe\\WQ"; 
  string s ="\\\\.\\pipe\\";
  string ss = "WQ";
  string sss = s+ss;
  //Print("Произвольный текст 2");
  //Print(sss);
  Print(S);
  int i = CreateNamedPipeW(S,3,0,1,10024,10024,0,0);
  Print(i); 
  CloseHandle(i);
  //Print("Произвольный текст 3");
  //Print(sss);
  //Print(S);
  
  }




В общем, таблица истинности для ситуации, когда в функцию передана переменная "S" инициализированная текстом, который не изменялся, следующая.

Столбцы вызова функции Print() c соответствующим содержанием, после вызова функции создания именованного канала. 

Столбцы вызова функции Print() c соответствующим содержанием, до вызова функции создания именованного канала.

Результат вызова.

Ноли не все отображены,для простоты восприятия.

CreateNamedPipeW(S)   Print("Пр.текст") Print(sss) Print(S) Print("Пр.текст") Print(sss) Print(S) ID
1





0
 0




1 1




1
2
 0



1 1 3
 0


1

4
 0


1
1 5
 1


1 1
6
 0


1 1 1 7
 1

1


8
 1

1

1 9
 0

1
1
10
 0

1
1 1 11
 1

1 1

12
 0

1 1
1 13
 0

1 1 1
14
 0

1 1 1 1 15
 1
1



16
 0
1


1 17
 1
1

1
18
 0
1

1 1 19
 1
1
1

20
 0
1
1
1 21
 0
1
1 1
22
 0
1
1 1 1 23
 0
1 1


24
 0
1 1

1 25
 0
1 1
1
26
 0
1 1
1 1 27
 0
1 1 1

28
 0
1 1 1
1 29
 0
1 1 1 1
30
 0
1 1 1 1 1 31
 0 1




32
 1 1



1 33
 1 1


1
34
 0 1


1 1 35
 0 1

1

36
 0 1

1
1 37
 1 1

1 1
38
 0 1

1 1 1 39
 1 1
1


40
 1 1
1

1 41
 0 1
1
1
42
 0 1
1
1 1 43
 1 1
1 1

44
 0 1
1 1
1 45
 0 1
1 1 1
46
 0 1
1 1 1 1 47
 1 1 1



48
 0 1 1


1 49
 1 1 1

1
50
 0 1 1

1 1 51
 1 1 1
1

52
 0 1 1
1
1 53
 0 1 1
1 1
54
 0 1 1
1 1 1 55
 0  1  1  1        56
 0  1  1  1  
 1  57
 0  1  1  1    1   58 
 0  1  1 1    1  1 59 
 0  1  1  1  1     60 
 0  1  1  1  1    1  61
 0  1  1 1  1  1    62
 0  1  1 1  1  1  1  63


Можно немного обобщить

Из 64 вариантов работают 18. Что это значит, я не знаю.)))

Для случая, когда в вызов функции подставляется переменная с конкатенированным значением, будет подобная таблица истинности... То есть - есть рабочие варианты. И, судя по всему, можно подправить код Класса  до рабочего варианта. С передачей конкатенированных значений в функцию создания именованного канала. 


Файлы:
36idbj.mq5  4 kb
 

Прошу прощения - в предыдущей таблице забыл отформатировать цветом.

Столбцы вызова функции Print() c соответствующим содержанием, после вызова функции создания именованного канала. 

Столбцы вызова функции Print() c соответствующим содержанием, до вызова функции создания именованного канала.

Результат вызова.

Ноли не все отображены,для простоты восприятия.

CreateNamedPipeW(S)   Print("Пр.текст") Print(sss) Print(S) Print("Пр.текст") Print(sss) Print(S) ID
1





0
 0




1 1
1 



1
2
 0



1 1 3
 0


1

4
 0


1
1 5
 1


1 1
6
 0


1 1 1 7
 1

1


8
 1

1

1 9
 0

1
1
10
 0

1
1 1 11
 1

1 1

12
 0

1 1
1 13
 0

1 1 1
14
 0

1 1 1 1 15
 1
1



16
 0
1


1 17
 1
1

1
18
 0
1

1 1 19
 1
1
1

20
 0
1
1
1 21
 0
1
1 1
22
 0
1
1 1 1 23
 0
1 1


24
 0
1 1

1 25
 0
1 1
1
26
 0
1 1
1 1 27
 0
1 1 1

28
 0
1 1 1
1 29
 0
1 1 1 1
30
 0
1 1 1 1 1 31
 0 1




32
 1 1



1 33
 1 1


1
34
 0 1


1 1 35
 0 1

1

36
 0 1

1
1 37
 1 1

1 1
38
 0 1

1 1 1 39
1 1
1


40
 1 1
1

1 41
 0 1
1
1
42
 0 1
1
1 1 43
 1 1
1 1

44
 0 1
1 1
1 45
 0 1
1 1 1
46
 0 1
1 1 1 1 47
 1 1 1



48
 0 1 1


1 49
 1 1 1

1
50
 0 1 1

1 1 51
 1 1 1
1

52
 0 1 1
1
1 53
 0 1 1
1 1
54
 0 1 1
1 1 1 55
 0  1  1  1        56
 0  1  1  1  
 1  57
 0  1  1  1    1   58 
 0  1  1 1    1  1 59 
 0  1  1  1  1     60 
 0  1  1  1  1    1  61
 0  1  1 1  1  1    62
 0  1  1 1  1  1  1  63
 

Если хочется использовать именованные каналы, то лучше использовать штатные средства терминала.

Также для передачи данных можно использовать запись/чтение файлов через общую папку терминалов (TERMINAL_COMMONDATA_PATH), если передача данных не планируется по несколько раз в секунду.

Связь с MetaTrader 5 через именованные каналы без применения DLL
Связь с MetaTrader 5 через именованные каналы без применения DLL
  • www.mql5.com
Перед многими разработчиками встает одинаковая проблема - как пробиться в песочницу торгового терминала без применения небезопасных DLL. Одним из простых и безопасных методов является использование стандартных именованных каналов (Named Pipes), которые работают как обычные файловые операции. Они позволяют организовать межпроцессорное клиент-серверное взаимодействие между программами. Посмотрите практические примеры на C++ и MQL5 в виде сервера, клиента, обмен данными между ними и замер производительности.
 
Уроборос:
CreateNamedPipeW

https://learn.microsoft.com/ru-ru/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew

возвращается HANDLE,

помниться что по размерности это sizeof(void *) и в 5-ке будет ulong :-)

CreateNamedPipeW - Win32 apps
CreateNamedPipeW - Win32 apps
  • 2023.03.08
  • karl-bridge-microsoft
  • learn.microsoft.com
Функция CreateNamedPipeW (Юникод) (winbase.h) создает экземпляр именованного канала и возвращает дескриптор для последующих операций с конвейером.
 
Maxim Kuznetsov #:
помниться что по размерности это sizeof(void *) и в 5-ке будет ulong :-)Возмо

Возможно, но при внесении в импорт функций "ulong", это ничего в ситуации не изменило. 

 
Ihor Herasko #:

Если хочется использовать именованные каналы, то лучше использовать штатные средства терминала.

Также для передачи данных можно использовать запись/чтение файлов через общую папку терминалов (TERMINAL_COMMONDATA_PATH), если передача данных не планируется по несколько раз в секунду.

Какие конкретно, "штатные средства терминала" создают именованные каналы?

 
Уроборос #:

Возможно, но при внесении в импорт функций "ulong", это ничего в ситуации не изменило. 

у вас код для 32-х битного MT4

для 5-ки везде где HANDLE должен быть тоже ulong, то есть у CloseHandle тоже

PS/ самая распространенная ошибка с DLL - неверные размерности типов

 
Maxim Kuznetsov #:

у вас код для 32-х битного MT4

для 5-ки везде где HANDLE должен быть тоже ulong, то есть у CloseHandle тоже

PS/ самая распространенная ошибка с DLL - неверные размерности типов

Хорошо, но это изменение ничего не дало.

Конкатенированная переменная даёт ошибку. И "таблица истинности" для не конкатенированной переменной сохранилась.

 
Уроборос #:

Какие конкретно, "штатные средства терминала" создают именованные каналы?

В ссылке это подробно описано, все-таки статья.

 
Maxim Kuznetsov #:

у вас код для 32-х битного MT4

для 5-ки везде где HANDLE должен быть тоже ulong, то есть у CloseHandle тоже

PS/ самая распространенная ошибка с DLL - неверные размерности типов

Но спасибо вам. Вы натолкнули на мысль. Проблема решена полностью.
Был следующий импорт:

ulong CreateNamedPipeW(string lpName, 
                     uint dwOpenMode, 
                     uint dwPipeMode, 
                     uint nMaxInstances, 
                     uint nOutBufferSize, 
                     uint nInBufferSize, 
                     uint nDefaultTimeOut, 
                     uint lpSecurityAttributes);
ulong CloseHandle(ulong fileHandle);

Уже с предложенным вами изменением на "ulong".
А при таком изменении, при любом варианте - стабильная работа:

ulong CreateNamedPipeW(string lpName, 
                     ulong dwOpenMode, 
                     ulong dwPipeMode, 
                     ulong nMaxInstances, 
                     ulong nOutBufferSize, 
                     ulong nInBufferSize, 
                     ulong nDefaultTimeOut, 
                     ulong lpSecurityAttributes);
ulong CloseHandle(ulong fileHandle);

Ещё раз - спасибо! ))) 

 
Ihor Herasko #:

В ссылке это подробно описано, все-таки статья.

Да, спасибо - разобрался.

Причина обращения: