Тема: WSH: обсуждаем DynamicWrapperX от YMP - 2
Продолжение темы: WSH: обсуждаем DynamicWrapperX от YMP.
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Серый форум → Общение → Windows Script Host, HTA (VBScript, JScript) → WSH: обсуждаем DynamicWrapperX от YMP - 2
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Продолжение темы: WSH: обсуждаем DynamicWrapperX от YMP.
Комрады, я продолжаю успешно использовать эту замечательную компоненту.
Вопрос у меня вот какой, будет ли кому интересен опыт допиливания ее, с возможность обработки SEH/VEH исключений.
Дело в том, что я активно занимаюсь перехватом различных функций внутри 1С-ки (версий 7.7 и 8.2) для расширения возможностей движков. Все нормально работает, пока в 1С не возникает нештатная ситуация. В обычном режиме (без перехвата), 1С-ка ругается на что-то, допустим на отсутствие какого-нить свойства объекта, выбрасывает предупреждение и далее продолжает нормально работать. Если вдруг это происходит в перехваченной функции, то 1С-ка после этого вылетает в космос.
Я давно хотел обернуть , IDispatch::Invoke в "транзакционные" скобки типа catch-try, даже проводил какие-то исследования в этом направлении, но вопрос мне казался через чур сложным, так что я бросал это занятие и обходил проблему какими-то другими способами. Но, в этот раз, все таки решил дожать ситуацию и вроде как это не очень и сложно оказалось.
Жду отклика.
Вы пишите, а мы почитаем. :-)
Для демонстрации ситуации, которая может привести к краху программы, предлагаю рассмотреть следующий пример.
Попробуем выполнить с помощью DWX следующий код:
XOR EAX,EAX
MOV EAX,DWORD PTR DS:[EAX]
Вот скрипт, который выполнит данный код, после нажатия на кнопку "Test":
<html>
<head>
<script language="vbscript">
Public Const HEAP_ZERO_MEMORY = &h8
Public Const PAGE_EXECUTE_READWRITE = &h40
Public Wrap
Public oServ
Class Service
Private hHeap
Private Ref
Private buf
Private Sub Class_Initialize
Set Wrap = CreateObject("DynamicWrapperX")
Wrap.Register "kernel32", "HeapAlloc", "i=lll", "r=l"
Wrap.Register "Kernel32", "HeapFree", "i=lll","r=l"
Wrap.Register "kernel32", "GetProcessHeap", "r=l"
Wrap.Register "kernel32", "VirtualProtect" , "i=lllp", "r=l"
hHeap = Wrap.GetProcessHeap()
buf = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4)
res = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4)
Wrap.VirtualProtect buf, 4, PAGE_EXECUTE_READWRITE, res
Wrap.NumPut &hC033, buf, 0, "n" 'XOR EAX,EAX
Wrap.NumPut &h008B, buf, 2, "n" 'MOV EAX,DWORD PTR DS:[EAX]
Wrap.RegisterAddr buf, "Test", "r=l"
End Sub
Private Sub Class_Terminate
Wrap.HeapFree hHeap, 0, buf
Wrap.HeapFree hHeap, 0, res
End Sub
End Class
Set oServ = New Service
Sub btnTest_onclick()
MsgBox "Test"
On Error Resume Next
Test = Wrap.Test()
MsgBox "Код ошибки: " & Hex(Err.Number)
End Sub
</script>
</head>
<body>
<button id=btnTest>Test</button>
</body>
</html>
Если этот скрипт выполнить в режиме "hta", по программа закроется, как выполнившая недопустимую операцию.
Под IE ситуация лучше - тут видимо код оборачивается в SEH по умолчанию.
Моя цель была такая - поправить DWX так, чтобы он обернул код
Test = Wrap.Test()
в SEH и по крайней мере не приводил к краху программы.
Ниже привожу код, который на лету поправляет DWX, оборачивая вызовы IDispatch::Invoke в SEH.
Код несколько длинный, но зато удобный для внесения правок, поскольку идет с комментариями.
<html>
<head>
<script language="vbscript">
Public Const HEAP_ZERO_MEMORY = &h8
Public Const PAGE_EXECUTE_READWRITE = &h40
Public Wrap
Public oServ
Class Service
Private hHeap
Private buf
Private buf1
Private Sub Class_Initialize
Set Wrap = CreateObject("DynamicWrapperX")
Wrap.Register "kernel32", "HeapAlloc", "i=lll", "r=l"
Wrap.Register "Kernel32", "HeapFree", "i=lll","r=l"
Wrap.Register "kernel32", "GetProcessHeap", "r=l"
Wrap.Register "kernel32", "VirtualProtect" , "i=lllp", "r=l"
hHeap = Wrap.GetProcessHeap()
res = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4)
pObj = Wrap.GetIDispatch(Wrap)
num = Wrap.NumGet(pObj, 4) 'Увеличим на счетчик ссылок, чтоб никогда не выгружался
Wrap.NumPut num + 1, pObj, 4
vt = Wrap.NumGet(pObj)
IDispatch_Invoke = Wrap.NumGet(vt, 6 * 4)
If Wrap.NumGet(IDispatch_Invoke, 0, "b") = &h68 Then
Exit Sub
End If
sz_buf = 140
buf = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sz_buf)
Wrap.VirtualProtect buf, sz_buf, PAGE_EXECUTE_READWRITE, res
'Оборачиваем в SEH
SEH IDispatch_Invoke, 9, buf
Wrap.VirtualProtect vt, 4, PAGE_EXECUTE_READWRITE, res
Wrap.NumPut buf, vt, 6 * 4
Wrap.VirtualProtect vt, 4, Wrap.Numget(res), res
buf1 = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4)
Wrap.VirtualProtect buf, 4, PAGE_EXECUTE_READWRITE, res
Wrap.NumPut &hC033, buf1, 0, "n" 'XOR EAX,EAX
Wrap.NumPut &h008B, buf1, 2, "n" 'MOV EAX,DWORD PTR DS:[EAX]
Wrap.RegisterAddr buf1, "Test", "r=l"
End Sub
Private Sub Class_Terminate
Wrap.HeapFree hHeap, 0, res
Wrap.HeapFree hHeap, 0, buf1
End Sub
Private Function SEH(callAddr, numParams, buf)
szParams = numParams * 4
Wrap.NumPut &h68, buf, 0, "b" 'PUSH SEH-обработчик
Wrap.NumPut buf + 52 + szParams, buf, 1 '
Wrap.NumPut &h64, buf, 5, "b" 'PUSH DWORD PTR FS:[0]
Wrap.NumPut &h35FF, buf, 6, "n" '
Wrap.NumPut 0, buf, 8 '
Wrap.NumPut &h64, buf, 12, "b" 'MOV DWORD PTR FS:[0],ESP
Wrap.NumPut &h2589, buf, 13, "n" '
Wrap.NumPut 0, buf, 15 '
Wrap.NumPut &h60, buf, 19, "b" 'PUSHAD Сохраняем регисты
For j = 0 To numParams - 1
Wrap.NumPut &hFF, buf, 20 + j * 4, "b" 'PUSH DWORD PTR SS:[ESP+(10 + numParams) * 4]
Wrap.NumPut &h74, buf, 21 + j * 4, "b"
Wrap.NumPut &h24, buf, 22 + j * 4, "b"
Wrap.NumPut 40 + szParams, buf, 23 + j * 4, "b"
Next
Wrap.NumPut &hE8, buf, 20 + szParams, "b" 'CALL ....callAddr
Wrap.NumPut callAddr - (buf +21 + szParams + 4), buf, 21 + szParams '
'Сохраняем значение ф-и в стек
Wrap.NumPut &hC483, buf, 25 + szParams, "n" 'ADD ESP,20
Wrap.NumPut &h20, buf, 27 + szParams, "b" '
Wrap.NumPut &h09EB, buf, 28 + szParams, "n" 'JMP SHORT ------------------------------------+
Wrap.NumPut &h1C2444C7, buf, 30 + szParams 'MOV DWORD PTR SS:[ESP+1C], DISP_E_EXCEPTION |
Wrap.NumPut &h80020009, buf, 34 + szParams ' |
Wrap.NumPut &h61, buf, 38 + szParams, "b" 'POPAD |
Wrap.NumPut &h64, buf, 39 + szParams, "b" 'POP DWORD PTR FS:[0] <-------------------+
Wrap.NumPut &h058F, buf, 40 + szParams, "n" '
Wrap.NumPut 0, buf, 42 + szParams '
Wrap.NumPut &hC483, buf, 46 + szParams, "n" 'ADD ESP,4
Wrap.NumPut &h4, buf, 48 + szParams, "b" '
Wrap.NumPut &hC2, buf, 49 + szParams, "b" 'RETN 24
Wrap.NumPut szParams, buf, 50 + szParams, "n" '
'___SEH
'__cdecl _except_handler(
' struct _EXCEPTION_RECORD *ExceptionRecord,
' void *EstablisherFrame,
' struct _CONTEXT *ContextRecord,
' void *DispatcherContext
' )
Wrap.NumPut &h0C24448B, buf, 52 + szParams 'MOV EAX,DWORD PTR SS:[ESP+C] pFrame
Wrap.NumPut &h08244C8B, buf, 56 + szParams 'MOV ECX,DWORD PTR SS:[ESP+8] pContext
Wrap.NumPut &hE983, buf, 60 + szParams, "n" 'SUB ECX,20 вычисляем правильную позицию стека
Wrap.NumPut &h20, buf, 62 + szParams, "b" '
'======Восстанавливаем ESP======
Wrap.NumPut &h8889, buf, 63 + szParams, "n" 'MOV DWORD PTR DS:[EAX+C4], ECX mov (CONTEXT ptr [eax]).regEsp,
Wrap.NumPut &hC4, buf, 65 + szParams '
'======Восстанавливаем EIP======
Wrap.NumPut &h80C7, buf, 69 + szParams, "n" 'MOV DWORD PTR DS:[EAX+B8], addr mov (CONTEXT ptr [eax]).regEip, offset ....
Wrap.NumPut &hB8, buf, 71 + szParams '
Wrap.NumPut buf + 30 + szParams, buf, 75 + szParams '
'======Сбрасываем бит у ExceptionFlags======
Wrap.NumPut &h04244C8B, buf, 79 + szParams 'MOV ECX,DWORD PTR SS:[ESP+4] ExceptionRecord
Wrap.NumPut &hFE046180, buf, 83 + szParams 'AND BYTE PTR DS:[ECX+4],FE очищаем флаг EXCEPTION_NONCONTINUABLE у ExceptionFlags
'======Выходим из обработки исключения======
Wrap.NumPut &hB8, buf, 87 + szParams, "b" 'MOV EAX,ExceptionContinueExecution
Wrap.NumPut 0, buf, 88 + szParams '
Wrap.NumPut &hC3, buf, 92 + szParams, "b" 'RETN
End Function
End Class
Set oServ = New Service
Sub btnTest_onclick()
MsgBox "Test"
'On Error Resume Next
Test = Wrap.Test()
MsgBox "Error code: " & Hex(Err.Number)
End Sub
</script>
</head>
<body>
<button id=btnTest>Test</button>
</body>
</html>
Код вроде бы нормально отрабатывает. Идея правильная, конечно, исключения надо обрабатывать. Я как-то уже делал это на ассемблере, но подробности подзабылись. Особо сложного чего-то действительно нет. К сожалению, на x64 технология изменилась и, как я понял, сложнее будет реализовать.
Честно говоря не знаю, что именно изменилось для x64, но у меня еще есть вариант с оберткой в VEH. В принципе тоже самое, только мне показалось менее удобно. Может этого будет достаточно для x64.
Отличная утилита. Я написал hta-скрипты "пианино" с использование midi библиотеки. А также находил окно VirtualDub, брал команды списка меню окна и управлял им. Всё работает.
Тем более, я написал шаблон, облегчающий подключение всех функций.
Однако вчера споткнулся и не знаю, почему CreateFile даёт ошибку. Мне нужно открыть мэппинг файла и на лету его читать-изменять оперативно. Одна моя утилита рабоотает в фоне и использует 4кб файл как псевдо-интерфейс (управляю параметрами изменением этого файла в Hiew). Hiew-дамп использовать несколько неудобно, решил в hta прямо dhtml путями менять параметры.
Вот скрипт.
Что я делаю не так?
Спасибо!
Вероятно, проблема в том, что результат операции GENERIC_READ | GENERIC_WRITE сохраняется JScript'ом в виде знакового целого числа (тип VT_I4), а учитывая, что в GENERIC_READ в старшем бите стоит 1, число получается отрицательное. А тип параметра у вас задан как uint.
Можно изменить тип параметра на int, но тогда может возникнуть та же ошибка при передаче GENERIC_READ самого по себе, т.к. он имеет тип VT_R8 (дробное число двойной точности), является положительным и выходит за пределы положительных значений int (максимум там 0x7FFFFFFF).
Можно заменить "GENERIC_READ | GENERIC_WRITE" на "GENERIC_READ + GENERIC_WRITE" — здесь результатом будет положительное число типа VT_R8. Либо изобрести собственный флаг GENERIC_READ_WRITE со значением 0xC0000000.
Возможно, я зря встроил эту проверку на валидность значения числа с точки зрения знаковости/беззнаковости. Я думал, это поможет против каких-нибудь случайных ошибок, но начинаю думать, что от этой фичи больше неудобства, чем пользы.
Приветствую!
YMP, спасибо за огромнейшую работу !!!
Подскажите, а как вызвать функцию из 32-битной бибилиотеки в 64-битной ОС ?
Если запускать cscript file.vbs из 32-битной cmd.exe, то все нормально отрабатывает.
Значение имеет битность процесса, а не ОС. Т.е. если скрипт выполнять 32-битным интерпретатором — тем, который в папке SysWOW64 — то будут использоваться 32-битные DLL.
Если хотите гарантировать, что скрипт будет выполняться в 32-битном процессе, то можно в начале скрипта через метод Bitness проверять битность, и если она 64, то запускать вторую копию скрипта с явным указанием 32-битного интерпретатора, а первую копию завершать.
YMP, огромное спасибо за вашу разработку!!!
Подскажите, как с помощью DynamicWrapperX воспользоваться Direct3D в JScript?
В C++ пишут так:
LPDIRECT3D9 pDirect3D=NULL;
bool InitDirectX(void)
{
if((pDirect3D=Direct3DCreate9(D3D_SDK_VERSION)) == NULL) return(false);
D3DDISPLAYMODE stDisplay;
if(FAILED(pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &stDisplay)))
return(false);
<...>
}
На Fasm:
d3d9 MACRO func, this, arglist :VARARG
mov eax , [this]
mov eax , [eax]
IFB <arglist>
INVOKE [IDirect3D9Vtbl. func][eax], this
ELSE
INVOKE [IDirect3D9Vtbl. func][eax], this, arglist
ENDIF
ENDM
IDirect3D9Vtbl STRUC
<...>
STDMETHOD GetAdapterDisplayMode, :PTR IDirect3D9, :UINT, :PTR D3DDISPLAYMODE,
<...>
IDirect3D9Vtbl ENDS
<...>
.COD
invoke Direct3DCreate9, D3D_SDK_VERSION
mov pd3d, eax
d3d9 GetAdapterDisplayMode, pd3d, D3DADAPTER_DEFAULT, ADDR d3ddm
<...>
А на JScript никак не получается:
var DWX=new ActiveXObject("DynamicWrapperX");
function InitDirect3D(){
DWX.Register("d3d9.dll","Direct3DCreate9","i=u","r=p");
var D3D_SDK_VERSION=32;
var pDirect3D=DWX.Direct3DCreate9(D3D_SDK_VERSION);
if(pDirect3D==0){WScript.Echo("Интерфейс Direct3D недоступен!");return 1}
var D3DADAPTER_DEFAULT=0;
var pDisplay=DWX.StrPtr(DWX.Space(16,""));
var Direct3D=DWX.ObjGet(pDirect3D); // получаем интерфейс IUnknown Direct3D
Direct3D.GetAdapterDisplayMode(D3DADAPTER_DEFAULT,pDisplay); // не работает ни один метод ((
return 0;
}
Так тем более не работает:
var DX=new ActiveXObject("DynamicWrapperX");
function InitDirect3D(){
DX.Register("d3d9.dll","Direct3DCreate9","i=u","r=p");
var D3D_SDK_VERSION=32;
var pDirect3D=DX.Direct3DCreate9(D3D_SDK_VERSION);
var D3DADAPTER_DEFAULT=0;
var IDirect3D9=new Object();
DX.NumPut(pDirect3D,IDirect3D9); // здесь пишет ошибку...
IDirect3D9.GetAdapterDisplayMode(D3DADAPTER_DEFAULT);
return 0;
}
Direct3D.GetAdapterDisplayMode(D3DADAPTER_DEFAULT,pDisplay);
Проблема в том, что в скриптах так можно вызывать только методы объектов, поддерживающих интерфейс IDispatch. С IUnknown так работать не получится. Нужно получать адрес соответствующего метода, регистрировать его в DWX через RegisterAddr и потом уже вызывать как метод DWX. Т.е. работать с методами как с функциями.
Указатель на интерфейс, возвращаемый Direct3DCreate9, указывает на другой указатель, который уже указывает на список указателей (адресов) методов (т.е. фактически на адрес метода QueryInterface). Методы IDirect3D9 должны идти после методов IUnknown.
pIUnknown -> pVTable -> pQueryInterface
pAddRef
pRelease
pCheckDepthStencilMatch
...
pGetAdapterDisplayMode
...
Если вас такой геморрой не пугает, могу попробовать написать пример вызова метода.
Был бы очень рад примеру! Адрес метода не знаю как из COM-интерфейса получить. Спасибо!
Вот так у меня работает. При регистрации и вызове методов нужно учитывать, что все они имеют первым параметром указатель на интерфейс (хотя в описании об этом не говорится). Порядок методов в VTable нужно смотреть не в описании интерфейса (там методы по алфавиту), а в header-файле.
DWX = new ActiveXObject("DynamicWrapperX");
PtrSize = DWX.Bitness() / 8;
DWX.Register("d3d9.dll","Direct3DCreate9","i=u","r=p");
D3D_SDK_VERSION = 32;
pDirect3D = DWX.Direct3DCreate9(D3D_SDK_VERSION);
if(pDirect3D == 0){WScript.Echo("Интерфейс Direct3D недоступен!");}
pVTable = DWX.NumGet(pDirect3D, 0, "p")
pGetAdapterDisplayMode = DWX.NumGet(pVTable, 8 * PtrSize, "p");
DWX.RegisterAddr(pGetAdapterDisplayMode, "GetAdapterDisplayMode", "i=pup", "r=l");
D3DADAPTER_DEFAULT = 0, D3D_OK = 0;
pDisplay = DWX.MemAlloc(32, 1);
ret = DWX.GetAdapterDisplayMode(pDirect3D, D3DADAPTER_DEFAULT, pDisplay);
if (ret == D3D_OK) {
Width = DWX.NumGet(pDisplay, 0, "u");
Height = DWX.NumGet(pDisplay, 4, "u");
RefreshRate = DWX.NumGet(pDisplay, 8, "u");
WSH.echo(Width, Height, RefreshRate);
}
else {
WSH.echo("Ошибка при вызове GetAdapterDisplayMode: " + ret);
}
DWX.MemFree(pDisplay);
Методы в VTable (файл d3d9.h):
DECLARE_INTERFACE_(IDirect3D9, IUnknown)
{
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IDirect3D9 methods ***/
STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE;
STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE;
STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE;
STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE;
STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE;
STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE;
STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE;
STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE;
STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE;
STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE;
STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE;
STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;
#ifdef D3D_DEBUG_INFO
LPCWSTR Version;
#endif
};
Спасибо!! Всё отлично работает! Вы мне о-очень помогли!
А какая разница между параметрами "p" и "v"? Зачем нужен "v"? Изменяется ли размер в зависимости от разрядности хоста?
p — это указатель. Для чисел влияет только на размер (4 или 8 байт в зависимости от битности процесса). В случае строки функции передастся указатель на неё, в случае объекта — указатель на объект. v — указатель на структуру VARIANT. В таких структурах передаются методам их аргументы в скриптовых языках. Если тип указан как "v", то значение из такой структуры не извлекается, как обычно, а функции передаётся указатель на саму структуру. Предполагается, что функция знает, что с ней делать.
По коду выше есть одно соображение: указатель pDirect3D в конце работы с интерфейсом, как я понял, положено освобождать. Т.е. нужно зарегистрировать метод Release
pRelease = DWX.NumGet(pVTable, 2 * PtrSize, "p");
DWX.RegisterAddr(pRelease, "Release", "i=p");
а где-то в конце вызвать
DWX.Release(pDirect3D);
Спасибо за важное замечание. Я заметил, что не только "h", "p" меняют область значений в зависимости от битности процесса, но и "f". В документации об этом нет информации.
Вот, если нужно, код:
var DWX=new ActiveXObject("DynamicWrapperX");
var DWXTypes=["l","u","m","q","h","p","n","t","c","b","f","d"];//,"w","s","z","v"];
var str="x"+DWX.Bitness()+"\n";
var t0;
var t1="яяяяяяяяяяяяяяяяяяяяяяяяяяяяяяяя"; // 1111 1111
var t2="UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; // 0101 0101
var ptr1=DWX.StrPtr(t1,"cp1251");
var ptr2=DWX.StrPtr(t2,"cp1251");
for(var i=0;i<DWXTypes.length;i++){
str+=DWXTypes[i]+"\t"+DWX.NumGet(ptr1,0,DWXTypes[i])+" \t\t "+DWX.NumGet(ptr2,0,DWXTypes[i])+"\n";
}
WSH.Echo(str);
Нет, там ошибка была в коде DWX. Спасибо за репорт! Попробуйте версию 2.0.0.1, сейчас должно быть одинаково.
Кстати, текст диалоговых окон можно копировать по Ctrl-C.
Нет, там ошибка была в коде DWX. Спасибо за репорт! Попробуйте версию 2.0.0.1, сейчас должно быть одинаково.
Кстати, текст диалоговых окон можно копировать по Ctrl-C.
Привет, Юрий!
Это официальная версия?
Я так понимаю, что ошибка была не критичная?
Да, можно сказать, официальная. Ошибка связана с сохранением в переменную дробного числа типа Float из регистра. Т.е., например, если функция возвращает такое значение или NumGet читает его из памяти. Я дробные сохраняю в переменные как Double, и вот забыл в 64-битной версии прописать преобразование.
Кроме того ещё ObjGet вызывал исключение, если его вызвать без сохранения результата в переменную (смысла нет так вызывать, но всё-таки). Это тоже исправлено.
Спасибо.
Нет, там ошибка была в коде DWX. Спасибо за репорт! Попробуйте версию 2.0.0.1, сейчас должно быть одинаково.
Кстати, текст диалоговых окон можно копировать по Ctrl-C.
Вам огромное спасибо за предоставленные возможности и оперативную техподдержку!
Библиотека работает отлично).
Помогите, пожалуйста с передачей строк в DWX.
Пробую подключить длл написанную на delphi 7.
Описание ф-ции
function fWriteDoc(Idtypedoc:Integer; Idsenddoc:integer; IDWAY:Integer;
BlockInfo: PChar; SenderKODOTD:Integer; SenderKODUS:Integer; Note: PChar;
Datedoc:TDateTime; SendNote:PChar; ParentIDDOC:Integer):Integer;
stdcall; external Name;
Как правильно описать входные параметры? Как правильно передавать строку PChar?
Я пробовал объявить так:
Set Wrap = CreateObject("DynamicWrapperX")
Wrap.Register "ElDocDll.dll", "fWriteDoc", "i=lllsllsdsl", "f=s", "r=l"
При вызове фц-ции пишет что не верно заданы параметры
Вот тут вызов ф-ции:
IdTypeDoc = 83
IdSendDoc = id
IDWay = 0
BlockInfo = ""
' pBlockInfo = Wrap.StrPtr(BlockInfo, "s")
' MsgBox pBlockInfo
SenderKODOTD = 268
SenderKODUS = 0
Note = Zakaz & " " & DOsn & Spec
' pNote = Wrap.StrPtr(note, "s")
' MsgBox Note
Datedoc = now
SendNote = ""
' pSendNote = Wrap.StrPtr(SendNote, "s")
ParentIDDOC = 0
res = Wrap.fWriteDoc(IdTypeDoc, IdSendDoc, IDWay, BlockInfo, SenderKODOTD, SenderKODUS, Note, Datedoc, SendNote, ParentIDDOC)
Что-то не пойму. Описание вы приводите для fSendDoc, а регистрируете fWriteDoc.
Ошибся ф-цией, сейчас поправлю
После того как загрузил новую версию отсюда yuripopov.ucoz.net/load/0-0-0-8-20 и скопировал в system32 вообще стал ругаться на строку
Set Wrap = CreateObject("DynamicWrapperX")
С этим разобрался так
Set Wrap = CreateObject("DynamicWrapperX.2")
Вам надо старую версию разрегистрировать, а новую зарегистрировать. У неё CLSID другой.
Что делать со строками в ф-циях? Integer в делфи это параметр "l" правильно? Дата и время это по-моему 8-байтное с плавающей точкой, передается параметром "d".
Не пойму никак с PChar что делать, перепробовал много всего ошибки лезут.
При чем если передавать в функцию не переменной, а статической строкой "Test1", то все работает отлично.
Какая-то особенность Vbscript в работе со строками, не пойму какая.
Что за ошибки и от кого сообщение о них приходит?
Простите за беспокойство, но после обновления DWX до 2 версии все заработало. Строки передаются корректно, информация в базу пишется. Спасибо за помощь.
Здравствуйте, Юрий. Я обычно работаю за Windows XP, но сегодня я запускал программу на восьмёрочке. На Windows XP x32/x64, Windows 7 x32/x64 работает, а на Windows 8 x64 - нет. Ничего не пишет даже, доходя до строчки создания окна. Типы пробовал менять, но не помогло. Вот та неработающая часть программы:
var IDI_APPLICATION=0x7F00, IDC_ARROW=0x7F00, CS_VREDRAW=0x1, CS_HREDRAW=0x2,
CS_DBLCLKS=0x8, CS_OWNDC=0x20, WS_OVERLAPPEDWINDOW=0xCF0000,
WS_VISIBLE=0x10000000, WS_EX_TOPMOST=8, WS_POPUP=0x80000000, WM_DESTROY=0x2,
WM_ACTIVATE=0x6, WM_CHAR=0x102, WM_SETCURSOR=0x20, WM_QUIT=0x12;
function WNDCLASSEX(){
// UINT, UINT, WNDPROC, int, int, HINSTANCE, HICON, HCURSOR, HBRUSH, LPCTSTR, LPCTSTR, HICON
var a=["cbSize","style","lpfnWndProc","cbClsExtra","cbWndExtra","hInstance",
"hIcon","hCursor","hbrBackground","lpszMenuName","lpszClassName","hIconSm"];
for(var i in a)this[a[i]]=arguments[i];
this._const="uupllhhhhpph";
}
function getStructWin(struct){
var types=struct._const, // строка типов в стандарте DynamicWrapperX
size=0, // размер структуры (вначале исходной, потом возвращаемой)
data; // информация для внесения в стуктуру
for(var i=0;i<types.length;i++)size+=DWXTypes[types.charAt(i).toLowerCase()]; // Вычисляем размер структуры /*на строку память не выделится!!*/
var ns=DWX.MemAlloc(size,1); // MemFree() // выделение памяти в БАЙТАХ // создание структуры (new struct)
size=0;i=0;
for(var p in struct){ // заполняем структуру
if(p=="_const")continue; // исключаем из структуры МОЮ строку типов
data=struct[p];
if(data==null)data=0;
DWX.NumPut(data,ns,size,types.charAt(i)); // заносим данные data в стуктуру ns со смещением size
size+=DWXTypes[types.charAt(i++).toLowerCase()];
}
return ns; // возвращаем преобразованную структуру
}
function WndProc(hWnd,uMsg,wParam,lParam){ // Callback function
switch(uMsg){
case WM_DESTROY:
Close=1;
break;
}
return DWX.DefWindowProcW(hWnd,uMsg,wParam,lParam);
}
function InitWindow(){
DWX.Register("user32","LoadIconW","i=hp","r=h");
DWX.Register("user32","LoadCursorW","i=hp","r=h")
DWX.Register("user32","RegisterClassExW","i=p","r=t"); // ATOM
var pWndProc=DWX.RegisterCallback(WndProc,"i=huul","r=l");
var szClass="Class_name";
var myWClass=new WNDCLASSEX((bit==32)?48:80, CS_VREDRAW|CS_HREDRAW|CS_DBLCLKS|CS_OWNDC,
pWndProc, 0, 0, hModule, DWX.LoadIconW(0,IDI_APPLICATION),
DWX.LoadCursorW(0,IDC_ARROW), 0, 0, szClass, 0);
var wc=getStructWin(myWClass);
if(DWX.RegisterClassExW(wc)==0){WSH.Echo("Ошибка при регистрации класса.");return false}
DWX.Register("user32.dll","CreateWindowExA","i=ussullllhhhl","r=h"); // i=DWORD,LPCTSTR,LPCTSTR,DWORD,int,int,int,int,HWND,HMENU,HINSTANCE,LPVOID r=HWND
var szTitle="My window";
hWnd=DWX.CreateWindowExA(0,szClass,szTitle,WS_OVERLAPPEDWINDOW|WS_VISIBLE,
20,20,500,400,0,0,hModule,0);
// hWnd=DWX.CreateWindowExA(WS_EX_TOPMOST,szClass,szTitle,WS_POPUP, 0,0,800,600,0,0,hModule,0);
if(hWnd==0){WSH.Echo("Ошибка при создании окна.");return false}
return true;
}
var DWX=new ActiveXObject("DynamicWrapperX");
var bit=DWX.Bitness(); // разрядность процесса
// типы по стандарту DynamicWrapperX в БАЙТАХ (строковые - 0)
var DWXTypes={"l":4,"u":4,"m":8,"q":8,"h":bit/8,"p":bit/8,"n":2,"t":2,"c":1,"b":1,"f":4,"d":8,"w":0,"s":0,"z":0,"v":0};
DWX.Register("kernel32","GetModuleHandleW","i=p","r=h"); // i=(LPCTSTR); r=HMODULE
DWX.Register("user32","DefWindowProcW","i=hupp","r=l"); // i=(HWND, UINT, WPARAM, LPARAM); r=LRESULT
DWX.Register("user32","ShowWindow","i=hl","r=l"); // i=(HWND, int); r=BOOL
DWX.Register("user32","UpdateWindow","i=h","r=l"); // i=(HWND); r=BOOL
var hModule=DWX.GetModuleHandleW(0);
var hWnd=0;
var Close=0;
if(!InitWindow())WSH.Quit(); // hWnd
DWX.ShowWindow(hWnd,1);
DWX.UpdateWindow(hWnd);
do{WSH.Sleep(200);}while(Close==0);
Да, я с этим тоже сталкивался. В регистрации WndProc нужно задать wParam и lParam типом "h" или "p", т.е. меняющим размер в соответствии с битностью.
Точно, спасибо! Странно, что в остальных 64-битных версиях винды работало.
Похожая проблема с IDirect3DDevice9::CreateVertexBuffer (типы uuuuph). Тоже только в WinNT6.2x64 не работает.
Ещё, волнует вопрос: обязательно ли освобождать память (DWX.MemFree), ведь по завершению процесса память сама, по-идее, высвобождается? Что особенного происходит после выполнения DWX.MemAlloc? Чем он лучше DWX.Space, память после использования которого не требуется очищать?
Могу только предположить, что там обнулённая память выделяется, а в восьмёрке нет. Поэтому в верхней части wParam и lParam оказывается мусор, если писать их как 32-битные в 64-битном процессе.
Да, по завершении процесса память освобождается, поэтому, если разово выделяются небольшие объёмы, то можно и не освобождать. Space возвращает строковую переменную, и тут проблема в том, что скриптовый движок может памятью строки манипулировать по своему усмотрению. Он-то думает, что это строка, а у вас там что угодно может храниться. Могут быть проблемы из-за этого.
Значение имеет битность процесса, а не ОС. Т.е. если скрипт выполнять 32-битным интерпретатором — тем, который в папке SysWOW64 — то будут использоваться 32-битные DLL.
Спасибо, YMP.
Сделал примерно так: Батник для регистрации DynamicWrapperX 2.0 (x32, x64) и вызова функций из 32/64-битных библиотек.
Может, кому окажется полезным.
Dragokas
Возвращаемое значение FindWindow и первый параметр ShowWindow — хэндл, поэтому лучше для них задать тип "h". Возможно, значения оконных хэндлов и не выходят на данный момент за пределы 32 бит, но за будущее ручаться мы ведь не можем. Что там придёт в голову разработчикам Windows.
Подскажите, как в буфер (MemAlloc) вставить строку (или скопировать другой буфер) со смещением, если StrPut (как и StrGet) не работают со смещением, как, например, NumPut?
Но ведь в x64 системах указатели типа VT_UI8, а движком JScript не поддерживаются (с ними нельзя выполнять арифметические операции). Или в данном случае от этого не зависит?
А если буферы копировать (часть одного буфера заменить другим), то это побайтово в цикле тогда делать?
Ну, MemAlloc такой тип вернёт только, если тип VT_R8 окажется недостаточен. А это 53 бита, т.е. до числа 0x1FFFFFFFFFFFFF включительно будет VT_R8. Думаете, система может выдать память с более высоких адресов? Хм... Поручиться я тут не могу, конечно.
Можно, в принципе, добавить параметр смещения в строковые функции. Что касается копирования участков памяти, то я думал, не сделать ли метод MemCopy. Раз он реально нужен, то тоже можно добавить. Хотя можно и RtlMoveMemory зарегистрировать и использовать.
DWX.Register("kernel32.dll", "RtlMoveMemory", "i=pph");
DWX.RtlMoveMemory(Dest, Source, Size);
Но RtlMoveMemory замещает опять же без смещения. И смещение тоже придётся делать с помощью сложения, которое не всегда допустимо (имею ввиду те, высокие, адреса).
Может добавить MemCopy со смещением, тогда и строки можно будет копировать, не боясь указателей типа VT_UI8? Хотя можно расширить функционал с помощью RegisterCode, но я в asm не силён.
Может добавить MemCopy со смещением, тогда и строки можно будет копировать, не боясь указателей типа VT_UI8?
Тогда нужно будет два смещения — к исходному и целевому адресу. Как-то громоздко получается. Либо сделать их необязательными и определять по алгоритму: если указаны 3 параметра, то это 2 адреса и размер; 5 параметров = адреса со смещениями и размер. Надо подумать. Или, может, сделать метод для операций с такими типами.
А всё потому, что в МС поленились добавить два типа в 64-битные движки. Неужели так трудно было?
MemCopy(Address, Source, Length [,Offset1] [,Offset2]));
Address - базовый адрес; Source - адрес замещаемого блока памяти; Length - количество байт на замену; Offset1 - смещение к исходному, Offset2 - к целевому. Хотя... можно и одним смещением обойтись, вызвав её 2 раза.
Метод для операций — сложение (+отрицательные чисела), которое нужно будет только для x64. Не знаю что лучше.
В функции RegisterCode преобразуется hex-строка (по-идее же). Можно было бы вынести это как отдельную функцию. Чтобы избежать костылей типа таких:
function hex2mem(str){var l=str.length-str.length%2, ptr=DWX.MemAlloc(l/2+1,1);
for(var i=0,q=0;i<l;i+=2,q++)DWX.NumPut("0x"+str.substr(i,2)-0,ptr,q,"b");
return ptr;
}
Да, в принципе, можно бы и в конце их указывать. Но есть одно "но" — хотелось бы избежать разнобоя с NumGet/NumPut. По моему опыту такие вещи обычно раздражают, когда в одной функции так, а в другой этак. Думаю, ничего страшного даже если они обязательными бы были — два нуля поставить недолго.
DWX.MemCopy(SrcAddr, 0, DestAddr, 0, BytesToCopy);
Про hex2mem (и наоборот) тоже были мысли. Иногда удобная вещь. Например, глянуть, что находится где-то в памяти.
DWX.MemRead(SrcAddr, Offset, BytesToRead [, BytesPerGroup] [, GroupsPerLine]);
DWX.MemWrite(HexStr, DestAddr, Offset [, BytesToWrite])
Последние два параметра в MemRead — число байт в группе и групп на строке, для удобства обзора. Хотя отформатировать и средствами скрипта не проблема, конечно, так что, может, это и лишнее.
А всё потому, что в МС поленились добавить два типа в 64-битные движки. Неужели так трудно было?
Это да. Они много чего поленились сделать. Указатели в тип Real теперь переводи
Согласен, два нуля - недолго. MemRead/MemWrite, я лично, считаю полезным. На этапе debug'а скорость выполнения не так важна, как в готовом алгоритме. А побайтово составлять буфер, используя substr и сложение строк для поллучения - неоптимально.
Ну, MemAlloc такой тип вернёт только, если тип VT_R8 окажется недостаточен. А это 53 бита, т.е. до числа 0x1FFFFFFFFFFFFF включительно будет VT_R8. Думаете, система может выдать память с более высоких адресов? Хм... Поручиться я тут не могу, конечно.
Почитал немного про память в х64. На данный момент для адресов используются 48 нижних бит (да и то только с Windows 8.1), причём это ограничение на уровне процессора. Так что VT_R8 хватит с запасом. Есть, правда, ещё память ядра, у которой верхние биты заполняются единицами, так что числа формально получаются больше. Но в эту память у приложений допуска нет.
Т.е. есть две зоны: 0 - 00007FFF'FFFFFFFF и FFFF8000'00000000 - FFFFFFFF'FFFFFFFF. В сумме это 256 терабайт, так что смысла расширять адрес дальше нет.
Большое спасибо за полезное разъяснение.
Я пытаюсь реализовать буферизированный вывод элементов структур. Чтобы скопировать информацию из структуры в буфер хотел воспользоваться RtlCopyMemory, но при её регистрации происходит ошибка: функция не найдена в библиотеке. Ни в ntdll, ни в kernel32. А RtlMoveMemory находит. Не пойму в чём дело. Или вообще нужно как-то по-другому составлять буфер?
Вот пример:
var DWX=WScript.CreateObject("DynamicWrapperX");
var bit=DWX.Bitness(); // разрядность процесса
var dwxsize={l:4,u:4,m:8,q:8,h:bit/8,p:bit/8,n:2,t:2,c:1,b:1,f:4,d:8}; // размеры типов
function ld0n(val,n){val+="";while(val.length<n)val="0"+val;return val}
function cnv_num(val,f,t){return parseInt(val,f).toString(t).toUpperCase()} // Lower
function gethex(addr,size){
for(var i=0,str="";i<size;i++)str+=ld0n(cnv_num(DWX.NumGet(addr,i,"b"),10,16),2);
return str;
}
DWX.Register("advapi32.dll","GetTokenInformation","i=hupup","r=l"); // HANDLE,enum,LPVOID,DWORD,PDWORD; BOOL
DWX.Register("kernel32.dll","GetCurrentProcess","r=h"); // void; HANDLE
DWX.Register("advapi32.dll","OpenProcessToken","i=hup","r=l"); // HANDLE,DWORD,PHANDLE; BOOL
DWX.Register("kernel32.dll","LocalFree","i=h","r=h");
DWX.Register("kernel32.dll","CloseHandle","i=h","r=l");
DWX.Register("ntdll.dll","RtlMoveMemory","i=ppu"); // VOID UNALIGNED,const VOID UNALIGNED,SIZE_T; VOID
// буфер для последующего вывода
var BUFFER=DWX.MemAlloc(256,1),nBUFFER=0;
// добавляем в буфер какую-нибудь информацию
var test_str="Another info\0";
DWX.RtlMoveMemory(BUFFER+nBUFFER,test_str,test_str.length*2);
nBUFFER+=test_str.length*2;
var TOKEN_QUERY=0x0008;
var phToken=DWX.MemAlloc(dwxsize.h,1);
var hProcess=DWX.GetCurrentProcess();
DWX.OpenProcessToken(hProcess,TOKEN_QUERY,phToken);
var hToken=DWX.NumGet(phToken,0,"h");
var uSizeToken=dwxsize.p*2+28;
var ptiUser=DWX.MemAlloc(uSizeToken);
DWX.GetTokenInformation(hToken,1,ptiUser,uSizeToken,0);
// ещё добавляем информацию (User SID)
DWX.RtlMoveMemory(BUFFER+nBUFFER,DWX.NumGet(ptiUser,0,"p"),28);
nBUFFER+=28;
DWX.MemFree(ptiUser);
DWX.CloseHandle(hToken);
DWX.MemFree(phToken);
DWX.CloseHandle(hProcess);
// выводим буфер
WScript.Echo(gethex(BUFFER,nBUFFER));
Судя по всему, это инлайновая функция. Т.е. она ниоткуда не экспортируется, а просто в то место, где она вызывается, компилятор вставляет её код.
В последней версии DWX есть метод MemCopy. Проще всего его использовать. А для чтения памяти в хекс-строку есть метод MemRead.
YMP, прошу прощение за неграмотность, но у меня не получилось реализовать нормальный рабочий манифест под вторую версию. Смысл в использовании Вашей библиотеки без ее регистрации в реестре. Буду признателен за помощь.
Также хотел уточнить будут ли отличатся манифесты на 86 и 64-битных версиях ОС?
JSman
Возможно, CLSID не поправили? Сейчас потестил, у меня для обеих битностей работают такие манифесты.
wscript.exe.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="wscript.exe"
version="1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="dynwrapx.sxs"
version="2.1.0.0" />
</dependentAssembly>
</dependency>
</assembly>
dynwrapx.sxs.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="dynwrapx.sxs"
version="2.1.0.0" />
<file name="dynwrapx.dll">
<comClass
clsid="{89565276-A714-4a43-912E-978B935EDCCC}"
threadingModel="Both"
progid="DynamicWrapperX" />
</file>
</assembly>
Да... Это я не смотрел за обновлениями. Огромное спасибо за MemCopy и MemRead/MemWrite!!
Вопрос как скрипту запросить права администратора? Запускаю скрипт обычным wscript.exe, в скрипт же не вставишь manifest.
Не знаю, не озадачивался таким вопросом. Наверно, как-то можно запросить повышение прав через API.
JSman
Возможно, CLSID не поправили? Сейчас потестил, у меня для обеих битностей работают такие манифесты.
Так и получилось. Причем на 64-битной ОС отлично работает 32-битная сборка. Получился универсальный вариант. Большое спасибо!
На разных архитектурах размер типа LRESULT изменяется (4/8). В справке к DynamicWrapperX он относится к знаковым целым 32-битным (l). По-идее UINT_PTR=WPARAM=LPARAM=LRESULT=LONG_PTR="p".
typedef LONG_PTR LRESULT;
Повысить права скрипту можно только его перезапуском от другого пользователя или так:
http://msdn.microsoft.com/en-us/library … s.85).aspx
На разных архитектурах размер типа LRESULT изменяется (4/8). В справке к DynamicWrapperX он относится к знаковым целым 32-битным (l).
typedef LONG_PTR LRESULT;
Да, действительно, надо будет исправить. Спасибо!
Кстати, вопрос, почему для указателей мы используем беззнаковый тип ("p"), ведь эти типы все знаковые?:
WNDPROC, LPCTSTR, LPCWSTR, UINT_PTR, WPARAM, LPARAM, LONG_PTR, LRESULT, LPVOID, LPMSG, PDWORD, PHANDLE, PSID, ULONG_PTR, PULONG_PTR...
В тоже время для дескрипторов используем знаковый ("h") (как оно и есть). Или вообще без разницы знаковые они или нет?
Ну, указатель — это адрес. Может ли адрес быть отрицательным?
Вообще-то сейчас я убрал проверку на знаковость для входных параметров. В 1-й версии нельзя было для параметра с типом "p" или "u" подать отрицательное число. Но проблема в том, что в скриптовых языках у нас нет полного контроля за типами, да и сам набор типов не полон. Так что в некоторых случаях такая строгая проверка вызывала неудобства.
Поэтому на данный момент разница между "p" и "h", "u" и "l", и т.д. — чисто мнемоническая. Просто чтобы видно было, знаковый или беззнаковый параметр в определении функции. Указатель или хэндл. По факту в обоих случаях передаётся тот же набор битов, а как его интерпретировать, функция знает сама.
Но для возвращаемых значений разница остаётся. Поскольку здесь уже сам DWX должен откуда-то знать, как ему интерпретировать, например, 32-битное число 0xFFFFFFFF: если как "l", тогда это -1 и её можно поместить в тип VT_I4; если как "u", тогда число положительно и выходит за диапазон VT_I4, нужно использовать тип VT_R8. Тогда и отображаться это число будет корректно, если вы захотите посмотреть, что вернула функция, и арифметические операции с ним будут правильны.
А как работать с типом long double? И можно ли ограничить (расширить) диапазон чтения строки StrGet (типа Bytes — размер в байтах)? Например,
var DWX=WScript.CreateObject("DynamicWrapperX.2");
var MyStruct=DWX.MemAlloc(14+2*4,0);
DWX.MemWrite("48656C6C6F2C20667269656E6473",MyStruct,14); // Hello, friends
DWX.NumPut(0x6C65645F,MyStruct,14,"u");
DWX.NumPut(0x00657465,MyStruct,18,"u");
var str=DWX.StrGet(MyStruct,0,"s");
DWX.MemFree(MyStruct);
WScript.Echo(str); // хочу получить "Hello, friends"
В данном случае можно обойтись строчкой
str=str.substr(0,14);
Второй случай, с необходимостью расширения строки:
var str1="Hello,\0World"; str1+=" my friend!"; // длина строки 23
WScript.Echo(DWX.MemRead(DWX.StrPtr(str1),str1.length*2,2,0));
// length=23; echo = 4800 6500 6C00 6C00 6F00 2C00 0000 5700 6F00 7200 6C00 6400 2000 6D00 7900 2000 6600 7200 6900 6500 6E00 6400 2100
// в JScript поддерживается работа со строками, содержащими терминальный 0
var MyStruct=DWX.MemAlloc(23*2+4,0);
DWX.StrPut(str1,MyStruct,0,"w");
DWX.NumPut(0x6C65645F,MyStruct,46,"u");
WScript.Echo(DWX.MemRead(MyStruct,23*2+4,2,0));
// echo = 4800 6500 6C00 6C00 6F00 2C00 0000 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 FFFF 5F64 656C
// NumPut записала строку до нуля (не всю)
DWX.MemCopy(DWX.StrPtr(str1),MyStruct,23*2); // теперь запишем всю строку
var str2=DWX.StrGet(MyStruct,0,"w"); // и получим её
WScript.Echo(str2.length); // её длина стала 6
DWX.MemFree(MyStruct);
В этом случае уже нужно пользоваться MemCopy.
Получается лучше сразу использовать MemCopy для чтения и записи строки; для чтения:
var str2=DWX.Space(23,"");
DWX.MemCopy(MyStruct,DWX.StrPtr(str2),23*2);
Полный код:
var DWX=WScript.CreateObject("DynamicWrapperX.2");
var MyStruct=DWX.MemAlloc(14+2*4,0);
DWX.MemWrite("48656C6C6F2C20667269656E6473",MyStruct,14); // Hello, friends
DWX.NumPut(0x6C65645F,MyStruct,14,"u");
DWX.NumPut(0x00657465,MyStruct,18,"u");
var str0=DWX.StrGet(MyStruct,0,"s");
DWX.MemFree(MyStruct);
WScript.Echo(str0); // хочу получить "Hello, friends"
// str0=str0.substr(0,14);
var str1="Hello,\0World"; str1+=" my friend!"; // длина строки 23
WScript.Echo(DWX.MemRead(DWX.StrPtr(str1),str1.length*2,2,0));
// length=23; echo = 4800 6500 6C00 6C00 6F00 2C00 0000 5700 6F00 7200 6C00 6400 2000 6D00 7900 2000 6600 7200 6900 6500 6E00 6400 2100
// в JScript поддерживается работа со строками, содержащими терминальный 0
var MyStruct=DWX.MemAlloc(23*2+4,0);
DWX.StrPut(str1,MyStruct,0,"w");
DWX.NumPut(0x6C65645F,MyStruct,46,"u");
WScript.Echo(DWX.MemRead(MyStruct,23*2+4,2,0));
// echo = 4800 6500 6C00 6C00 6F00 2C00 0000 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 FFFF 5F64 656C
// NumPut записала строку до нуля (не всю)
DWX.MemCopy(DWX.StrPtr(str1),MyStruct,23*2); // теперь запишем всю строку
var str2=DWX.StrGet(MyStruct,0,"w"); // и получим её
WScript.Echo(str2.length); // её длина стала 6
str2=DWX.Space(23,"");
DWX.MemCopy(MyStruct,DWX.StrPtr(str2),23*2);
WScript.Echo(DWX.MemRead(DWX.StrPtr(str2)-4,23*2+4,2,0));
// 2E00 0000 4800 6500 6C00 6C00 6F00 2C00 0000 5700 6F00 7200 6C00 6400 2000 6D00 7900 2000 6600 7200 6900 6500 6E00 6400 2100
// Всё в порядке
DWX.MemFree(MyStruct);
Параметр для длины строки сделать можно, конечно. Хотя логичнее задавать его в символах, а не байтах. Но тут не всё так просто в многобайтных кодировках вроде UTF-8 или каких-нибудь восточно-азиатских.
А как работать с типом long double?
А где он реально нужен? Возвращать его в скрипт не в чем. Для 64-битного целого типы хотя и не используются в JScript, но существуют, а тут типа вообще нет. По логике он бы должен называться VT_R10. Если возвращать в VT_R8, будет обрезаться по точности. Да и по величине может не влезть.
Передача его в функцию и возврат из неё нигде не оговорены, по кр. мере я не нашёл. Видимо, передаётся и возвращается по указателю. А в Visual Studio он вообще не отличается от double: Type long double.
Да, стандартных функций, передающих long double, я не встречал. Но в структуре встретиться может. В принципе, можно было бы использовать 10-символьную строку (10-байтовый участок памяти). А если вычисления производить, тогда преобразовать в double.
Я делаю объект struct, чтобы легче было работать с winapi (но скорость теряется). В него и хотел добавить поддержку long doube.
Пример использования "структуры" (поиск всех dll в папке windows):
var DWX=WScript.CreateObject("DynamicWrapperX");
var bit=DWX.Bitness(); // разрядность процесса
var dwxsize={l:4,u:4,m:8,q:8,h:bit/8,p:bit/8,n:2,t:2,c:1,b:1,f:4,d:8}; // размеры типов
var defWINAPI={
/* 4 */ LONG:"l", INT:"l", BOOL:"l", HRESULT:"l", NTSTATUS:"l", "int":"l",
/* u4 */ ULONG:"u", UINT:"u", DWORD:"u", PUINT:"u", LPUINT:"u", ULONG32:"u", "enum":"u",
/* 8 */ INT64:"m", LONGLONG:"m",
/* u8 */ UINT64:"q", ULONGLONG:"q", DWORDLONG:"q", ULONG64:"q",
/* $ */ HANDLE:"h", HWND:"h", HMODULE:"h", HLOCAL:"h", HINSTANCE:"h", HICON:"h", HCURSOR:"h", HBRUSH:"h", HDROP:"h", PVOID:"h", INT_PTR:"h", LONG_PTR:"h", /*?*/
/* u$ */ WNDPROC:"p", LPCTSTR:"p", LPCWSTR:"p", UINT_PTR:"p", WPARAM:"p", LPARAM:"p", LPVOID:"p", ULONG_PTR:"p", PULONG_PTR:"p", LRESULT:"p", SIZE_T:"p" /*unsigned*/, LPMSG:"p", PDWORD:"p", PHANDLE:"p", PSID:"p",
/* 2 */ SHORT:"n",
/* u2 */ USHORT:"t", WORD:"t", PWORD:"t", LPWORD:"t", WCHAR:"t", OLECHAR:"t", wchar_t:"t",
/* 1 */ CHAR:"c", CCHAR:"c",
/* u1 */ UCHAR:"b", BYTE:"b", PBYTE:"b", BOOLEAN:"b",
/* 4 */ FLOAT:"f",
/* 8 */ DOUBLE:"d",
BSTR:"w", LPWSTR:"w", LPOLESTR:"w",
LPSTR:"s", LPCSTRL:"s"
};
var struct={ // добавить описание обязательно к этому объекту
use: [], // для очистки
create: function(name,crtaddr){ // если crtaddr задан числом, отличным от 0, то память на структуру выделяться не будет
if(typeof(struct[name])=="undefined"){WScript.Echo("Error, struct "+name+" is undefined!");WScript.Quit();return false} // исключение
var obj={
addr: crtaddr?crtaddr:DWX.MemAlloc(struct[name].$_size,1), // при crtaddr не выделяем память
type: name,
put: function(aStr,data){struct.put(this,aStr,data)},
get: function(aStr){return struct.get(this,aStr)},
size: struct[name].$_size,
_nid: crtaddr?-1:struct.use.length // номер структуры, для destroy
};
if(!crtaddr)struct.use.push(obj);
return obj
},
put: function(obj,aStr,data){
var tp=struct[obj.type],sz=0; aStr=aStr.split(".");
for(var i=0;i<aStr.length-1;i++){sz+=tp[aStr[i]].offset;tp=tp[aStr[i]].obj;}
tp=tp[aStr.pop()];
DWX.NumPut(data,obj.addr,(sz+tp.offset),tp.type);
},
get: function(obj,aStr){ // use: 'var SysTimeInfo=struct.create("SYSTEM_TIME_OF_DAY_INFORMATION"); SysTimeInfo.get("CurrentTime.LowPart");' - вернёт значение поля, а 'SysTimeInfo.get("CurrentTime");' вернёт указатель на поле
var tp=struct[obj.type],sz=0,tmp; aStr=aStr.split(".");
var charonly=false;
for(var i=0;i<aStr.length;i++){
tmp=aStr[i].split(/(\[)|(\])/g); // разделяем каждый элемент строки адреса по []
tp=tp[tmp[0]]; sz+=tp.offset;
if(tmp[1]){tmp=tmp[1]-0;charonly=true;if(tp.obj)sz+=tp.obj.$_size*tmp; else sz+=dwxsize[tp.type]*tmp;} // tmp=eval(tmp[1])-0; (убрал eval)
else{charonly=false;}
if(tp.obj)tp=tp.obj; else break;
}
if(tp.$_size)return obj.addr+sz; // если это не "лист" дерева структуры, то просто возвращаем указатель на память
// DebugStr+=typeof(tmp)+": "+tmp+"\n";
if((!tp.count)||charonly/*(tmp>=0) - bad*/)return DWX.NumGet(obj.addr,sz,tp.type); // (tmp>=0), где tmp=[srt,n] - чтобы при запросе str[n] не выводилась оставшаяся часть строки - bag solution (tp.count - количество элементов массива)
// else: tmp=[str] => массив (пока обработчики только строки)
if(tp.elemtype=="WCHAR")return DWX.StrGet(obj.addr+sz,"w"); // если это строка
if(tp.elemtype=="UCHAR")return DWX.StrGet(obj.addr+sz,"s"); // если это строка
WScript.Echo("TODO: добавить обработчик для типа: "+tp.elemtype); // debug
return obj.addr+sz;
},
define: function(name,str,pack){ // pack - выравнивание для структуры (default(0): по максимальному члену)
var obj={},sz=0,arg,tp;
var align=1,tost; // new vars (renamed)
if(!pack)pack=8; // размеры всех типов не превышают 8
str=str.split(";");
for(var i=0;i<str.length;i++){
arg=str[i].split(/\s/gm);
if(arg.length<2)continue;
tp=defWINAPI[arg[0]];
if(typeof(tp)=="undefined"){WScript.Echo("Error, type "+arg[0]+" is undefined!");WScript.Quit();return false} // исключение
/* // без выравнивания:
if(typeof(tp)=="string"){obj[arg[1]]={type:tp,offset:sz};sz+=dwxsize[tp];}
else{obj[arg[1]]={obj:tp,offset:sz};sz+=tp.$_size}
// с выравниванием:
if(typeof(tp)=="string"){ // если тип является простым
align=Math.max(align,dwxsize[tp]); // высчитываем общее выравнивание структуры
tost=Math.min(pack,dwxsize[tp]); // выравнивание текущего члена (исключительно при изменении выравнивания pack)
sz=Math.ceil(sz/tost)*tost; // отступ по выравниванию
obj[arg[1]]={type:tp,offset:sz}; // создание члена в структуре
sz+=dwxsize[tp];
} else { // иначе (составным)
align=Math.max(align,tp.$_align);
tost=Math.min(pack,tp.$_align);
sz=Math.ceil(sz/tost)*tost;
obj[arg[1]]={obj:tp,offset:sz};
sz+=tp.$_size;
}
*/
// с множественным обозначением (массивами)
var data,kdata;
tost=arg[1].split(/(\[)|(\])/g); // temp var
if(tost.length==1){data=arg[1];kdata=1;}else{data=tost[0];kdata=tost[1]-0;} // kdata=eval(tost[1])-0; (убрал eval)
if(typeof(tp)=="string"){ // простой тип
align=Math.max(align,dwxsize[tp]); // общее выравнивание структуры
tost=Math.min(pack,dwxsize[tp]);
sz=Math.ceil(sz/tost)*tost;
obj[data]={type:tp,offset:sz};
if(kdata>1){obj[data].count=kdata;obj[data].elemtype=arg[0];} // если массив (кол-во членов больше 1)
sz+=dwxsize[tp]*kdata;
} else { // составной тип
align=Math.max(align,tp.$_align);
tost=Math.min(pack,tp.$_align);
sz=Math.ceil(sz/tost)*tost;
obj[data]={obj:tp,offset:sz};
if(kdata>1){obj[data].count=kdata;obj[data].elemtype=arg[0];}
sz+=tp.$_size*kdata;
}
}
tost=Math.min(pack,align);
sz=Math.ceil(sz/tost)*tost; // выравниваем последний элемент структуры, оставляя после ного пустоту (правило выравнивание последнего поля)
obj.$_size=sz;
obj.$_align=Math.min(align,pack); // для маленьких структур
defWINAPI[name]=obj;
struct[name]=obj;
},
destroy: function(obj){DWX.MemFree(obj.addr);obj.addr=0;struct.use[obj._nid]=null;},
destroyAll: function(){
var t; while(struct.use.length){t=struct.use.pop();if(t!=null)struct.destroy(t);}
}
/* Пример структуры - результат struct.define("WIN32_FIND_DATAW","DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; WCHAR cFileName[260]; WCHAR cAlternateFileName[14]"):
struct.WIN32_FIND_DATAW = defWINAPI.WIN32_FIND_DATAW = {
"dwFileAttributes": {type: "u", offset: 0},
"ftCreationTime": {
obj: {
"dwLowDateTime": {type: "u", offset: 0},
"dwHighDateTime": {type: "u", offset: 4},
$_size: 8,
$_align: 4
},
offset: 4
},
"ftLastAccessTime": {
obj: {
"dwLowDateTime": {type: "u", offset: 0},
"dwHighDateTime": {type: "u", offset: 4},
$_size: 8,
$_align: 4
},
offset: 12
},
"ftLastWriteTime": {
obj: {
"dwLowDateTime": {type: "u", offset: 0},
"dwHighDateTime": {type: "u", offset: 4},
$_size: 8,
$_align: 4
},
offset: 20
},
"nFileSizeHigh": {type: "u", offset: 28},
"nFileSizeLow": {type: "u", offset: 32},
"dwReserved0": {type: "u", offset: 36},
"dwReserved1": {type: "u", offset: 40},
"cFileName": {type: "t", offset: 44, count: 260, elemtype: "WCHAR"},
"cAlternateFileName": {type: "t", offset: 564, count: 14, elemtype: "WCHAR"},
$_size: 592,
$_align: 4
}
*/
}
// include
DWX.Register("kernel32.dll","FindFirstFileW","i=pp","r=h"); // LPCTSTR,LPWIN32_FIND_DATAW; HANDLE
DWX.Register("kernel32.dll","FindNextFileW","i=hp","r=l"); // HANDLE,LPWIN32_FIND_DATAW; BOOL
DWX.Register("kernel32.dll","FindClose","i=h","r=l"); // HANDLE; BOOL
// typedef
struct.define("FILETIME","DWORD dwLowDateTime; DWORD dwHighDateTime");
struct.define("WIN32_FIND_DATAW","DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD dwReserved0; DWORD dwReserved1; WCHAR cFileName[260]; WCHAR cAlternateFileName[14]"); // MAX_PATH=260
// define
var INVALID_HANDLE_VALUE=-1;
function FindFiles(path){
var Files=[];
var FindFileData=struct.create("WIN32_FIND_DATAW");
var hFind=DWX.FindFirstFileW(path,FindFileData.addr);
if(hFind!=INVALID_HANDLE_VALUE){
while(1){
if((FindFileData.get("dwFileAttributes")&0x10)==0) // FILE_ATTRIBUTE_DIRECTORY=0x00000010
Files.push(FindFileData.get("cFileName")); // если не дирректория
if(DWX.FindNextFileW(hFind,FindFileData.addr)==0)break;
}
DWX.FindClose(hFind);
}
struct.destroy(FindFileData);
return Files;
}
// main
var DllFiles=FindFiles("C:\\Windows\\*.dll");
WScript.Echo(DllFiles.join("\n"));
Т.е. вам реально попадались такие структуры или это чисто теоретически? Я думаю, этот тип используется внутри функций, чтобы в промежуточных вычислениях уменьшить потери в точности. А результат всё равно будет возвращён как double.
Да, про объект Struct я тоже думал и даже начал реализовывать некоторое время назад, как дочерний объект DWX. Синтаксис создания структуры, например, такой:
struct = DWX.Struct( "uwd", 1000, "Hello, world!", 33.44 );
Т.е. сначала список типов членов, потом их значения. И впоследствии доступ к членам по их порядковым номерам. Можно, наверно, и вариант с именованными членами сделать.
struct = DWX.Struct( "size:u name:w value:d", 1000, "Hello", 44.55 );
Нет, чисто теоретически. Допустим мы сохраняем 3D-модель, представленную в виде структуры (работать со структурированными файлами проще).
Я первоначально структуры делал, используя конструкторы (этот код я уже присылал, когда у меня проблема с IDirect3DDevice9 была на Windows8 x64 (и осталась)):
var DWX=new ActiveXObject("DynamicWrapperX");
var bit=DWX.Bitness(); // разрядность процесса
// типы по стандарту DynamicWrapperX в БАЙТАХ (строковые - 0)
var DWXTypes={"l":4,"u":4,"m":8,"q":8,"h":bit/8,"p":bit/8,"n":2,"t":2,"c":1,"b":1,"f":4,"d":8,"w":0,"s":0,"z":0,"v":0};
// функция преобразования (создания) структуры из объекта JScript (без строк)
function getStructWin(struct){
var types=struct._const, // строка типов в стандарте DynamicWrapperX
size=0, // размер структуры (вначале исходной, потом возвращаемой)
data; // информация для внесения в структуру
for(var i=0;i<types.length;i++)size+=DWXTypes[types.charAt(i).toLowerCase()]; // Вычисляем размер структуры /*на строку память не выделится!!*/
var ns=DWX.MemAlloc(size,1); // MemFree() // выделение памяти в БАЙТАХ // создание структуры (new struct)
size=0;i=0;
for(var p in struct){ // заполняем структуру
if(p=="_const")continue; // исключаем из структуры МОЮ строку типов
data=struct[p];
if(data==null)data=0;
DWX.NumPut(data,ns,size,types.charAt(i)); // заносим данные data в структуру ns со смещением size
size+=DWXTypes[types.charAt(i++).toLowerCase()];
}
return ns; // возвращаем преобразованную структуру
}
function getStructJS(ns,struct){ /*строки!!*/
var types=struct._const, size=0;
for(var i in struct){
struct[i]=DWX.NumGet(ns,size,types.charAt(i))
size+=DWXTypes[types.charAt(i).toLowerCase()]; // получая размер параметра
}
return struct; // возвращаем преобразованную структуру
}
// typedef struct
function WNDCLASSEX(){
// UINT, UINT, WNDPROC, int, int, HINSTANCE, HICON, HCURSOR, HBRUSH, LPCTSTR, LPCTSTR, HICON
var a=["cbSize","style","lpfnWndProc","cbClsExtra","cbWndExtra","hInstance",
"hIcon","hCursor","hbrBackground","lpszMenuName","lpszClassName","hIconSm"];
for(var i in a)this[a[i]]=arguments[i];
this._const="uupllhhhhpph";
}
var IDI_APPLICATION=0x7F00, IDC_ARROW=0x7F00, CS_VREDRAW=0x1, CS_HREDRAW=0x2,
CS_DBLCLKS=0x8, CS_OWNDC=0x20;
// использование:
DWX.Register("user32","LoadIconW","i=hp","r=h");
DWX.Register("user32","LoadCursorW","i=hp","r=h")
DWX.Register("user32","RegisterClassExW","i=p","r=t");
DWX.Register("user32","DefWindowProcW","i=hupp","r=l"); // HWND,UINT,WPARAM,LPARAM; LRESULT
DWX.Register("kernel32","GetModuleHandleW","i=p","r=h"); // i=(LPCTSTR); r=HMODULE
function WndProc(hWnd,uMsg,wParam,lParam){return DWX.DefWindowProcW(hWnd,uMsg,wParam,lParam);}
var hModule=DWX.GetModuleHandleW(0);
var pWndProc=DWX.RegisterCallback(WndProc,"i=hupp","r=l");
var szClass="DirectX9"; // То самое имя класса, которое мы будем регистрировать
// заполняем структуру и получаем её в нужном нам формате
var myWClass=new WNDCLASSEX((bit==32)?48:80, CS_VREDRAW|CS_HREDRAW|CS_DBLCLKS|CS_OWNDC,
pWndProc, 0, 0, hModule, DWX.LoadIconW(0,IDI_APPLICATION),
DWX.LoadCursorW(0,IDC_ARROW), 0/*цвет фона*/, 0, szClass, 0);
var wc=getStructWin(myWClass);
// регистрация класса
if(DWX.RegisterClassExW(wc)==0){WSH.Echo("Ошибка при регистрации класса.");/*return false*/}
// ... и т.д.
Но я столкнулся с некоторыми проблемами: это не удобно и громоздко; у структур есть выравнивание, которое не всегда одинаковое; в структуре может быть массив (строка); не слишком просто вручную переводить типы в формат DWX, к тому же бывали случаи, когда приходилось всем одинаковым типам (например, LRESULT) сменить их "значение" DWX, к тому же наличие типов в стиле winapi (хотя бы в комментариях) облегчает написание программы (в документациях они же используются). И к тому же мало возможностей. Вот и получился тот вариант struct. Хотел, кстати, ещё копирование структур/части структур сделать и ограничить вывод строк, но пока это "горло не резало" (об этом я уже здесь спрашивал).
Ещё думаю как сделать структуру с динамическим размером (но как выделить дополнительную память в уже имеющуюся структуру (и изменить смещения и т.п.) я не придумал), типа:
typedef struct {
...
LONG nSize; // размер следующего поля
WCHAR cText[nSize]; // массив длины не определённой изначально
...
} MYTMPSTRUCT;
Интересно, сколько их вообще, этих производных типов. Теоретически можно было бы их свести в какую-то таблицу и использовать в обозначениях типов параметров и т.п., но впечатление такое, что их куева туча и всё равно какие-то останутся не охваченными. Может быть, держать их в текстовом файле, который можно было бы постепенно пополнять по мере надобности.
Дополнительную память — ну, вряд ли. Перевыделять тогда память большего размера. Ведь нельзя гарантировать, что рядом с уже выделенной памятью будет свободное место для расширения.
Типов таких очень много. В зависимости от используемых технологий. Я их по мере необходимости в тот список добавлял. В моём случае их можно добавлять непосредственно, можно написать defWINAPI["HMENU"]=defWINAPI["HANDLE"] или сделать функцию. Мне кажется что носить с собой текстовый файл будет неудобно, лучше, наверное, непосредственно в код вставлять.
Вот пример из структур сохранения игры (SAS.bt):
...
// Block 2: Players & Objects
struct tBlock02_Players_n_Objects
{
tblockHeader header;
int nPlayers;
tPlayer players[nPlayers];
int nObjects;
tObject objects[nObjects];
} block2;
// Block 3: Garages
struct tBlock03_Garages
{
tblockHeader header;
int nGarages;
byte boolFreeBombs, boolFreeResprays;
byte boolRespraysDisabled;
DWORD unknown[8];
tGarageVehiclesTown GarageVehicles[4];
tGarage Garages[nGarages];
} block3;
...
Как тогда происходит чтение/запись таких "полуструктурированных" файлов? Может она вначале в кучу всё считывает, а потом, считав и узнав размер, уже размечает память? Хотел сделать поддержку таких структур тоже.
Т.е. такие структуры хранятся в файле? Тогда значения nPlayers и т.п. там тоже есть и несложно вычислить размер массивов, а значит и структуры в целом.
Отбой, не бывает таких структур. Этот файл со структурой интерпретируется не как сишная структура, всё считывается отдельно написанным кодом.
Да, можно, но заранее (перед прочтением поля структуры, содержащего размер) размера мы не знаем. И в ходе выполнения программы это количество и размер структуры соответственно может измениться. Я подумал, что можно допустим массив players в структуре представить указателем на связный список nPlayers элементов, а работать как с обычной структурой ("struct" это должен будет обеспечивать) в том числе и чтение/запись её. К тому же, создавая пустую такую структуру, предполагается, что nPlayers=0.
Глобальные хуки устанавливаются только с использованием DLL.
http://www.prog.org.ru/index.php?topic= … #msg157278
Может добавить возможность устанавливать хуки для скриптов? Поставить хук на всё, а скрипту передавать только то, что требуется.
Для мыши и клавиатуры DLL не обязательна, если использовать WH_MOUSE_LL и WH_KEYBOARD_LL.
Ставить хук на всё, по-моему, незачем. От DLL требуется только callback-функция, причём для всех хуков она одинакова по набору параметров и возвращаемому значению. По идее функция может просто передавать свои параметры скрипту, а их обработка будет уже там, в зависимости от того, какой это хук. Вопрос в том, как осуществить эту передачу.
Здравствуйте, YMP! Спасибо, с низкоуровневыми хуками действительно работает.
Пытался получить имя файла с сервера и заметил падение хоста при использовании возвращаемого значения функции DWX.LastError(1).
function ld0n(val,n){val+=""; while(val.length<n)val="0"+val; return val;}
function alert(val){WScript.Echo(val);}
var DWX;
try{DWX=new ActiveXObject("DynamicWrapperX.2");}
catch(e){WScript.Echo("Ошибка. Не установлен компонент DynamicWrapperX 2.");WScript.Quit();}
// include <wininet.h>
DWX.Register("wininet.dll","InternetOpenW","i=puppu","r=h"); // LPCTSTR,DWORD,LPCTSTR,LPCTSTR,DWORD; HINTERNET
DWX.Register("wininet.dll","InternetConnectW","i=hpuppuup","r=h"); // HINTERNET,LPCTSTR,INTERNET_PORT,LPCTSTR,LPCTSTR,DWORD,DWORD,DWORD_PTR; HINTERNET
DWX.Register("wininet.dll","InternetCloseHandle","i=h","r=l"); // HINTERNET; BOOL
DWX.Register("wininet.dll","HttpOpenRequestW","i=hpppppup","r=h"); // HINTERNET,LPCTSTR,LPCTSTR,LPCTSTR,LPCTSTR,LPCTSTR,DWORD,DWORD_PTR; HINTERNET
DWX.Register("wininet.dll","HttpQueryInfoW","i=huppp","f=l","r=l"); // HINTERNET,DWORD,LPVOID,LPDWORD,LPDWORD; BOOL
// defile
var NULL = 0;
var HTTP_QUERY_CONTENT_DISPOSITION = 47;
var INTERNET_OPEN_TYPE_PRECONFIG = 0;
var INTERNET_FLAG_RELOAD = 0x80000000;
var INTERNET_SERVICE_HTTP = 3;
var INTERNET_DEFAULT_HTTP_PORT = 80;
var INTERNET_FLAG_PRAGMA_NOCACHE = 0x00000100;
function GetUrlInfo(dwInfoLevel, FileURL){
var result = "<Error>";
var servname = FileURL.substr(0, FileURL.indexOf("/"));
var objname = FileURL.substr(FileURL.indexOf("/")+1);
var hInternet = DWX.InternetOpenW(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(hInternet){
var hConnect = DWX.InternetConnectW(hInternet, servname, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL/*""*/);
if(hConnect){
var hRequest = DWX.HttpOpenRequestW(hConnect, "GET", objname, NULL, NULL, NULL, INTERNET_FLAG_RELOAD || INTERNET_FLAG_PRAGMA_NOCACHE, NULL);
if(hRequest){
var size = 2048;
var pBuffer = DWX.MemAlloc(size, 0);
var pSizeBuffer = DWX.MemAlloc(4, 0); // DWORD
DWX.NumPut(size, pSizeBuffer, 0, "u");
if(DWX.HttpQueryInfoW(hRequest, dwInfoLevel, pBuffer, pSizeBuffer, NULL)) result = DWX.StrGet(pBuffer);
else{
// 12150: ERROR_HTTP_HEADER_NOT_FOUND (Не удалось найти запрошенный заголовок.)
alert("Error: "+DWX.LastError(0)+"");
alert(DWX.LastError(1)); // На этой строчке происходит crash
}
alert("Test alert"); // соответственно, не сработает
DWX.MemFree(pBuffer);
DWX.MemFree(pSizeBuffer);
DWX.InternetCloseHandle(hRequest);
}
DWX.InternetCloseHandle(hConnect);
}
DWX.InternetCloseHandle(hInternet);
}
return result;
}
GetUrlInfo(HTTP_QUERY_CONTENT_DISPOSITION, "www.yandex.ru/images/today");
Спасибо за сообщение, ошибку исправил. Использовалось возвращаемое значение LoadString вместо указателя на переданный ей буфер.
Что делает недокументированная функция UseLastError?
Ничего, это артефакт, оставшийся от процесса брожения идей по поводу того, как реализовать использование API GetLastError. Видимо, была идея включать и выключать её использование для данного экземпляра объекта. Т.е. сразу для всех зарегистрированных им функций.
Прошу помощи в освоении DynamicWrapperX.
Интересует пример активации установленной на ПК программы с параметрами.
Конкретней: запуск Adobe Reader, с открытием конкретного файла и посылке в Adobe Reader
комбинации клавиш CTRL+L.
Если сообщение не в тему - прошу извинить.
Для таких вещей гораздо удобнее использовать AutoHotkey. Тут на форуме есть раздел про него. А так же описание возможностей: http://www.script-coding.com/AutoHotkeyTranslation.html. Если нет желания или возможности устанавливать AutoHotkey на целевой машине, можно будет скрипт скомпилировать в EXE.
По некоторым причинам не могу использовать AutoHotkey.
Нужен vbscript.
Запуск на ПК Adobe Reader в скрипте реализована, но не могу послать именно ему комбинацию клавиш. Думал может с помощью DynamicWrapperX получиться.
В принципе-то это возможно, конечно. Надо только найти соответствующие API-функции. Мне не приходилось таким способом это реализовывать, так что прямо сейчас ответить не могу.
vzaitsev
Я подобные вещи обычно делаю стандартными средствами:
With CreateObject("WScript.Shell")
Set Exec = .Exec("C:\Path to Program.exe ""C:\Path to my file.ext""")
PID = Exec.ProcessID : Do : A = .AppActivate(PID) : Loop Until A
Wscript.Sleep 50 : .SendKeys "^l"
End With
А если прямо таки необходимо работать с классами/хэндлами окон, то лучше посмотреть в сторону AutiItX3.
Flasher
Работает. Только лучше маленькую букву посылать, иначе результат не тот. Не полноэкранный режим, а просто максимальное растягивание окна.
Через API у меня работает так. При условии, что программа уже запущена с открытым документом.
Set DWX = CreateObject("DynamicWrapperX")
DWX.Register "user32", "FindWindowW", "i=ww", "r=h"
DWX.Register "user32", "SetForegroundWindow", "i=h", "r=l"
DWX.Register "user32", "SendInput", "i=upl", "r=u"
window_class = "AcrobatSDIWindow"
hwnd = DWX.FindWindowW( window_class, 0 )
If hwnd = 0 Then
MsgBox "Окно не найдено."
WScript.Quit
End If
If DWX.Bitness = 32 Then
input_size = 28
input_data = "01000000 A200 0000 00000000 00000000 00000000 00000000 00000000" &_
"01000000 4C00 0000 00000000 00000000 00000000 00000000 00000000" &_
"01000000 4C00 0000 02000000 00000000 00000000 00000000 00000000" &_
"01000000 A200 0000 02000000 00000000 00000000 00000000 00000000"
Else
input_size = 40
input_data = "01000000 00000000 A200 0000 00000000 00000000 00000000 0000000000000000" &_
"0000000000000000" &_
"01000000 00000000 4C00 0000 00000000 00000000 00000000 0000000000000000" &_
"0000000000000000" &_
"01000000 00000000 4C00 0000 02000000 00000000 00000000 0000000000000000" &_
"0000000000000000" &_
"01000000 00000000 A200 0000 02000000 00000000 00000000 0000000000000000" &_
"0000000000000000"
End If
INPUTS = DWX.MemAlloc( input_size * 4 )
DWX.MemWrite input_data, INPUTS
ret = DWX.SetForegroundWindow( hwnd )
If ret = 0 Then
MsgBox "Ошибка активации окна."
DWX.MemFree INPUTS
WScript.Quit
End If
ret = DWX.SendInput( 4, INPUTS, input_size )
If ret = 0 Then
MsgBox "Ошибка посылки клавиш."
End If
DWX.MemFree INPUTS
Не полноэкранный режим, а просто максимальное растягивание окна.
В XP быть не должно, это в последующих версиях добавка шифта появилась при задействовании верхнего регистра. Исправил.
Через API у меня работает так. При условии, что программа уже запущена с открытым документом.
Классссс! Работает. Проверил на XP (x32). Большое Спасибо!
Если можно в код вставить комментарии, т.к. мне не все понятны команды.
Через API я еще не работал.
Я так понимаю, что данный код будет работать на х32 и х64, при условии регистрации в системе библиотеки dynwrapx.dll (http://www.script-coding.com/dynwrapx.html).
При условии, что в системе обе версии зарегистрированы (32 и 64 бита). Какая из них будет использоваться, зависит от того, каким интерпретатором будет выполняться скрипт. В 64-битных Windows есть как 64-, так и 32-битный интерпретатор.
Чтобы понять, как работает код, почитайте описание функции SendInput. Ей передаётся массив из 4-х структур INPUT (они тоже описаны на MSDN). input_size — это размер такой структуры, разный для 32 и 64 бит. В первой прописано нажатие Ctrl, во второй нажатие L, потом отпускания этих клавиш. Клавиши обозначены соответствующими кодами виртуальных клавиш: Ctrl — A2, L — 4C. Список этих кодов можно найти в Интернете. Единица в начале означает, что передаётся событие клавиатуры, а двойка в двух последних структурах означает отпускание.
Классссс! Работает. Проверил на XP (x32). Большое Спасибо!
К сожалению рано радовался.
Скрипт работает нормально если его запустить на ПК нажав на нем Ентер.
Если запускать скрипт при старте ОС, то может не сработать. Фокус на окне Adobe Reader не устанавливается, если загружаются еще программы.
Т.е. скрипт с подключением библиотеки dynwrapx.dll аналогичен следующему без библиотеки:
WshShell.AppActivate ("Adobe Reader")
WScript.Sleep 1000
WshShell.SendKeys "^l"
Вопрос: можно-ли послать комбинацию клавиш именно окну (приложению)?
vzaitsev
Так а мой скрипт чем не устроил?
vzaitsev
Так а мой скрипт чем не устроил?
Код клавиш посылается на клавиатуру, а не приложению.
Если окно не в фокусе, оно не получит эту комбинацию.
vzaitsev
Комбинация клавиш посылается после активации окна приложения вообще-то. Сперва стоило попробовать, не так ли?
vzaitsev
Комбинация клавиш посылается после активации окна приложения вообще-то. Сперва стоило попробовать, не так ли?
Да именно так, после активации окна. Но если в промежутке активации окна и посылки комбинации клавиш, запустится или активируется еще программа (например:какой то месенжер, фар, тотал ком.), то комбинация клавиш пойдет уже другой программе.
vzaitsev
Этот промежуток составляет 50 мс, половину доли секунды. Какова вероятность, что какое-то ещё окно в этом промежутке появится? Не говоря о том, что этот промежуток условный, т.е. его можно изменить под себя.
P.S.: В цитировании предыдущего поста нет никакой необходимости.
Вопрос: можно-ли послать комбинацию клавиш именно окну (приложению)?
Можно — например, с помощью функции PostMessage — только нужно знать, какому окну. Кроме родительского окна есть ещё содержащиеся в нём дочерние. Которые ещё в себе могут содержать окна. Посылка родительскому окну (класс AcrobatSDIWindow) не работает. Вероятно, нужно как-то определить, на каком из дочерних окон у Ридера клавиатурный фокус, и посылать конкретно ему. Всё это усложняет задачу.
Кроме того, программа может и не реагировать на сообщения о нажатиях, если видит, что её окно неактивно. Хотя конкретно про Ридер ничего не могу сказать в этом отношении, может, ему и всё равно.
Столкнулся с проблемой: в оконной функции у некоторых сообщений, например WM_NCPAINT и WM_ERASEBKGND параметр wParam иногда получается типа unknown.
DWX.RegisterCallback(MainWndProc, "i=hupp", "r=p"); // HWND,UINT,WPARAM,LPARAM
...
function MainWndProc(hWnd, uMsg, wParam, lParam) {
if(uMsg == WM_ERASEBKGND) stdout("WM_ERASEBKGND: wParam = ("+typeof(wParam)+")"+wParam);
if(uMsg == WM_NCPAINT) stdout("WM_NCPAINT: wParam = ("+typeof(wParam)+")"+wParam);
switch(uMsg) {
case WM_DESTROY:
DWX.PostQuitMessage(0);
return 0;
}
return DWX.DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
Тут, в wParam, судя по описанию WM_ERASEBKGND и WM_NCPAINT - дескриптор.
Аналогичная программа на C:
В принципе, решить проблему просто - тип wParam указать не "p", а "h". Но это может всплыть в ином месте, где используется этот тип "p".
Поэтому на данный момент разница между "p" и "h", "u" и "l", и т.д. — чисто мнемоническая.
Получается, "p" и "h" не эквивалентны - при работе с "h" проблем не возникает:
// Различия между типами "p" и "h"
function stdout(val){try{WScript.StdOut.WriteLine(val);}catch(e){}} // debug
var DWX;
try{DWX=new ActiveXObject("DynamicWrapperX.2");}
catch(e){WScript.Echo("Ошибка. Не установлен компонент DynamicWrapperX 2.");WScript.Quit();}
if(DWX.Bitness()!=64) WScript.Quit();
var fn0=function(v_P, v_H){
stdout("("+typeof(v_P)+")"+v_P+", ("+typeof(v_H)+")"+v_H);
return 0;
}
var lpnf0=DWX.RegisterCallback(fn0, "i=ph", "r=l");
DWX.RegisterAddr(lpnf0, "TestFunction", "i=ph", "r=l");
DWX.TestFunction(-0x8000000000000400, -0x8000000000000400); // минимальное число (при ...401 уже не сработает) (- не "нейтрализуется")
DWX.TestFunction( 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF); // реальное минимальное число
DWX.TestFunction( 0xFFFFFFFFFFFFFBFF, 0xFFFFFFFFFFFFFBFF); // максимальное число
DWX.TestFunction(-0x0000000000000001, -0x0000000000000001); // реальное максимальное число (unsigned, если)
DWX.TestFunction(-150903375, -150903375); // на все отрицательные числа p "ответит" unknown
DWX.TestFunction(-0x7FFFFFFFFFFFFBFF, -0x7FFFFFFFFFFFFBFF); // и тут от p unknown
DWX.TestFunction(-0x7FFFFFFFFFFFFA00, -0x7FFFFFFFFFFFFA00); // тут уже преобразовывается
Версия DWX: 2.1.1.1 x64
Версия JScript: 5.812 x64
Версия Windows: 10.0.10586.104 x64
Да, они эквивалентны при передаче числа из скрипта, но не эквивалентны при передаче числа в скрипт (для возвращаемых значений и параметров callback-функций).
Если вы передаёте число из скрипта, то обозначите ли вы его как 0xFFFFFFFFFFFFFFFF или как -1, в памяти будет один и тот же набор битов. Т.е. вызываемой API-функции это без разницы. Она получает набор битов, тип числа там никак не обозначен, и она сама знает, как его интерпретировать в смысле знаковости.
В то же время скриптовый движок может не иметь типа переменной для 64-битных беззнаковых чисел, и втайне от вас поместит 0xFFFFFFFFFFFFFFFF в знаковый тип, т.е. передаст его в DWX как -1. Или вообще его забракует, и тогда вы сами можете попытаться использовать -1, если знаете о том, что в памяти разницы не будет. Первая версия DWX отрицательные числа для указателей не принимала, т.к. это адрес, а адрес не может быть отрицательным. Такой строгий контроль вызывал неудобства в силу вышеозвученных причин, и я его убрал.
Что касается передачи числа извне в скрипт, то тут уже не всё равно, знаковое число или беззнаковое, поскольку его тип должен быть явным образом указан в передаваемой скрипту переменной. Если передать указатель как отрицательное, а вы станете с ним производить какие-то вычисления, вполне логично ожидая, что он положителен, то получится неверный результат. Исходя из этого, тип "p" интерпретируется как положительный, и если величина числа превысит диапазон дробного двойной точности (VT_R8), то используется тип VT_UI8, который JScript'у неизвестен, поэтому и обозначается им как unknown.
Хэндлы в принципе тоже беззнаковый тип, но они используются просто как некие условные числа, идентифицирующие какие-то объекты Винды. Вычислять с ними нечего, максимум можно их сравнить, чтобы узнать, один это объект или разные. Этому их знаковость никак не мешает. Поэтому они интерпретируются DWX как знаковые. Теоретически и там величина числа может выйти за пределы VT_R8, и тогда будет использован VT_I8, который тоже неизвестен JScript'у. Но здесь это, думаю, маловероятно, поскольку не видно смысла использовать такие большие числа для условных идентификаторов. Разве что адрес объекта может использоваться как его идентификатор.
Кстати, wParam в обеих упомянутых вами функциях — это хэндл, поэтому вполне логично использовать для них тип "h".
А как работать со структурами?
Примерчик один можно?
DWX = new ActiveXObject("DynamicWrapperX");
DWX.Register("kernel32.dll", "CreateDirectoryW", "i=wv", "r=l");
a = DWX.CreateDirectoryW("C:\\111", "");
В данном примере, во втором параметре передаётся структура безопасности.
Как это должно выглядить?
А вам она там точно нужна? Можно просто нуль передать, типом "p".
DWX = new ActiveXObject("DynamicWrapperX");
DWX.Register("kernel32.dll", "CreateDirectoryW", "i=wp", "r=l");
a = DWX.CreateDirectoryW("C:\\111", 0);
А вообще под структуры выделяется память методом MemAlloc и в неё пишутся значения её членов через NumPut.
Хотелось бы увидеть любой пример, как работать со структурами.
Ведь большинство API функций, как раз, без структур не обходятся.
Вот, как пример работы со структурой, с учётом битности хоста. Если нужно много работать со структурами, лучше написать класс, который будет вычислять offset'ы с учетом alignment'ов при инициализации нового типа структуры в зависимости от разрядности хоста и потом по ним получать/записывать данные. Потому что с этими магическими числами-смещениями с увеличением количества структур легко запутаться.
var DWX = new ActiveXObject("DynamicWrapperX.2");
DWX.Register("kernel32", "CreateDirectoryW", "i=wp", "r=l"); // LPCWSTR,LPSECURITY_ATTRIBUTES; BOOL
var NULL = 0, FALSE = 0;
// Если безопасность важна, то нужно выделить под структуру память и заполнить параметрами (причем для хостов разной битности будут разные размеры указателей, соответственно, разные смещения):
var bit = DWX.Bitness();
// для x32 размер структуры 12, т.к. sizeof(DWORD) {4 байта} + sizeof(LPVOID) {4 байта} + sizeof(BOOL) {4 байта}, т.е. все alignment'ы равны 0.
// для x64 размер структуры 24, т.к. sizeof(DWORD) {4 байта} + alignment {4 байта} + sizeof(LPVOID) {8 байт} + sizeof(BOOL) {4 байта} + alignment {4 байта}
var nLength = bit === 32 ? 12 : 24; // sizeof(SECURITY_ATTRIBUTES)
var lpSecAttrib = DWX.MemAlloc(nLength);
DWX.NumPut(nLength, lpSecAttrib, 0, "u"); // DWORD
DWX.NumPut(NULL, lpSecAttrib, bit === 32 ? 4 : 8, "p"); // LPVOID
DWX.NumPut(FALSE, lpSecAttrib, bit === 32 ? 8 : 16, "l"); // BOOL
var bResult = DWX.CreateDirectoryW("New folder (1)", lpSecAttrib);
if(bResult !== FALSE) WScript.Echo("OK (1)");
else WScript.Echo("Error (1)");
DWX.MemFree(lpSecAttrib);
// Если безопасность не важна, то функция может принимать и NULL:
var bResult = DWX.CreateDirectoryW("New folder (2)", NULL);
if(bResult !== FALSE) WScript.Echo("OK (2)");
else WScript.Echo("Error (2)");
maksim32, спасибо.
А почему Bool равен 4 байта? Это результат выравнивания?
И для чего вообще делается выравнивание?
Чтобы отправить ответ, вы должны войти или зарегистрироваться