Python WMI или winreg, получаем серийный номер USB устройства
И снова здравствуйте, у меня опять странный вопрос, на который я никак не могу ответить. Начну сначала.
Задача: Локальный сбор данных о количестве напечатанных страниц с принтеров, с последующей отправкой в MSSQL и дальнейший свод в отчет с сопоставлением информации по серийным номерам, на случай переноса принтера.
Описание проблемы, с самого начала: Все было хорошо, пока я не пришел к выводу, что нужно вытаскивать серийные номера из WMI. На Windows 10 в целом возможно сопоставить имя очереди печати с именем принтера и преодолев небольшой путь назад, по списку полученных от Win32_USBControllerDevice устройств получить желаемое, за редкими исключениями (зависит от принтера).
Но на Windows 7 нет такого понятия, как очередь печати, точнее есть, но оно не отражается в Диспетчере устройств так, как это делается в Windows 10.
Вообще, можно было бы сформулировать вопрос так:
Как получить по имени принтера информацию о его USB порте. То есть, есть имя принтера, которое можно увидеть в "панели управления-устройства и принтеры" и есть USB-интерфейс этого принтера, как их сопоставить и сейчас я не о имени порта, вроде USB003, а о строке, вроде этой:
DeviceID = "USB\\VID_0A5F&PID_0027\\СЕРИЙНЫЙ НОМЕР ТУТ";
Какие данные я получаю: 1. Данные о постановке задачи на печать:
{'JobId': 3, 'pPrinterName': 'PRINTER_NAME', 'pMachineName': '\\\\localhost', 'pUserName': 'aaseev', 'pDocument': 'Microsoft Word - Документ.rtf', 'pDatatype': 'RAW', 'pStatus': None, 'Status': 9, 'Priority': 1, 'Position': 1, 'TotalPages': 1, 'PagesPrinted': 0, 'Submitted': pywintypes.datetime(2020, 4, 14, 3, 20, 42, 147000, tzinfo=TimeZoneInfo('GMT Standard Time', True))}
- Информация о принтере из Win32_Printer:
{
Attributes = 2625;
AveragePagesPerMinute = 0;
Capabilities = {4, 2};
CapabilityDescriptions = {"Copies", "Color"};
Caption = "PRINTER_NAME_1";
CreationClassName = "Win32_Printer";
Default = FALSE;
DefaultPriority = 0;
DetectedErrorState = 0;
DeviceID = "PRINTER_NAME_1";
Direct = FALSE;
DoCompleteFirst = TRUE;
DriverName = "PRINTER_NAME";
EnableBIDI = TRUE;
EnableDevQueryPrint = FALSE;
ExtendedDetectedErrorState = 0;
ExtendedPrinterStatus = 2;
Hidden = FALSE;
HorizontalResolution = 203;
JobCountSinceLastReset = 0;
KeepPrintedJobs = FALSE;
Local = TRUE;
Name = "PRINTER_NAME_1";
Network = FALSE;
PaperSizesSupported = {1};
PortName = "USB003";
PrinterPaperNames = {"User defined"};
PrinterState = 0;
PrinterStatus = 3;
PrintJobDataType = "RAW";
PrintProcessor = "winprint";
Priority = 1;
Published = FALSE;
Queued = TRUE;
RawOnly = FALSE;
Shared = FALSE;
ShareName = "PRINTER_NAME";
SpoolEnabled = TRUE;
Status = "Unknown";
SystemCreationClassName = "Win32_ComputerSystem";
SystemName = "localhost";
VerticalResolution = 203;
WorkOffline = FALSE;
};
- Информация о принтере из Win32_USBControllerDevice:
DEPENDENT instance of Win32_PnPEntity {
Caption = "Поддержка USB принтера";
ClassGuid = "{36fc9e60-c465-11cf-8056-444553540000}";
CompatibleID = {"USB\\Class_07&SubClass_01&Prot_02", "USB\\Class_07&SubClass_01", "USB\\Class_07"};
ConfigManagerErrorCode = 0;
ConfigManagerUserConfig = FALSE;
CreationClassName = "Win32_PnPEntity";
Description = "Поддержка USB принтера";
DeviceID = "USB\\VID_0A5F&PID_0027\\**СЕРИЙНЫЙ НОМЕР ВОТ ТУТ**";
HardwareID = {"USB\\VID_0A5F&PID_0027&REV_0001", "USB\\VID_0A5F&PID_0027"};
Manufacturer = "Microsoft";
Name = "Поддержка USB принтера";
PNPClass = "USB";
PNPDeviceID = "USB\\VID_0A5F&PID_0027\\**СЕРИЙНЫЙ НОМЕР ВОТ ТУТ**";
Present = TRUE;
Service = "usbprint";
Status = "OK";
SystemCreationClassName = "Win32_ComputerSystem";
SystemName = "LOCALHOST";
};
DEPENDENT instance of Win32_PnPEntity {
Caption = "PRINTER_NAME";
ClassGuid = "{4d36e979-e325-11ce-bfc1-08002be10318}";
CompatibleID = {"CID_MS_GENERICPRINT"};
ConfigManagerErrorCode = 0;
ConfigManagerUserConfig = FALSE;
CreationClassName = "Win32_PnPEntity";
Description = "PRINTER_NAME";
DeviceID = "USBPRINT\\...\\6&840F8D5&0&USB003";
HardwareID = {"USBPRINT\\...", "..."};
Manufacturer = "ZDesigner";
Name = "PRINTER_NAME";
PNPClass = "Printer";
PNPDeviceID = "USBPRINT\\...\\6&840F8D5&0&USB003";
Present = TRUE;
Status = "OK";
SystemCreationClassName = "Win32_ComputerSystem";
SystemName = "LOCALHOST";
};
DEPENDENT instance of Win32_PnPEntity {
Caption = "PRINTER_NAME_1";
ClassGuid = "{1ed2bbf9-11f0-4084-b21f-ad83a8e6dcdc}";
CompatibleID = {"GenPrintQueue", "SWD\\GenericRaw", "SWD\\Generic"};
ConfigManagerErrorCode = 0;
ConfigManagerUserConfig = FALSE;
CreationClassName = "Win32_PnPEntity";
Description = "Локальная очередь печати";
DeviceID = "SWD\\PRINTENUM\\{4A2629B1-899A-4722-9728-82576A160020}";
HardwareID = {"PRINTENUM\\LocalPrintQueue"};
Manufacturer = "...";
Name = "PRINTER_NAME_1";
PNPClass = "PrintQueue";
PNPDeviceID = "SWD\\PRINTENUM\\{4A2629B1-899A-4722-9728-82576A160020}";
Present = TRUE;
Status = "OK";
SystemCreationClassName = "Win32_ComputerSystem";
SystemName = "LOCALHOST";
};
Если вы дочитали до этого места, то вам явно не все равно и возможно даже интересно, а может вы и вовсе знаете, как это сделать и я помогу вам совсем немножко этим:
Пожалуйста, не говорите про SNMP и всевозможные сервисы по сбору информации, SNMP вообще волшебная вещь, но не везде это реализуемо.
Пожалуйста, не говорите, что я дурачок (я и так знаю) и что Python не для того сделан, чтобы Windows-сервисы писать и информацию от WMI получать. Я люблю Python и мне очень хочется сделать это именно на нем, впрочем как сделать это на чем-нибудь другом я не знаю.
Ответы (3 шт):
Я не смог сделать требуемое средствами WMI, но смог с помощью реестра. Решение рабочее, но сырое, поэтому гарантировать 100% работоспособность я не могу. Если вам известен другой, метод, я с удовольствием его рассмотрю.
import winreg
def getPrinterSerialFromReg(printerName):
def findInUSBEnum(usbKey,instanceId):
try:
for i in range(9999):
vidpidName = winreg.EnumKey(usbKey,i)
vidpidKey = winreg.OpenKey(usbKey,vidpidName)
try:
for vp in range(10):
SNName = winreg.EnumKey(vidpidKey,vp)
SN = winreg.OpenKey(vidpidKey,SNName)
try:
if str(winreg.QueryValueEx(SN,'ParentIdPrefix')[0]).lower() in str(instanceId).lower():
return SNName
except:
continue
except:
continue
except:
return 'SN not found'
try:
mainKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\\'+printerName+r'\\PnPData')
except:
return 'printer name not found'
try:
instanceId = (winreg.QueryValueEx(mainKey,'DeviceInstanceId')[0]).split('\\')[-1]
usbKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r'SYSTEM\CurrentControlSet\Enum\USB')
except:
return 'get key error'
if 'dot4'.lower() in str((winreg.QueryValueEx(mainKey,'DeviceInstanceId')[0])).lower():
dot4Key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r'SYSTEM\CurrentControlSet\Enum\DOT4')
dot4fok = 0
try:
for i in range(9999):
if dot4fok == 1:
break
vidpidName = winreg.EnumKey(dot4Key,i)
vidpidKey = winreg.OpenKey(dot4Key,vidpidName)
try:
for vp in range(10):
SNName = winreg.EnumKey(vidpidKey,vp)
SN = winreg.OpenKey(vidpidKey,SNName)
try:
if str(winreg.QueryValueEx(SN,'ParentIdPrefix')[0]).lower() in str(instanceId).lower():
instanceId = SNName
dot4fok = 1
break
except:
continue
except:
continue
except:
return 'dot4 parent not found'
SN = findInUSBEnum(usbKey,instanceId)
while '&' in SN:
instanceId = SN
SN = findInUSBEnum(usbKey,instanceId)
return SN
print (getPrinterSerialFromReg('PRINTER NAME'))
.py
import winreg
printerName = 'hp LaserJet 1010 HB'
mainKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\\'+printerName+r'\\PnPData')
instanceId = (winreg.QueryValueEx(mainKey,'DeviceInstanceId')[0]).split('\\')[-1] # 7&29A2BFB9&1&1
usbKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r'SYSTEM\CurrentControlSet\Enum\USB')
dot4Key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r'SYSTEM\CurrentControlSet\Enum\DOT4')
vidpidName = winreg.EnumKey(dot4Key,0) #VID_03F0&PID_0C17&REV_0100&PRINT
vidpidKey = winreg.OpenKey(dot4Key,vidpidName)
SNName = winreg.EnumKey(vidpidKey,0) #6&1c93366b&2&0
SN = winreg.OpenKey(vidpidKey,SNName)
instanceId = SNName #6&1c93366b&2&0
vidpidName = winreg.EnumKey(usbKey,4) #VID_03F0&PID_0C17
vidpidKey = winreg.OpenKey(usbKey,vidpidName)
SNName = winreg.EnumKey(vidpidKey,0) #00CNFF157864
SN = winreg.OpenKey(vidpidKey,SNName)
SN = SNName #00CNFF157864
.vbs
Option Explicit
Dim oWMISrv, collDvcs, collUSBDvcs, iUSBDvc , iDvc, sDvcID, sPID, sVID, sSN
Function ReplaceX(ByVal sValue, ByVal sPattern, ByVal sNValue)
Dim oReg : Set oReg = New RegExp
oReg.Pattern = sPattern
ReplaceX = oReg.Replace(sValue, sNValue)
Set oReg = Nothing
End Function
Set oWMISrv = GetObject("winmgmts:\\.\root\cimv2")
Set collDvcs = oWMISrv.ExecQuery("Select * From Win32_USBControllerDevice")
For Each iDvc In collDvcs
If InStr(iDvc.Dependent, "VID_") Then
sDvcID = ReplaceX(iDvc.Dependent, ".*""(.*)""", "$1")
sPID = ReplaceX(sDvcID, ".*PID_([^\\]*).*", "$1")
sVID = ReplaceX(sDvcID, ".*VID_([^&]*).*", "$1")
sSN = ReplaceX(sDvcID, ".*\\(.*)", "$1")
Set collUSBDvcs = oWMISrv.ExecQuery _
("Select * From Win32_PnPEntity Where DeviceID = '" & sDvcID & "'")
For Each iUSBDvc in collUSBDvcs
If ReplaceX(sSN, ".*(&).*", "$1") = sSN then
Wscript.Echo _
"Name : " & iUSBDvc.Description & vbCrLf & _
"VID : "& sVID & vbCrLf & _
"PID : "& sPID & vbCrLf & _
"SN : " & sSN & vbCrLf
End If
Next
Set collUSBDvcs = Nothing
End If
Next
Set collDvcs = Nothing
Set oWMISrv = Nothing
У меня нет usb-принтера. Вся информация, которая сейчас будет, мною лично не проверена (имейте совесть, не минусуйте). Поможет, значит поможет, нет, не обессудьте.
Первое - Powershell (из Питон запустить такую команду легко):
Get-PrinterPort | Where-Object { $_.Name -like "*USB*" } | format-list *
Второе - Питон:
import win32com.client
def get_printer_ports():
wmi = win32com.client.GetObject("winmgmts:")
for printer in wmi.InstancesOf("Win32_Printer"):
if "USB" in printer.PortName:
print(f"Printer: {printer.Name}, Port: {printer.PortName}")
get_printer_ports()