блогъ

автоподключение USBIP устройств на windows-сервере

Поднимаю избитую, изжеванную, но так толком и не освещенную в интернете тему автоматического подключения проброшенных посредством USBIP устройств, в особенности — капризных принтеров. Тут уже и технология Spice на подходе, а виндовый драйвер usbip все еще нулевой версии (0.2), и ни гуёв тебе, ни автоматизации, ни уверенности в отсутствии завтрашнего BSOD’а никакой. Поэтому, по беглому напутствию с форумов «пропиши скриптом автоподключение» приходится извращаться, дабы превратить из бесплатного решения некоторое подобие платного (USB over IP, например, где есть все от блекджека до сами знаете чегокого).

В большинстве случаев проще вообще не заморачиваться usbip для проброса принтеров на терминальный сервер, а использовать возможности штатного клиента сервера терминалов. Но данный подход обязывает устанавливать драйвера для принтера на клиентскую станцию. Все хорошо и понятно, но нужно учитывать тот факт, что принтеров в сети может быть огромное количество, в том числе и злосчастный Canon, который, мягко говоря, забил большой жирный болт на всяких там пингвинов и чёртиков. Также ситуация может усугубляться тем, что системы на клиенте вообще никакой может и не быть, и даже жесткого диска, и вообще, образ линукса грузится по сети, при чем образ один для всех. В таком случае, мало того, что все необходимые драйвера должны быть установлены на самом сервере терминалов, так нам еще придется создавать нагромождение глюковдрайверов уже в образе линукса, что будет крутится на тонких клиентах. Учитывая, что принтеров много, и вряд ли получится подружить всех их с линуксом, а также то, что все-равно придется как-то расшаривать всякие там флешки, токены/смарткарты для клиент-банкинга, донглы для всяких там Лиг, и видео-наблюдений я все-же остановился на пробросе устройств посредством USBIP.

Но бесплатный сыр сами знаете где. Хотя будь я даже директором крупного холдинга, платить по 100 вечнозеленых за проброс каждой usb-фенечки вряд ли бы согласился, не то что текущее руководство, которое хочет все и сразу, и обязательно забесплатно.

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

Итак, согласно инструкции все просто прекрасно — даем команду:

usbip -a [IP_машины] [идентификатор устройства, присвоенный на той стороне]

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

Проблема №1. Если связь с usbip-сервером (машиной, на которой расшарено устройство) была ВНЕЗАПНО прервана, т.е. тонкий клиент могли просто выключить — то usbip-клиент виснет намертво, и хорошо, если он был просто запущен из командной строки, можно нажать CTRL+C. А если он был запущен из скрипта, да еще от имени системной учетной записи, т.к. скрипт запущен как служба? Вроде бы решение назревает само-собой — все время пингуем станцию, если пинг пропадает — убиваем процесс usbip, вот как описано здесь. Но как оказалось, не все так просто. Во первых, решение, описанное по предыдущей ссылке у меня адекватно не заработало при проброшенном десятке устройств, собственно из-за чего и пишется данная статья. Во-вторых, если убить процесс usbip из скрипта, то его бездыханная тушка еще долго будет конвульсивно трепыхаться в памяти, прежде чем действительно завершится. Именно завершать процессы usbip я решил по причине того, что если связи с тонким клиентом все также нету, то у меня usbip при подключении вис, не помню уже точно, но вроде надолго.

Решение было принято кардинальное — в цикле все так же пингуем станцию, если нет связи, убиваем процесс usbip, но убиваем хитро, а именно закрываем его хэндлы. Сначала хэндл \Device\0000003a, после чего процесс завершается если уже был произведен проброс устройства. Также закрываем хэндл \Device\Afd в таком случае процесс завершится гарантированно, даже если он в этот момент просто ломился на тонкий клиент. С запуском usbip с параметром -d я морочиться не стал, ибо непонятно, как себя поведет USBIP в том случае, если у нас проброшено несколько устройств с одинаковым идентификатором. Итак нам понадобятся такие утилиты от Марка Руссиновича:

Название утилиты handle говорит само за себя, она нужна нам для того, чтобы управлять дескрипторами приложения. А вот Process Explorer нужен для того, чтобы наглядно посмотреть номера нужных нам дескрипторов (номера в каждой системе вроде как отличаются) да и просто для того, чтобы было удобно наблюдать за процессом работы скриптов. Но прошу обратить внимание на то, что драйвер Process Explorer’a procexp141.sys свалил мой сервер терминалов в BSOD, благо, это было в нерабочее время. Так что настоятельно рекомендую крайне осторожно устанавливать какие-либо драйвера, утилиты и тому подобное на сервер, в котором работают десятки пользователей и отказоустойчивость которого должна быть превыше всего.

UPD 19.10.11. В процессе использования обнаружилось, что синий экран появляется все-таки из-за утилиты handle (или от совокупности handle и process explorer, при отображении в последнем открытых дескрипторов) в том случае если скрипт работает несколько суток подряд. На данный момент активно ищется решение данной проблемы. Возможными решениями являются включение файла подкачки а также запуск скриптов только в нужное нам время. В любом случае — следите за обновлениями статьи.

Также, в случае, если сервер терминалов у нас не англоязычный, нужно найти в сети, или скачать из подвала статьи утилиту ping, что идет в составе любой англоязычной Windows. Обрабатывать вывод такой утилиты намного проще, нежели русскоязычной.

Итак, создадим скрипт с условным названием _pinger.vbs (подчеркивание нужно для выразительности из сотни скриптов, об этом позже) и таким содержимым:

Do While True

	for count = 1 to 100

		wshell.Run "cmd /c c:\USBIP\ping.exe 192.168.0." & count & " _
-n 1 -l 1 -4 -w 1 > pinger.output", 1, True

		if objFSO.FileExists("c:\USBIP\pinger.output") then

			set f = objFSO.GetFile("c:\USBIP\pinger.output")
			set ts = f.OpenAsTextStream(ForReading, 0)

			Do While ts.AtEndOfStream  True

				TextLine = ts.ReadLine

				if Instr(TextLine, "Lost = 1") then 

					Set colProcessList = objWMIService.ExecQuery ("SELECT * FROM _
Win32_Process WHERE Name = 'usbip.exe' AND CommandLine LIKE '%192.168.0." & count & " %'")

					For Each objProcess in colProcessList

						wshell.Run "handle -p " & objProcess.ProcessId & " -c 128 -y", 1, True '\Device\0000003a
						wshell.Run "handle -p " & objProcess.ProcessId & " -c EC -y", 1, True '\Device\Afd

					Next

				end if

			Loop

			ts.Close
				
		end if

		WScript.Sleep 50

	next

	'WScript.Sleep 1000

Loop

Итак, в бесконечном цикле «Do While True … Loop» выполняем такие команды:

1. Цикл от одного до 100, что будет означать перебор всех IP-адресов 192.168.0.1 до 192.168.0.100.

2. Далее выполняем пинг каждого адреса с параметрами ‘-n 1 -l 1 -4 -w 1’ и выводом в файл pinger.output, который мы будем далее парсить. Конструкцию предваряет «cmd /c». Без этой конструкции не будет работать команда вывода в файл «… > file.txt». Параметры ping означают, что число отправляемых запросов будет равно одному (-n 1), размер буфера также будет равен единице (-l 1), используемым протоколом будет IPv4 (-4), и, наконец, минимальный таймаут, в 1 миллисекунду (-w 1). Такие параметры позволят нам сэкономить время на определении, есть ли станция на связи, или нет. Конечно, данный подход требует безупречного функционирования локальной сети.

3. После этого открываем для чтения файл с результатами пинга станции, и ищем там конструкцию Lost = 1, что подразумевает отсутствие станции на связи. Если это произошло, то ищем процесс «usbip.exe» запущенный с параметром в виде нужного нам IP-адреса, узнаем его process-id и передаем этот параметр утилите handle, которая и начнет закрывать дескрипторы данного процесса, что приведет к его закрытию. Не ошибитесь при построении запроса запущенных процессов. В конструкции

...AND CommandLine LIKE '%192.168.0." & count & " %'" 

важен пробел перед закрывающим процентом %, который означает инструкцию «все остальное не имеет значения». Без этого пробела поиск процесса, запущенного с параметрами 192.168.0.1 найдет также и 192.168.0.10, 192.168.0.11 и т. д.

Здесь, и далее по статье конструкция wshell.Run запускается с параметрами «1, True», что означает: стандартно отобразить окно приложения и ждать его завершения, это не даст скрипту обрабатывать последующие команды до завершения текущей.

Проблема №2. Данная проблема разветвляется сразу на три: во-первых: если удалить неактивный принтер, после того, как тонкий клиент был выключен, то при следующем переподключении некоторые принтеры уже не появляются сами по себе и нужно вручную зайти в Диспетчер устройств и нажать там кнопку «Обновить конфигурацию оборудования». Во вторых: в некоторых случаях не помогает даже обновление конфигурации в Диспетчере устройств. В таком случае нужно вручную удалить устройство «Устройство поддержки USB-принтера» и обновить конфигурацию оборудования, или же устанавливать принтер вручную, вручную указав действительный порт USB0xx. В третьих: порядок подключения устройств к серверу нам заранее не известен, а драйвер usbip вешает каждое устройство на любой свободный порт, который нумеруется как USB001, USB002 и так далее. Таким образом сейчас у нас принтер может висеть на порту USB002, а завтра уже на порту USB006. Соответственно, даже если не удалять неактивный принтер, то в случае изменения номера порта будет создан новый принтер, с таким же названием, но уже активный и пригодный для работы. Количество вновь создаваемых принтеров будет возрастать пропорционально количеству всех проброшенных USB-устройств.

Решил я эту проблему таким образом: в цикле подключаем устройство командой usbip -a [ip] [id] и ждем ее завершения, после чего удаляем все задания печати для данного принтера, удаляем сам принтер и удаляем ветку реестра, которая характеризует устройство поддержки USB-принтера. Если же есть проблемные принтеры, которые не устанавливаются автоматически как обычные PnP-устройства, то параллельным скриптом в цикле сканируем наличие в системе устройства с именем принтера, и, если таковое имеется — устанавливаем его посредством командной строки с помощью команды «rundll32 printui.dll,PrintUIEntry …»

Для реализации задуманного нам понадобятся такие утилиты:

Утилита Devcon нам понадобится для выполнения функций Диспетчера устройств, только из командной строки. Так как ветку реестра, отвечающую за устройства нам просто так не удалить, не дав соответствующие права нужным пользователям — используем утилиту Regperm, с помощью которой можно из командной строки установить необходимые права на любую ветку реестра.

Текст скрипта, который автоматически подключает принтер, и, если он был отключен, чистит реестр и удаляет сам принтер:

Const HKEY_CLASSES_ROOT  = &H80000000
Const HKEY_CURRENT_USER  = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS         = &H80000003

strComputer = "." 
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") 
Set objFSO = CreateObject("Scripting.FileSystemObject")
set wshell = WScript.CreateObject("Wscript.Shell")
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv") 

wshell.CurrentDirectory = "C:\USBIP"

Do While True

	wshell.Run "usbip -a 192.168.0.16 2-1", 1, True

	Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer") 

	For Each objPrinter in colInstalledPrinters 
		if Instr(objPrinter.Name, "сервисный отдел") then

			objPrinter.CancelAllJobs()
			objPrinter.Delete_

			strKeyPath = "SYSTEM\CurrentControlSet\Enum\USBPRINT\SamsungML-1210"
			wshell.Run "regperm.exe /A:Администраторы:F /K " & chr(34) & "HKEY_LOCAL_MACHINE\" _
& strKeyPath & chr(34) & " /f /i", 1, True
			DeleteSubkeys HKEY_LOCAL_MACHINE, strKeypath 

		end if
	Next 

	WScript.Sleep 4000

Loop

Sub DeleteSubkeys(HKEY_LOCAL_MACHINE, strKeyPath) 
	objRegistry.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubkeys 
	If IsArray(arrSubkeys) Then 
		For Each strSubkey In arrSubkeys 
			DeleteSubkeys HKEY_LOCAL_MACHINE, strKeyPath & "\" & strSubkey 
		Next 
	End If 
	objRegistry.DeleteKey HKEY_LOCAL_MACHINE, strKeyPath 
End Sub

Тут все также в бесконечном цикле пытаемся подключить устройство, а после его отключения ищем принтер с названием, которое выделяет его среди всех остальных. В данном примере — «сервисный отдел». Методами CancelAllJobs, и Delete_ (подчеркивание обязательно) очищаем очередь печати и удаляем принтер. Далее, изменяем разрешения на ветку реестра описывающую устройство USB-принтера, которая обычно названа именем принтера и находится по пути HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBPRINT\, а именно — даем группе Администраторы полные права командой «regperm /A:Администраторы:F … /f /i», где параметр «/f» означает присвоение аналогичных разрешений дочерним веткам, а параметр «/i» указывает на дополнительное наследование прав от родительских веток, дабы не удалить права для системной учетной записи «SYSTEM». После этого рекурсивно удаляем данную ветку. Здесь кроется большая проблема, которую я пока так и не смог решить — мы удаляем устройства всех принтеров с таким названием, что создаст проблемы, если в сети есть принтеры с одинаковыми названиями. Буду признателен, если кто-нибудь выскажет свои идеи на этот счет. Отмечу также, что если мы дали права изменение ветки группе администраторов, то и запуск скрипта (или службы, об этом ниже) должен производится от имени администратора.

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

Const ForReading = 1, ForWriting = 2, ForAppending = 3
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objFSO = CreateObject("Scripting.FileSystemObject")
set wshell = WScript.CreateObject("Wscript.Shell")

wshell.CurrentDirectory = "C:\USBIP"

Do While True

	Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer where _
Name = 'сервисный отдел'") 

	if colInstalledPrinters.Count > 0 then
		WScript.Sleep 10000
	else	
		
		wshell.Run "cmd /c devcon findall *SamsungML-1210* > c:\usbip\ivanov.p.p.output"

		if objFSO.FileExists("c:\usbip\ivanov.p.p.output") then

			set f = objFSO.GetFile("c:\usbip\ivanov.p.p.output")

			set ts = f.OpenAsTextStream(ForReading, 0)

			Do While ts.AtEndOfStream  True

				TextLine = ts.ReadLine

				if Instr(TextLine, "USBPRINT\SAMSUNGML-1210") then 

					PrtName = mid(TextLine, Instr(TextLine, "USB0"), 6)

					wshell.Run "rundll32 printui.dll,PrintUIEntry /if /z /b " & chr(34) & "сервисный отдел" _
& chr(34) & " /u /r " & chr(34) & PrtName & chr(34) & " /f " & chr(34) _
& "%windir%\ML-1210\SPLV1.inf" & chr(34) & " /m " & chr(34) _
& "Samsung ML-1200 Series" & chr(34), 1, True

					exit do

				end if

			Loop

			ts.Close

		end if

		WScript.Sleep 5000

	end if

Loop

В этом скрипте мы проверяем наличие нужного на принтера, если он есть — делаем небольшую паузу и начинаем все заново, если нету — проверяем наличие нужного устройства. Наличие устройства будем проверять утилитой devcon, которая укажет нам порт, на котором висит принтер, будь то USB001 или USB084. Порт нам нужен для того, чтобы вручную, с помощью командной строки установить принтер. В этом есть своя положительная сторона — принтер мы создаем сразу с нужными драйверами и нужным, удобочитаемым названием. Итак, по порядку.

Сначала выполняем команду «devcon findall *SamsungML-1210*», вывод которой направляем в файл ivanov.p.p.output Тут следует отметить, что названия скриптам, и результирующим файлам я давал в соответствии с доменными логинами, для более легкого ориентирования в большом количестве скриптов а также предварял подчеркиванием общие скрипты типа _pinger.vbs, _set_online_printers.vbs и прочих. Если устройство найдено, то в файле ivanov.p.p.output появится строка следующего вида:

USBPRINT\SAMSUNGML-1210\2&2451A4D6&0&USB006                 : SamsungML-1210 

где USB006 и есть порт, на котором сейчас висит принтер. Командой

mid(TextLine, Instr( TextLine, "USB0" ), 6) 

вырезаем это название из строки и после этого уже даем команду на установку принтера ‘rundll32 printui.dll,PrintUIEntry /if /z /b «сервисный отдел» /u /r USB006 /f «%windir%\ML-1210\SPLV1.inf» /m «Samsung ML-1200 Series»‘ где параметр /if указывает на то, что будет использоваться конкретный *.inf файл, параметр /z указывает на то, что давать общий доступ принтеру не нужно, /b задает имя принтера, /u означает использование уже существующего драйвера, если таковой имеется. Это поможет нам в том случае, если драйвер не подписанный, и из скрипта принтер не установится, даже при отключенном предупреждении о подписях драйверов. Поэтому нужно один раз установить принтер вручную, подтвердить использование не подписанного драйвера, а уже в дальнейшем установка будет проходить без вопросов. Параметр /r задает порт, который мы определили ранее, /f указывает путь к *.inf-файлу, и, наконец, параметр /m указывает модель принтера, по которой будет вестись поиск в *.inf-файле. Отмечу, что параметры данной команды регистрозависимы, так что будьте внимательны! Подробнее о возможных параметрах можно почитать здесь.

Как я уже сказал, с подключением других устройств обычно проблем не возникает, и для их работы достаточно запуска скриптов такого вида:

set wshell = WScript.CreateObject("Wscript.Shell")

wshell.CurrentDirectory = "C:\USBIP"

Do While True

	wshell.Run "usbip -a 192.168.0.3 1-4", 1, True

	WScript.Sleep 1000

Loop

Для идеальной работы нам еще понадобятся такие скрипты:

strComputer = "." 

Do While True

	Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") 
	Set objFSO = CreateObject("Scripting.FileSystemObject")
	Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer") 

	For Each objPrinter in colInstalledPrinters 

		if Instr(objPrinter.Name, "1606") then

			objPrinter.RenamePrinter "бухгалтерия"

		end if

		if Instr(objPrinter.Name, "1510") then

			objPrinter.RenamePrinter "инженеры"

		end if

	Next 

	WScript.Sleep 1000

Loop
  • скрипт установки оперативного режима принтеров. Предназначен для принтеров, которые подключены по LPT и не всегда при переподключении становятся активными (работающими в оперативном режиме):
  • Set Shell = CreateObject("Shell.Application")
    
    Do While True
    
    	Set objFolder = Shell.NameSpace(4)
    	For each printer in objFolder.Items
    
    		if Instr(printer, "бухгалтерия") then 
    
    			If objFolder.GetDetailsOf(printer, 2)="Не подключен" then
    
    				printer.InvokeVerbEx("&Использовать принтер в оперативном режиме")
    
    			end if
    
    		end if
    
    	Next
    
    	WScript.Sleep 60000
    
    Loop
    

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

  • и наконец, инициализирующий остальные скрипт:
  • set wshell = WScript.CreateObject("Wscript.Shell")
    wshell.CurrentDirectory = "C:\USBIP"
    
    wshell.Run "_pinger.vbs"
    wshell.Run "_rename_printers.vbs"
    wshell.Run "_set_online_printers.vbs"
    
    wshell.Run "petrenko.e.s.vbs"
    wshell.Run "vasilenko.e.a.1.vbs"
    wshell.Run "vasilenko.e.a.2.vbs"
    wshell.Run "ivanenko.l.d.vbs"
    wshell.Run "adminenko.s.e.1.vbs"
    wshell.Run "adminenko.s.e.1.1.vbs"
    wshell.Run "directorenko.a.v.1.vbs"
    wshell.Run "directorenko.a.v.1.1.vbs"
    

    Данный скрипт будет запускаться в качестве службы. Для этого нам понадобятся такие утилиты, входящие в состав Windows Resource Kits:

    Для запуска скрипта как сервиса нам необходимо скопировать утилиту srvany в папку c:\WINDOWS\system32\ и выполнить команду «instsrv.exe USBIP c:\WINDOWS\system32\srvany.exe». Эта команда создаст службу с названием USBIP. После этого нам необходимо прописать, что конкретно эта служба будет запускать. Для этого идем в реестр по пути HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\USBIP и добавляем раздел «Parameters», в котором добавляем REG_SZ-параметр с именем «Application» и значением «cscript c:\usbip\_start.vbs», где _start.vbs и есть название нашего инициализирующего скрипта. В данном примере также нужно зайти в свойства данной службы и назначить ее запуск от имени Администратора, т.к. права на модифицирование веток реестра мы даем группе администраторов. Также, для того, чтобы утилита handle запустилась под системной учетной записью, нам нужно «принять лицензионное соглашение» под ней. Но так как само окно с лицензионным соглашением мы не увидим, даже запустив процесс под системной учеткой, мы поступим проще, а именно создадим в реестре такой ключ «HKEY_USERS\.DEFAULT\Software\Sysinternals\Handle» с DWORD-параметром EulaAccepted и значением 1.

    Также советую установить на запуск каждому пользователю вот такой небольшой, но не менее полезный скрипт, который делает ближайший принтер принтером по-умолчанию:

    On Error Resume Next
    
    strComputer = "." 
    
    WScript.Sleep 5000
    
    Do While True
    
    	Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") 
    	Set objFSO = CreateObject("Scripting.FileSystemObject")
    	Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer") 
    
    	For Each objPrinter in colInstalledPrinters 
    
    		if Instr(objPrinter.Name, "менеджеры") then
    
    			objPrinter.SetDefaultPrinter
    
    		end if
    	Next 
    
    	WScript.Sleep 10000
    Loop
    

    Обращаю внимание на конструкцию On Error Resume Next, которая указывает серверу сценариев не обрабатывать ошибки и продолжать работу скрипта в случае возникновения оных. Дело в том, что команда objPrinter.SetDefaultPrinter не всегда отрабатывает корректно, поэтому, будем повторять попытку установки принтера по-умолчанию даже в случае ошибки.

    Также хочу заметить, что во время всей этой эпопеи я столкнулся с тем, что под некоторые устройства начисто отсутствуют драйвера под x64 систему. Для этого пришлось параллельно подымать домен (виртуальную машину в терминологии гипервизора Xen) с Windows XP Pro SP3 на борту. Но имейте в виду, что лично у меня под Windows XP usbip.exe второй версии категорически отказался запускаться. Помог только откат до версии v0.1, при чем достаточно заменить только исполняемый файл.

    Еще настоятельно рекомендую проводить установку USBIP и скриптов в каталог c:\USBIP, т.е. поближе к корню, и с кратчайшим названием.

    Преимуществом использования vbs-скриптов перед auto-it программированием в данном случае является меньшее потребление памяти. В нашем случае выделяется память только под процессы usbip, сам процесс скрипта занимает намного меньше памяти нежели скомпилированный auto-it скрипт. Данное преимущество становится ключевым при подключении большого количества устройств. Также в случае с vbs-скриптами все прозрачно и понятно, и при необходимости быстро модифицируется.

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

    Необходимые скрипты:

    скачать одним архивом: scripts.zip (612 Загрузок)

    Вот такой вот монструозный скрипт получился, но тем не менее работающий уже как 2 недели в энтерпрайзе. Напоминаю, есть нерешенная проблема с одинаковыми принтерами. Буду рад любым замечаниям и предложениям или более изящным решениям.

    VN:F [1.8.8_1072]
    обождите...
    Rating: 5.0/5 (1 vote cast)
    автоподключение USBIP устройств на windows-сервере5.051