gifts2017

Вызов функций Win 32 API в 1C

Опубликовал Денис Денин (MrDen) в раздел Программирование - Практика программирования

Эксперименты с функциями Win 32 API в 1C

А для чего это нужно? ЭКСПЕРИМЕНТЫ!

Конечно же 1С не предоставляет пользователю возможность использования функций Win 32 API. Такую возможность можно решить при помощи ActiveX OLE-клиента такого как dynwrap.dll или dynwrapx.dll, причем dynwrapх.dll намного интересен своими возможностями.

Немного истории: Библиотека dynwrap.dll является результатом труда нескольких разработчиков и была выпущена 10 лет назад. Основное предназначение вызов функций Win 32 API в WHS скриптах. В сентябре 2008 года появился новый расширенный вариант библиотеки dynwrapх.dll, на примере использования которой мы и остановимся.

Пример использования библиотеки для посылки сообщения в mailslot\messngr аля Net Send:

Для начала ее зарегистрируем:
regsvr32.exe <путь-к-компоненту>\dynwrapx.dll - для всех пользователей.
regsvr32.exe /i <путь-к-компоненту>\dynwrapx.dll - для текущего пользователя.

Используем в 1С - Wrap = CreateObject("DynamicWrapperX"); далее регистрируем вызовы функций

Wrap.Register("KERNEL32.DLL", "CreateFile", "i=sllllll", "r=l");
Wrap.Register("KERNEL32.DLL", "WriteFile", "i=lllll", "r=l");
Wrap.Register("KERNEL32.DLL", "CloseHandle", "i=l", "r=l");

Инициализируем переменные

  GENERIC_READ = 2147483648;
  GENERIC_WRITE = 1073741824;
  FILE_SHARE_READ = 1;
  FILE_SHARE_WRITE = 2;
  OPEN_EXISTING = 3;
  FILE_ATTRIBUTE_NORMAL = 128;
  MessageText = AnsiToOem("Всем ПРИВЕТ!");
  AddFrom = AnsiToOem("От друзей");
  AddTo = "SERVER";
  MailslotName = "\\" + AddTo+"\mailslot\messngr";
  Если Wrap<>0 Тогда
  hFile = Wrap.CreateFile(MailslotName, GENERIC_WRITE, FILE_SHARE_READ, 0, 
  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  Если hFile>0 Тогда
  // структура MSG должна быть 
  // FromName + CHR(0) + ToComp + CHR(0) + MSG + CHR(0) + CHR(0)
  // длина не более 424 символов
  MSG = Wrap.Space(425);

Обязательно запоминаем ссылку на переменную
  pStr = Wrap.StrPtr(MSG);
  BytesBeSend = 0;
  Для i=1 По СтрДлина(AddFrom) Цикл
    Wrap.NumPut(КодСимв(Сред(AddFrom,i,1)),pStr,BytesBeSend,"b");
    BytesBeSend = BytesBeSend +1;
  КонецЦикла;  
  Wrap.NumPut(0,pStr,BytesBeSend,"b");
  BytesBeSend = BytesBeSend +1;
  Для i=1 По СтрДлина(AddTo) Цикл
    Wrap.NumPut(КодСимв(Сред(AddTo,i,1)),pStr,BytesBeSend,"b");
    BytesBeSend = BytesBeSend +1;
  КонецЦикла;
  Wrap.NumPut(0,pStr,BytesBeSend,"b");
  BytesBeSend = BytesBeSend +1;  
  Для i=1 По СтрДлина(MessageText) Цикл
    КодС = КодСимв(Сред(MessageText,i,1));
    Wrap.NumPut(КодС,pStr,BytesBeSend,"b");
    BytesBeSend = BytesBeSend +1;
  КонецЦикла;  
  Wrap.NumPut(0,pStr,BytesBeSend,"b");
  ByesBeSend = BytesBeSend +1;
  Wrap.NumPut(0,pStr,BytesBeSend,"b");
  BytesBeSend = BytesBeSend +1;  
  cbWritten = " ";
  pcbWritten = Wrap.StrPtr(cbWritten);
  fResult = Wrap.WriteFile(hFile, pStr, BytesBeSend, pcbWritten, 0);
  Wrap.CloseHandle(hFile);
  Если fResult=1 Тогда
    cbWritten = Wrap.NumGet(pcbWritten,0,"l");
    Если cbWritten>0 Тогда
      Сообщить("Сообщение доставлено");
    Иначе
      Сообщить("Сообщение не отправлено");
    КонецЕсли;
  КонецЕсли;

Вот и все сообщение отправили или не отправили :-) самое главное при использовании функций возвращающих или передаваемых значения пользоваться структурами передачи/приема в данном примере передается указатель на строку содержащую 3 раздела От Кому Сообщение после каждого символ 0 и в конце лидирующий 0. Такую структуру создать штатными средствами 1С "очень сложно".

Это только один из примеров. В библиотека MSDN можно найти множество полезных функций.

Данный пример позволяет реализацию функции "копирования файлов" при чем как показала практика максимальная скорость достигается применение буфера данных 65535 байт при этом возможно отслеживание копирования (%, различные прогресс-бары, докачка) и конечно не забываем про разгрузку процессора 1 мс.

Пример №2 Получение абсолютных координат окна

Регистрируем функцию

Wrap.Register("USER32.DLL", "GetWindowRect", "i=lp", "r=l");
1 параметр дескриптор окна (его можно найти как минимум по 2 параметрам 1 заголовок, 2 класс) или при помощи ВК FormEx (респект! разработчикам)
2 ссылка на структуру Rect (типа)
typedef struct _RECT
{
  LONG left;
  LONG top;
  LONG right;
  LONG bottom;
} RECT, *PRECT;

в 1с будет немного кузяво
Rect = Wrap.Space(256); // необходимо 4 DWord
Обязательно сохраняем указатель на переменную
pStr = Wrap.StrPtr(Rect);
Wrap.GetWindowRect(ДискрипторОкна, Rect);
Учитывая что такой тип данных в 1С не существует, то Rect 1С убила - но идем по сохраненному указателю
и вычитываем Rect только так
left = Wrap.NumGet(pStr,0,"l");
top = Wrap.NumGet(pStr,4,"l");
right = Wrap.NumGet(pStr,8,"l");
bottom = Wrap.NumGet(pStr,12,"l");

Не забываем, что мы получили абсолютные координаты окна (компонента формы и тп) далее мы можем размер окна изменить (как минимум 3 способа Win 32 API + FormEx).

 Чуть позже рассмотрим пример как же все таки обработать внешнее событие если окно не имеет фокуса или открыто выпадающее меню на форме (созданное разными вариантами в тч и штатном - ВыбратьЗначение)

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Сергей (seermak) 22.12.08 07:48
>Это только один из примеров. В библиотека MSDN можно найти множество полезных функций
продолжайте....
2. Василий Демидов (Душелов) 22.12.08 09:26
Продолжайте, продолжайте!.. :)
3. mrden (MrDen) 22.12.08 10:03
Спасибо продолжим на примере 1С работа с окном получение координат окна
4. buran_ (Yasen) 22.12.08 11:14
5. Сергей Старых (tormozit) 22.12.08 11:21
(4) Да не. Пока в фреймворк не влезем, не увидим ничего полезного с координатами. Нормально идентифицировать элементы управления и окна через WINAPI не получится.
6. buran_ (Yasen) 22.12.08 11:26
(5) Я о том, что тебе надо было имя хоста узнавать через API
А про идентификацию элементов согласен.
7. Сергей Старых (tormozit) 22.12.08 11:29
(6) Так через dynwrap я уже сделал бы, но админ права всю малину портят.
8. mrden (MrDen) 22.12.08 11:31
(5) С фреймворком не пробовал но 1С Spy++ получение информации об окнах классах сделать несложно допустим перебором получение стилей окна и классов стиля и установка отрабатывает нормально, конечно бы хотелось и перехват сообщений окна :-)
создам приложение фреймворка и посмотрю MS Spy++, что выдаст
9. mrden (MrDen) 22.12.08 11:34
(7) я тоже использовал dynwrap.dll но в нем нет возможности вычитывать память по указателю увы :-( а по поводу прав можно попробовать стартануть задачу от системы
10. Максим (Fuego) 22.12.08 20:43
Фигня это всё. Ничего путнего не добьёшься с врапперами в 1С. Чё там врапперы - я непосредственно в класс окон влезал с перехватчиками сообщений, но там всё через ... (стандартный интерфейс) у них сделано. Все чайлды на паренте отрабатываюца, и чайлду уже тока WM_PAINT приходит.
11. mrden (MrDen) 23.12.08 09:23
(5) приложения на фреймворке нормально определяются окна классы стили
(10) а кто мешает дочернему окну послать SendMessage? и потом все таки не стоит забывать, что это 1С :-) и ньянсов много например Drag&Drop у дочернего окна не установлено на прием только у главного окна но кто мешает установить
Теперь внимание как установить перехватчик сообщения окна или компонента окна? Надо попробовать
12. Максим (Fuego) 23.12.08 09:53
(11) речь моя не о том, что я хочу послать сообщение, а о том, что я хочу его перехватить и обработать в своих интересах.
13. mrden (MrDen) 23.12.08 10:07
(12) вот это уже интереснее с этим действительно проблемы но есть GetMessage другой вопрос куда его сунуть вот тут нужны эксперименты. Не считаю, что это "невозможно".
На всяк случай есть вариант изменения параметра окна без прорисовки, а прорисовку делаеш сам. Тут не стоит забывать, что сторонние окна в 1С сами обрабатывают сообщения, например Grid из Vtools.
14. Максим (Fuego) 23.12.08 13:37
(13) GetMessage используется для других целей:

Код
   hWnd = CreateWindow(...
   ....
   ....

   ShowWindow( hWnd, nCmdShow );

   while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0 ) 
   {
      if (bRet == -1)
      {
         // handle the error and possibly exit
      }
      else
      {
         TranslateMessage( &msg );
         DispatchMessage( &msg );
      }
   }

   return;
Показать полностью


Тут организовывается цикл, в котором DispatchMessage() отправляет сообщения на обработку в CallBack окна. Но ведь в том-то и проблема - Этот цикл может и не отправлять в CallBack сообщения. Ведь есть возможность вообще отработать их по-своему, и программисту никто не запрещает делать этого... А ещё можно сделать Hook-процедуры, которые вообще даже в цикл дочернего окна не пропустят сообщения.
15. mrden (MrDen) 23.12.08 14:05
(14) BOOL GetMessage (LPMSG lpMsg, // указатель на структуру
HWND hWnd, // указатель окна чьи сообщения нужно обрабатывать
UINT wMsgFilterMin, // номер мимимального сообщения для выборки
UINT wMsgFilterMax // номер максимального сообщения для выборки);
несомненно можно сделать лучше поток который будет ждать сообщение,
а такой цикл завесит форму 1С
по поводу HOOK это самый правильный вариант, но как его реализовать в 1С?
Резюмируем:
1 - Задача получить сообщение(я) окну, выбрать то что надо
2 - передать управление дальше или нет (хук)
16. mrden (MrDen) 23.12.08 14:13
извиняюсь не завесит форму :-)
17. vip (vip) 23.12.08 14:41
to mrden
Если так свербит попраграммить, пиши ВК на нормальном языке, не извращайся ты с врапом. Больно смотреть.
Нагородили семь бочек арестантов.
18. mrden (MrDen) 23.12.08 14:59
to vip ВК всегда выход :-) если не получится то так и придется
19. Максим (Fuego) 23.12.08 17:03
Кстати, я где-то брался писать ВК на чистом Win32API, но чёта лень было обёртку писать под IDispatch. ATL меня полностью не устраивает. Да и чистый код шустрее работает :) Есть такая обёртка? (pure C++/Win32API)

А лучше бы на самом деле путёвые функции включить в ВК, и не париться с врапперами :)
20. Максим (Fuego) 23.12.08 17:04
(15) НООК я через ВК организовывал. В бардаке не могу найти уже написанный код.
21. Артур Аюханов (artbear) 24.12.08 07:01
(19) А в чем проблема использования ATL/MFC ?
На чистом АПИ слишком много деталей описывать нужно :(
22. Максим (Fuego) 24.12.08 13:25
(21) да так, хочется знать, что в моём коде нет непонятных идей мелкософтовских программеров. А на чистом API я стал писать, когда захотел глубже понять программирование :) Больше практики получил. А теперь привык уже.
23. Александр (ksma) 09.12.09 22:17
24. Александр Цегельников (markers) 20.09.10 11:37
Я конечно извиняюсь что встреваю :) Но нет ли у кого-нибудь простенького примера COM объекта с исходниками на Delphi? Очень хочется, а мозга понять как сделать не хватает по видимому.... Заранее спасибо!
25. Александр Топольский (AlexanderKai) 20.11.14 09:55
Зарегистрировал. При создании DynamicWrapperX = Новый COMОбъект("DynamicWrapperX.2") вылетает с ошибкой "Класс не зарегистрирован". Никто не сталкивался?
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа