Статья размещена автором Бетке Сергей Сергеевич

Распространение драйверов принтеров

Читал статью коллеги “Скрипт, выполняющий подключение сетевого принтера и установку его принтером по умолчанию” и вспомнил о своём приятном сценарии для публикации драйверов принтеров. Дело в том, что когда у Вас большое количество клиентов для сетевых принтеров, желательно обновлять драйверы на них централизовано. А нелюбовь производителей к WSUS всем известна, поэтому приходится “колдовать” самим. Дальше и пойдёт речь о публикации драйверов принтеров на машинах домена средствами GPO+WSH.

Итак, приступим. Ссылка на SVN архив файлов сценария — http://svn.novgaro.ru/svn/tools/WindowsXP/DeployPrinterDrivers/trunk. Для начала приведу файл конфигурации для нашего сценария – config.xml:

<pre><?xml version="1.0" encoding="windows-1251" standalone="no"?>
<config>
	<common>
		<DSL path="\\novgaro.ru\files$\AMF\DSL"/>
		<WindowsPrintersDrivers path="%windir%\inf\ntprint.inf"/>
	</common>
	<printerDrivers>
		<printerModel model="[ГАРО-ITG] Kyocera FS-6900">
			<driverInf selected="yes" version="2004/10/20" driverInfFile="Kyocera\Printer\Drv\KPDL\4.04\Drv\adm\KyoSetup.inf"/>
		</printerModel>
		<printerModel model="[ГАРО-ITG] Kyocera Mita FS-1800">
			<driverInf selected="yes" driverInfFile="Kyocera\Printer\Drv\KPDL\4.04\Drv\adm\KyoSetup.inf"/>
		</printerModel>
		<printerModel model="Canon LASER SHOT LBP-1120">
			<driverInf selected="yes" driverInfFile="Canon\Printer\LBP\1120\Drv\1.00.000.0007\Drv\adm\WinXP\CAP3SETK.INF"/>
		</printerModel>
		<printerModel model="Canon LBP-800">
			<driverInf selected="yes" driverInfFile="Canon\Printer\LBP\800\Drv\1.00.001.0005\Drv\src\win2k\CAP1SETK.INF"/>
		</printerModel>
		<printerModel model="Canon LBP-810">
			<driverInf selected="yes" driverInfFile="Canon\Printer\LBP\810\Drv\1.00.001.0012b\Drv\adm\Win2000\CAP1ASTK.INF"/>
		</printerModel>
		<printerModel model="Epson LX-300">
			<driverInf selected="yes" version="2004/10/20"/>
		</printerModel>
		<printerModel model="Epson LX-300+">
			<driverInf selected="yes"/>
		</printerModel>
		<printerModel model="HP DesignJet 500 24 by HP">
			<driverInf selected="yes" driverInfFile="HP\Printer\GL2\Drv\5.02\Drv\src\oemsetup.inf"/>
		</printerModel>
		<printerModel model="HP LaserJet 1100 (MS)">
			<driverInf selected="yes"/>
		</printerModel>
		<printerModel model="HP LaserJet 1100">
			<driverInf selected="yes" driverInfFile="HP\Printer\LaserJet\1100\Drv\4.03.002.0201\Drv\src\hp201ip5.inf"/>
		</printerModel>
		<printerModel model="HP LaserJet 2100">
			<driverInf selected="yes"/>
		</printerModel>
		<printerModel model="HP LaserJet 2100 Series PCL 6">
			<driverInf selected="yes" driverInfFile="HP\Printer\LaserJet\2100\Drv\PCL\6\4.03.002.0177\Drv\src\hp2124p6.inf"/>
		</printerModel>
		<printerModel model="HP PhotoSmart 7550 Series">
			<driverInf selected="yes" driverInfFile="HP\Printer\PhotoSmart\7550\Drv\4.02.041.0000\Drv\adm\hphp2k11.inf"/>
		</printerModel>
		<printerModel model="Lexmark E320">
			<driverInf selected="yes" driverInfFile="Lexmark\Printer\Drv\PCL\7.04.000.0000\Drv\adm\LMPCL2A.INF"/>
		</printerModel>
		<printerModel model="Lexmark Optra E310 (MS)">
			<driverInf selected="yes"/>
		</printerModel>
		<printerModel model="Samsung ML-1200 Series">
			<driverInf selected="yes" driverInfFile="Samsung\Printer\ML\1200\Drv\4.26\Drv\adm\winxp\ssgs1.inf"/>
		</printerModel>
		<printerModel model="Samsung SCX-4x16 Series">
			<driverInf selected="yes" driverInfFile="Samsung\Printer\4X16\Drv\5.24\Drv\src\WinXP\ssgR1.inf"/>
		</printerModel>
		<printerModel model="Xerox DocuPrint P8ex">
			<driverInf selected="yes" driverInfFile="Xerox\Printer\DocuPrint\P8Ex\Drv\1.02\Drv\adm\Win2k\P8EXP6.INF"/>
		</printerModel>
		<printerModel model="Xerox WorkCentre PE16">
			<driverInf selected="yes" driverInfFile="Xerox\Printer\WorkCenter\PE16\Drv\5.23\Drv\adm\WinXP\xrxR1.inf"/>
		</printerModel>
		<printerModel model="Xerox WorkCentre PE114 Series">
			<driverInf selected="yes" driverInfFile="Xerox\Printer\WorkCenter\PE114e\drv\5.54\drv\adm\winxp\XRXR3.inf"/>
		</printerModel>
		<printerModel model="OKI B4100">
			<driverInf selected="yes" driverInfFile="Oki\Printer\B4100\Drv\1.0.6.0\Drv\adm\winxp\Ok26v2ie.inf"/>
		</printerModel>
		<printerModel model="Kyocera FS-1016MFP">
			<driverInf selected="yes" driverInfFile="Kyocera\Printer\FS-1016MFP\Drv\1.0.3.0\Drv\adm\Win2K_XP\OEMSETUP.inf"/>
		</printerModel>

	</printerDrivers>
</config>

Сохраняем его в file part объекта GPO, например — \\novgaro.ru\SysVol\novgaro.ru\Policies\{125FFE90-F500-4E0F-8A9B-6592D1EA2DCD}\Machine\Scripts\Startup. Сразу прошу не ругать – схемы к файлу не прилагаю. Как видно, в этом файле мы описываем модели принтеров, у нас используемых, и драйверы к ним (по сути, ссылаемся на наши сетевые ресурсы и на inf файл драйвера, указываем версию. Атрибут selected=”yes” позволяет нам выбрать конкретную версию драйвера, которую и и будем “разворачивать”.

Теперь приведу текст xslt трансформации getPrinterDrivers.xsl, которая и будет использована сценарием для получения информации из файла конфигурации:

<pre><?xml
	version="1.0"
	encoding="windows-1251"
	standalone="no"
?>
<xsl:transform
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
	version="1.0"
>
	<xsl:output
		method="xml"
		version="1.0"
		encoding="windows-1251"
		omit-xml-declaration="no"
		standalone="no"
		indent="yes"
	/>

	<xsl:template match="/config/printerDrivers">
		<printerDrivers>
		<xsl:for-each select="printerModel">
			<printerDriver>
				<xsl:copy-of select="@model"/>
				<xsl:attribute name="driverInfFile">
					<xsl:choose>
						<xsl:when test="driverInf[@selected]/@driverInfFile">
							<xsl:value-of select="concat(/config/common/DSL/@path, '\', driverInf[@selected]/@driverInfFile)"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:value-of select="/config/common/WindowsPrintersDrivers/@path"/>
						</xsl:otherwise>
					</xsl:choose>
				</xsl:attribute>
				<xsl:attribute name="version">
					<xsl:choose>
						<xsl:when test="driverInf[@selected]/@version">
							<xsl:value-of select="driverInf[@selected]/@version"/>
						</xsl:when>
						<xsl:otherwise>
							<xsl:text>0</xsl:text>
						</xsl:otherwise>
					</xsl:choose>
				</xsl:attribute>
			</printerDriver>
		</xsl:for-each>
		</printerDrivers>
	</xsl:template>

</xsl:transform>

Как уже видно, трансформация возвращает только “выбранные” драйверы и уже конкретную версию, которую и будем распространять. Теперь приведу сам сценарий SetupPrintersDrivers.wsf (сознательно опускаю код обёртки):

<?xml version="1.0" encoding="windows-1251" standalone="yes"?>
<package>
	<job>
		<runtime>
			<description>Установка драйверов принтеров</description>
			<unnamed name="configFileName" helpstring="xml файл конфигурации" type="string" required="true"/>
			<example>

Пример:

SetupPrinterDrivers.wsf commonConfig.xml

</example>
		</runtime>
		<reference object="ADs" version="1.0"/>
		<object progid="Scripting.FileSystemObject" id="fso"/>
		<object progid="Microsoft.XMLDOM" id="domCfg"/>

<script language="JScript"><![CDATA[

// инициализация -----------------------------------------------------------------------

var configFileName;
var wsh = WScript.CreateObject("WScript.Shell");

// -------------------------------------------------------------------------------------------

function debugInformation (
	operation
)
{
	if (operation) this.operation = operation;
};

debugInformation.prototype.operation = "";
debugInformation.prototype.step = null;

debugInformation.prototype.registerError = function (
	error
) {
	var errMsg = 
		"[" + this.operation + "] : Ошибка"
		+ (this.step ? " (" + this.step + ")" : "")
		+ ":"
		+ (error.number ? error.number & 16383 : "")
		+ (error.description ? " \"" + error.description + "\"" : "")
		+ "."
	;
//	WScript.StdOut.Write(errMsg + "\n");
	wsh.LogEvent(1, errMsg);
};

debugInformation.prototype.main = function (
) {
	try {
		main();
	} catch(e) {
		this.registerError(e);
	};
};

function errorDescription(
	description,
	number
)
{
	this.description = description ? description : "неожиданная ошибка";
	this.number = number ? number : 0;
};

debugInformation.prototype.error = function (
	description,
	number
) {
	var e = new errorDescription(description, number);
	throw (e);
};

debugInformation.prototype.info = function (
	description
) {
	var errMsg = 
		"[" + this.operation + "] : информация "
		+ (this.step ? " (" + this.step + ")" : "")
		+ ":"
		+ (description ? description : "")
		+ "."
	;
//	WScript.StdOut.Write(errMsg + "\n");
	wsh.LogEvent(0, errMsg);
};

var _debug = new debugInformation(
	"обновление конфигурации"
);

// main -------------------------------------------------------------------------------------

_debug.main();

// main -------------------------------------------------------------------------------------

function main() {
	_debug.step = "загрузка файла конфигурации";

	if (WScript.Arguments.Unnamed.length == 0) {
		WScript.Arguments.ShowUsage();
		_debug.error ("требуется указать имя файла конфигурации в командной строке");
	};
	
	configFileName = WScript.Arguments.Unnamed.Item(0);
	domCfg.load(configFileName);

	_debug.step = "проверка и установка драйверов";

	verifyAndInstallDrivers(domCfg);	
};

// выборка из конфигурации драйверов принтеров, корректировка пути, выбор только активного драйвера

function getPrinterDrivers (
	cfg
) {
	_debug.step = "загрузка файла трансформации getPrinterDrivers.xsl";

	var XSLT = WScript.CreateObject("Microsoft.XMLDOM");
	XSLT.load(fso.BuildPath(wsh.CurrentDirectory, "getPrinterDrivers.xsl"));

	_debug.step = "трансформация - выделение списка драйверов";

	var result = new ActiveXObject("Microsoft.XMLDOM");
	cfg.transformNodeToObject(XSLT, result);

	return result;
};

// проверка наличия драйверов и установка в случае отсутствия -----------

function verifyAndInstallDrivers (
	cfg
) {
	var printerDrivers = getPrinterDrivers(cfg);
	var reDrvName = /(.+),(\d+),(.+)/;
	var objWMIService = GetObject("winmgmts:\\\\.\\root\\cimv2");

	_debug.step = "проверка и установка драйверов";

	for (
		var i = new Enumerator(printerDrivers.selectNodes("/printerDrivers/printerDriver"));
		!i.atEnd();
		i.moveNext()
	) {
		var printerDriver = i.item();
		var drvName = printerDriver.selectSingleNode("@model").text;
		var drvInf = printerDriver.selectSingleNode("@driverInfFile").text;
		var drvVersion = printerDriver.selectSingleNode("@version").text;
		// WScript.StdOut.Write(drvName + ":" + drvInf + "\n");
		var prepDrvName = drvName.replace(/[\[\]]/g, "_");
		var WMIisDrvPresent = (objWMIService.ExecQuery (
			"Select * from Win32_PrinterDriver where  (__CLASS = \"Win32_PrinterDriver\") and (Name like \"" + prepDrvName + ",%\")"
		).Count);

		var currentInf = "";
		var currentVersion = "0";
		try {
			currentInf = wsh.RegRead("HKLM\\Software\\ITG\\printerDrivers\\" + drvName + "\\infFile");
			currentVersion = wsh.RegRead("HKLM\\Software\\ITG\\printerDrivers\\" + drvName + "\\itgVersion");
		} catch (e) {
		};
		if (
			(currentInf != drvInf) ||
			(currentVersion != drvVersion) ||
			!WMIisDrvPresent
		) {
			if (WMIisDrvPresent) {
				var instError = wsh.Run(
					"rundll32 printui.dll,PrintUIEntry /dd " +
					"/m \"" + 
					drvName + "\"",
					0,
					true
				);
			};

			var instError = wsh.Run(
				"rundll32 printui.dll,PrintUIEntry /ia /f \"" +
				drvInf + 
				"\" /m \"" + 
				drvName + "\"",
				0,
				true
			);
			
			var info = 
				"\n\tМодель : \"" + drvName  + "\""
				+ "\n\tДрайвер : \"" + drvInf  + "\""
				+ "\n\tВерсия (в рамках отдела ИТ) : \"" + drvVersion  + "\""
			;
			if (instError) {
				_debug.error ("ошибка при установке драйвера принтера." + info, instError);
			} else {
				wsh.RegWrite("HKLM\\Software\\ITG\\printerDrivers\\" + drvName + "\\infFile", drvInf);
				wsh.RegWrite("HKLM\\Software\\ITG\\printerDrivers\\" + drvName + "\\itgVersion", drvVersion);
				_debug.info ("установлен драйвер принтера." + info);
			};
		};
	};
};

]]></script>
	</job>
</package>

Поясню сценарий. Загружаем файл конфигурации, после чего его трансформируем. Далее через WMI запрос проверяем, а установлен ли драйвер, указанный в нашем файле конфигурации. Если нет, то устанавливаем его через printUI.dll. Вариантов установки драйвера принтера множество, но этот — самый приятный, он выдаёт «типовой» диалог, привычный для пользователя (не диалог, а окошно, отображающее статус текущей операции). После всего этого — отмечаем в реестре установленный драйвер (в своей ветке). На этом всё.

Теперь для публикации нового драйвера выкладываем его на наше сетевой ресурс, добавляем запись в файл конфигурации — и на утро он у всех стоит.

В будущем планирую оформить его в виде msi пакета, при «установке» которого буду активировать сценарий (уже, видимо, powershell), который проделает ту же работу, но при этом при каждой загрузке пользователя не будет выполняться лишний сценарий каждый раз. Либо же — писать собственное расширение к GPP, через которое и публиковать драйверы принтеров. Второй вариант, безусловно, более симпатичен.

Опубликовать комментарий

XHTML: Вы можете использовать следующие HTML теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Tags Связь с комментариями статьи:
RSS комментарии
Обратная ссылка