В моем приложении HTA есть некоторые действия, которые выполняют некоторые трудоемкие задачи на сервере. Например, действие использует старый компонент ActiveX для чтения некоторых свойств файла (таких как Тема и Комментарии) файлов в определенной папке (от 0 до ~200 файлов в папке).
Первоначально это делалось путем установки интервала и чтения свойств файла файл за файлом. Замедление приложения было приемлемым при подключении к серверу с использованием быстрых локальных соединений. Но теперь, когда удаленная работа значительно увеличилась, а удаленные подключения на порядок медленнее, чем интранет-соединения, этот интервал больше не подходит для этой задачи.
Чтобы приложение работало быстрее во время поиска файловых свойств, я передал код на аутсорсинг задания wsh. Результаты сохраняются в файле, существование которого соблюдается через интервал (5 секунд). Тем не менее, некоторые пользователи по-прежнему наблюдают заметное замедление работы приложения, даже при опросе существования файла с указанным интервалом в 5 секунд.
Теперь я хотел знать, есть ли событие или какой-то другой внутренний механизм, который я мог бы использовать, чтобы определить, когда скрипт wsh выполнил свою работу? И если возможно, то даже, возможно, есть способ отправить результаты непосредственно из задания wsh в HTA, вообще не используя промежуточный временной файл?
Вот некоторый упрощенный код для фактической задачи, выполняемой в файле wsh и приложении HTA. HTA имеет HTML5 DTD и работает в пограничном режиме с использованием IE11. ui
— это служебная библиотека, мы надеемся, что упомянутые имена свойств достаточно точно описывают использование.
ВСФ:
<package>
<job id = "getFileProps">
<script language = "JScript">
(function () {
var topRoot = WScript.Arguments(0), // The starting folder <String>
fso = WScript.CreateObject('Scripting.FileSystemObject'), // Filesystem object <ActiveXObject>
fileProps = readProps(topRoot), // Fileprops, the result <Array>
file; // The result file to read in HTA <FileObject>
function readProps (root) {
var fileProperties = [];
// A bunch of code reading the fileproperties on a disk
return fileProperties;
}
file = fso.openTextFile(topRoot + '\\$fileprops$.txt', 2, true);
file.Write(fileProps.join(',');
file.Close();
WScript.Quit();
)());
</script>
</job>
</package>
ЗАО «РИПТ»:
ui.winShell.Exec('WScript //Job:getFileProps ' + '"' + ui.appRoot + '"');
function fpCheck () {
var file, fileProps;
try {
file = ui.fileIO.OpenTextFile('$fileprops$.txt', 1, false);
} catch (err) {
file && file.Close();
setTimeout(fpCheck, 5000);
return;
}
// Write the fileprops to the view
}
Если кого-то интересует средство чтения свойств файла, его имя DSOFile.OleDocumentProperties
. Он происходит где-то в архивах глубокого интернета, но я не могу вспомнить, откуда я его загрузил.
@Kul-Tigin Да, файл результата сохраняется в удаленной папке, но только для части пользователей (на самом деле я не сообразил проверить, используют ли жалобщики общую копию с сервера или собственную локальную копию). Звучит разумно, чтобы создать его как локальный, я обязательно попробую. Спасибо за предложение.
Бах... Некоторые пользователи переустановили старую версию. Но все же я хотел бы знать, есть ли способ получить сообщение от wsh, когда он выполнил свою работу, или передать список свойств напрямую в HTA без использования файла.
Будет ли ваш сценарий поддерживать использование объекта словаря или списка массивов для хранения только имени файла и последнего обновления, а также для извлечения полного набора свойств (во второй список) для новых или измененных файлов (и удалений). Думаю, зависит от того, как часто файлы приходят и уходят или обновляются. Это может быть быстрее, чем создание всего набора данных свойств файла, если большая часть деталей не меняется между опросами.
Это пользовательский файловый браузер с деревом папок и списком файлов. Список файлов показывает только имя файла и тему. Имена файлов извлекаются и отображаются быстро, но работа DSOFile.OleDocumentProperties
несколько медленная, и поэтому она вызывается асинхронно после отображения имен файлов. Опрос полученного файла, по-видимому, по какой-то причине замедляет работу приложения для некоторых пользователей, возможно, у Кул-Тигина есть точка на нем (хотя я еще не проверял).
В конце концов, вам нужна межпроцессная связь.
Ни HTA, ни WSH не предлагают ничего встроенного для IPC.
Итак, я собираюсь поделиться хакерским решением, которое я редко использую.
Хитрость заключается в использовании общего объекта на двух концах, как в сценарии клиент-сервер.
Мы собираемся использовать объект Internet Explorer в качестве сервера IPC, мы будем использовать методы GetProperty и PutProperty.
Поскольку окна Internet Explorer можно перечислить с помощью метода объектов оболочки Windows(), можно получить доступ к объекту, созданному в HTA, из другого приложения.
Изучите и создайте следующие два файла, затем запустите test.hta
и нажмите кнопку Start WSF
.
test.hta
:
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content = "width=device-width, initial-scale=1.0">
<meta http-equiv = "X-UA-Compatible" content = "IE=Edge">
<title>IPC</title>
<script>
sharedObject = {
sendData: function(data){
// blocking here is not a good idea
// so we process the data within another callback
// leaving the method as soon as possible
var receivedData = data;
setTimeout(function(){alert("Data received: " + receivedData)}, 50);
}
};
shareName = "ipcShare";
serverId = createIPCServer(shareName);
function createIPCServer(shareName){
var ie = new ActiveXObject("InternetExplorer.Application");
ie.PutProperty(shareName, sharedObject);
window.onbeforeunload = function(){ ie.Quit(); };
return ie.HWND; // window handle
}
function starWSFJob() {
alert("please wait about 5 seconds...");
var wshShell = new ActiveXObject("Wscript.Shell");
wshShell.Run('wscript.exe test.wsf //Job:TestIPC /serverId:"' + serverId + '" /shareName:"' + shareName + '"');
}
</script>
</head>
<body>
<button onclick = "starWSFJob()">Start WSF</button>
</body>
</html>
test.wsf
:
<package>
<job id = "TestIPC">
<script language = "JScript">
function getSharedObject(serverId, shareName){
var shellApp = new ActiveXObject("Shell.Application");
var windows = new Enumerator(shellApp.Windows());
for (;!windows.atEnd();windows.moveNext()){
var window = windows.item();
if (window.HWND == serverId) {
return window.GetProperty(shareName);
}
}
}
function App() {
var serverId = Number(WScript.Arguments.Named("serverId"));
var shareName = WScript.Arguments.Named("shareName");
var sharedObject = getSharedObject(serverId, shareName);
if (!sharedObject){
WScript.Echo("shared object not found.");
return 1;
}
// simulate long running job
WScript.Sleep(5000);
// send data
sharedObject.sendData("Hello from WSF Job!");
return 0;
}
WScript.Quit(App());
</script>
</job>
</package>
Ух ты! Вы действительно работали над этим. Может быть, это немного сложно, но это то, о чем я просил. Я проверю это завтра и отмечу ваш ответ, если ничего более простого не произойдет. Я использовал экземпляр IE для применения ES5 в сценарии WSH, но я никогда не думал, что его можно использовать и таким образом.
@Teemu Ничто не просто, когда дело доходит до IPC :) Но вы можете убедиться, что это одна из самых простых реализаций IPC, которые вы можете сделать в Windows. Потому что за кулисами выполняются ужасные внепроцессные операции маршалинга COM, и нам не нужно иметь дело ни с одной из них, и это благословение.
Истинный. На самом деле я не мог заставить ваш скрипт работать на меня (поскольку WScript.Sleep нельзя использовать в HTA), и я выбрал немного другой подход, чтобы ускорить чтение свойства. Но ваш фрагмент дал мне массу идей, которые я могу использовать в некоторых других задачах, большое спасибо за фрагмент.
@Teemu Мой плохой. Никогда не попадал в эту строку в своих тестах, видимо, мой компьютер был достаточно быстрым. Вызов Navigate
и цикл даже не понадобились, поэтому я их удалил.
Поскольку я не мог запустить экземпляр IE (пока), как вы думаете, можно ли запустить задачу с этим экземпляром IE без WSH? Я предполагаю, что я мог бы передать даже живой объект в качестве аргументов скрипту в экземпляре IE, и скрипт решал бы некоторые задачи, используя даже прямые ссылки на объекты JS в HTA?
@ Teemu Я бы сказал да, но модели безопасности Internet Explorer похожи на минное поле, поэтому я бы точно придерживался WSH. Возможность прямой ссылки на объекты HTA также применяется в WSH в зависимости от того, какой объект вы используете совместно.
Пользователи жалуются на медлительность из-за того, что файл данных находится на удаленном ресурсе? Если да, то почему бы вместо удаленного файла данных не опросить файл
local
, который будет изначально создан заданием WSH и удален после завершения процесса? Я уверен, что это будет довольно быстро.