1с dynamicwrapperx как раздать права пользователям на регистрацию
- функций из библиотек DLL (в частности функций Windows API);
- вообще любых функций, адрес которых в памяти вам известен;
- функций, чей машинный код (в виде хекс-строки) у вас имеется.
- обратный вызов (callback) скриптовой функции из вызванной вами внешней функции;
- прямой доступ к памяти (чтение и запись числа по адресу);
- выделение и освобождение памяти;
- чтение и запись строк в произвольной кодировке;
- получение указателя на строку, объект, массив, переменную;
- получение объекта по указателю на него;
- определение битности (32 или 64) процесса, в котором выполняется скрипт.
- в Windows 98 не работает;
- в Windows Vista, 7 и 8 при включенном UAC объект будет недоступен приложениям, запущенным с правами администратора.
Примечание для пользователей 1-й версии. Если в системе уже установлена 1-я версия DWX, 2-я зарегистрируется параллельно с ней. В этом случае она будет доступна в скриптах только по имени DynamicWrapperX.2. Чтобы заменить старую версию новой, нужно сначала разрегистрировать старую. В этом случае новая версия будет доступна также и под общим именем DynamicWrapperX.
Если 2-я версия была установлена параллельно с 1-й, её разрегистрация никак не затронет регистрацию 1-й (и наоборот).
Этот метод регистрирует функцию из DLL в качестве метода объекта. Функция задаётся именем или ординалом. Значение аргументов "i=", "r=" и "f=" см. в примечаниях.
- Если файл библиотеки имеет расширение dll, его можно не указывать. Если файл не имеет расширения, имя должно быть указано с точкой на конце: "mylib."
- Для системных DLL путь указывать не нужно. Для сторонних зависит от того, где они находятся.
- Функции Windows API, принимающие или возвращающие строки, часто имеют два варианта: например, MessageBoxW для строк в Юникоде и MessageBoxA для строк в ANSI-кодировке. Букву "А" на конце можно не добавлять, DWX сделает это сам, если не найдёт функцию MessageBox, но имена юникодных функций должны указываться точно, c "W" на конце.
- Ординал можно задавать десятичным или шестнадцатеричным числом. Учтите, что ординал функции может быть разным в разных версиях библиотеки и в x86 и x64 вариантах одной и той же версии.
- Наличие аргументов "i=", "r=" и "f=" зависит от функции. Порядок их произвольный, т.к. они опознаются по префиксам.
- "i=" задаёт тип каждого из параметров функции (целое число, дробное, строка и т.п.). Может опускаться только, если функция не имеет параметров. Расшифровка буквенных обозначений здесь.
- "r=" — тип возвращаемого значения. Можно не указывать, если возвращаемое значение не нужно, даже если функция что-то возвращает.
- "f=" — флаги, влияющие на то, как будет вызываться функция. На данный момент имеет одно возможное значение "t" (соглашение вызова thiscall). Имеет смысл только для 32-битного DWX и только со 2-й версии. 64-битным и 1-й версией игнорируется.
Этот метод регистрирует функцию по её адресу в памяти. Остальные параметры те же, что у метода Register.
Метод принимает в первом аргументе машинный код функции, представленный в виде хекс-строки. Остальные параметры те же, что у метода Register.
- Под код выделяется память, и он записывается туда уже в бинарном виде. После чего его можно вызывать по присвоенному ему во втором аргументе имени. Возвращаемое значение содержит адрес кода в памяти. Машинный код для 32 и 64 бит различен, отсюда необходимость проверки битности процесса (через проверку битности объекта DWX), чтобы определить, какой код использовать. В качестве возможного применения для такого кода можно привести обработку больших объёмов данных. Машинный код может работать в десятки или даже сотни раз быстрее скриптового.
- Если вы хотите использовать код по его адресу, без создания метода, можно опустить все аргументы кроме первого. Но имейте в виду, что выделенная под код память всё же связана с объектом и будет освобождена в случае его уничтожения.
- Хекс-строка может быть сплошной или содержать пробелы, табуляции и переводы строки.
- Комментарии можно поместить прямо в хекс-строку, обрамив их скобками.
Code = "4889C8 (mov rax,rcx) 48F7EA (imul rdx) C3 (ret)"
Этот метод нужен для получения указателя на скриптовую функцию, пригодного для передачи какой-либо функции API. Которая потом, используя этот указатель, могла бы данную скриптовую функцию вызывать. Пример такой API-функции — EnumWindows (см. код ниже). Она перебирает существующие окна и хэндл каждого окна передаёт callback-функции в качестве параметра. После этого она ждёт, что вернёт callback-функция. Если 1, то перебор идёт дальше, если 0 — прекращается.
В JScript и VBScript функция является объектом, и ссылка на неё для таких целей не годится. Поэтому ссылка на скриптовую функцию сначала передаётся методу RegisterCallback, а API-функция получает возвращённый им указатель на одну из вспомогательных процедур внутри dynwrapx.dll, вызовы которой будут транслироваться в вызовы скриптовой функции, а возвращаемые значения — в обратном направлении.
В JScript ссылка на функцию — это её имя без скобок, а в VBScript сначала нужно использовать GetRef. Кроме ссылки на функцию задаются также типы её параметров и возвращаемого значения — аналогично методу Register (но используются только маленькие буквы).
Пример на JScript (VBScript см. ниже):
Пример на VBScript:
Параметры в квадратных скобках необязательны, но нельзя опускать такой параметр, если следующий за ним вы указываете.
NumGet( Address [, Offset] [, Type] ) — чтение числа из памяти. Address — базовый адрес. Offset — смещение от него (в байтах), положительное или отрицательное; по умолчанию 0. Type — тип числа: те же буквенные обозначения, что и для метода Register (см. список здесь), но используются только маленькие буквы; по умолчанию — l. Считанное число помещается в возвращаемое методом значение.
NumPut( Var, Address [, Offset] [, Type] ) — запись числа в память. Var — либо литеральное числовое значение, либо переменная, содержащая значение, которое нужно записать. Остальное — как в NumGet. Возвращаемое методом значение содержит адрес сразу за последним записанным байтом.
В обоих методах выше Address может быть как числом, так и строкой, в последнем случае в качестве базового адреса используется указатель на эту строку. Это позволяет использовать строки в качестве буфера для данных, размещать там массивы, структуры и т.п. (о связанном с этим риске см. примечание к методу Space ниже).
StrPtr( Var [, Type/Codepage] ) — возвращает указатель на строку (на оригинал). Var — строковая переменная или константа. Type/Codepage — тип/кодировка. Возможные значения: w (по умолчанию), s, z, либо явное указание кодировки — например, "cp1251". При значениях s, z либо кодировке строка предварительно конвертируется на месте. Поддерживается кодировка "cp1201", т.е. UTF-16 Big Endian.
Если результирующая строка длиннее исходной, она будет обрезана. Поскольку исходная строка в Юникоде, где каждый символ занимает два байта, места должно быть более чем достаточно в большинстве случаев. Возможно, какой-нибудь восточно-азиатский текст может быть длиннее в ANSI-кодировке или в UTF-8, чем он есть в UTF-16, но это только мои догадки.
StrGet( Address [, Type/Codepage] ) — чтение строки с указанного адреса. Возвращает копию строки. Address может быть как указателем в виде числа, так и строковой переменной. Type/Codepage — то же, что у StrPtr. Значения s и z нужны для чтения строки в ANSI— или OEM—кодировке по умолчанию. Строка при чтении конвертируется в Юникод.
StrPut( String, Address [, Type/Codepage] ) — запись строки по указанному адресу. Type/Codepage — то же, что у двух методов выше. Строка при записи конвертируется в заданную кодировку. Возвращается адрес после нулевого терминатора строки. Если Address указан как 0, метод вернёт размер буфера в памяти, необходимый для записи строки в указанной кодировке, включая нулевой терминатор на конце строки.
Space( Count [, Char] ) — создание строки (BSTR) заданной длины. Возвращает строковую переменную. Count — число символов (двухбайтных). Char — символ, которым будет заполнена строка. По умолчанию строка заполняется пробелами — так же, как это делает функция Space в VBScript. Чтобы заполнить строку двоичными нулями, задайте Char как пустую строку ("").
NB: Метод Space был задуман для создания строк, используемых как буферы памяти — для чисел, структур, выходных строк. Но, как выяснилось на практике, такое использование строк в общем случае ненадёжно. Контроль над памятью, выделенной под строку, отдаётся скриптовому движку, и если он в процессе своей работы произведёт с ней какие-то манипуляции, помещённые в неё данные могут быть потеряны или испорчены. Поэтому я рекомендую для выделения памяти под буфер использовать метод MemAlloc (см. ниже).
ObjPtr( Object ) — получение указателя на объект.
ObjGet( ObjPtr ) — получение объекта по указателю на него. Если объект поддерживает интерфейс IDispatch, возвращается переменная типа VT_DISPATCH; если нет — VT_UNKNOWN. Счётчик ссылок объекта увеличивается на 1.
ArrPtr( Array ) — получение указателя на структуру SAFEARRAY массива. Не работает для массивов в JScript, т.к. это объекты.
VarPtr( Variable ) — в языках, где переменные передаются в методы по ссылке, как в VBScript, можно получить указатель на структуру VARIANT переменной.
MemAlloc( Bytes [, ZeroMem] ) — выделение памяти. Если вторым аргументом указать 1, память будет обнулена. Возвращается указатель на выделенную память.
MemFree( MemPtr ) — освобождение ранее выделенной памяти.
Bitness() — определение битности объекта DynamicWrapperX (и, тем самым, битности процесса, его использующего). Возвращает число 32 или 64.
Version( [Field] ) — возвращает указанное поле/поля из полного номера версии DynamicWrapperX. Задайте параметру Field значение согласно приведённой ниже таблице. Если Field опущен, это равносильно 0.
Пример для версии 2.5.7.10.
0 — полная версия как строка: "2.5.7.10"
1 — старший номер версии: 2
2 — младший номер версии: 5
3 — номер сборки: 7
4 — номер ревизии: 10 (0xA)
5 — старший + младший номер версии: 0x20005
6 — номер сборки + ревизии: 0x7000A
7 — полный номер версии: 0x200050007000A
Как вы можете видеть, каждое поле является 16-битным целым и занимает 2 байта.
Типы входных параметров и возвращаемых значений
m — знаковое целое 64 бита — INT64, LONGLONG, .
q — беззнаковое целое 64 бита — UINT64, ULONGLONG, .
l — знаковое целое 32 бита — LONG, INT, LRESULT, BOOL, .
u — беззнаковое целое 32 бита — ULONG, UINT, DWORD, .
h — хэндл, дескриптор — HANDLE, HWND, HMODULE, HINSTANCE, HICON, . — 32 бита (x86) или 64 бита (x64)
p — указатель, для чисел то же, что u (x86) или q (x64), но также можно использовать для передачи объекта (IDispatch *) и строки.
n — знаковое целое 16 бит — SHORT
t — беззнаковое целое 16 бит — USHORT, WORD, WCHAR, OLECHAR, .
c — знаковое целое 8 бит — CHAR
b — беззнаковое целое 8 бит — UCHAR, BYTE, .
f — дробное число одинарной точности (32 бита) — FLOAT
d — дробное число двойной точности (64 бита) — DOUBLE
w — строка в Юникоде — BSTR, LPWSTR, LPOLESTR, OLECHAR *, WCHAR *, .
s — строка в кодировке ANSI/Windows по умолчанию — LPSTR, LPCSTR, CHAR *, .
z — строка в кодировке OEM/DOS по умолчанию — LPSTR, LPCSTR, CHAR *, .
v — указатель на структуру VARIANT
- Подробнее о строковых типах см. здесь.
- Кроме хэндлов и указателей, есть и другие типы, меняющие свою битность с битностью процесса. Например, LPARAM, WPARAM, SIZE_T. Для них также лучше использовать тип h или p, чтобы код скрипта корректно работал при его выполнении как 32-, так и 64-битным интерпретатором.
- Типам m и q (а также h и p в x64) в скриптах соответствовали бы типы VT_I8 и VT_UI8. Движками JScript и VBScript эти типы не поддерживаются, что ограничивает возможности работы с 64-битными целыми. Пока значение возвращаемого функцией числа это позволяет, DWX преобразует его к типам VT_I4 (знаковое целое 32 бита) либо VT_R8 (дробное число двойной точности). Поскольку мантисса VT_R8 имеет только 53 бита, этот тип не может отобразить все числа в диапазоне 64-битного целого. В таком случае возвращается тип VT_I8 или VT_UI8. Всё, что можно с ними сделать в скрипте, — передать как аргумент в какой-то другой метод или вывести значение числа в WScript.Echo либо MsgBox. Никакие расчёты с ними невозможны.
- Когда большое целое возвращается как VT_R8 и вы хотите посмотреть его значение в диалоговом окне, оно может отображаться неточно из-за ограничения на число знаков после запятой в строковом представлении дробного числа. Поэтому, например, число 9223372036854775808 может отобразиться как 9,22337203685478E+18 вместо 9,223372036854775808E+18. Однако само число в переменной не округляется и остаётся точным.
- Если значение 64-битного целого не укладывается ни в один из доступных числовых типов, можно указать его в виде строки, десятичной или шестнадцатеричной (с префиксом 0x).
Выходные параметры
M — указатель на число (его адрес в памяти) — LONGLONG *, PLONGLONG и т.п.
Q — то же — ULONGLONG *, PULONGLONG, .
L — то же — LONG *, LPLONG, .
H — то же — HANDLE *, PHANDLE, LPHANDLE, .
U — то же — ULONG *, LPDWORD, .
P — то же
N — то же — SHORT *
T — то же — USHORT *, LPWORD, WCHAR *, OLECHAR *, .
C — то же — CHAR *, .
B — то же — UCHAR *, LPBYTE, .
F — то же — FLOAT *, PFLOAT
D — то же — DOUBLE *, PDOUBLE
W — выходная строка в Юникоде
S — выходная строка в ANSI
Z — выходная строка в OEM
- Использование выходных типов для чисел имеет смысл для скриптовых движков, которые передают переменные в метод по ссылке, как это делает VBScript. В этом случае DWX может передать функции указатель на значение переменной, которое функция может изменить. В движках, где аргументы передаются по значению, как в JScript, методу передаётся копия числа, поэтому изменить оригинал нет возможности. В этом случае решением будет выделение нужного количества памяти, например, методом MemAlloc, передача функции указателя на эту память (используя тип p) и затем, после вызова функции, считывание помещённого ею туда числа методом NumGet.
- Некоторые движки копируют строки при передаче их в метод; в этом случае использование выходных типов для строк также теряет смысл. Решение аналогично: выделение памяти, передача указателя на неё типом p и затем считывание оттуда строки методом StrGet.
В JScript и VBScript используются строки типа BSTR. Это строка в Юникоде, т.е. код каждого символа занимает 2 байта. За последним символом расположен ограничитель - 2 нулевых байта. Кроме того, перед первым символом находится 4-байтное число, содержащее длину строки в байтах (без учёта нулевых на конце). Указатель, содержащийся в скриптовых строковых переменных, является адресом первого символа такой строки (т.е. байты, содержащие длину строки, как бы остаются за кадром).
Выходная строка, как и прочие выходные параметры, предназначена для того, чтобы функция API в неё что-нибудь записала, так что её длина должна быть соответствующей.
Использование строк как буферов памяти может работать или нет в зависимости от того, как внутри скриптового движка реализована работа со строками. Предпочтительнее выделять память методом MemAlloc, т.к. она будет под вашим полным контролем.
[/accordion]
Ранее у VBS / JS не было возможностей для вызова API-функций.
Так было до тех пор, пока Юрий Попов не написал замечательную библиотеку DynamicWrapperX.
Библиотеку нужно зарегистрировать :
1) в системе (нужны права администратора)
Коллеги, хочу предложить Вам небольшое исследование на предмет изменения функционала серверных контекстов. Речь пойдет о x64-версии 1С сервера приложений для ОС Windows.
Несколько слов о терминах.
Наверное, я не ошибусь, если скажу, что большинство объектов(в широком смысле этого слова), которые мы привыкли использовать на внутреннем языке платформы, в реальности представляют собой COM-объекты. Именно ими оперирует система. Такие объекты, как , , и т.д, суть COM-объекты внутри движка. Даже числа и строки "оборачиваются" в COM-интерфейсы.
В данном изложении я буду использовать термин Контекст, подразумевая под ним набор интерфейсов подобных Com-объектов. Описание самих интерфейсов и их IID вы вряд ли найдете в MSDN и regedit'ом в реестре, поскольку это сугубо внутренние интерфейсы платформы. Я не говорю сейчас об IUnknown, IDispatch и прочих стандартных интерфейсах. Среди набора интерфейсов 'внутренних' COM-объектов нам будет интересен интерфейс "вызова через <.>". Мне он видится как некий аналог IDispatch. Я буду ссылаться на него как IContextExtImpBase.
Когда мы пишем, например, Справочники.<. >, именно в этот момент происходит обращение к методам интерфейса IContextExtImpBase. Хотя нет, если посмотреть на это более детально, то окажется, что объект 'Справочники', это свойство одного из глобальных контекстов (которых в 1С порядка 33), т.е. внутри обращение к 'Справочники' выглядит так .Справочники. И каждый из ГК поддерживает тот же IContextExtImpBase.
Как известно, любой COM-интерфейс имеет строго определенную последовательность методов в виртуальной таблице, поэтому можно попытаться выполнить подмену какого-либо методов интерфейса. Например, за вызовы методов 1С-объектов отвечает метод "IContextExtImpBase::call". Если его подменить на что-то своё, то все вызовы методов 1С-го объекта будут проходить через ваш метод. Причем, это коснется всего серверного рабочего процесса.
Моя цель была, попытаться минимальными усилиями, после перехвата, "всплыть" на уровень модуля 1С и уже тут продолжить обработку. Для экспериментов я использовал объект Запрос, как наиболее интересный в плане изменения функционала.Перехватывая его методы, можно, например, собирать статистику, ну или налету подменять текст запроса.(Не могу с уверенностью утверждать, но даже если конфигурация поставляется без текста модулей, текст запроса можно получить.)
Вариант реализации.
Я применил не совсем стандартный подход для решения данной задачи, каюсь. Основные инструменты, которые я использовал, это COM-объект DynamicWrapperX и кусок бинарного кода. (Исходник есть в тестовой базе. Я старался делать подробные комментарии.). Оба написаны на goAsm'е. Весь остальной код написан на стандартном языке 1С. Уверен, что существуют более изящные варианты реализации, но, в любом случае, это всего лишь пример того, как это можно сделать.
Теперь предлагаю посмотреть, что есть в тестовой конфигурации, и рассмотреть, как работает перехват.
- Серверный общий модуль 'DWX_Информатор'. Тут находятся служебные процедуры и функции для доступа к методам интерфейса IContextExtImpBase. Вообще говоря, сам модуль может использоваться независимо от данной разработки, но отдельно представляет скорее теоретический интерес. Возможно, его можно применить для UNIT-тестирования.
- Служебная обработка 'Перехват', которая забрасывает кусок бинарного кода на сервер и устанавливает/снимает перехват.
- Служебная обработка '_proxyInterceptor'. Основная обработка, в которой происходят перенаправления вызовов. Именно сюда (в модуль обработки) "всплывает" перехваченный вызов.
- Обработка 'ОбработчикПерехвата'. Содержит пользовательские обработчики методов перехваченного контекста.
Вот один из вариантов, как мог бы выглядеть перехваченный метод Запроса 'Выполнить':
(Имена пользовательских процедур/функций строятся по принципу Событие_ТипКонтекста_ИмяМетода).
Как работает перехват?
Предлагаю для простоты считать, что мы имеем дело с объектом Запрос. Как сказано выше, перехват устанавливается глобально на весь рабочий процесс, в котором он вызван, а это значит, что все базы, которые подключены к этому процессу будут иметь дело с измененным методом "IContextExtImpBase::call" интерфейса. В момент вызова какого-либо метода объекта Запрос, выполняется поиск обработки с именем '_proxyInterceptor' в соответствующей базе. Если таковая существует, то выполняется поиск экспортного метода 'ВызватьПерехватчик'. Далее либо отрабатывает переопределенный метод из обработки 'ОбработчикПерехвата', либо выполняется вызов оригинального метода. Если обработки '_proxyInterceptor' не существует, то происходит вызов оригинального метода.
Важно (1)! Чтобы упростить реализацию, я предполагаю, что право на использование обработки '_proxyInterceptor' есть у каждого пользователя, во всех базах, где она присутствует. Это критично. Если это не так хотя бы для какого-то пользователя, в какой-либо базе, где есть эта обработка, то произойдет перезагрузка серверного процесса (если будет использован объект Запрос). Так же имеет смысл выдать право на использование обработки "ОбработчикПерехвата'. В случае отсутствия прав у пользователя, будет просто вызван стандартный метод.
Важно (2)! Всё вышесказанное, тестировалось на релизе 8.3.9.1818. Скорее всего, на релизах 8.3.9.xxxx тоже будет работать (требует проверки). Про поддержку других релизов имеет смысл говорить, когда будет понимание, нужна ли данная "конструкция" вообще.
Ограничения.
Что не удалось реализовать? "Нормальную" обработку 1С-х исключений. (Тут я привожу технические подробности, для понимания процесса.) В момент, когда тестовая база была практически готова, выяснилось, что при возникновении 1С-го исключения (например синтаксическая ошибка в тексте запроса) всё рушилось, т.е. рабочий процесс на сервере перегружался.
Поэтому, пришлось сделать рефакторинг 1С-го кода и обернуть критичные участки в Попытка. Исключение.
Далее. Если ошибка возникла при обработке оригинального события,
то устанавливается "флаг" ошибки и, уже в бинарном коде, повторно вызывается оригинальное событие, чтобы сгенерировать 1С-е исключение.
Если ошибка произошла внутри модуля обработчика, например, внутри функции,
В заключении, хочу обратить внимание на ряд технических моментов, которые могут быть полезны при использовании DynamicWrapperX. Должен сказать, что с момента выхода DynamicWrapperX под x64, прошло более 2-х лет. И до последнего времени я и сам на x64-серверах ничего путного, кроме как Sleep, не использовал. Здесь, в тестовой базе, можно посмотреть другие варианты применения этого Com-объекта. Например, передача параметров через "слой" 'бинарный код-1С' и наоборот, получение функций-оберток, использование интерфейсов COM-объектов, прочее.
Сегодня попробую рассказать о том, какие методы работы со звуком доступны в 1С.
Штатные средства
На самом деле, штатных средств для работы со звуком, насколько я знаю, в 1С ровно одно — убогая процедура «Сигнал».
Она не принимает никакие параметры и просто издает системный звук.
Штатные методы закончились, а из нештатных есть следующие варианты.
Microsoft Speech API (SAPI)
Разработку Microsoft для синтеза голоса вполне можно использовать для воспроизведения wav-файлов, делается это так:
Кроме этого с помощью Speech API можно реализовать голосовое произнесение какого-либо текста — функция «Speak», параметр — строка текста, сама технология поддерживает 26 языков, включая русский, но, насколько я знаю, для поддержки русского необходимо скачивать и устанавливать специальную библиотеку (возможно, позднее разберусь с этим и напишу статью). Почитать подробнее об функционале Speech API можно на MSDN (на английском).
DynamicWrapperX
DynamicWrapperX — достаточно известная в некоторых кругах библиотека, которая позволяет зарегистрировать и использовать функции какой-либо другой библиотеки. Саму DynamicWrapperX можно скачать на сайте автора.
С помощью DynamicWrapperX можно зарегистрировать «sndPlaySoundA» из библиотеки «winmm.dll» и использовать эту функцию для воспроизведения wav-файлов.
Это может выглядеть так:
В этом примере подразумевается наличие макета «dynwrapx» (тип — двоичные данные). Для успешной регистрации библиотеки (DynamicWrapperX), 1С нужно запустить от имени администратора.
И, наконец, последний на сегодня способ — при помощи html-тега «bgsound». На форме должен быть элемент «Поле HTML документа». Насколько я понял, этот элемент должен именно быть виден на форме, т.к. у меня звук не воспроизводился если я скрывал это элемент (Видимость=Ложь).
Главный плюс этого способа — возможность воспроизвести mp3-файлы (а вот wav-файлы, наоборот нельзя), а в минусы можно записать необходимость присутствия на форме поля HTML документа (если я не ошибаюсь) и зависимость от настроек Internet Explorer-а (в настройках Explorer-а должно быть включено воспроизведение звуков страницы).
DynamicWrapperX — это ActiveX компонент (СОМ-сервер), созданный по мотивам DynamicWrapper, как попытка более полной реализации идеи. Он предоставляет возможность в скриптах на JScript и VBScript регистрировать в качестве методов объекта и затем вызывать функции, экспортируемые dll-библиотеками, в частности функции Windows API. Версия 1 работает под Windows 98. С версии 2 также возможна регистрация функции по её адресу в памяти и регистрация машинного кода функции, представленного в виде хекс-строки. Этот компонент не является модификацией оригинального кода DynamicWrapper, он написан с нуля на языке ассемблера GoAsm. Версия 2.2 тестировалась под Windows XP SP3, Windows 7 SP2, Windows 8.1, Windows 10.
При решении одной задачи столкнулся с трудностью, в 64-разрядной версии Windows 8.1 никак не получалось зарегистрировать библиотеку dynwrap.dll.
Благо нашел отличную разработку от Юрия Попова
DynamicWrapperX 2.2
Распространяется с согласия автора абсолютно бесплатно.
Корректно работает в том числе под 64-разрядной Windows 10.
Версия 2.2 тестировалась под Windows XP SP3, Windows 7 SP2, Windows 8.1, Windows 10.
Версия 1.0 работает под Windows 98.
Нововведения в версии 1.0.0.0 по отношению к DynamicWrapper:
- Добавлена возможность регистрации компонента в системе для текущего пользователя. Это может пригодиться, если у пользователя нет прав администратора.
- Расширен набор типов входных параметров и возвращаемых значений.
- Добавлены выходные параметры.
- Унифицирована работа со строками в JScript и VBScript.
- Реализован обратный вызов (callback), т.е. возможность для функций API в свою очередь вызывать функции скрипта. Последнее нужно для использования таких API-функций, как EnumWindows.
- Под Windows 98 отпала необходимость создавать отдельный объект для каждой используемой функции.
- Добавлены методы NumGet, NumPut, StrPtr, StrGet, Space.
Изменения в версии 2 по отношению к версии 1:
Изменения в версии 2.1 по отношению к версии 2.0:
- Добавлено: методы LastError, MemZero, MemCopy, MemRead, MemWrite.
- Добавлено: необязательный параметр Offset для StrGet и StrPut.
- Добавлено: возможность использования имён кодировок — "utf-8" и т.п. — в StrPtr, StrGet и StrPut.
- Изменено: необязательный параметр Offset в NumGet и NumPut может опускаться из середины списка параметров.
- версия 2.1.1.0 собрана без сжатия компрессором Mpress для избежания ложных срабатываний некоторых антивирусов.
- в версии 2.1.1.1 исправлена ошибка в методе LastError, вызывавшая исключение при его вызове с аргументом 1, если описание для кода ошибки не было найдено.
В версию 2.2 добавлен флаговый параметр в метод RegisterCallback для указания соглашения вызова callback-функции.
В прикрепленных файлах версия 32-разрядная, 64-разрядная и мануал по установке и использованию от разработчика на английском и русском языках.
Коллеги, предлагаю еще один вариант получения картинки из буфера обмена.
Несмотря на то, что на Инфостарте есть ряд разработок по этой теме,
возможно, кому-то пригодится данная обработка.
Поддерживается работа на платформе 8.3, в режимах толстого клиента обычного приложения, толстого/тонкого клиентов управляемого приложения. Клиенты x32/x64.
Несколько слов о реализации. В процессе разработки, сначала был подготовлен вариант с использованием временного файла. Т.е. картинка из буфера сохранялась на диск и уже потом загружалась в 1С. Всё работало без проблем. Но сама по себе схема, с использованием файловых операций, не вызывала особого восторга. Поэтому, была предпринята попытка отказаться от данного метода.
Вариант, с использованием потока, мне показался лучше. Но встал вопрос, о том, каким образом живые данные из потока переместить в 1С-й объект картинка? В одной из приведенных выше разработок это делалось с использованием 1С-й функции глобального контекста «Base64Значение()». Оставалось только загнать данные в Base64-строку. Откровенно говоря, писать свою собственную функцию для кодирования данных, совсем не хотелось. Более того, было понимание, что в самом движке 1С, эти функции уже реализованы. И это действительно так. В core83.dll есть несколько вариантов экспортной функции encode_base64 (аналог Base64Строка()). Таким образом, вызывая соответствующую функцию и передавая ей данные потока, мы получаем на выходе строку в кодировке Base64.
Дополнение: версия 2.
Во всей этой истории с использованием Base64-строки огорчало то, что для получения объекта "ДвоичныеДанные" выполнялась абсолютно не нужная работа по кодированию/раскодированию данных. В версии 2 данные потока прописываются напрямую в предварительно созданный объект "ДвоичныеДанные". Благодаря этому, мы получаем полноценный объект BinaryData с актуальными данными, без каких-либо дополнительных манипуляций.
Тут же: вместо PNG формата используется JPEG.
Дополнение: версия 3.
Добавлена поддержка Web-клиента x32/x64 (IE). Как показала практика, вариант с передачей данных через строку Base64, является наиболее универсальным. Код функции для кодирования данных в Base64, оказался не таким большим, как это виделось изначально. Буквально несколько десятков байт. Таким образом, это позволило отказаться от использования экспортной функции encode_base64 (версия 1), упростить код и в тоже самое время добавить поддержку web-клиента.
Тестирование проводилось выборочно на релизах начиная с 8.3.4.437 по 8.3.11.2924 (последний на момент публикации).
Читайте также: