Что нового

Как перевести скрипт Vbs на язык Autoit (проблема с обработкой событий)

VladUs

Скриптер
Сообщения
621
Репутация
181
Добрый день. Подскажите пожалуйста как перевести код Vbs на код Autoit.
До этого, проблем с WMI не было. Но вот столкнулся с такой проблемой.
Не могу обработать события от WMI в асинхронном режиме.
Для того чтобы обработать события в данном режиме, необходима процедура (типа Sub ... End Sub) обработки события "OnObjectReady". Но в Autoit отсутствует такая конструкция. Запихнуть данную конструкцию (Sub ... End Sub) в функцию не удается. Чуть ниже два скрипта.
Один на Vbs - рабочий, другой на Autoit - не рабочий. Назначение скрипта: Оповещать пользователя о запуске нового процесса. Прошу не предлагать другие способы выполнения задачи скрипта. Интересует именно обработка событий WMI в асинхронном режиме (в синхронном режиме более менее все понятно).


Код:
strComputer = "." 
'подключение к пространству имён
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2") 
'создание объекта синхронизации MySink
Set MySink = WScript.CreateObject( "WbemScripting.SWbemSink","SINK_")
'асинхронный запрос
objWMIservice.ExecNotificationQueryAsync MySink, _
    "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'"

'цикл ожидания для получения всех событий о создании процессов
WScript.Echo "Waiting for events..."

While (True)
    Wscript.Sleep(1000)
Wend

'процедура  обработки события OnObjectReady  (Эта злосчастная процедура)
Sub SINK_OnObjectReady(objObject, objAsyncContext)
    Wscript.Echo objObject.TargetInstance.Name
End Sub


Код:
;подключение к пространству имён
$WMIService = ObjGet("winmgmts:\\" & @ComputerName & "\root\CIMV2") 
;создание объекта синхронизации
$MySink = ObjCreate( "WbemScripting.SWbemSink","SINK_")
;асинхронный запрос
$WMIService.ExecNotificationQueryAsync  ($MySink, "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'")

; Ожидаем события
While 1
    Sleep(1000)
Wend

;обработчик события OnObjectReady
Func SINK_OnObjectReady($objWbemObject, $objWbemAsyncContext)
	$Name = $objWbemObject.TargetInstance.Name
    MsgBox(1,"", $Name)
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
Re: Как первести скрипт Vbs на язык Autoit (проблема с обработкой событий)

В vbs используется «ExecNotificationQueryAsync», а в AutoIt «ExecQueryAsync», возможно в этом проблема?
 
Автор
V

VladUs

Скриптер
Сообщения
621
Репутация
181
Re: Как первести скрипт Vbs на язык Autoit (проблема с обработкой событий)

Одно и тоже
Код:
C:\Documents and Settings\?????????????\??????? ????\ProcessDetect.au3 (49) : ==> The requested action with this object has failed.:
$WMIService.ExecQueryAsync  ($MySink, "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'")
$WMIService.ExecQueryAsync  ($MySink, "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'")^ ERROR
На vb6 все нормально , здесь не получается...

CreatoR
В vbs используется «ExecNotificationQueryAsync», а в AutoIt «ExecQueryAsync», возможно в этом проблема?
А как Вы определили что в vbs «ExecNotificationQueryAsync», а в AutoIt «ExecQueryAsync» ?
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
Re: Как первести скрипт Vbs на язык Autoit (проблема с обработкой событий)

VladUs [?]
как Вы определили что в vbs «ExecNotificationQueryAsync», а в AutoIt «ExecQueryAsync» ?
:laugh: Я посмотрел на vbs код в первом сообщений темы.
 
Автор
V

VladUs

Скриптер
Сообщения
621
Репутация
181
Re: Как первести скрипт Vbs на язык Autoit (проблема с обработкой событий)

Просто я уже до этого менял по разному. Но от этого не легче....
Исправил...
 
Автор
V

VladUs

Скриптер
Сообщения
621
Репутация
181
Все разобрался.. На основе примера из офф. форума.
Вот код, может кому - то пригодится
Скрипт сидит в трее и сообщает информацию о запускаемых процессах (Имя, Pid, и путь)

Код:
local $objAsyncContext, $objLatestEvent, $objWbemObject
;подключение к пространству имён
$objWMIService = ObjGet("winmgmts:" & "!\\" & @ComputerName & "\root\cimv2")
;Строка запроса
$strQuery = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'"
; Объект синхронизации
$Sink = ObjCreate( "WbemScripting.SWbemSink")
; Обработать входящие события
ObjEvent($Sink , "SINK_")
$objContext = ObjCreate("WbemScripting.SWbemNamedValueSet")

; Запрос
If Not @error Then
	$objWMIService.ExecNotificationQueryAsync ($SINK, $strQuery, Default, Default, Default, $objContext)
	ConsoleWrite("Подключен к : " & @ComputerName &  @CRLF)
EndIf

; Ожидаем события
While 1
    Sleep(1000)
Wend

;обработчик события OnObjectReady
Func SINK_OnObjectReady($objLatestEvent, $objAsyncContext)
	$Name=$objLatestEvent.TargetInstance.Name
	$Pid = $objLatestEvent.TargetInstance.Handle
	traySetState(1)
	TrayTip ("Запущен новый процесс", "Имя   :  " & $Name & @CRLF & "PID    :  " &  $Pid  &  @CRLF &  "Путь :  " & _Patch($Name)  ,5 ,1)
    ConsoleWrite("  Process: " & $Name & " PId:" & $Pid &   " Путь:" & _Patch($Name) & @CRLF)
EndFunc   

; Фунукция возвращает путь запущенного процесса
Func _Patch($Process)
$PathWMIService = ObjGet("winmgmts:\\" & @ComputerName & "\root\CIMV2") 
$PathcolItems = $PathWMIService.ExecQuery('SELECT * FROM Win32_Process WHERE Name ="' & $Process & '"') 
For  $PathobjItem in $PathcolItems 
     $Path = $PathobjItem.ExecutablePath
Next
Return $Path
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
VladUs [?]
Скрипт сидит в трее и сообщает информацию о запускаемых процессах (Имя, Pid, и путь)
Это можно было бы сделать и без WMI, на чистом AutoIt (+API естественно).
 
Автор
V

VladUs

Скриптер
Сообщения
621
Репутация
181
CreatoR [?]
Это можно было бы сделать и без WMI, на чистом AutoIt (+API естественно).
Хм ... То есть если Autoit + WinApi, то тогда это получается чистый Autoit, а вот если Autoit +WMi тогда Autoit ...какой ... грязный ?
Во-первых,я невижу ничего плохого чтобы использовать данную связку. WMI очень мощный инструмент администрирования. Во - вторых дело не в примере скрипта, а то что в нем показано как с помощью такой связки подписываться на разнообразные события происходящие во время работы операционной системы. Это и запуск и остановка процессов, создание и удаление файлов, работа с оборудованием (напр. извлечение и подключение USB - накопителей и многое другое).И почему бы все это богатство информации, предоставляемое операционной системой не использовать в своих скриптах. Да WinApi это круто (с этим трудно поспорить), но к сожалению оно достаточно сложно для понимания новичка. И здесь начинаешь сожалеть, что ты(в смысле я) не CreatoR или Yashied в программировании.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
VladUs [?]
Хм ... То есть если Autoit + WinApi, то тогда это получается чистый Autoit, а вот если Autoit +WMi тогда Autoit ...какой ... грязный ?
WinAPI на мой взгляд, в отличий от WMI, более надёжная технология, тем более что WMI не на каждой системе может быть включён.

P.S
И API в этом случае понадобится только для получения пути к исполняемому файлу процесса.
 
Автор
V

VladUs

Скриптер
Сообщения
621
Репутация
181
CreatoR [?]
WinAPI на мой взгляд, в отличий от WMI, более надёжная технология, тем более что WMI не на каждой системе может быть включён.
Если бы я был такой знаток программирования на WinApi как Вы, то подписался бы под Вашеми словами.
Я думаю, что Вам просто проще и превычнее обратиться к функции WinApi чем сделать запрос к WMI. Хотя само WMI использует свой API.

Для прямого взаимодействия с WMI Windows - программы используют WMI COM API, основной API управления. Остальные API размещаются поверх COM API ...
Внутреннее устройство Windows
Марк Руссинович, Дэвид Соломон
Насчет примера...Не берите во внимание сам конкретный случай примера. Я повторюсь, что данный пример показывает способ взаимодействия Autoit и WMI для подписки на события операционной системы
 

joiner

Модератор
Локальный модератор
Сообщения
3 362
Репутация
586
CreatoR сказал(а):
Это можно было бы сделать и без WMI, на чистом AutoIt (+API естественно).
а можно показать на примере? то есть то же самое что у VladUs, только используя WinApi
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
[?]
Это можно было бы сделать и без WMI, на чистом AutoIt
Вот так:

Код:
#include <Array.au3>

Global $aProcesses = ProcessList()

Opt("TrayAutoPause", 0)
Opt("TrayMenuMode", 1)

HotKeySet("^+q", "_Quit")
AdlibRegister("_ProcessMonitor", 1000)

While 1
	Sleep(100)
WEnd

Func _ProcessMonitor()
	Local $iProcAdded, $iAsk, $aTmpProcArr = ProcessList()
	Local $sProcName, $iProcPID
	
	If Not _Array2DIsIdentical($aTmpProcArr, $aProcesses, 1) Then
		If UBound($aTmpProcArr) > UBound($aProcesses) Then
			$iProcAdded = 1
			$aArr1 = $aTmpProcArr
			$aArr2 = $aProcesses
		Else
			$iProcAdded = 0
			$aArr1 = $aProcesses
			$aArr2 = $aTmpProcArr
		EndIf
		
		For $i = 1 To $aArr1[0][0]
			$iIndx = _ArraySearch($aArr2, $aArr1[$i][1], 1, 0, 0, 0, 1, 1)
			
			If $iIndx = -1 Then
				$sProcName = $aArr1[$i][0]
				$iProcPID = $aArr1[$i][1]
				
				ExitLoop
			EndIf
		Next
		
		$aProcesses = $aTmpProcArr
		
		If $iProcAdded Then
			$sTitleInfo = "Запущен новый процесс"
			$sBodyInfo = StringFormat("Имя\t: %s\r\nPID\t: %i\r\nПуть\t: %s", $sProcName, $iProcPID, _ProcessGetPath($iProcPID))
		Else
			$sTitleInfo = "Процесс закрыт"
			$sBodyInfo = StringFormat("Имя\t: %s\r\nPID\t: %i", $sProcName, $iProcPID)
		EndIf
		
		TrayTip($sTitleInfo, $sBodyInfo, 5, 1)
		ConsoleWrite($sTitleInfo & ":" & @CRLF & $sBodyInfo & @CRLF & "--------------------------------" & @CRLF)
	EndIf
EndFunc

Func _ProcessGetPath($vProcess)
	Local $iPID = ProcessExists($vProcess)
	If Not $iPID Then Return SetError(1, 0, -1)
	
	Local $aProc = DllCall('kernel32.dll', 'hwnd', 'OpenProcess', 'int', BitOR(0x0400, 0x0010), 'int', 0, 'int', $iPID)
	If Not IsArray($aProc) Or Not $aProc[0] Then Return SetError(2, 0, -1)
	
	Local $vStruct = DllStructCreate('int[1024]')
	
	Local $hPsapi_Dll = DllOpen('Psapi.dll')
	If $hPsapi_Dll = -1 Then $hPsapi_Dll = DllOpen(@SystemDir & '\Psapi.dll')
	If $hPsapi_Dll = -1 Then $hPsapi_Dll = DllOpen(@WindowsDir & '\Psapi.dll')
	If $hPsapi_Dll = -1 Then Return SetError(3, 0, '')
	
    DllCall($hPsapi_Dll, 'int', 'EnumProcessModules', _
		'hwnd', $aProc[0], _
		'ptr', DllStructGetPtr($vStruct), _
		'int', DllStructGetSize($vStruct), _
		'int_ptr', 0)
	Local $aRet = DllCall($hPsapi_Dll, 'int', 'GetModuleFileNameEx', _
		'hwnd', $aProc[0], _
		'int', DllStructGetData($vStruct, 1), _
		'str', '', _
		'int', 2048)
	
	DllClose($hPsapi_Dll)
	
	If Not IsArray($aRet) Or StringLen($aRet[3]) = 0 Then Return SetError(4, 0, '')
	Return $aRet[3]
EndFunc

Func _Array2DIsIdentical($av2DArray_a, $av2DArray_b, $iStart = 0, $iSubItems = 1)
	If UBound($av2DArray_a) <> UBound($av2DArray_b) Then
		Return SetError(1, 0, 0)
	EndIf
	
	For $i = $iStart To UBound($av2DArray_a)-1
		For $j = $iStart To $iSubItems
			If $av2DArray_a[$i][$j] <> $av2DArray_b[$i][$j] Then
				Return 0
			EndIf
		Next
	Next
	
	Return 1
EndFunc

Func _Quit()
	Exit
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
VladUs [?]
Если бы я был такой знаток программирования на WinApi как Вы, то подписался бы под Вашеми словами.
Я не такой уж и знаток WinAPI, просто у меня был горький опыт с WMI ;)
 

madmasles

Модератор
Глобальный модератор
Сообщения
7 790
Репутация
2 319
CreatoR [?]
Этот принцип я знал. Мне было интересно привязаться к чему-нибудь другому. Вот что у меня получилось, вроде работает:
Код:
#include <Date.au3>
#include <WinAPIEx.au3>

Opt('TrayMenuMode', 1)

HotKeySet('^{F5}', '_Exit')

Global $iPause = 1

AdlibRegister('_NewProcessSearch', $iPause * 1000)

While 1
	Sleep(100)
WEnd

Func _NewProcessSearch()
	Local $i_Now, $a_ProcessList, $i_Dif, $f_New = False, $s_Text, $s_Title = 'Новый процесс'

	$a_ProcessList = ProcessList()
	If Not $a_ProcessList[0][0] Then Return
	$i_Now = _NowTime()
	For $i = 1 To $a_ProcessList[0][0]
		$i_Dif = _GetSecDiff($i_Now, _GetTimeStart($a_ProcessList[$i][1]))
		If $i_Dif > 0 And $i_Dif < $iPause + 1 Then
			$f_New = True
			$s_Text &= _GetProcessInfo($a_ProcessList[$i][1], $a_ProcessList[$i][0])
		EndIf
	Next
	If $f_New Then
		ConsoleWrite($s_Text & @CR & '<=====================>' & @CR)
		TrayTip($s_Title, $s_Text, 5, 1)
	EndIf
EndFunc   ;==>_NewProcessSearch

Func _GetProcessInfo($i_PID, $s_Name)
	Local $s_Memory = 0, $a_Data, $s_ComLine, $s_Path, $s_Return
	$a_Data = _WinAPI_GetProcessMemoryInfo($i_PID)
	If Not @error Then
		$s_Memory = $a_Data[2] / 1024
	EndIf
	$s_ComLine = _WinAPI_GetProcessCommandLine($i_PID)
	$s_Path = _WinAPI_GetProcessFileName($i_PID)
	$s_Return = StringFormat('Имя\t: %s\r\nPID\t: %i\r\nПамять\t: %i kb\r\nКлюч\t: %s\r\nПуть\t: %s', _
			$s_Name, $i_PID, $s_Memory, $s_ComLine, $s_Path)
	Return $s_Return
EndFunc   ;==>_GetProcessInfo

Func _GetTimeStart($i_PID)
	Local $aFT, $tFT, $tST, $s_Time = '0:0:0'
	$aFT = _WinAPI_GetProcessTimes($i_PID)
	If @error Then Return $s_Time
	$tFT = _Date_Time_FileTimeToLocalFileTime(DllStructGetPtr($aFT[0]))
	If @error Then Return $s_Time
	$tST = _Date_Time_FileTimeToSystemTime(DllStructGetPtr($tFT))
	If @error Then Return $s_Time
	$s_Time = _WinAPI_GetTimeFormat(0, $tST)
	If @error Then Return $s_Time
	Return $s_Time
EndFunc   ;==>_GetTimeStart


Func _GetSecDiff($s_Now, $s_Old)
	Local $a_Now, $a_Old, $i_Now, $i_Old
	$a_Old = StringSplit($s_Old, ':')
	If $a_Old[0] <> 3 Then Return -1
	$a_Now = StringSplit($s_Now, ':')
	If $a_Now[0] <> 3 Then Return -1
	$i_Old += $a_Old[1] * 3600
	$i_Old += $a_Old[2] * 60
	$i_Old += $a_Old[3]
	$i_Now += $a_Now[1] * 3600
	$i_Now += $a_Now[2] * 60
	$i_Now += $a_Now[3]
	Return $i_Now - $i_Old
EndFunc   ;==>_GetSecDiff

Func _Exit()
	Exit
EndFunc   ;==>_Exit

WinAPIEx.au3
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 473
Репутация
2 403
madmasles [?]
Мне было интересно привязаться к чему-нибудь другому
Почему?
И если честно не удалось понять к чему :whistle:
И кстати, в этом примере не учитываются закрытые процессы.

P.S
«Ctrl + F5» неудачное сочетание, у меня рука вроде не маленькая (всё таки на пиано играл когда то), но едва пальцы растягиваю :laugh:
 

madmasles

Модератор
Глобальный модератор
Сообщения
7 790
Репутация
2 319
CreatoR [?]
не удалось понять к чему
Ко времени запуска процесса. Функция _GetTimeStart() это, фактически, пример к _WinAPI_GetProcessTimes() из WinAPIEx.au3. Потом время запуска процесса перевожу в секунды и сравниваю с настоящим временем (тоже в секундах). Если разность больше 0 и меньше, чем интервал + 1 секунда, значит наш процесс. :smile:
не учитываются закрытые процессы.
Это да...
но едва пальцы растягиваю
А у меня ноутбук, мне удобно. Поменять Ctrl + F5 на любое удобное сочетание - 2 секунды. ;)

Попробовал использовать Ctrl + F5 на стационарной клавиатуре, действительно не удобно. ;)
 
Верх