51

Re: WSH: обмен данными и объектами между скриптами — 2

kaster, я обычно обхожусь консольным интерфейсом, в цикличный код достаточно просто добавить вывод на консоль, потому я особо многопоточностью и не интересовался.

Реально, где многопоточность мне требовалась — в одновременном опросе N-ного числа машин посредством WMI (ибо практикуемый многими последовательный опрос растягивается на очень длительное время, особливо при наличии выключенных машин). Данный вопрос решился этой темой: VBScript: асинхронная обработка множественных запросов WMI.

52

Re: WSH: обмен данными и объектами между скриптами — 2

На мой взгляд для "псевдомногопоточности" можно использовать setInterval и setTimeout.

В этой теме
я как раз и привёл такой пример.

------ добавил позже ------

Хотели многопоточности ? Принимайте. )

Создаём 3 псевдопотока. ))


Dim log, Thread1, Thread2, Thread3, state

Set Thread1 = New clsThread
Set Thread2 = New clsThread
Set Thread3 = New clsThread

Thread1.Start getRef("Proc1")
Thread2.Start getRef("Proc2")
Thread3.Start getRef("Proc3")

Sub Proc1
    For i=1 to 9000000
    Next
    log = log & Time & ": Proc1 complete" & vbCrlf
    state = state + 1
End Sub

Sub Proc2
    For i=1 to 10000
    Next
    log = log & Time & ": Proc2 complete" & vbCrlf
    state = state + 1
End Sub

Sub Proc3
    For i=1 to 100
    Next
    log = log & Time & ": Proc3 complete" & vbCrlf
    state = state + 1
End Sub

Do
    WScript.Sleep 100
Loop Until State => 3

MsgBox log

Class clsThread
    Private document
    Private Sub Class_Initialize()
        set document = CreateObject("htmlfile")
    End Sub
    Sub Start(proc)
        document.parentWindow.setTimeout proc,0
    End Sub
End Class
Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

53

Re: WSH: обмен данными и объектами между скриптами — 2

Xameleon пишет:

Да собственно всё уже решено до нас - WshController. Не очень удобно, ибо требует настройки на локальном компе. Но куда уж многопоточнее ? ) Либо ScriptControl как вариант.

Можно и своё нагородить. Но хоть убейте - не вижу смысла.

alexii пишет:

Угу. Если задуматься — зачем? — то реальных приложений, не просто требующих многопоточности, а и умеющих её использовать — раз-два и обчёлся.

Вот реальный пример надобности многопоточности

Времени не хватает... :-(

54 (изменено: Евген, 2011-03-29 19:32:36)

Re: WSH: обмен данными и объектами между скриптами — 2

Xameleon пишет:

Создаём 3 псевдопотока. ))

Как этот пример кода переделать под произвольное количество потоков (от 1 до 30-ти) ?
Под пример использования в одном посте выше...

Времени не хватает... :-(

55

Re: WSH: обмен данными и объектами между скриптами — 2

Евген, избегайте излишнего цитирования. Я поправил Ваш пост.

56

Re: WSH: обмен данными и объектами между скриптами — 2

2 alexii Виноват, исправлюсь...

Времени не хватает... :-(

57 (изменено: mikser, 2011-03-29 20:29:18)

Re: WSH: обмен данными и объектами между скриптами — 2

На сколько я понял ключевой момент в коде хамелеона это CreateObject("htmlfile")
то есть создаем много хтмл документов и внутри них все исполняется в отдельных "потоках".

Тока тут одна проблема - если внутри созданого html документа создать какой-нить потенциально опасный ActiveX объект типа FileSystemObject то
виндовс вас спросит а вы доверяете этому приложению? и так для каждого потока. Как это отключить не знаю sad

58

Re: WSH: обмен данными и объектами между скриптами — 2

2 Евген: А самому попытаться ? ) Я вроде и так максимально простой пример сгенерил.
2 mikser: А кто сказал, что мы будем создавать потенциально опасные объекты внутри htmlfile ? От него нам нужен только setTimeout. А потенциально опасные объекты у нас все в WSH. )

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

59

Re: WSH: обмен данными и объектами между скриптами — 2

Xameleon, если добавить вывод и, для наглядности, немного поменять местами:

Dim log, Thread1, Thread2, Thread3, state
 
Set Thread1 = New clsThread
Set Thread2 = New clsThread
Set Thread3 = New clsThread
 
Thread1.Start getRef("Proc1")
Thread2.Start getRef("Proc2")
Thread3.Start getRef("Proc3")
 
Sub Proc1
    For i=1 to 90000
        WScript.StdOut.Write "1"
    Next
    log = log & Time & ": Proc1 complete" & vbCrlf
    state = state + 1
End Sub
 
Sub Proc2
    For i=1 to 100
        WScript.StdOut.Write "2"
    Next
    log = log & Time & ": Proc2 complete" & vbCrlf
    state = state + 1
End Sub
 
Sub Proc3
    For i=1 to 10000
        WScript.StdOut.Write "3"
    Next
    log = log & Time & ": Proc3 complete" & vbCrlf
    state = state + 1
End Sub
 
Do
    WScript.Sleep 100
Loop Until State => 3
 
MsgBox log
 
Class clsThread
    Private document
    Private Sub Class_Initialize()
        set document = CreateObject("htmlfile")
    End Sub
    Sub Start(proc)
        document.parentWindow.setTimeout proc,0
    End Sub
End Class

то становится видно, что многопоточности, увы, нет. Процедуры исполняются строго последовательно, в порядке, обратном созданию.

60

Re: WSH: обмен данными и объектами между скриптами — 2

2 alexii: Мда. Видимо придётся покорпеть над GlobalObject ) Эх.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

61

Re: WSH: обмен данными и объектами между скриптами — 2

Потому что нельзя передавать ссылки на функции, так как они будут исполняться в контексте их потока smile

62

Re: WSH: обмен данными и объектами между скриптами — 2

2 JSman: Так точно. ) Что MsgBox, что StdOut.WriteLine и циклы. Всё это держит основной поток. Как раз думаю - стоит ли на GlobalObject вешать отдельные процессы или нет. Пока в сомнениях.

Либо надо выносить необходимый код во внешние элементы. К примеру в WSC и исполнять его в контексте этих объектов.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

63

Re: WSH: обмен данными и объектами между скриптами — 2

Есть возможность вешать все на ScriptControl, но при этом о WScript можно забыть. С другой стороны использовать GlobalObject вполне безопасно для основного скрипта.

64

Re: WSH: обмен данными и объектами между скриптами — 2

Есть идея использовать mshta. В принципе код можно генерировать на лету, получать асинхронные события, не использовать WScript.Sleep.

65

Re: WSH: обмен данными и объектами между скриптами — 2

2 JSman: От слов к действиям. Есть что посмотреть ? wink

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

66

Re: WSH: обмен данными и объектами между скриптами — 2

http://funportal.info/smiles/smile284.gif

autoit@conference.jabber.ru - Конференция скриптового языка AutoIt на jabber.ru

67 (изменено: mikser, 2011-03-30 04:34:40)

Re: WSH: обмен данными и объектами между скриптами — 2

Xameleon пишет:

Либо ScriptControl как вариант.

JSman пишет:

Есть возможность вешать все на ScriptControl,

Да расскажите уже наконец как вы запустите ScriptControl'овский код в отдельном потоке?
Код в скрипт контроле синхронно с основным кодом выполняется.

68 (изменено: Евген, 2011-03-30 15:08:20)

Re: WSH: обмен данными и объектами между скриптами — 2

Коллеги, вот мой вариант создания многопоточности WshController (не до конца рабочий - не пойму почему), аргумент потоку приходит, процесс создаётся, признак нормального завершения удалённого процесса по статусу есть.
Почему не отрабатывает на удалённом хосте - ХЗ hmm
Кто сможет помочь ?

SN="SomeScript.vbs"
Set fso = CreateObject("Scripting.FileSystemObject")
ParentFolderName = fso.GetParentFolderName(Wscript.ScriptFullName) 
DestFilePath = fso.BuildPath(ParentFolderName,SN) 
LF="result.log"


If Wscript.Arguments.Count=0 Then  ' признак основного потока

arrComputers = Array("strhost02","strhost03","strhost04","strhost06","strhost07","strhost08","strhost10","strhost13","strhost14","strhost15","strhost16","strhost17","strhost18","strhost20","strhost21","strhost22","strhost23","strhost24","strhost25","strhost26","strhost27","strhost29")


Set objService     = GetObject("winmgmts:\\.\Root\CIMV2")
Set objSinkProcess = WScript.CreateObject("WbemScripting.SWbemSink", "SinkProcess_")
objService.ExecNotificationQueryAsync objSinkProcess, "SELECT * FROM __InstanceDeletionEvent WITHIN 0.2 WHERE TargetInstance ISA 'Win32_Process'"
Set objSinkPing    = WScript.CreateObject("WbemScripting.SWbemSink", "SinkPing_")
Set objProcess     = objService.Get("Win32_Process")
Set objDictionary  = CreateObject("Scripting.Dictionary")



ret=LogEvent(String(28,"-") & " " & date & "  " & time & String(28,"-"))

bdoneping    = False
bdoneprocess = False
strCount=0
intThread=0

For Each strComputer In arrComputers
  Set objContext = CreateObject("WbemScripting.SWbemNamedValueSet")
  objContext.Add "hostname", strComputer
  objService.ExecQueryAsync objSinkPing, "select * from Win32_PingStatus where address ='" & strComputer & "'", , , , objContext
Next

While Not bdoneping

If strCount=ubound(arrComputers)+1 Then
bdoneping=True
End If
WScript.Sleep 100

Wend

objSinkPing.Cancel


While Not bdoneprocess

If intThread=0 and bdoneping=True Then
bdoneprocess=True
End If
WScript.Sleep 100

Wend

objSinkProcess.Cancel

MsgBox "Готово !!!"


Else           ' признак потока с аргументом
strComputer=WScript.Arguments(0)
ret=LogEvent(strComputer & " - argument OK")
Set oController = CreateObject("WSHController")

On Error Resume Next
Set oProcess = oController.CreateScript(SN,strComputer)   ' вот здесь надо полный путь к файлу скрипта писать
oProcess.Execute
If Err.Number<>0 Then  '2
ret=LogEvent(strComputer & " - Connection Error !!!")
Err.Clear
On Error Goto 0
Else                   '2                  ' если нет ошибок при удалённом запуске
Do While oProcess.Status <> 2              ' ждём окончания удалённой работы скрипта
    WScript.Sleep 100
Loop
Set oController = Nothing
ret=LogEvent(strComputer & " - Ok !!!")
End If

End If        ' конец условия по признакам потоков


Function LogEvent(strInput)
Set fso = CreateObject("Scripting.FileSystemObject")
ParentFolderName = fso.GetParentFolderName(Wscript.ScriptFullName) 
DestFilePath = fso.BuildPath(ParentFolderName,LF) 
Set objFile = fso.OpenTextFile(DestFilePath, 8, True)
objFile.WriteLine strInput
objFile.Close
End Function



Sub SinkPing_OnObjectReady(objWbemObject, objWbemAsyncContext)
strCount=strCount+1
Set strComputer = objWbemAsyncContext.Item("hostname")
res=""
  Select Case objWbemObject.StatusCode
    Case 0        ' хост пингуется
	objProcess.Create "wscript.exe " & Wscript.ScriptFullName & " " & strComputer, null, null, intProcessID
	objDictionary.Add intProcessID, intProcessID
	intThread=intThread+1
	ret=LogEvent("PID: " & intProcessID)
    Case Else     ' хост не пингуется
	ret=LogEvent(strComputer & " - not respond to ping")
  End Select
End Sub




Sub SinkProcess_OnObjectReady(objLatestEvent, objAsyncContext)  
	ret=LogEvent("Terminate PID: " & objLatestEvent.TargetInstance.ProcessID)
   If objDictionary.Exists(objLatestEvent.TargetInstance.ProcessID) Then
	objDictionary.Remove(objLatestEvent.TargetInstance.ProcessID)
	intThread=intThread-1
	ret=LogEvent("Thread: " & intThread & "        " & objDictionary.Count)
   End If

End Sub
Времени не хватает... :-(

69 (изменено: Евген, 2011-03-30 08:42:04)

Re: WSH: обмен данными и объектами между скриптами — 2

При запуске скрипта с параметром на удалённом хосте нормально отрабатывает посылаемый на выполнение скрипт, а когда сам себя плодит - то не отрабатывает... sad


Хоть опишу принцип работы:
        Работа без аргумента - основной (управляющий) скрипт
1) асинхронно пингуем удалённые хосты
2) по каждому откликнувшемуся на пинг хосту создаём поток (запускаем самого себя с аргументом имя хоста) запоминаем его ProcessID в словарь
3) асинхронно мониторим события по завершению процессов
4) если попадаются наши ID процессов - то учитываем их
5) как все созданные процессы завершаться - сигнализируем о завершении
6) всё хозяйство логируем в файле "result.log"
        Работа с аргументом (созданный поток) - результат работы управляющего потока (запуск самого себя с аргументом)
1) получаем аргумент
2) поднимаем объект wshcontroller
3) запускаем на удалённом хосте указанный скрипт
4) дожидаемся признака нормального завершения удалённого скрипта
5) логируем удачное/неудачное завершение

Времени не хватает... :-(

70

Re: WSH: обмен данными и объектами между скриптами — 2

Коллеги, дико извиняюсь...
в WshControllere надо было указать полный путь к скрипту посылаемому на удалённое выполнение.

А так - вариант польностью рабочий !!!  :-)

Кого заинтересовало - подправьте и пользуйтесь...

Времени не хватает... :-(

71 (изменено: Евген, 2011-04-01 19:15:20)

Re: WSH: обмен данными и объектами между скриптами — 2

Свой скрипт немного модифицировал, добавил ограничение потоков очередью, что придало стабильность работе скрипта и поместил в коллекцию.
Старый вариант иногда вешал компьютер, видимо из-за того, что у Windows XP есть ограничение на 10 одновременных соединений по TCP/IP протоколу , и из-за этого EventLog украшался "кирпичами" по событиям DCOM, сейчас очередь ограничил 8 потоками - и скрипт заработал стабильно, не "вешая" систему.

Времени не хватает... :-(

72

Re: WSH: обмен данными и объектами между скриптами — 2

Евген, желательно не забывать про вложенные отступы — это повышает читабельность кода.

73 (изменено: Rumata, 2011-04-27 12:07:40)

Re: WSH: обмен данными и объектами между скриптами — 2

Первоначально объект назывался ConnectObject. В связи с тем, что существует одноименный метод WScript.ConnectObject, и чтобы избежать возможной путаницы, объект был переименован в GlobalContainer. Соответственно, все упоминания ConnectObject были изменены на GlobalContainer как в тексте, так и коде. Это изменение было сделано после дальнейшего обсуждения в этой же теме.

Решил вернуться к основам этой темы. За основу взял код, представленный JSman, но внес некоторые изменения и дополнения. Мне показалось, что GlobalObject это спорное название для объекта, так как во всех диалектах Javascript существует глобальный объект. Поэтому объект назван GlobalContainer, чтобы не было путаницы и точнее отражалось назначение объекта. Но это косметические изменения. После различных тестов было добавлено несколько строк для перехвата возможных исключений. Например, если первичный скрипт был преждевременно завершен, то в созданном объекте остаются "мертвые" ссылки на уже несуществующие объекты, таким образом присваивание значения, возвращаемого методом IWebBrowser2::GetProperty приводит к исключению. Добавлен метод GlobalContainer::close(), который отчасти помогает корректно закрыть объект IWebBrowser2.

GlobalContainer.js



function GlobalContainer(signature)
{
	signature = signature || GlobalContainer.signature;

	this.appWindow = GlobalContainer.getInstance(signature);

	if ( ! this.appWindow ) {
		this.appWindow = GetObject('new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}');
		this.appWindow.StatusText = signature;
	}
};

GlobalContainer.signature = 'GlobalContainer';

GlobalContainer.getInstance = function(signature)
{
	signature = signature || GlobalContainer.signature;
	var appList = (new ActiveXObject('Shell.Application')).Windows();
	var i = appList.Count;
	while ( i-- ) {
		var e;
		try {
			var app = appList.Item(i);
			if ( app.StatusText && app.StatusText.indexOf(signature) == 0 ) {
				return app;
			}
		} catch (e) {
		}
	}
};

GlobalContainer.prototype.getProperty = function(name)
{
//	return this.appWindow.GetProperty(name);
	var e;
	try {
		return this.appWindow.GetProperty(name);
	} catch (e) {
	}
};

GlobalContainer.prototype.setProperty = function(name, value)
{
//	return this.appWindow.PutProperty(name, value);
	var e;
	try {
		return this.appWindow.PutProperty(name, value);
	} catch (e) {
	}
};

GlobalContainer.prototype.close = function()
{
//	return this.appWindow.Quit();
	var e;
	try {
		return this.appWindow.Quit();
	} catch (e) {
	}
};

Тестовый файл
test.js



if ( WScript.Arguments.Named.Exists('init') ) {

	var flag = false;

	var co = new GlobalContainer();
	co.setProperty('that', this);

	while ( ! flag ) {
		WScript.Sleep(10);
	}

	WScript.Echo("Эта строка выполняется не всегда!!!");
	co.close();

}

if ( WScript.Arguments.Named.Exists('soft') ) {

	var co = new GlobalContainer();
	var that = co.getProperty('that');

	if ( ! that ) {
		WScript.Echo("Связь не обнаружена.");
	} else {
		WScript.Echo("Обнаружена связь с внешним процессом.");
		var e;
		try {
			WScript.Echo("Попытаемся мягко завершить внешний процесс.");
			that.flag = true;
		} catch (e) {
			WScript.Echo("Попытка мягко завершить неудачна. Возможно процесс уже удален.");
		}
	}

	WScript.Echo("Закроем связного.");
	co.close();

}

if ( WScript.Arguments.Named.Exists('hard') ) {

	var co = new GlobalContainer();
	var that = co.getProperty('that');

	if ( ! that ) {
		WScript.Echo("Связь не обнаружена.");
	} else {
		WScript.Echo("Обнаружена связь с внешним процессом.");
		var e;
		try {
			WScript.Echo("Попытаемся жестко завершить внешний процесс.");
			that.WScript.Quit();
		} catch (e) {
			WScript.Echo("Попытка жестко завершить неудачна. Возможно процесс уже удален.");
		}
	}

	WScript.Echo("Закроем связного.");
	co.close();

}

Других примеров у меня нет, поэтому довольствовался существующими примерами.

Тестирование
test.wsf


<?xml version="1.0" encoding="utf-8" ?>

<package>
<job id="wscmd">
<?job error="true" debug="false" ?>
<script language="javascript" src="./GlobalContainer.js"></script>
<script language="javascript" src="./test.js"></script>
</job>
</package>

Мягкое закрытие процесса
1. в отдельном окне выполнить

cscript //nologo test.wsf /init

2. в другом окне выполнить

cscript //nologo test.wsf /soft

Жесткое закрытие процесса
3. в отдельном окне выполнить

cscript //nologo test.wsf /init

4. в другом окне выполнить

cscript //nologo test.wsf /hard

Повторить пункты 1-4, но после пунктов 1 и 3 эмулировать аварийное завершение процесса по CTRL+C,

( 2 * b ) || ! ( 2 * b )

74

Re: WSH: обмен данными и объектами между скриптами — 2

OFF:

Rumata пишет:

Поэтому объект назван ConnectObject, чтобы не было путаницы…

Есть такой метод в WSH — тоже получается как бы не зер гут.

75 (изменено: Rumata, 2011-04-26 21:44:50)

Re: WSH: обмен данными и объектами между скриптами — 2

Есть такой метод в WSH

Не знал о его существовании. Пересечение имен объекта и метода вроде бы не критично. Может быть GlobalConnect? Хотя... Польза от этого объекта невелика.

( 2 * b ) || ! ( 2 * b )