DynamicWrapperX 2.0
Автор: Юрий Попов
Лицензия: freeware
Содержание
- Введение
- Регистрация в системе
- Встроенные методы
- Входные параметры и возвращаемые значения
- Выходные параметры
- О строках
Введение
DynamicWrapperX — это ActiveX компонент (СОМ-сервер), написанный мной по мотивам DynamicWrapper как попытка более полной реализации идеи. Он предоставляет возможность в скриптах на JScript и VBScript регистрировать в качестве методов объекта и затем вызывать функции, экспортируемые dll-библиотеками, в частности функции Windows API. С версии 2 также возможна регистрация функции по её адресу в памяти и регистрация машинного кода функции, представленного в виде хекс-строки. Этот компонент не является модификацией оригинального кода DynamicWrapper, он написан с нуля на языке ассемблера GoAsm. Версия 2.0 тестировалась под Windows XP SP3, Windows 7 SP1 и Windows 8.1.
Нововведения в версии 1 по отношению к DynamicWrapper:
- Добавлена возможность регистрации компонента в системе для текущего пользователя. Это может пригодиться, если у пользователя нет прав администратора.
- Расширен набор типов входных параметров и возвращаемых значений.
- Добавлены выходные параметры.
- Унифицирована работа со строками в JScript и VBScript.
- Реализован обратный вызов (callback), т.е. возможность для функций API в свою очередь вызывать функции скрипта. Последнее нужно для использования таких API-функций, как EnumWindows (см. примеры ниже).
- Под Windows 98 отпала необходимость создавать отдельный объект для каждой используемой функции.
- Добавлено несколько полезных, на мой взгляд, встроенных методов.
Изменения в версии 2 по отношению к версии 1:
- Компонент не тестировался под Windows 98 и официально её не поддерживает.
- Доступны два варианта: x86 и x64.
- Изменён CLSID компонента.
- При установке и удалении проверяется наличие 1-й версии; дальнейшее зависит от результатов проверки (см. раздел Регистрация в системе).
- Расширен набор типов параметров и возвращаемых значений: добавлены типы m (Int64), q (UInt64), v (VARIANT).
- Количество callback-функций теперь неограничено.
- Расширены возможности метода Register (регистрация функции по ординалу, регистрация под другим именем, добавлен флаговый параметр "f=").
- Добавлены встроенные методы RegisterAddr, RegisterCode, StrPut, ObjPtr, ObjGet, ArrPtr, VarPtr, MemAlloc, MemFree, Bitness, Version.
- В методах StrPtr, StrGet, StrPut вместо типа строки можно прямо указать её кодировку.
---------------------------------------------------------------------------
P.S. Хотя я и постарался протестировать компонент в различных ситуациях, идеальную его работу гарантировать не могу. Буду благодарен за сообщения на форуме о глюках и багах, буде такие обнаружатся.
Регистрация в системе
Зарегистрировать компонент можно двумя способами:
regsvr32.exe <путь-к-компоненту>\dynwrapx.dll — для всех пользователей.
regsvr32.exe /i <путь-к-компоненту>\dynwrapx.dll — для текущего пользователя.
Если dynwrapx.dll лежит в папке System32, System, Windows, в текущей, или в одной из тех, что перечислены в переменной окружения Path, путь можно не указывать. При регистрации для текущего пользователя объект будет доступен только этому пользователю. Кроме того, в Windows Vista, Windows 7 и Windows 8 он будет недоступен для процессов, выполняющихся с правами администратора.
В 64-битной системе нужно зарегистрировать оба варианта, x86 и x64, чтобы компонент могли использовать как 64-, так и 32-битные приложения.
Если в системе уже зарегистрирована 1-я версия, 2-я регистрируется параллельно с ней. В этом случае она будет доступна в скриптах только по имени DynamicWrapperX.2. Чтобы заменить старую версию новой, нужно сначала разрегистрировать старую. В этом случае новая версия будет доступна также под именем DynamicWrapperX.
Разрегистрировать компонент можно так:
regsvr32.exe /u <путь-к-компоненту>\dynwrapx.dll — для всех пользователей.
regsvr32.exe /u /i <путь-к-компоненту>\dynwrapx.dll — для текущего пользователя.
Если 2-я версия была установлена параллельно с 1-й, её разрегистрация никак не затронет регистрацию 1-й (и наоборот).
Встроенные методы объекта
(щёлкните по номеру, чтобы перейти к подробному описанию)
[1]  Register( DllName[:Ordinal/:FuncName], FuncName [, i=ParamTypes] [, r=RetValType] [, f=Flags] ) — регистрация функции из DLL в качестве метода объекта.
[2]  RegisterAddr( Addr, FuncName, [, i=ParamTypes] [, r=RetValType] [, f=Flags] ) — регистрация функции по её адресу в памяти.
[3]  RegisterCode( HexStr [, FuncName] [, i=ParamTypes] [, r=RetValType] [, f=Flags] ) — регистрация машинного кода функции, представленного в виде хекс-строки.
[4]  RegisterCallback( FuncRef [, i=ParamTypes] [, r=RetValType] ) — регистрация скриптовой функции как callback-функции.
[5]  NumGet( Address [, Offset] [, Type] ) — чтение числа из памяти по заданному адресу.
[6]  NumPut( Var, Address [, Offset] [, Type] ) — запись числа в память по заданному адресу.
[7]  StrPtr( Var [, Type/Codepage] ) — получение указателя на строку в виде числовой переменной.
[8]  StrGet( Address [, Type/Codepage] ) — считывание строки по заданному адресу (указателю).
[9]  StrPut( Str, Address [, Type/Codepage] ) — запись строки по указанному адресу.
[10]  Space( Count [, Char] ) — создание строки заданной длины.
[11]  ObjPtr( Object ) — получение указателя на объект.
[12]  ObjGet( ObjPtr ) — получение объекта по указателю на него.
[13]  ArrPtr( Array ) — получение указателя на структуру SAFEARRAY массива.
[14]  VarPtr( Variable ) — получение указателя на структуру VARIANT скриптовой переменной.
[15]  MemAlloc( Bytes [, ZeroMem] ) — выделение памяти.
[16]  MemFree( MemPtr ) — освобождение памяти.
[17]  Bitness() — получение битности (32 или 64) используемого объекта DynamicWrapperX.
[18]  Version( [Field] ) — возвращает указанное поле/поля из полного номера версии DynamicWrapperX.
Метод Register
Этот метод регистрирует функцию, находящуюся в указанной dll-библиотеке, в качестве метода объекта. После этого она может вызываться так же, как и встроенные методы, по своему имени через точку. Первый параметр — имя библиотеки; второй — имя функции. Наличие остальных параметров зависит от функции, порядок их произвольный, т.к. они различаются по префиксу. Префикс i= обозначает параметры функции, r= — возвращаемое значение, f= — флаги, влияющие на то, как будет вызываться функция (см. описание ниже).
JScript
DWX = new ActiveXObject("DynamicWrapperX"); // Создание объекта. DWX.Register("user32.dll", "MessageBoxW", "i=hwwu", "r=l"); // Регистрация функции из dll. res = DWX.MessageBoxW(0, "Hello, world!", "Test", 4); // Вызов.
VBScript
Set DWX = CreateObject("DynamicWrapperX") ' Создание объекта. DWX.Register "user32.dll", "MessageBoxW", "i=hwwu", "r=l" ' Регистрация функции из dll. res = DWX.MessageBoxW(0, "Hello, world!", "Test", 4) ' Вызов.
Имя библиотеки, указанное без пути к ней, означает поиск по имени — сначала в памяти процесса среди уже загруженных библиотек (в порядке их загрузки), затем на диске. В Windows XP порядок поиска на диске будет таким:
- Каталог приложения (в данном случае приложение — это либо wscript.exe, либо cscript.exe)
- Текущий каталог
- System32
- System
- Windows
- Каталоги, перечисленные в переменной окружения Path
Если библиотека находится в файле с расширением dll, расширение указывать не обязательно, т.е. выше можно было бы написать просто "user32". Если библиотека находится в файле без расширения, в конце имени нужно поставить точку, например: "mylib."
Если функция регистрируется по её ординалу, он указывается рядом с именем библиотеки через двоеточие. То же самое, если функция регистрируется с заменой имени. Последнее может пригодиться, если имя функции содержит символы, недопустимые для имён методов в используемом скриптовом языке.
Учтите, что ординал функции может быть разным в разных версиях библиотеки и в x86 и x64 вариантах одной и той же версии.
Ординал можно задавать десятичным или шестнадцатеричным числом (с префиксом 0x).
JScript
DWX.Register("user32:110", "MethodName", "i=hwwu", "r=l"); // Регистрация функции по ординалу. DWX.Register("user32:BadName", "GoodName", "i=hwwu", "r=l"; // Регистрация с заменой имени.
VBScript
DWX.Register "user32:110", "MethodName", "i=hwwu", "r=l" ' Регистрация функции по ординалу. DWX.Register "user32:BadName", "GoodName", "i=hwwu", "r=l" ' Регистрация с заменой имени.
Имя функции в Windows API может иметь варианты. Например, функция, описанная в документации как MessageBox, реально присутствует в user32.dll в двух воплощениях: MessageBoxW (для строк в Юникоде) и MessageBoxA (для строк в традиционной 8-битной кодировке ANSI). Следуя традиции, я сохранил логику поиска функций прежней: например, если указано имя MessageBox и такой функции в user32.dll не будет найдено, то поиск автоматически повторяется для варианта MessageBoxA. Имена юникодовских функций нужно указывать точно, с буквой W на конце.
Список параметров можно опускать, только если функция таковых не имеет, при этом не нужно оставлять на этом месте запятую.
Возвращаемое значение, если оно не требуется, можно опускать независимо от того, возвращает что-то функция или нет.
JScript
DWX = new ActiveXObject("DynamicWrapperX"); DWX.Register("kernel32", "GetCommandLine", "r=s"); // У этой функции нет параметров. CmdLine = DWX.GetCommandLine(); // Команда, запустившая данный процесс. WScript.Echo(CmdLine);
VBScript
Set DWX = CreateObject("DynamicWrapperX") DWX.Register "kernel32", "Beep", "i=uu" ' Функция Beep возвращает значение, но оно не нужно. DWX.Beep 800, 1000 ' Звук через динамик системного блока.
Флаговый параметр на данный момент имеет только одно возможное значение "t", обозначающее, что функция использует соглашение вызова thiscall. Оно принимается во внимание только 32-битным DynamicWrapperX, а 64-битным игнорируется.
JScript
DWX.Register("SomeLib", "SomeFunc", "i=hwwu", "r=l", "f=t"); // Использовать thiscall при вызове функции.
VBScript
DWX.Register "SomeLib", "SomeFunc", "i=hwwu", "r=l", "f=t" ' Использовать thiscall при вызове функции.
Метод RegisterAddr
Этот метод регистрирует функцию по её адресу в памяти. Остальные параметры те же, что и у Register.
JScript
DWX.RegisterAddr(Address, "FuncName", "i=hwwu", "r=l");
VBScript
DWX.RegisterAddr Address, "FuncName", "i=hwwu", "r=l"
Метод RegisterCode
Метод принимает в качестве первого аргумента машинный код функции, представленный в виде хекс-строки. Под код выделяется память, и он записывается туда уже в бинарном виде. После чего его можно вызывать по присвоенному ему во втором аргументе имени. Возвращаемое значение содержит адрес кода в памяти. Машинный код для 32 и 64 бит различен, отсюда необходимость проверки битности процесса (через проверку битности объекта DynamicWrapperX), чтобы определить, какой код использовать. В качестве возможного применения для такого кода можно привести обработку больших объёмов данных. Машинный код может работать в десятки или даже сотни раз быстрее скриптового.
Если вы хотите использовать код по его адресу, без создания метода, можно опустить все аргументы кроме первого. Но имейте в виду, что выделенная под код память всё же связана с объектом и будет освобождена в случае его уничтожения.
Хекс-строка может быть сплошной или содержать пробелы, табуляции и переводы строки.
JScript
DWX = new ActiveXObject("DynamicWrapperX"); if (DWX.Bitness == 32) { Code = "8B442404F76C2408C3" // Функция возвращает произведение своих аргументов. } else { Code = "4889C8 48F7EA C3" // mov rax,rcx; imul rdx; ret } CodeAddr = DWX.RegisterCode(Code, "Multiply", "i=ll", "r=l"); WScript.Echo(DWX.Multiply(5, 4));
VBScript
Set DWX = CreateObject("DynamicWrapperX") If DWX.Bitness = 32 Then Code = "8B442404F76C2408C3" ' Функция возвращает произведение своих аргументов. Else Code = "4889C8 48F7EA C3" ' mov rax,rcx; imul rdx; ret End If CodeAddr = DWX.RegisterCode(Code, "Multiply", "i=ll", "r=l") WScript.Echo(DWX.Multiply(5, 4))
Комментарии можно поместить прямо в хекс-строку, обрамив их скобками.
Code = "4889C8 (mov rax,rcx) 48F7EA (imul rdx) C3 (ret)"Если хекс-строка является многострочным текстом, комментарии можно обозначать точкой с запятой.
JScript
Code = "4889C8 ; mov rax,rcx \n" + "48F7EA ; imul rdx \n" + "C3 ; ret"VBScript
Code = "4889C8 ; mov rax,rcx" & vbCrLf & _ "48F7EA ; imul rdx" & vbCrLf & _ "C3 ; ret"
Но учтите, что в этом случае каждая строка, кроме последней, должна иметь на конце перевод строки, т.к. он используется как признак конца комментария.
Типы входных параметров и возвращаемых значений
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-битными числами. До тех пор, пока значение возвращаемого функцией числа это позволяет, DynamicWrapperX преобразует его к типам 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).
DWX.Register("lib.dll", "func", "i=m") DWX.func("0xFFFFFFFFFFFFFFFF") DWX.func("-0x7FFFFFFFFFFFFFFF") DWX.func("18446744073709551615") DWX.func("-9223372036854775807")
Выходные параметры
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. В этом случае DynamicWrapperX может передать функции указатель на значение переменной, которое функция может изменить. В движках, где аргументы передаются по значению, как в JScript, методу передаётся копия числа, поэтому изменить оригинал нет возможности. В этом случае решением будет выделение нужного количества памяти, например, методом MemAlloc, передача функции указателя на эту память (используя тип p) и затем, после вызова функции, считывание помещённого ею туда числа методом NumGet.
Некоторые движки копируют строки при передаче их в метод; в этом случае использование выходных типов для строк также теряет смысл. Решение аналогично: выделение памяти, передача указателя на неё типом p и затем считывание оттуда строки методом StrGet.
О строках
В JScript и VBScript используются строки типа BSTR. Это строка в Юникоде, т.е. код каждого символа занимает 2 байта. За последним символом расположен ограничитель - 2 нулевых байта. Кроме того, перед первым символом находится 4-байтное число, содержащее длину строки в байтах (без учёта нулевых на конце). Указатель, содержащийся в скриптовых строковых переменных, является адресом первого символа такой строки (т.е. байты, содержащие длину строки, как бы остаются за кадром).
Передать строку можно тремя различными способами:
1) Входная строка: w, s, z. Для типов s и z строка копируется с конвертацией в соответствующую кодировку, и API-функция получает указатель на эту копию. После завершения работы функции память, запрошенная под копию строки, освобождается, т.е. эта копия перестаёт существовать. В случае w функции передаётся указатель на оригинал строки.
2) Выходная строка: W, S, Z. Передаётся указатель на оригинал строки, но S и Z предварительно конвертируются (на месте). После завершения функции строки S и Z конвертируются обратно в Юникод, у них измеряется длина, а у строки W только измеряется длина. Длина (в байтах) записывается перед строкой. Без этой последней операции потом в скрипте будут глюки, например, при конкатенации этой строки с другими.
3) Указатель: p. Это самый простой способ. Передаётся указатель на оригинал строки, без конвертации. После завершения функции нет ни конвертации, ни корректировки длины. Так что если функция туда что-то запишет, оно там будет в нетронутом виде. Здесь строка фактически является просто буфером в памяти для хранения любых данных.
Может показаться, что это то же самое, что w, но это не так. Разница в том, что параметр, объявленный как p, принимает не только строковые переменные, но и числовые.
Возвращая строку как p, получаем числовую переменную, содержащую указатель на строку, которую вернула функция. Возвращая строку как w, s или z, получаем строковую переменную, содержащую указатель на копию этой строки. Строки s и z копируются с конвертацией в Юникод.
Вызовы API, принимающие строковые аргументы, как правило, представлены в двух вариантах - например, MessageBoxA и MessageBoxW. Использование в скриптах юникодовских вариантов, с окончанием W, представляется более логичным, т.к. при этом нет дополнительных перекодировок строк туда и обратно.
Выходная строка, как и прочие выходные параметры, предназначена для того, чтобы функция API в неё что-нибудь записала, так что её длина должна быть соответствующей.
Использование строк как буферов памяти может работать или нет в зависимости от того, как внутри скриптового движка реализована работа со строками. Предпочтительнее выделять память методом MemAlloc, т.к. она будет под вашим полным контролем.
Метод RegisterCallback
Этот метод нужен для получения указателя на скриптовую функцию, пригодного для передачи какой-либо функции API. Которая потом, используя этот указатель, могла бы данную скриптовую функцию вызывать. Примером API-функции, которая этого требует, может служить EnumWindows. Она перебирает существующие окна и хэндл каждого окна передаёт callback-функции в качестве параметра. После этого она ждёт, что вернёт callback-функция. Если 1, то перебор идёт дальше, если 0 — прекращается.
В JScript и VBScript функция является объектом и ссылка на неё для таких целей не годится. Поэтому ссылка на скриптовую функцию сначала передаётся методу RegisterCallback, а API-функция получает возвращённый им указатель на одну из вспомогательных процедур внутри dynwrapx.dll, вызовы которой будут транслироваться в вызовы скриптовой функции, а возвращаемые значения — в обратном направлении.
В JScript ссылка на функцию — это её имя, только без скобок, а в VBScript сначала нужно использовать GetRef. Кроме ссылки на функцию задаются также типы её параметров и возвращаемого значения — аналогично методу Register (но используются только маленькие буквы).
JScript
DWX = new ActiveXObject("DynamicWrapperX"); DWX.Register("user32", "EnumWindows", "i=ph"); DWX.Register("user32", "GetWindowTextW", "i=hpl"); // Unicode //DWX.Register("user32", "GetWindowText", "i=hpl"); // ANSI pCbkFunc = DWX.RegisterCallback(CbkEnumWin, "i=hh", "r=l"); // Регистрация CbkEnumWin // как функции обратного // вызова и получение // указателя. n=0, m=0, WinList=""; Buf = DWX.MemAlloc(256); // Буфер под заголовок окна (выходная строка). DWX.EnumWindows(pCbkFunc, 0); // Вызов EnumWindows с передачей ей указателя на // callback-функцию. DWX.MemFree(Buf); WScript.Echo("Всего окон: " + m + "\nС заголовками: " + n + "\n\n" + WinList); // ............... Собственно callback-функция .................... function CbkEnumWin(hwnd, lparam) { var Title; DWX.GetWindowTextW(hwnd, Buf, 128); // Unicode Title = DWX.StrGet(Buf, "w"); //DWX.GetWindowText(hwnd, Buf, 256); // ANSI //Title = DWX.StrGet(Buf, "s"); if(Title.length > 0) { // Если длина заголовка больше 0, заносим в список. WinList += hwnd + "\t" + Title + "\n"; ++n; } ++m; return 1; // Если вернуть 0, вызовы прекратятся. }
VBScript
Set DWX = CreateObject("DynamicWrapperX") DWX.Register "user32", "EnumWindows", "i=ph" DWX.Register "user32", "GetWindowTextW", "i=hpl" ' Unicode 'DWX.Register "user32", "GetWindowText", "i=hpl" ' ANSI Set Ref = GetRef("CbkEnumWin") ' Получение ссылки на функцию. pCbkFunc = DWX.RegisterCallback(Ref, "i=hh", "r=l") ' Регистрация CbkEnumWin ' как функции обратного вызова ' и получение указателя. n = 0 : m = 0 : WinList = "" Buf = DWX.MemAlloc(256) ' Буфер под заголовок окна (выходная строка). DWX.EnumWindows pCbkFunc, 0 ' Вызов EnumWindows с передачей ей указателя на ' callback-функцию. DWX.MemFree Buf WScript.Echo "Всего окон: " & m & vbCrLf & "С заголовками: " & n & _ vbCrLf & vbCrLf & WinList ' ................ Собственно callback-функция ....................... Function CbkEnumWin(hwnd, lparam) DWX.GetWindowTextW hwnd, Buf, 128 ' Unicode Title = DWX.StrGet(Buf, "w") 'DWX.GetWindowText hwnd, Buf, 256 ' ANSI 'Title = DWX.StrGet(Buf, "s") If Len(Title) > 0 Then ' Если длина заголовка больше 0, заносим в список. WinList = WinList & hwnd & vbTab & Title & vbCrLf n = n+1 End If m = m+1 CbkEnumWin = 1 ' Если вернуть 0, вызовы прекратятся. End Function
Дополнительные методы
NB: параметры в квадратных скобках необязательны, но нельзя опускать такой параметр, если следующий за ним вы указываете.
NumGet( Address [, Offset] [, Type] ) — чтение числа из памяти. Address — базовый адрес. Offset — смещение от него (в байтах), положительное или отрицательное: его можно применять в циклах для считывания/записи последовательно расположенных чисел; по умолчанию 0. Type — тип считываемых данных: те же буквенные обозначения, что и для метода Register, но используются только маленькие буквы; по умолчанию — l. Считанные данные помещаются в возвращаемое методом значение.
NumPut( Var, Address [, Offset] [, Type] ) — запись числа в память. Var — либо литеральное числовое значение, либо переменная, содержащая значение, которое нужно записать. Остальное — как в NumGet. Возвращаемое методом значение содержит адрес сразу за последним записанным байтом.
В обоих методах Address может быть как числом, так и строкой, в последнем случае в качестве базового адреса используется указатель на эту строку. Это позволяет использовать строки в качестве буфера для данных, размещать там массивы, структуры и т.п.
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: Изначально этот метод был задуман для создания строк, которые впоследствии будут использованы как буферы памяти — для чисел, структур, выходных строк. Но, как выяснилось на практике, такое использование строк в общем случае ненадёжно. Контроль над памятью, выделенной под строку, отдаётся скриптовому движку, и если он в процессе своей работы произведёт с ней какие-то манипуляции, помещённые вами в неё данные могут быть потеряны или испорчены. Поэтому я рекомендую для выделения памяти под буфер использовать метод 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 байта.
Примеры использования методов
JScript
DWX = new ActiveXObject("DynamicWrapperX"); str = "Hello, world! Это я."; // Чтение из памяти. Читаются коды символов строки. codes = ""; for(i=0; i < str.length; ++i) codes += DWX.NumGet(str, i*2, "t") + " "; // i умножается на 2, т.к. смещение должно // быть в байтах, а тип "t" - двухбайтный. WScript.Echo("Коды символов:\n" + codes); // Чтение и запись. Строка записывается в обратном порядке символов. len = str.length; buf = DWX.MemAlloc(len * 2 + 2); // Буфер для записи. Каждый символ Юникода = 2 байта. DWX.NumPut(0, buf, len * 2, "t"); // Записать 2 нулевых байта в конец (терминатор строки). for(i=0, j=len-1; i < len; ++i, --j) { // По смещению len-1 символов находится последний символ. code = DWX.NumGet(str, i*2, "t"); // Читаем слева направо (смещение растёт). DWX.NumPut(code, buf, j*2, "t"); // Пишем справа налево (смещение уменьшается). } str1 = DWX.StrGet(buf); DWX.MemFree(buf); WScript.Echo("Строка наоборот:\n" + str1); // Операции со строками. ptr = DWX.StrPtr(str); // Получение указателя на строку в виде числа, // строка остаётся в Юникоде. WScript.Echo("Адрес строки: " + ptr); ptr = DWX.StrPtr(str, "z"); // Получение указателя на ту же строку, // предварительно конвертированную в OEM/DOS. str1 = DWX.StrGet(ptr, "z"); // Чтение этой строки по указателю, // с обратной конвертацией. WScript.Echo("Восстановленная строка:\n" + str1); // Создать копию строки в UTF-8. bufsize = DWX.StrPut(str1, 0, "cp65001"); buf = DWX.MemAlloc(bufsize); DWX.StrPut(str1, buf, "cp65001"); // Прочитать обратно в Юникод. str2 = DWX.StrGet(buf, "cp65001"); DWX.MemFree(buf); WScript.Echo("Прочитано из UTF-8:\n" + str2);
VBScript
Set DWX = CreateObject("DynamicWrapperX") str = "Hello, world! Это я." ' Чтение из памяти. Читаются коды символов строки. strlen = Len(str) codes = "" For i=0 To strlen-1 codes = codes & DWX.NumGet(str, i*2, "t") & " " ' i умножается на 2, т.к. смещение должно Next ' быть в байтах, а тип "t" - двухбайтный. WScript.Echo "Коды символов:" & vbCrLf & codes ' Чтение и запись. Строка записывается в обратном порядке символов. buf = DWX.MemAlloc(strlen * 2 + 2) ' Буфер для записи. Каждый символ Юникода = 2 байта. DWX.NumPut 0, buf, strlen * 2, "t" ' Записать 2 нулевых байта в конец (терминатор строки). j = strlen-1 ' По смещению strlen-1 символов находится последний символ. For i=0 To strlen-1 code = DWX.NumGet(str, i*2, "t") ' Читаем слева направо (смещение растёт). DWX.NumPut code, buf, j*2, "t" ' Пишем справа налево (смещение уменьшается). j = j-1 Next str1 = DWX.StrGet(buf) DWX.MemFree buf WScript.Echo "Строка наоборот:" & vbCrLf & str1 ' Операции со строками. ptr = DWX.StrPtr(str) ' Получение указателя на строку в виде числа, ' строка остаётся в Юникоде. WScript.Echo "Адрес строки: " & ptr ptr = DWX.StrPtr(str, "z") ' Получение указателя на ту же строку, ' предварительно конвертированную в OEM/DOS. str1 = DWX.StrGet(ptr, "z") ' Чтение этой строки по указателю, с обратной конвертацией. WScript.Echo "Восстановленная строка:" & vbCrLf & str1 ' Создать копию строки в UTF-8. bufsize = DWX.StrPut(str1, 0, "cp65001") buf = DWX.MemAlloc(bufsize) DWX.StrPut str1, buf, "cp65001" ' Прочитать обратно в Юникод. str2 = DWX.StrGet(buf, "cp65001") DWX.MemFree buf WScript.Echo "Прочитано из UTF-8:" & vbCrLf & str2