Вызов функций 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) 656 22.12.08 07:48 Сейчас в теме
>Это только один из примеров. В библиотека MSDN можно найти множество полезных функций
продолжайте....
2. Василий Демидов (Душелов) 3787 22.12.08 09:26 Сейчас в теме
Продолжайте, продолжайте!.. :)
3. mrden (MrDen) 22.12.08 10:03 Сейчас в теме
Спасибо продолжим на примере 1С работа с окном получение координат окна
4. buran_ (Yasen) 22.12.08 11:14 Сейчас в теме
5. Сергей Старых (tormozit) 4168 22.12.08 11:21 Сейчас в теме
(4) Да не. Пока в фреймворк не влезем, не увидим ничего полезного с координатами. Нормально идентифицировать элементы управления и окна через WINAPI не получится.
6. buran_ (Yasen) 22.12.08 11:26 Сейчас в теме
(5) Я о том, что тебе надо было имя хоста узнавать через API
А про идентификацию элементов согласен.
7. Сергей Старых (tormozit) 4168 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) 415 22.12.08 20:43 Сейчас в теме
Фигня это всё. Ничего путнего не добьёшься с врапперами в 1С. Чё там врапперы - я непосредственно в класс окон влезал с перехватчиками сообщений, но там всё через ... (стандартный интерфейс) у них сделано. Все чайлды на паренте отрабатываюца, и чайлду уже тока WM_PAINT приходит.
11. mrden (MrDen) 23.12.08 09:23 Сейчас в теме
(5) приложения на фреймворке нормально определяются окна классы стили
(10) а кто мешает дочернему окну послать SendMessage? и потом все таки не стоит забывать, что это 1С :-) и ньянсов много например Drag&Drop у дочернего окна не установлено на прием только у главного окна но кто мешает установить
Теперь внимание как установить перехватчик сообщения окна или компонента окна? Надо попробовать
12. Максим (Fuego) 415 23.12.08 09:53 Сейчас в теме
(11) речь моя не о том, что я хочу послать сообщение, а о том, что я хочу его перехватить и обработать в своих интересах.
13. mrden (MrDen) 23.12.08 10:07 Сейчас в теме
(12) вот это уже интереснее с этим действительно проблемы но есть GetMessage другой вопрос куда его сунуть вот тут нужны эксперименты. Не считаю, что это "невозможно".
На всяк случай есть вариант изменения параметра окна без прорисовки, а прорисовку делаеш сам. Тут не стоит забывать, что сторонние окна в 1С сами обрабатывают сообщения, например Grid из Vtools.
14. Максим (Fuego) 415 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) 415 23.12.08 17:03 Сейчас в теме
Кстати, я где-то брался писать ВК на чистом Win32API, но чёта лень было обёртку писать под IDispatch. ATL меня полностью не устраивает. Да и чистый код шустрее работает :) Есть такая обёртка? (pure C++/Win32API)

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