В статье "Процедуры чтения и записи в реестр стали проще" я познакомил читателей с объектом Penton.RegObject. Использование этого объекта может существенно упростить работу с методами класса WMI StdRegProv. Во второй публикации данной серии "Процедуры чтения и записи в реестр стали проще. Часть 2", было показано практическое применение объекта Penton.RegObject при реализации операций чтения и записи в реестр в сценариях VBScript и JScript. Используя свойства и методы данного объекта, можно упростить процедуру доступа к реестру как на локальном, так и на удаленном компьютере. В третьей части описывается сценарий JScript RunMgr.js, с помощью которого можно управлять параметрами раздела реестра Run на локальных и удаленных компьютерах.

Назначение раздела Run

Раздел реестра Run - это одно из тех мест, где хранятся ссылки на размещение программ, запускаемых при загрузке операционной системы. Windows обращается к этому разделу всякий раз, когда пользователь выполняет регистрацию в системе. Чтобы получить информацию о содержимом раздела Run в версиях операционной системы, начиная с Windows XP, можно запустить утилиту Msconfig и выбрать в появившемся окне закладку Startup. Однако я рекомендую пользоваться для этих целей утилитой Autoruns от Sysinternals. Данная утилита обладает значительной гибкостью и отображает более подробный список размещения программ, запускаемых при старте системы. Раздел Run располагается в реестре по следующему адресу: HKEY_LOCAL_MACHINESOFTWAREMicrosoft WindowsCurrentVersionRun. Все программы, указанные в данном разделе, запускаются в процессе регистрации в системе, причем это происходит до запуска программ, перечисленных в папке StartUp (Автозагрузка).

Использование сценария RunMgr.js

Файл RunMgr.js представляет собой сценарий, написанный на языке JScript, который предназначен для управления значениями параметров раздела Run реестра локального или удаленного компьютера из командной строки. Чтобы запускать этот сценарий, необходимо иметь на компьютере установленный компонент JScript версии 5.6 или более поздней. На Windows 2000, JScript 5.6 устанавливается вместе с Microsoft Internet Explorer (IE) версии 6.0 или более поздней. Установочный комплект (scripten.exe) также может быть загружен и отдельно с сайта Microsoft. Что касается операционных систем Windows XP и последующих версий, то они уже имеют в своем составе компонент JScript 5.6.

Для возможности выполнения рассматриваемого сценария необходимо также зарегистрировать в системе компонентов RegObject.wsc. Процедура регистрации RegObject.wsc была описана во второй части данной серии статей.

Синтаксис запуска RunMgr.js из командной строки выглядит следующим образом:

[cscript] RunMgr.js [computername]
[/list | /exists:value | /add:name=value
| /delete:value]

Сценарий RunMgr.js должен запускаться через сервер сценариев CScript, поэтому если CScript не является сервером сценариев, используемым по умолчанию в вашей системе, то команда запуска сценария должна в этом случае начинаться с ключевого слова "cscript". Все остальные параметры командной строки могут указываться в произвольном порядке.

Для того чтобы сделать CScript сервером сценариев, используемым по умолчанию, выполните из командной строки следующую команду:

cscript //h:CScript //nologo //s

В принципе команда может выглядеть более просто:

cscript //h:cscript

однако в целях ограничения вывода на экран ненужной информации я добавил в команду ключи //nologo и //s. Параметр //nologo отключает вывод на экран информацию об авторских правах и версии Windows Script Host (WSH), которая в противном случае отображается при каждом запуске сценария через CScript. При наличии параметра //s внесенные изменения будут сохранены в системе.

Если выполняется подключение к удаленной системе, то в аргументе computername указывается имя соответствующего компьютера. Если аргумент computername не задан, то сценарий будет выполняться на локальном компьютере. Все эти параметры не являются чувствительными к регистру.

Параметр /list служит для формирования списка данных, содержащихся в разделе Run. На рисунке 1 приведен пример списка параметров раздела Run и их значений.

Параметр /exists позволяет включить проверку существования в реестре того или иного параметра. Если заданный параметр существует, сценарий возвращает код завершения, равный 1, в противном случае возвращается 0. Если в качестве примера рассмотреть параметры раздела реестра, показанные на экране 1, и запустить приведенную ниже команду:

runmgr.js /exists:pctvoice

то сценарий возвратит 1 (т.к. данное значение существует).

С помощью раздела /add можно добавить заданный параметр в раздел реестра Run. Здесь name представляет собой произвольную текстовую строку, описывающую исполняемую программу. Поскольку каждая строка в разделе Run должна быть уникальной, целесообразно будет указать здесь имя того исполняемого файла, который должен быть запущен. В параметре value указывается полное имя исполняемого файла, а также все необходимые аргументы командной строки, которые должны использоваться при его запуске. В сценарии RunMgr.js для параметра value предусмотрено преобразование одинарных (') кавычек в двойные ("). Это позволяет указывать внутри параметра value строковые значения, содержащие кавычки. Следует также отметить, что для параметра value в сценарии используется тип данных REG_EXPAND_SZ, что допускает использование переменных среды с двойным символом процента (%). Например, с помощью приведенной ниже команды в реестр добавляется параметр SetDefaultPrinter и выполняется командная строка "%ProgramFiles%sdp.exe" "PSMy Printer":

runmgr.js /add:SetDefaultPrinter=
"'%%ProgramFiles%%SDPsdp.exe'
'PSMy Printer'"

Обратите внимание на то, что значение параметра value целиком заключено в двойные кавычки.

Раздел /delete служит для удаления из реестра заданного параметра. Например, для того чтобы удалить параметр, созданный в предыдущем примере, можно воспользоваться следующей командой:

runmgr.js /delete:setdefaultprinter

Внутренняя структура RunMgr.js

В начале сценария RunMgr.js объявляются глобальные переменные. Далее в объекты JScript Array и String добавляются методы prototype. Таким образом обеспечивается возможность вызова этих методов из сценария для любых объектов JScript Array и String. Использование свойств prototype для расширения функциональности объектов описано в статье Microsoft, доступной по адресу http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/js56jsproprototype.asp.  Далее RunMgr.js вызывает метод Quit объекта WScript, используя в качестве кода завершения значение, возвращаемое основной функцией.

Функция main.В данной функции сначала объявляются переменные, которые будут в ней использоваться. Далее с помощью переменных allargs и namedargs реализуются ссылки на коллекции WScript.Arguments и WScript.Arguments.Named, соответственно. Именованными (named) аргументами называются любые аргументы, начинающиеся с символа прямого слеша (/). Если именованных аргументов не задано, либо был указан аргумент /?, вызывается функция usage, которая выводит на экран краткое сообщение о правильном синтаксисе запуска сценария, и завершает его выполнение.

Далее функция main проверяет, имеются ли в командной строке не именованные (unnamed) аргументы (т.е. те аргументы, которые не имеют в начале символа прямого слеша). Если таких аргументов нет, то переменная computername будет содержать символ точки ("."), что соответствует ссылке на локальный компьютер.

После этого выполняется проверка используемого сервера сценариев, для чего вызывается функция scripthost, которая возвращает имя исполняемого файла сервера сценариев. Если это имя не cscript.exe, сценарий завершает выполнение с выводом сообщения об ошибке и ненулевым кодом возврата.

Проверка корректности задания аргументов командной строки. В Листинге 1 меткой A обозначен фрагмент кода, показывающий, как в функции main реализована проверка аргументов командной строки. Сначала функция создает массив, содержащий перечень всех допустимых именованных аргументов. Далее, чтобы реализовать обработку элементов коллекции WScript.Arguments.Named, создается объект JScript Enumerator. Для тех, кто не знаком с языком Jscript, следует пояснить, что в этом языке конструкция for не может выполнять обработку коллекций, аналогично тому, как это делается в языке VBScript при помощи цикла For Each. Поэтому в JScript для выполнения таких операций необходимо создавать объект Enumerator. После этого перебор элементов коллекции можно реализовать с помощью оператора for и метода MoveNext объекта Enumerator. При этом обращение к текущему элементу коллекции осуществляется с помощью метода Item.

Внутри цикла в переменную arg заносится значение текущего элемента коллекции в нижнем регистре (т.е. переменная arg содержит копию значения аргумента командной строки, преобразованного в нижний регистр). Если текущий элемент коллекции существует в массиве допустимых аргументов, то переменная valid (типа Boolean) будет содержать значение True. В том случае, если переменная arg содержит значение "list", выполнение цикла прерывается оператором break (т.к. при наличии в строке запуска раздела /list никакие другие параметры не требуются).

Все ключи командной строки, за исключением /list, требуют задания параметров. Если заданы ключи /exist или /delete, то каждый из них должен иметь соответствующий параметр value. Если этот параметр не задан, то переменная valid будет содержать значение False. Если же valid равна False, то выполнение сценария прерывается, и он завершает работу с сообщением об ошибке и ненулевым кодом возврата. Если указан раздел /add, то сценарий вызывает функции leftofdelim и rightofdelim, с помощью которых определяется, задан ли этот параметр в виде name=value.

Подключение к компьютеру. Меткой B обозначен фрагмент кода, в котором функция main создает экземпляр объекта Penton.RegObject, после чего с помощью метода Connect данного объекта выполняется подключение к компьютеру. Если возвращается ненулевой результат (result) , то выполнение сценария прерывается, и он завершает работу с сообщением об ошибке и ненулевым кодом возврата.

Доступ к реестру. Метка C обозначает фрагмент кода, в котором функция main с помощью команды switch языка JScript осуществляет переход к необходимому блоку кода в соответствии с указанными в командной строке ключами:

  • Ключ /list использует метод EnumValuesAndData объекта Penton.RegObject для перебора параметров, содержащихся в разделе реестра Run. После вызова данного метода с помощью свойства EnumDict объекта Penton.RegObject формируется ссылка на объект Scripting.Dictionary, в котором находятся параметры раздела Run и их значения. Для копирования содержимого объекта Scripting.Dictionary в массив JScript функция main вызывает метод dictToJSArray объекта Penton.RegObject. Сформированный массив может далее обрабатываться оператором for языка JScript.
  • Ключ /exists выполняет проверку существования заданного параметра, для чего используется метод ExistValue объекта Penton.RegObject. Если результат равен True (т.е. параметр существует), то переменная result будет содержать значение 1; в противном случае она будет равна 0.
  • С помощью ключа /delete осуществляется удаление заданного параметра, для чего используется метод DeleteValue объекта Penton.RegObject.
  • Ключ /add служит для добавления в реестр данных, сформированных в виде name=value. Для преобразования символов одинарных кавычек в двойные функция main вызывает метод replace объекта String. Затем, с помощью метода Write объекта Penton.RegObject, эти данные заносятся в реестр. Обратите внимание, что в функции main выделение части аргумента, являющейся именем параметра, реализуется вызовом функции leftofdelim, а функция rightofdelim выделяет ту часть аргумента, которая соответствует значению параметра. Здесь также осуществляется запись данных в реестр, для чего используется тип данных REG_EXPAND_SZ.

Также обратите внимание на использование операторов break в каждом из блоков кода, определяемых операторами case. В отличие от имеющегося в языке VBScript оператора Select Case, в JScript необходимо использовать оператор break, поскольку в противном случае сценарий продолжит выполнение, перейдя к следующему блоку case.

Теперь можно управлять содержимым раздела Run

Сценарий RunMgr.js может служить хорошим примером того, как просто можно реализовать управление реестром с помощью объекта Penton.RegObject даже из сценариев JScript. Теперь, используя сценарий RunMgr.js, вы можете управлять содержимым раздела Run любого компьютера сети.


Рисунок 1. Пример списка параметров раздела реестра Run

PCTVOICE=pctspk.exe
SynTPLpr=C:Program FilesSynapticsSynTPSynTPLpr.exe
SynTPEnh=C:Program FilesSynapticsSynTPSynTPEnh.exe
WinVNC="C:Program FilesUltraVNCWINVNC.EXE" -servicehelper
ShStatEXE="C:Program FilesNetwork AssociatesVirusScanSHSTAT.EXE" /STANDALONE
McAfeeUpdaterUI="C:Program FilesNetwork AssociatesCommon FrameworkUpdaterUI.exe" /StartedFromRunKey

 


Листинг 1. RunMgr.js

/*

Для управления содержимым раздела Run реестра локального или удаленного компьютера используется объект Penton.RegObject.

 

С помощью данного сценария можно отображать содержимое раздела Run (/list),

выполнять проверку существования того или иного параметра (/exists), добавлять (/add) и удалять заданный параметр (/delete) из реестра.

*/

 

var SCRIPT_NAME = "RunMgr.js",

REG_SUBKEY = "SOFTWAREMicrosoftWindowsCurrentVersionRun";

 

// Добавление метода Exists() объекту Array:

// Возвращается true, Если заданное значение существует как элемент массива

Array.prototype.Exists = function(item)

{

var i, exists = false;

 

for (i = 0; i < this.length; i++) {

exists = item == this[i];

if (exists) break;

}

 

return exists;

}

 

// Добавление метода Trim() объекту String:

// Возвращает копию строки с удаленными пробелами в начале и в конце

String.prototype.Trim = function()

{

return this.replace(/(^s*)|(s*$)/g, "");

}

 

WScript.Quit(main());

 

// Отображение сообщения о корректном синтаксисе запуска сценария.

function usage()

{

WScript.Echo("Управление параметрами раздела реестра Run. " +

" " +

"Синтаксис: " + SCRIPT_NAME + " [] /list " +

"или: " + SCRIPT_NAME + " [] /exists: " +

"или: " + SCRIPT_NAME + " [] /add:= | /delete: " +

" " +

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

"к локальному компьютеру. " +

" " +

"/list список параметров, содержащихся в разделе Run. " +

" " +

"/exists проверка существования заданного параметра в разделе Run. Если он существует, то возвращается код завершения 1, " +

"в противном случае возвращается 0. " +

" " +

"/add запись заданного параметра в реестр. " +

" " +

"/delete удаление из реестра заданного параметра.");

 

WScript.Quit(0);

}

 

// Завершение выполнения сценария с выдачей сообщения об ошибке и соответствующим кодом завершения.

function die(message, exitcode)

{

WScript.Echo(message);

WScript.Quit(exitcode);

}

 

// Возвращает имя сервера сценариев (в нижнем регистре), через который выполняется данный сценарий

// (обычно это cscript.exe или wscript.exe).

function scripthost()

{

return WScript.FullName.substr(WScript.Path.length + 1).toLowerCase();

}

 

// Возвращает заданное числовое значение в шестнадцатеричном виде. Если число отрицательное,

// то для формирования положительного числа добавляется 0x100000000. Является аналогом

// функции Hex() языка VBScript.

function hex(n)

{

if (n >= 0)

return n.toString(0x10).toUpperCase();

else

return (n + 0x100000000).toString(0x10).toUpperCase();

}

 

// Возвращает имя компьютера в отформатированном виде (в верхнем регистре, без символа в начале).

function fixcomputername(computername)

{

// Удаление пробелов в начале и в конце.

computername = computername.Trim();

 

// Удаление обратных слешей в начале имени.

if (computername.substring(0, 2) == "\")

computername = computername.substring(2, computername.length);

 

return computername.toUpperCase();

}

 

// Для строк, содержащих символ разделителя, возвращается содержимое, расположенное слева от данного символа.

// Если в строке разделителя нет, то возвращается пустая строка.

// Если задан раздел /add, данная функция используется в сценарии для извлечения имени параметра,

// расположенного слева от символа =.

function leftofdelim(s, delim)

{

var i, result = "";

 

s = s.Trim();

i = s.indexOf(delim);

if (i > 0) result = s.substring(0, i);

 

return result;

}

 

// Для строк, содержащих символ разделителя, возвращается содержимое, расположенное справа от данного символа

// Если в строке разделителя нет, то возвращается пустая строка.

// Если задан раздел /add, то данная функция используется в сценарии для извлечения значения параметра,

// расположенного справа от символа =.

function rightofdelim(s, delim)

{

var i, result = "";

 

s = s.Trim();

i = s.indexOf(delim);

if (i > 0) result = s.substring(i + 1, s.length);

 

return result;

}

 

function main()

{

var allargs, namedargs, computername;

var validargs, arg, valid, result;

var valuelist, valuename;

 

allargs = WScript.Arguments;

namedargs = allargs.Named;

 

// Если нет именованных аргументов или задан аргумент /?.

if ((namedargs.Length == 0) || (namedargs.Exists("?")))

usage();

 

// Извлечение имени компьютера (первый неименованный аргумент).

if (allargs.Unnamed.Length > 0)

computername = fixcomputername(allargs.Unnamed(0));

 

// Если имя компьютера не задано, подключаемся к локальному компьютеру.

if ((computername == null) || (computername == ""))

computername = ".";

 

// Для выполнения сценария необходим сервер сценариев CScript.

if (scripthost() != "cscript.exe")

die("Сценарий должен запускаться через сервер CScript.", 1);

 

// BEGIN CALLOUT A

// Формирование массива допустимых значений аргументов командной строки.

validargs = new Array("list", "exists", "add", "delete");

 

// Обработка коллекции namedargs.

args = new Enumerator(namedargs);

 

// Перед подключением к компьютеру выполняем проверку

// всех аргументов командной строки.

for (; ! args.atEnd(); args.moveNext()) {

arg = args.item().toLowerCase();

valid = validargs.Exists(arg);

if (valid)

valid = arg == "list";

if (valid) break; // раздел /list не требует никаких аргументов

valid = (namedargs(arg) != null) && (namedargs(arg) != "");

if (valid) { // для всех остальных ключей аргументы должны быть заданы

if (arg == "add") // для раздела /add аргумент должен быть задан в виде name=value

valid = (leftofdelim(namedargs(arg), "=") != "") &&

(rightofdelim(namedargs(arg), "=") != "");

if (valid) break;

}

}

 

if (! valid)

die("Параметры командной строки заданы некорректно. Для получения справки используйте раздел /?.", 1);

// END CALLOUT A

 

// BEGIN CALLOUT B

regobj = new ActiveXObject("Penton.RegObject");

 

// Подключение к компьютеру

result = regobj.Connect(computername);

 

if (result != 0)

die("Ошибка 0x" + hex(result) + " подключения к " + computername, result);

// END CALLOUT B

 

// BEGIN CALLOUT C

switch (arg) {

case "list": {

result = regobj.EnumValuesAndData("HKLM", REG_SUBKEY);

if (result == 0) {

// Копирование содержимого объекта dictionary в ассоциативный массив JScript

valuelist = regobj.dictToJSArray(regobj.EnumDict);

// Обработка массива и формирование перечня содержащихся в нем пар value/data.

for (valuename in valuelist)

WScript.Echo(valuename + "=" + valuelist[valuename]);

}

break;

}

case "exists": {

result = regobj.ExistValue("HKLM",

REG_SUBKEY,

namedargs(arg));

// Если обнаружено, возвращается единица 1, в противном случае возвращается 0 (используется троичный оператор языка JScript)

result = result ? result = 1 : result = 0;

break;

}

case "delete": {

result = regobj.DeleteValue("HKLM",

REG_SUBKEY,

namedargs(arg));

break;

}

case "add": {

// Символ ' заменяется на "; это позволяет записывать в реестр

// строки, содержащие кавычки.

valuename = namedargs(arg).replace(/'/g, """);

result = regobj.WriteValue("HKLM",

REG_SUBKEY,

leftofdelim(valuename, "="),

"REG_EXPAND_SZ",

rightofdelim(valuename, "="));

break;

}

}

// END CALLOUT C

 

if (namedargs.Exists("v"))

WScript.Echo("Exit code: " + result.toString() + " (0x" + hex(result) + ")");

 

return result;

}