Определение оборудования компьютера, на котором вы работаете, сведения об операционной системе и прочие параметры порою бывают очень нужны. Цели, в которых их можно использовать – самые разнообразные. От простого любопытства, до определения версии чипсета, чтобы подобрать под него необходимое железо для апгрейда. И мы все, за годы использования привыкли пользоваться программами сторонних производителей. Это и не хорошо, и не плохо. Каждый инструмент выполняет свою задачу в нужное время и в нужном месте. Но, для большего понимания того, как и откуда берется информация, в те же самые программы, можно написать свое собственное, небольшое консольное приложение на Python. Давайте рассмотрим несколько вариантов того, как и с помощью каких модулей это можно сделать. А также попробуем оценить быстродействие получившихся скриптов, количество выводимой информации.
А начнем мы написание наших скриптов с попытки получения информации из реестра операционной системы Windows.
Давайте чуть подробнее остановимся на том, что же это вообще такое. В современных операционных системах реестр используется для хранения настроек, конфигурационных данных, информации об установленных программах и еще множество самых разнообразных параметров и настроек, которые используются операционной системой для работы с железом, разнообразными настройками системы, запуска и управления службами и прочими компонентами.
Реестра был введен в 1992 году в ОС Windows 3.1 и продолжает использоваться во всех, более поздних версиях ОС. Нужно понимать, что реестр представляет собой иерархическую структуру, подобную дереву, которая содержит множество ветвей и ключей. Каждый ключ содержит данные в виде значений. В общем, это довольно важный компонент операционной системы, неправильное использование которого может привести к непредсказуемым последствиям или отказу работы системы. Но, в рамках данной статьи изменять реестр мы не собираемся. А только лишь будем считывать его значения.
Первым скриптом, который мы напишем для получения данных о параметрах ОС и ее оборудовании, это будет скрипт получающий данные из реестра.
Что потребуется?
По большему счету для работы данного скрипта в установке сторонних модулей нет необходимости. Но, если вы хотите сохранить полученные данные в не текстовый файл, то установить сторонний модуль все же придется. В данном скрипте я использую модуль python-docx, который позволит нам сохранить полученные данные в документ Microsoft Word. Поэтому, для его установки нужно написать в терминале команду:
Также, в работе данного скрипта я использовал модуль windows-tools, с помощью которого получал данные о ключе активации операционной системы. По большему счету можно было бы обойтись и без него, так как, если мы заглянем внутрь данного модуля, то увидим, что в своей работе он также, как и мы, использует библиотеку winreg, а также дополнительную функцию для дешифровки ключа активации. Для установки данного модуля пишем в терминале команду:
После того, как будут установлены модули, давайте выполним импорт необходимых библиотек в скрипт.
Получение версии операционной системы Windows
Давайте напишем, в рамках данного скрипта, первую функцию, с помощью которой получим версию, сборку, название и ключ активации из реестра. Назовем ее winreg_os() -> dict. Никаких параметров данная функция не принимает, а вот возвращает словарь с полученными значениями в случае, если они были получены.
Создадим пустой словарь win_info, в который будем складывать полученные данные. Для начала получим имя компьютера. Для этого откроем ветку реестра, в котором содержится данное значение: HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\ и прочитаем значение ключа ComputerName.
Затем проделаем то же самое с получением времени последнего выключения компьютера. Для этого открываем ветку реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows и читаем значение ключа ShutdownTime. Так как значение данного ключа содержится в реестре в микросекундах, для начала конвертируем это значение в человекочитаемый формат, а уж только после этого добавим в словарь.
Все остальные значения ключей, которые нам нужно получить, находятся в одной ветке реестра. Поэтому, для начала открываем ее с помощью OpenKeyEx: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion.
Запускаем цикл, в котором будем итерироваться по списку названий ключей реестра. По очереди будем получать значение каждого ключа. Перед тем, как добавить значение в словарь, проверим, не является ли ключ датой установки, так как ее нужно конвертировать в человекочитаемый вид. И добавляем все значения которые получим в словарь.
Двигаемся дальше. И следующее значение, которое нам будет нужно, это временная зона. Хотя, по большому счету оно особой роли ни для чего не играет, но для информации пригодится. Открываем ключ реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation и читаем значение ключа TimeZoneKeyName, после чего добавляем его в словарь.
Ну и последняя операция, которая, можно сказать, будет некоей вишенкой на данном торте функции )). Получим ключ активации операционной системы. На самом деле, знать ключ активации достаточно важно. И хотя, Microsoft уже перешла на цифровые лицензии, которые подхватываются автоматически при установке ОС, кто знает, может вам понадобиться установить Windows на другое железо, в другой сети. Не думаю, что ключ активации подхватиться с такой же легкостью. Вот потому, знать его желательно.
Поэтому, получаем данное значение с помощью модуля windows_tools и его метода product_key. Данный метод позволяет получить ключ из реестра даже в том случае, если он вшит в BIOS компьютера, так как его значение при установке копируется в реестр. Поэтому, для начала пробуем получить стандартный ключ. Если не получиться, тогда ключ из BIOS. Ну и если все получено, добавляем данные в словарь.
После того, как будут собраны все необходимые данные, проверяем, содержит ли что-то словарь, который мы создавали в начале. Если да, возвращаем его из функции.
Получение информации о BIOS
Перейдем к следующей части нашего «
Создадим функцию bios_winreg() -> dict, которая будет возвращать словарь с полученными значениями. Для начала, определим словарь, куда будем эти значения складывать. Затем подключимся к первой ветке реестра. Из нее мы получим версию BIOS и добавим ее в словарь.
Создаем цикл, который будет итерироваться по списку ключей, после чего выполняем подключение ко второй ветке реестра, и по очереди забираем значения из ключей, подставляемых с помощью цикла. Добавляем их также в созданный ранее словарь. Обработаем исключение, на всякий случай, если не будет найдено нужного ключа. В этом случае мы просто продолжаем цикл и перебираем ключи до конца.
После того, как все значения получены, возвращаем словарь из функции, если он не пуст.
Получение информации о материнской плате
Следующая функция, которую мы создадим: motherboard_winreg() -> (dict, bool), будет получать из реестра информацию о материнской плате и возвращать словарь с полученными значениями. Информация о материнской плате содержится в ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SystemInformation. Поэтому, для начала создаем словарь, в который будем складывать полученные значения, а после подключимся к этой ветке реестра. Заберем информацию о производителе и названии материнской платы.
После того, как вся нужная информация будет получена, вернем словарь со значениями, если он не пуст из функции.
Получение информации о процессоре
Теперь перейдем к получению информации о процессоре. Данная информация содержится в ветке реестра: HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor. В данной ветке находятся ключи, каждый из которых содержит информацию о ядре процессора. Независимо от того, физическое оно или логическое. Для примера, вот скриншот, как это может выглядеть:
Таким образом, узнав количество данных веток, мы сможем понять, сколько ядер у нашего процессора. Поэтому, подключаемся к ветке и с помощью QueryInfoKey забираем информацию о подкатегориях. Добавляем в словарь значение о количестве ядер.
Если мы откроем каждую из веток, то увидим, что информация о каждом ядре, по сути, является дубликатом информации самого первого ядра. За исключением некоторых параметров, таких как идентификаторы. Но получать мы их не будем, а ограничимся только общей информацией. Таким образом, перебирать каждую ветку не имеет, в нашем случае, особого смысла. Достаточно получить информацию из одной. Поэтому, подключаемся к самой первой ветке имеющей индекс 0. После чего забираем оттуда нужные параметры, такие как название процессора, идентификатор, производитель и частоту.
После того, как нужные параметры получены и добавлены в словарь, возвращаем его из функции. Если он, конечно же, не пуст.
Получение информации о GPU
С получением информации о GPU из реестра, немного неоднозначная ситуация. Дело в том, что в системе нет стандартной ветки, откуда можно было бы забрать все по умолчанию. При установке драйверов на видеокарту конкретного производителя в реестре создается отдельная ветка, в которой и содержится нужная информация. Но, так как у каждого производителя и ветка будет своей, протестировать получение данной информации из реестра у меня не представляется возможным. Так как в наличии просто нет необходимого оборудования. Тем не менее, мы получим из реестра, в данном случае, хотя бы модель.
Создадим функцию gpu_winreg(), которая будет получать данные о модели и возвращать словарь с полученным значением. Информация о модели GPU содержится в следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000. Подключаемся к данной ветке. Забираем значение ключа DriverDesc, добавляем его в словарь и возвращаем из функции.
Получение информации о HDD/SSD
Создадим функцию hdd_ssd_winreg(). С ее помощью мы будем получать данные о всех жестких дисках, которые установлены в компьютере. Информация об этом находится в ветке реестра: HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi. Подключаемся к ней.
Следующее, что нам понадобится, так как мы не знаем, сколько дисков установлено в системе, узнать, сколько подразделов содержится в данном разделе реестра. Так же, как мы делали, когда получали информацию о процессоре и количестве его ядер.
Вот как выглядит соответствующая ветка реестра:
Как вы видите, далеко не каждый раздел содержит информацию. Более того, количество разделов будет равно количеству портов для подключения на плате. А значит, информация о диске будет содержаться только в том разделе, соответствующем порту, к которому диск подключен. Следовательно, нам нужно перебрать каждый раздел и подраздел, и получить значения ключей из каждого из них. А также обработать исключение, когда мы будем натыкаться на раздел, в котором нет информации о подключенном диске.
Для начала, создаем цикл, в котором будем итерироваться по диапазону соответствующему количеству разделов. Ну, а далее, будем в каждом из циклов подключаться все глубже и глубже по дереву, пока не достигнем конечной точки. Откуда и будем забирать значения ключей.
После того, как информация будет собрана, возвращаем ее из функции. Хочу еще обратить ваше внимание на то, что, так как ключи, содержащие значения неуникальны, их нужно поместить в качестве словаря в ключ, содержащий уникальное значение о диске. Или номер, или название. Это не имеет особого значения. Главное, чтобы создаваемый ключ больше не повторялся.
Получение информации о CD/DVD-ROM
Вы можете сказать, что получение данной информации не имеет большого значения, так как данные устройства постепенно выходят из обихода и их уже можно встретить только на старых машинах. Так-то оно так, вот только не совсем. Дело в том, что с помощью данной информации можно узнать, используется ли в системе USB-модем, так как у каждого современного модема есть раздел, который определяется системой как внешний привод. А соответственно, узнав название и модель, мы получим представление, какой модем используется.
Создадим функцию cdrom_winreg(), которая будет возвращать словарь с полученными значениями. Информация о приводах содержится в следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\cdrom\Enum. Уже, подключившись к данной ветке, мы будем получать из нее значения ключей.
Создадим словарь, куда будем складывать полученные значения. В данной ветке есть хороший ключ Count, получив значение которого мы узнаем, сколько приводов установлено в системе. Тем более, что проверив это значение и поняв, что оно равно нулю, мы можем вовсе не итерироваться в поисках приводов, а просто вернуть False.
Теперь, если значение больше нуля, итерируемся по диапазону значений равному количеству приводов. Собираем значения, которые присваиваем в переменные. Сразу же обработаем возможные исключения, если полученный ключ, к примеру, не сможет быть обработан. И уже после этого добавляем полученные значения в словарь, который возвращаем из функции.
Получаем информацию о сетевых интерфейсах
Для получения данной информации придется несколько порыскать по веткам реестра. Собрать значения в одном месте, потом на основании собранных данных получить в другом. Нужно оговориться, что получать информацию мы будем только о физических сетевых интерфейсах. Все остальные, то есть виртуальные, нас будут волновать в последнюю очередь.
Создадим функцию nic_winreg(). На выходе она будет возвращать словарь с полученными данными. Для начала проверим ветку реестра: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards. Информации здесь немного, но, мы получим отсюда имя сетевого адаптера и его идентификатор в системе. Сразу же создадим словарь, куда добавим полученные значения. Идентификатор будет служить в качестве ключа для всей остальной информации об интерфейсах. Также создадим список, куда сложим идентификаторы. Они понадобятся нам в дальнейшей работе функции.
Подключаемся к следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}. Здесь содержаться подразделы, в которых содержится чуть больше информации. Но, для начала, нужно понять, из какого подраздела нужно получать информацию, так как нам нужно будет забраться чуть-чуть поглубже.
Как вы помните, ранее мы создавали список с идентификаторами. Поэтому, перебираем каждый подраздел, забираем из него значение NetCfgInstanceId и сравниваем со списком. Если данное значение содержится в списке, создаем новый список и добавляем в него полученные значения.
Теперь двигаемся глубже. Имя адаптера мы получили, необходимо теперь получить такие данные, как шлюз по умолчанию, ip-адрес. Переходим в следующую ветку реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces и забираем оттуда данные, которые добавляем в словарь.
И еще один раздел реестра, куда нам нужно заглянуть: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}. В данном разделе содержится имя адаптера в системе. Помните, вот это вот: Ethernet, Ethernet2 и т.д. Вышеуказанная ветка – это только первая часть пути. Далее, нужно указать идентификатор адаптера и добавить слово «Connection». Чтобы получилось, для примера, как-то так: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{1ECDE6DD-4799-4A4B-AEDB-F2C8C2AD528F}\Connection.
Затем подключаемся к данной ветке и уже оттуда забираем значение ключа Name.
После того, как данные будут получены, возвращаем словарь с полученными значениями из функции.
Что же, думаю, что на этот раз, достаточно. В довесок к предыдущей информации можно было бы получить данные обо всех установленных программах. Но найти об этом информацию и реализовать нужную функцию самостоятельно не особо сложно. А мы, тем временем, завершим с получением информации и перейдем к обработке полученных данных, выводу информации для пользователя в терминал и сохранению данных в документ Microsoft Word.
Обрабатываем полученные данные
Для того, чтобы обработать полученные данные я создал функцию print_wmic(part, dict_info). На вход она получает part, содержащий текст о той информации, которую нужно вывести в терминал. dict_info – собственно, сам словарь с полученными данными.
Для того чтобы не присваивать значения вручную, да и не присвоишь их при обработке в циклах множества словарей, я создал словарь, в котором содержится ключ, соответствующий названию параметра и его расшифровка на русском. Так что, большую часть функции занимает именно он.
Вот он собственно и сам:
А теперь, функция печати. Для того чтобы сохранить полученные данные в документ, я создал глобальную переменную wmic_info = "", куда и буду помещать полученные данные. То есть, по сути, там будет содержаться большая строка разделенная переносами.
Ну, а дальше все просто. Проверяю тип ключа. Если это словарь, запускаю еще один цикл и добавляю информацию в созданную глобальную переменную. Если не словарь, то добавляю полученные значения.
Возвращать здесь ничего не нужно.
Функция main
В данной функции мы будем проверять возвращаемые значения на истинности и в соответствии с этим запускать или не запускать функцию обработки. Перед этим добавляем с документ создаваемый с помощью python-docx заголовок. Ну и в конце добавляем в документ параграф в котором будет текст с полученными данными. Сохраняем документ с именем компьютера. И выводим в терминал время работы скрипта.
Что же. На этом мы завершим создание скрипта по получению данных из реестра. Хочу сказать дополнительно, что к большому сожалению не нашел информации о том, как получить, хотя бы, общий объем оперативной памяти из реестра. Не знаю, может быть, плохо искал. Если кто-то знает, буду благодарен за подсказку.
А теперь давайте посмотрим на время его работы:
Что же, достаточно шустро. Но, это только первая часть из небольшого цикла о получении информации об ос и оборудовании различными способами, а также сравнении времени работы каждого способа. Продолжение в следующей статье. Далее мы рассмотрим способы получения информации с помощью subprocess.check_out, который будет парсить вывод утилиты wmic, запускаемой с различными параметрами. А в конце статьи также посмотрим на скорость работы полученной функции.
А на сегодня, пожалуй, все.
Спасибо за внимание. Надеюсь, данная информация будет вам полезна
А начнем мы написание наших скриптов с попытки получения информации из реестра операционной системы Windows.
Давайте чуть подробнее остановимся на том, что же это вообще такое. В современных операционных системах реестр используется для хранения настроек, конфигурационных данных, информации об установленных программах и еще множество самых разнообразных параметров и настроек, которые используются операционной системой для работы с железом, разнообразными настройками системы, запуска и управления службами и прочими компонентами.
Реестра был введен в 1992 году в ОС Windows 3.1 и продолжает использоваться во всех, более поздних версиях ОС. Нужно понимать, что реестр представляет собой иерархическую структуру, подобную дереву, которая содержит множество ветвей и ключей. Каждый ключ содержит данные в виде значений. В общем, это довольно важный компонент операционной системы, неправильное использование которого может привести к непредсказуемым последствиям или отказу работы системы. Но, в рамках данной статьи изменять реестр мы не собираемся. А только лишь будем считывать его значения.
Первым скриптом, который мы напишем для получения данных о параметрах ОС и ее оборудовании, это будет скрипт получающий данные из реестра.
Что потребуется?
По большему счету для работы данного скрипта в установке сторонних модулей нет необходимости. Но, если вы хотите сохранить полученные данные в не текстовый файл, то установить сторонний модуль все же придется. В данном скрипте я использую модуль python-docx, который позволит нам сохранить полученные данные в документ Microsoft Word. Поэтому, для его установки нужно написать в терминале команду:
pip install python-docx
Также, в работе данного скрипта я использовал модуль windows-tools, с помощью которого получал данные о ключе активации операционной системы. По большему счету можно было бы обойтись и без него, так как, если мы заглянем внутрь данного модуля, то увидим, что в своей работе он также, как и мы, использует библиотеку winreg, а также дополнительную функцию для дешифровки ключа активации. Для установки данного модуля пишем в терминале команду:
pip install windows-tools
После того, как будут установлены модули, давайте выполним импорт необходимых библиотек в скрипт.
Python:
import time
from datetime import datetime as dt, timedelta
from platform import node
from struct import unpack
from winreg import OpenKeyEx, QueryValueEx, HKEY_LOCAL_MACHINE, QueryInfoKey, EnumKey, KEY_READ
from docx import Document
from windows_tools import product_key
Получение версии операционной системы Windows
Давайте напишем, в рамках данного скрипта, первую функцию, с помощью которой получим версию, сборку, название и ключ активации из реестра. Назовем ее winreg_os() -> dict. Никаких параметров данная функция не принимает, а вот возвращает словарь с полученными значениями в случае, если они были получены.
Создадим пустой словарь win_info, в который будем складывать полученные данные. Для начала получим имя компьютера. Для этого откроем ветку реестра, в котором содержится данное значение: HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\ и прочитаем значение ключа ComputerName.
Затем проделаем то же самое с получением времени последнего выключения компьютера. Для этого открываем ветку реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows и читаем значение ключа ShutdownTime. Так как значение данного ключа содержится в реестре в микросекундах, для начала конвертируем это значение в человекочитаемый формат, а уж только после этого добавим в словарь.
Python:
def winreg_os() -> dict:
win_info = dict()
if comp_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"):
win_info.update({'ComputerName': QueryValueEx(comp_info, 'ComputerName')[0]})
if comp_shutdown := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Windows"):
shutdown_time_bin = QueryValueEx(comp_shutdown, 'ShutdownTime')[0]
shutdown_time = (dt(1601, 1, 1) + timedelta(microseconds=float(unpack("<Q", shutdown_time_bin)[0]) / 10)). \
strftime('%Y-%m-%d %H:%M:%S')
Все остальные значения ключей, которые нам нужно получить, находятся в одной ветке реестра. Поэтому, для начала открываем ее с помощью OpenKeyEx: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion.
Запускаем цикл, в котором будем итерироваться по списку названий ключей реестра. По очереди будем получать значение каждого ключа. Перед тем, как добавить значение в словарь, проверим, не является ли ключ датой установки, так как ее нужно конвертировать в человекочитаемый вид. И добавляем все значения которые получим в словарь.
Python:
if win_ver := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"):
for key in ["ProductName", "EditionID", "DisplayVersion", "CurrentBuild", "UBR", "InstallDate",
"RegisteredOwner"]:
try:
if key == "InstallDate":
win_info.update({key: str(dt.fromtimestamp(QueryValueEx(win_ver, f'{key}')[0]))})
else:
win_info.update({key: QueryValueEx(win_ver, f'{key}')[0]})
except FileNotFoundError:
continue
Двигаемся дальше. И следующее значение, которое нам будет нужно, это временная зона. Хотя, по большому счету оно особой роли ни для чего не играет, но для информации пригодится. Открываем ключ реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation и читаем значение ключа TimeZoneKeyName, после чего добавляем его в словарь.
Python:
if tz_key := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"):
win_info.update({"TimeZone": QueryValueEx(tz_key, 'TimeZoneKeyName')[0]})
Ну и последняя операция, которая, можно сказать, будет некоей вишенкой на данном торте функции )). Получим ключ активации операционной системы. На самом деле, знать ключ активации достаточно важно. И хотя, Microsoft уже перешла на цифровые лицензии, которые подхватываются автоматически при установке ОС, кто знает, может вам понадобиться установить Windows на другое железо, в другой сети. Не думаю, что ключ активации подхватиться с такой же легкостью. Вот потому, знать его желательно.
Поэтому, получаем данное значение с помощью модуля windows_tools и его метода product_key. Данный метод позволяет получить ключ из реестра даже в том случае, если он вшит в BIOS компьютера, так как его значение при установке копируется в реестр. Поэтому, для начала пробуем получить стандартный ключ. Если не получиться, тогда ключ из BIOS. Ну и если все получено, добавляем данные в словарь.
Python:
if pkey := product_key.get_windows_product_key_from_reg():
win_info.update({"ActivateKey": pkey})
elif pkey := product_key.get_windows_product_key_from_wmi():
win_info.update({"ActivateKey": pkey})
else:
win_info.update({"ActivateKey": "No Key"})
return win_info if win_info else False
После того, как будут собраны все необходимые данные, проверяем, содержит ли что-то словарь, который мы создавали в начале. Если да, возвращаем его из функции.
Python:
# Узнаем версию ОС
def winreg_os() -> dict:
win_info = dict()
if comp_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"):
win_info.update({'ComputerName': QueryValueEx(comp_info, 'ComputerName')[0]})
if comp_shutdown := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Windows"):
shutdown_time_bin = QueryValueEx(comp_shutdown, 'ShutdownTime')[0]
shutdown_time = (dt(1601, 1, 1) + timedelta(microseconds=float(unpack("<Q", shutdown_time_bin)[0]) / 10)). \
strftime('%Y-%m-%d %H:%M:%S')
win_info.update({'ShutdownTime': shutdown_time})
if win_ver := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"):
for key in ["ProductName", "EditionID", "DisplayVersion", "CurrentBuild", "UBR", "InstallDate",
"RegisteredOwner"]:
try:
if key == "InstallDate":
win_info.update({key: str(dt.fromtimestamp(QueryValueEx(win_ver, f'{key}')[0]))})
else:
win_info.update({key: QueryValueEx(win_ver, f'{key}')[0]})
except FileNotFoundError:
continue
if tz_key := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"):
win_info.update({"TimeZone": QueryValueEx(tz_key, 'TimeZoneKeyName')[0]})
if pkey := product_key.get_windows_product_key_from_reg():
win_info.update({"ActivateKey": pkey})
elif pkey := product_key.get_windows_product_key_from_wmi():
win_info.update({"ActivateKey": pkey})
else:
win_info.update({"ActivateKey": "No Key"})
return win_info if win_info else False
Получение информации о BIOS
Перейдем к следующей части нашего «
Ссылка скрыта от гостей
». После некоторого гугления или яндексения… даже не знаю, как правильно сказать… в общем, после поиска информации в интернете и ковыряния в реестре, я обнаружил, что информация о BIOS храниться в следующих ветках: HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System; HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS. И в них уже содержаться все необходимые нам ключи, значение которых необходимо прочитать.Создадим функцию bios_winreg() -> dict, которая будет возвращать словарь с полученными значениями. Для начала, определим словарь, куда будем эти значения складывать. Затем подключимся к первой ветке реестра. Из нее мы получим версию BIOS и добавим ее в словарь.
Python:
def bios_winreg() -> dict:
md_dict = dict()
if sbv := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System"):
md_dict.update({"SystemBiosVersion": QueryValueEx(sbv, "SystemBiosVersion")[0][0]})
Создаем цикл, который будет итерироваться по списку ключей, после чего выполняем подключение ко второй ветке реестра, и по очереди забираем значения из ключей, подставляемых с помощью цикла. Добавляем их также в созданный ранее словарь. Обработаем исключение, на всякий случай, если не будет найдено нужного ключа. В этом случае мы просто продолжаем цикл и перебираем ключи до конца.
Python:
for key in ["BIOSVendor", "BIOSVersion", "BIOSReleaseDate"]:
if bios := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\BIOS"):
try:
md_dict.update({key: QueryValueEx(bios, key)[0]})
except FileNotFoundError:
continue
else:
return False
return md_dict if md_dict else False
После того, как все значения получены, возвращаем словарь из функции, если он не пуст.
Python:
# BIOS
def bios_winreg() -> dict:
md_dict = dict()
if sbv := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System"):
md_dict.update({"SystemBiosVersion": QueryValueEx(sbv, "SystemBiosVersion")[0][0]})
for key in ["BIOSVendor", "BIOSVersion", "BIOSReleaseDate"]:
if bios := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\BIOS"):
try:
md_dict.update({key: QueryValueEx(bios, key)[0]})
except FileNotFoundError:
continue
else:
return False
return md_dict if md_dict else False
Получение информации о материнской плате
Следующая функция, которую мы создадим: motherboard_winreg() -> (dict, bool), будет получать из реестра информацию о материнской плате и возвращать словарь с полученными значениями. Информация о материнской плате содержится в ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SystemInformation. Поэтому, для начала создаем словарь, в который будем складывать полученные значения, а после подключимся к этой ветке реестра. Заберем информацию о производителе и названии материнской платы.
Python:
# Материнская плата
def motherboard_winreg() -> (dict, bool):
md_dict = dict()
if mb_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\SystemInformation"):
md_dict.update({'SystemManufacturer': QueryValueEx(mb_info, 'SystemManufacturer')[0]})
md_dict.update({'SystemProductName': QueryValueEx(mb_info, 'SystemProductName')[0]})
return md_dict if md_dict else False
return False
После того, как вся нужная информация будет получена, вернем словарь со значениями, если он не пуст из функции.
Получение информации о процессоре
Теперь перейдем к получению информации о процессоре. Данная информация содержится в ветке реестра: HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor. В данной ветке находятся ключи, каждый из которых содержит информацию о ядре процессора. Независимо от того, физическое оно или логическое. Для примера, вот скриншот, как это может выглядеть:
Таким образом, узнав количество данных веток, мы сможем понять, сколько ядер у нашего процессора. Поэтому, подключаемся к ветке и с помощью QueryInfoKey забираем информацию о подкатегориях. Добавляем в словарь значение о количестве ядер.
Python:
def cpu_winreg():
proc_info = dict()
loc = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor"
with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
if QueryInfoKey(h_apps)[0]:
proc_info.update({"CoreCount": QueryInfoKey(h_apps)[0]})
Если мы откроем каждую из веток, то увидим, что информация о каждом ядре, по сути, является дубликатом информации самого первого ядра. За исключением некоторых параметров, таких как идентификаторы. Но получать мы их не будем, а ограничимся только общей информацией. Таким образом, перебирать каждую ветку не имеет, в нашем случае, особого смысла. Достаточно получить информацию из одной. Поэтому, подключаемся к самой первой ветке имеющей индекс 0. После чего забираем оттуда нужные параметры, такие как название процессора, идентификатор, производитель и частоту.
Python:
try:
core = OpenKeyEx(h_apps, EnumKey(h_apps, 0))
proc_info.update({
"ProcessorNameString": QueryValueEx(core, 'ProcessorNameString')[0].strip(),
"Identifier": QueryValueEx(core, 'Identifier')[0].strip(),
"VendorIdentifier": QueryValueEx(core, 'VendorIdentifier')[0].strip(),
"~MHz": QueryValueEx(core, '~MHz')[0]
})
except FileNotFoundError:
return False
return proc_info if proc_info else False
После того, как нужные параметры получены и добавлены в словарь, возвращаем его из функции. Если он, конечно же, не пуст.
Python:
# CPU
def cpu_winreg():
proc_info = dict()
loc = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor"
with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
if QueryInfoKey(h_apps)[0]:
proc_info.update({"CoreCount": QueryInfoKey(h_apps)[0]})
try:
core = OpenKeyEx(h_apps, EnumKey(h_apps, 0))
proc_info.update({
"ProcessorNameString": QueryValueEx(core, 'ProcessorNameString')[0].strip(),
"Identifier": QueryValueEx(core, 'Identifier')[0].strip(),
"VendorIdentifier": QueryValueEx(core, 'VendorIdentifier')[0].strip(),
"~MHz": QueryValueEx(core, '~MHz')[0]
})
except FileNotFoundError:
return False
return proc_info if proc_info else False
Получение информации о GPU
С получением информации о GPU из реестра, немного неоднозначная ситуация. Дело в том, что в системе нет стандартной ветки, откуда можно было бы забрать все по умолчанию. При установке драйверов на видеокарту конкретного производителя в реестре создается отдельная ветка, в которой и содержится нужная информация. Но, так как у каждого производителя и ветка будет своей, протестировать получение данной информации из реестра у меня не представляется возможным. Так как в наличии просто нет необходимого оборудования. Тем не менее, мы получим из реестра, в данном случае, хотя бы модель.
Создадим функцию gpu_winreg(), которая будет получать данные о модели и возвращать словарь с полученным значением. Информация о модели GPU содержится в следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000. Подключаемся к данной ветке. Забираем значение ключа DriverDesc, добавляем его в словарь и возвращаем из функции.
Python:
# GPU
def gpu_winreg():
loc = r'SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000'
key = OpenKeyEx(HKEY_LOCAL_MACHINE, loc, 0, KEY_READ)
value = {"Name": QueryValueEx(key, "DriverDesc")[0]}
return value if value else False
Получение информации о HDD/SSD
Создадим функцию hdd_ssd_winreg(). С ее помощью мы будем получать данные о всех жестких дисках, которые установлены в компьютере. Информация об этом находится в ветке реестра: HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi. Подключаемся к ней.
Python:
hdd_ssd_info = dict()
loc = "HARDWARE\\DEVICEMAP\\Scsi"
with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
Следующее, что нам понадобится, так как мы не знаем, сколько дисков установлено в системе, узнать, сколько подразделов содержится в данном разделе реестра. Так же, как мы делали, когда получали информацию о процессоре и количестве его ядер.
Вот как выглядит соответствующая ветка реестра:
Как вы видите, далеко не каждый раздел содержит информацию. Более того, количество разделов будет равно количеству портов для подключения на плате. А значит, информация о диске будет содержаться только в том разделе, соответствующем порту, к которому диск подключен. Следовательно, нам нужно перебрать каждый раздел и подраздел, и получить значения ключей из каждого из них. А также обработать исключение, когда мы будем натыкаться на раздел, в котором нет информации о подключенном диске.
Для начала, создаем цикл, в котором будем итерироваться по диапазону соответствующему количеству разделов. Ну, а далее, будем в каждом из циклов подключаться все глубже и глубже по дереву, пока не достигнем конечной точки. Откуда и будем забирать значения ключей.
Python:
# HDD, SSD
def hdd_ssd_winreg():
hdd_ssd_info = dict()
loc = "HARDWARE\\DEVICEMAP\\Scsi"
with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
for idx in range(QueryInfoKey(h_apps)[0]):
try:
scsi_port = OpenKeyEx(h_apps, EnumKey(h_apps, idx))
for ids in range(QueryInfoKey(scsi_port)[0]):
scsi_bus = OpenKeyEx(scsi_port, EnumKey(scsi_port, ids))
for idb in range(QueryInfoKey(scsi_bus)[0]):
target = OpenKeyEx(scsi_bus, EnumKey(scsi_bus, idb))
for idc in range(QueryInfoKey(target)[0]):
log_unit = OpenKeyEx(target, EnumKey(target, idc))
hdd_ssd_info.update({QueryValueEx(log_unit, 'SerialNumber')[0].strip(): {
"Vendor": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[0].strip(),
"Model": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[1].strip(),
"SerialNumber": QueryValueEx(log_unit, 'SerialNumber')[0].strip()
}})
except FileNotFoundError:
continue
return hdd_ssd_info if hdd_ssd_info else False
После того, как информация будет собрана, возвращаем ее из функции. Хочу еще обратить ваше внимание на то, что, так как ключи, содержащие значения неуникальны, их нужно поместить в качестве словаря в ключ, содержащий уникальное значение о диске. Или номер, или название. Это не имеет особого значения. Главное, чтобы создаваемый ключ больше не повторялся.
Получение информации о CD/DVD-ROM
Вы можете сказать, что получение данной информации не имеет большого значения, так как данные устройства постепенно выходят из обихода и их уже можно встретить только на старых машинах. Так-то оно так, вот только не совсем. Дело в том, что с помощью данной информации можно узнать, используется ли в системе USB-модем, так как у каждого современного модема есть раздел, который определяется системой как внешний привод. А соответственно, узнав название и модель, мы получим представление, какой модем используется.
Создадим функцию cdrom_winreg(), которая будет возвращать словарь с полученными значениями. Информация о приводах содержится в следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\cdrom\Enum. Уже, подключившись к данной ветке, мы будем получать из нее значения ключей.
Создадим словарь, куда будем складывать полученные значения. В данной ветке есть хороший ключ Count, получив значение которого мы узнаем, сколько приводов установлено в системе. Тем более, что проверив это значение и поняв, что оно равно нулю, мы можем вовсе не итерироваться в поисках приводов, а просто вернуть False.
Python:
def cdrom_winreg():
disks = dict()
loc = r"SYSTEM\CurrentControlSet\Services\cdrom\Enum"
cd_rom = OpenKeyEx(HKEY_LOCAL_MACHINE, loc)
count = QueryValueEx(cd_rom, 'Count')[0]
if count > 0:
Теперь, если значение больше нуля, итерируемся по диапазону значений равному количеству приводов. Собираем значения, которые присваиваем в переменные. Сразу же обработаем возможные исключения, если полученный ключ, к примеру, не сможет быть обработан. И уже после этого добавляем полученные значения в словарь, который возвращаем из функции.
Python:
# CD-ROM
def cdrom_winreg():
disks = dict()
loc = r"SYSTEM\CurrentControlSet\Services\cdrom\Enum"
cd_rom = OpenKeyEx(HKEY_LOCAL_MACHINE, loc)
count = QueryValueEx(cd_rom, 'Count')[0]
if count > 0:
for num in range(count):
try:
ven = QueryValueEx(cd_rom, f'{num}')[0].split("&")[1].split("_")[1]
except Exception:
ven = None
try:
prod = " ".join(QueryValueEx(cd_rom, f'{num}')[0].split("&")[2].split("_")[1:])
except Exception:
prod = None
try:
rev = QueryValueEx(cd_rom, f'{num}')[0].split("&")[3].split("_")[1].split("\\")[0]
except Exception:
rev = None
try:
serial = QueryValueEx(cd_rom, f'{num}')[0].split("&")[6]
except Exception:
serial = None
disks.update({num: {
"Vendor": ven,
"Product": prod,
"Revision": rev,
"SerialNumber": serial
}})
return disks if disks else False
return False
Получаем информацию о сетевых интерфейсах
Для получения данной информации придется несколько порыскать по веткам реестра. Собрать значения в одном месте, потом на основании собранных данных получить в другом. Нужно оговориться, что получать информацию мы будем только о физических сетевых интерфейсах. Все остальные, то есть виртуальные, нас будут волновать в последнюю очередь.
Создадим функцию nic_winreg(). На выходе она будет возвращать словарь с полученными данными. Для начала проверим ветку реестра: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards. Информации здесь немного, но, мы получим отсюда имя сетевого адаптера и его идентификатор в системе. Сразу же создадим словарь, куда добавим полученные значения. Идентификатор будет служить в качестве ключа для всей остальной информации об интерфейсах. Также создадим список, куда сложим идентификаторы. Они понадобятся нам в дальнейшей работе функции.
Python:
def nic_winreg():
nic = dict()
loc = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards'
desc = []
if adapt_name := OpenKeyEx(HKEY_LOCAL_MACHINE, loc):
for idx in range(QueryInfoKey(adapt_name)[0]):
adapter = OpenKeyEx(adapt_name, EnumKey(adapt_name, idx))
nic[QueryValueEx(adapter, 'ServiceName')[0]] = dict()
nic[QueryValueEx(adapter, 'ServiceName')[0]].update({
"Description": QueryValueEx(adapter, 'Description')[0]
})
desc.append(QueryValueEx(adapter, 'Description')[0])
Подключаемся к следующей ветке реестра: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}. Здесь содержаться подразделы, в которых содержится чуть больше информации. Но, для начала, нужно понять, из какого подраздела нужно получать информацию, так как нам нужно будет забраться чуть-чуть поглубже.
Как вы помните, ранее мы создавали список с идентификаторами. Поэтому, перебираем каждый подраздел, забираем из него значение NetCfgInstanceId и сравниваем со списком. Если данное значение содержится в списке, создаем новый список и добавляем в него полученные значения.
Python:
loc_adapt = r'SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}'
cfg = []
if adapt := OpenKeyEx(HKEY_LOCAL_MACHINE, loc_adapt):
for idc in range(QueryInfoKey(adapt)[0]):
try:
adpt = OpenKeyEx(adapt, EnumKey(adapt, idc))
if QueryValueEx(adpt, 'DriverDesc')[0] in desc:
nic[QueryValueEx(adpt, 'NetCfgInstanceId')[0]].update({
"Description": QueryValueEx(adpt, 'DriverDesc')[0]
})
cfg.append(QueryValueEx(adpt, 'NetCfgInstanceId')[0])
except (FileNotFoundError, PermissionError):
continue
Теперь двигаемся глубже. Имя адаптера мы получили, необходимо теперь получить такие данные, как шлюз по умолчанию, ip-адрес. Переходим в следующую ветку реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces и забираем оттуда данные, которые добавляем в словарь.
Python:
if inter_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, inter):
for idb in range(QueryInfoKey(inter_cfg)[0]):
if EnumKey(inter_cfg, idb).upper() in cfg:
nic[EnumKey(inter_cfg, idb).upper()].update({
"NetCfgInstanceId": EnumKey(inter_cfg, idb).upper()})
intr = OpenKeyEx(inter_cfg, EnumKey(inter_cfg, idb))
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpDefaultGateway": QueryValueEx(intr, 'DhcpDefaultGateway')[0]})
except FileNotFoundError:
pass
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
except FileNotFoundError:
pass
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
except FileNotFoundError:
pass
И еще один раздел реестра, куда нам нужно заглянуть: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}. В данном разделе содержится имя адаптера в системе. Помните, вот это вот: Ethernet, Ethernet2 и т.д. Вышеуказанная ветка – это только первая часть пути. Далее, нужно указать идентификатор адаптера и добавить слово «Connection». Чтобы получилось, для примера, как-то так: HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{1ECDE6DD-4799-4A4B-AEDB-F2C8C2AD528F}\Connection.
Затем подключаемся к данной ветке и уже оттуда забираем значение ключа Name.
Python:
netw = r'SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}' + '\\' + \
EnumKey(inter_cfg, idb).upper() + '\\' + 'Connection'
if netw_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, netw):
nic[EnumKey(inter_cfg, idb).upper()].update({
"Name": QueryValueEx(netw_cfg, 'Name')[0]})
return nic if nic else False
После того, как данные будут получены, возвращаем словарь с полученными значениями из функции.
Python:
# Network Interface
def nic_winreg():
nic = dict()
loc = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards'
desc = []
if adapt_name := OpenKeyEx(HKEY_LOCAL_MACHINE, loc):
for idx in range(QueryInfoKey(adapt_name)[0]):
adapter = OpenKeyEx(adapt_name, EnumKey(adapt_name, idx))
nic[QueryValueEx(adapter, 'ServiceName')[0]] = dict()
nic[QueryValueEx(adapter, 'ServiceName')[0]].update({
"Description": QueryValueEx(adapter, 'Description')[0]
})
desc.append(QueryValueEx(adapter, 'Description')[0])
loc_adapt = r'SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}'
cfg = []
if adapt := OpenKeyEx(HKEY_LOCAL_MACHINE, loc_adapt):
for idc in range(QueryInfoKey(adapt)[0]):
try:
adpt = OpenKeyEx(adapt, EnumKey(adapt, idc))
if QueryValueEx(adpt, 'DriverDesc')[0] in desc:
nic[QueryValueEx(adpt, 'NetCfgInstanceId')[0]].update({
"Description": QueryValueEx(adpt, 'DriverDesc')[0]
})
cfg.append(QueryValueEx(adpt, 'NetCfgInstanceId')[0])
except (FileNotFoundError, PermissionError):
continue
inter = r'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces'
if inter_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, inter):
for idb in range(QueryInfoKey(inter_cfg)[0]):
if EnumKey(inter_cfg, idb).upper() in cfg:
nic[EnumKey(inter_cfg, idb).upper()].update({
"NetCfgInstanceId": EnumKey(inter_cfg, idb).upper()})
intr = OpenKeyEx(inter_cfg, EnumKey(inter_cfg, idb))
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpDefaultGateway": QueryValueEx(intr, 'DhcpDefaultGateway')[0]})
except FileNotFoundError:
pass
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
except FileNotFoundError:
pass
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
except FileNotFoundError:
pass
netw = r'SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}' + '\\' + \
EnumKey(inter_cfg, idb).upper() + '\\' + 'Connection'
if netw_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, netw):
nic[EnumKey(inter_cfg, idb).upper()].update({
"Name": QueryValueEx(netw_cfg, 'Name')[0]})
return nic if nic else False
Что же, думаю, что на этот раз, достаточно. В довесок к предыдущей информации можно было бы получить данные обо всех установленных программах. Но найти об этом информацию и реализовать нужную функцию самостоятельно не особо сложно. А мы, тем временем, завершим с получением информации и перейдем к обработке полученных данных, выводу информации для пользователя в терминал и сохранению данных в документ Microsoft Word.
Обрабатываем полученные данные
Для того, чтобы обработать полученные данные я создал функцию print_wmic(part, dict_info). На вход она получает part, содержащий текст о той информации, которую нужно вывести в терминал. dict_info – собственно, сам словарь с полученными данными.
Для того чтобы не присваивать значения вручную, да и не присвоишь их при обработке в циклах множества словарей, я создал словарь, в котором содержится ключ, соответствующий названию параметра и его расшифровка на русском. Так что, большую часть функции занимает именно он.
Вот он собственно и сам:
Python:
def print_wmic(part, dict_info, num):
global wmic_info
synonyms = {"ComputerName": "Имя компьютера", "Caption": "Название", "InstallDate": "Дата установки",
"LastBootUpTime": "Время последней загрузки", "Version": "Версия",
"WindowsDirectory": "Директория Windows", "TimeZone": "Часовой пояс", "UserName": "Имя пользователя",
"Manufacturer": "Производитель", "Name": "Название", "Product": "Изделие",
"MaxClockSpeed": "Максимальная тактовая частота", "SocketDesignation": "Название сокета",
"NumberOfPhysicalProcessors": "Количество физических процессоров", "VideoProcessor": "Видеопроцессор",
"NumberOfLogicalProcessors": "Количество логических процессоров", "Capacity": "Емкость",
"AdapterRAM": "Оперативная память адаптера", "CurrentRefreshRate": "Текущая частота обновления",
"Resolution": "Разрешение", "TotalPhysicalMemory": "Общий объем физической памяти", "Socket": "Сокет",
"ConfiguredClockSpeed": "Настроенная тактовая частота", "PartNumber": "Номер партии",
"SerialNumber": "Серийный номер", "DeviceID": "Идентификатор устройства", "MediaType": "Тип носителя",
"FirmwareRevision": "Ревизия прошивки", "Partitions": "Разделы", "Size": "Объем", "Drive": "Диск",
"VolumeName": "Имя тома", "VolumeSerialNumber": "Серийный номер тома", "MACAddress": "MAC-адрес",
"NetConnectionID": "Идентификатор сетевого подключения", "DHCPServer": "DHCP-сервер",
"IPAddress": "IP-адрес", "BuildNumber": "Номер сборки", "ID": "Идентификатор", "Status": "Статус",
"DefaultIPGateway": "IP-адрес шлюза по-умолчанию", "DNSHostName": "DNS Имя хоста",
"IPv4Address": "IPv4-адрес", "IPv6Address": "IPv6-адрес", "IPSubnet": "Маска подсети",
"ServiceName": "Название службы", "CurrentBuild": "Текущая сборка", "UBR": "Номер версии",
"RegisteredOwner": "Имя пользователя", "ActivateKey": "Ключ активации",
"SystemBiosVersion": "Версия Bios системы", "BIOSVendor": "Производитель", "BIOSVersion": "Версия",
"BIOSReleaseDate": "Дата выпуска релиза", "ShutdownTime": "Время выключения", "ProductName": "Название",
"EditionID": "Идентификатор редакции", "DisplayVersion": "Версия для отображения",
"SystemManufacturer": "Производитель", "SystemProductName": "Название сокета",
"CoreCount": "Количество ядер", "ProcessorNameString": "Название", "Identifier": "Идентификатор",
"VendorIdentifier": "Производитель", "~MHz": "Тактовая частота", "Vendor": "Производитель",
"Model": "Модель", "Revision": "Ревизия", "Description": "Название",
"NetCfgInstanceId": "Идентификатор", "DhcpDefaultGateway": "Шлюз по-умолчанию",
"DhcpIPAddress": "IP-адрес"}
А теперь, функция печати. Для того чтобы сохранить полученные данные в документ, я создал глобальную переменную wmic_info = "", куда и буду помещать полученные данные. То есть, по сути, там будет содержаться большая строка разделенная переносами.
Ну, а дальше все просто. Проверяю тип ключа. Если это словарь, запускаю еще один цикл и добавляю информацию в созданную глобальную переменную. Если не словарь, то добавляю полученные значения.
Возвращать здесь ничего не нужно.
Python:
part += f'{"-" * 50}\n'
for key in dict_info:
if type(dict_info[key]) == dict:
for item in dict_info[key]:
part += f'{synonyms[item]}: {dict_info[key][item]}\n'
part += "\n"
else:
part += f'{synonyms[key]}: {dict_info[key]}\n'
print(part)
wmic_info += f'{part}\n'
Python:
def print_wmic(part, dict_info):
global wmic_info
synonyms = {"ComputerName": "Имя компьютера", "Caption": "Название", "InstallDate": "Дата установки",
"LastBootUpTime": "Время последней загрузки", "Version": "Версия",
"WindowsDirectory": "Директория Windows", "TimeZone": "Часовой пояс", "UserName": "Имя пользователя",
"Manufacturer": "Производитель", "Name": "Название", "Product": "Изделие",
"MaxClockSpeed": "Максимальная тактовая частота", "SocketDesignation": "Название сокета",
"NumberOfPhysicalProcessors": "Количество физических процессоров", "VideoProcessor": "Видеопроцессор",
"NumberOfLogicalProcessors": "Количество логических процессоров", "Capacity": "Емкость",
"AdapterRAM": "Оперативная память адаптера", "CurrentRefreshRate": "Текущая частота обновления",
"Resolution": "Разрешение", "TotalPhysicalMemory": "Общий объем физической памяти", "Socket": "Сокет",
"ConfiguredClockSpeed": "Настроенная тактовая частота", "PartNumber": "Номер партии",
"SerialNumber": "Серийный номер", "DeviceID": "Идентификатор устройства", "MediaType": "Тип носителя",
"FirmwareRevision": "Ревизия прошивки", "Partitions": "Разделы", "Size": "Объем", "Drive": "Диск",
"VolumeName": "Имя тома", "VolumeSerialNumber": "Серийный номер тома", "MACAddress": "MAC-адрес",
"NetConnectionID": "Идентификатор сетевого подключения", "DHCPServer": "DHCP-сервер",
"IPAddress": "IP-адрес", "BuildNumber": "Номер сборки", "ID": "Идентификатор", "Status": "Статус",
"DefaultIPGateway": "IP-адрес шлюза по-умолчанию", "DNSHostName": "DNS Имя хоста",
"IPv4Address": "IPv4-адрес", "IPv6Address": "IPv6-адрес", "IPSubnet": "Маска подсети",
"ServiceName": "Название службы", "CurrentBuild": "Текущая сборка", "UBR": "Номер версии",
"RegisteredOwner": "Имя пользователя", "ActivateKey": "Ключ активации",
"SystemBiosVersion": "Версия Bios системы", "BIOSVendor": "Производитель", "BIOSVersion": "Версия",
"BIOSReleaseDate": "Дата выпуска релиза", "ShutdownTime": "Время выключения", "ProductName": "Название",
"EditionID": "Идентификатор редакции", "DisplayVersion": "Версия для отображения",
"SystemManufacturer": "Производитель", "SystemProductName": "Название сокета",
"CoreCount": "Количество ядер", "ProcessorNameString": "Название", "Identifier": "Идентификатор",
"VendorIdentifier": "Производитель", "~MHz": "Тактовая частота", "Vendor": "Производитель",
"Model": "Модель", "Revision": "Ревизия", "Description": "Название",
"NetCfgInstanceId": "Идентификатор", "DhcpDefaultGateway": "Шлюз по-умолчанию",
"DhcpIPAddress": "IP-адрес"}
part += f'{"-" * 50}\n'
for key in dict_info:
if type(dict_info[key]) == dict:
for item in dict_info[key]:
part += f'{synonyms[item]}: {dict_info[key][item]}\n'
part += "\n"
else:
part += f'{synonyms[key]}: {dict_info[key]}\n'
print(part)
wmic_info += f'{part}\n'
Функция main
В данной функции мы будем проверять возвращаемые значения на истинности и в соответствии с этим запускать или не запускать функцию обработки. Перед этим добавляем с документ создаваемый с помощью python-docx заголовок. Ну и в конце добавляем в документ параграф в котором будет текст с полученными данными. Сохраняем документ с именем компьютера. И выводим в терминал время работы скрипта.
Python:
def main():
global wmic_info
t = time.monotonic()
document = Document()
document.add_heading(f'Сводная информация о компьютере: {node()}')
if os_info := winreg_os():
print_wmic("Информация об операционной системе\n", os_info)
if bios_info := bios_winreg():
print_wmic("Информация о BIOS\n", bios_info)
if mb_info := motherboard_winreg():
print_wmic("Информация о материнской плате\n", mb_info)
if cpu_info := cpu_winreg():
print_wmic("Информация о процессоре\n", cpu_info)
if gpu_info := gpu_winreg():
print_wmic("Информация о видеокарте\n", gpu_info)
if drive_info := hdd_ssd_winreg():
print_wmic("Информация о HDD и SSD\n", drive_info)
if cd_rom_info := cdrom_winreg():
print_wmic("Информация о CD/DVD-ROM\n", cd_rom_info)
if nic_info := nic_winreg():
print_wmic("Информация о физических сетевых интерфейсах\n", nic_info)
document.add_paragraph(wmic_info)
document.save(f'{node()}.docx')
print(f"Собранная информация сохранена в файл: {node()}.docx")
print(f'\nВремя работы скрипта: {time.monotonic() - t} с.')
if __name__ == "__main__":
main()
Что же. На этом мы завершим создание скрипта по получению данных из реестра. Хочу сказать дополнительно, что к большому сожалению не нашел информации о том, как получить, хотя бы, общий объем оперативной памяти из реестра. Не знаю, может быть, плохо искал. Если кто-то знает, буду благодарен за подсказку.
А теперь давайте посмотрим на время его работы:
Что же, достаточно шустро. Но, это только первая часть из небольшого цикла о получении информации об ос и оборудовании различными способами, а также сравнении времени работы каждого способа. Продолжение в следующей статье. Далее мы рассмотрим способы получения информации с помощью subprocess.check_out, который будет парсить вывод утилиты wmic, запускаемой с различными параметрами. А в конце статьи также посмотрим на скорость работы полученной функции.
Python:
# pip install python-docx
# pip install windows-tools
import time
from datetime import datetime as dt, timedelta
from platform import node
from struct import unpack
from winreg import OpenKeyEx, QueryValueEx, HKEY_LOCAL_MACHINE, QueryInfoKey, EnumKey, KEY_READ
from docx import Document
from windows_tools import product_key
# Узнаем версию ОС
def winreg_os() -> dict:
win_info = dict()
if comp_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"):
win_info.update({'ComputerName': QueryValueEx(comp_info, 'ComputerName')[0]})
if comp_shutdown := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Windows"):
shutdown_time_bin = QueryValueEx(comp_shutdown, 'ShutdownTime')[0]
shutdown_time = (dt(1601, 1, 1) + timedelta(microseconds=float(unpack("<Q", shutdown_time_bin)[0]) / 10)). \
strftime('%Y-%m-%d %H:%M:%S')
win_info.update({'ShutdownTime': shutdown_time})
if win_ver := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"):
for key in ["ProductName", "EditionID", "DisplayVersion", "CurrentBuild", "UBR", "InstallDate",
"RegisteredOwner"]:
try:
if key == "InstallDate":
win_info.update({key: str(dt.fromtimestamp(QueryValueEx(win_ver, f'{key}')[0]))})
else:
win_info.update({key: QueryValueEx(win_ver, f'{key}')[0]})
except FileNotFoundError:
continue
if tz_key := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"):
win_info.update({"TimeZone": QueryValueEx(tz_key, 'TimeZoneKeyName')[0]})
if pkey := product_key.get_windows_product_key_from_reg():
win_info.update({"ActivateKey": pkey})
elif pkey := product_key.get_windows_product_key_from_wmi():
win_info.update({"ActivateKey": pkey})
else:
win_info.update({"ActivateKey": "No Key"})
return win_info if win_info else False
# BIOS
def bios_winreg() -> dict:
md_dict = dict()
if sbv := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System"):
md_dict.update({"SystemBiosVersion": QueryValueEx(sbv, "SystemBiosVersion")[0][0]})
for key in ["BIOSVendor", "BIOSVersion", "BIOSReleaseDate"]:
if bios := OpenKeyEx(HKEY_LOCAL_MACHINE, r"HARDWARE\DESCRIPTION\System\BIOS"):
try:
md_dict.update({key: QueryValueEx(bios, key)[0]})
except FileNotFoundError:
continue
else:
return False
return md_dict if md_dict else False
# Материнская плата
def motherboard_winreg() -> (dict, bool):
md_dict = dict()
if mb_info := OpenKeyEx(HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\SystemInformation"):
md_dict.update({'SystemManufacturer': QueryValueEx(mb_info, 'SystemManufacturer')[0]})
md_dict.update({'SystemProductName': QueryValueEx(mb_info, 'SystemProductName')[0]})
return md_dict if md_dict else False
return False
# CPU
def cpu_winreg():
proc_info = dict()
loc = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor"
with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
if QueryInfoKey(h_apps)[0]:
proc_info.update({"CoreCount": QueryInfoKey(h_apps)[0]})
try:
core = OpenKeyEx(h_apps, EnumKey(h_apps, 0))
proc_info.update({
"ProcessorNameString": QueryValueEx(core, 'ProcessorNameString')[0].strip(),
"Identifier": QueryValueEx(core, 'Identifier')[0].strip(),
"VendorIdentifier": QueryValueEx(core, 'VendorIdentifier')[0].strip(),
"~MHz": QueryValueEx(core, '~MHz')[0]
})
except FileNotFoundError:
return False
return proc_info if proc_info else False
# GPU
def gpu_winreg():
loc = r'SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000'
key = OpenKeyEx(HKEY_LOCAL_MACHINE, loc, 0, KEY_READ)
value = {"Name": QueryValueEx(key, "DriverDesc")[0]}
return value if value else False
# HDD, SSD
def hdd_ssd_winreg():
hdd_ssd_info = dict()
loc = "HARDWARE\\DEVICEMAP\\Scsi"
with OpenKeyEx(HKEY_LOCAL_MACHINE, loc) as h_apps:
for idx in range(QueryInfoKey(h_apps)[0]):
try:
scsi_port = OpenKeyEx(h_apps, EnumKey(h_apps, idx))
for ids in range(QueryInfoKey(scsi_port)[0]):
scsi_bus = OpenKeyEx(scsi_port, EnumKey(scsi_port, ids))
for idb in range(QueryInfoKey(scsi_bus)[0]):
target = OpenKeyEx(scsi_bus, EnumKey(scsi_bus, idb))
for idc in range(QueryInfoKey(target)[0]):
log_unit = OpenKeyEx(target, EnumKey(target, idc))
hdd_ssd_info.update({QueryValueEx(log_unit, 'SerialNumber')[0].strip(): {
"Vendor": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[0].strip(),
"Model": QueryValueEx(log_unit, 'Identifier')[0].strip().split()[1].strip(),
"SerialNumber": QueryValueEx(log_unit, 'SerialNumber')[0].strip()
}})
except FileNotFoundError:
continue
return hdd_ssd_info if hdd_ssd_info else False
# CD-ROM
def cdrom_winreg():
disks = dict()
loc = r"SYSTEM\CurrentControlSet\Services\cdrom\Enum"
cd_rom = OpenKeyEx(HKEY_LOCAL_MACHINE, loc)
count = QueryValueEx(cd_rom, 'Count')[0]
if count > 0:
for num in range(count):
try:
ven = QueryValueEx(cd_rom, f'{num}')[0].split("&")[1].split("_")[1]
except Exception:
ven = None
try:
prod = " ".join(QueryValueEx(cd_rom, f'{num}')[0].split("&")[2].split("_")[1:])
except Exception:
prod = None
try:
rev = QueryValueEx(cd_rom, f'{num}')[0].split("&")[3].split("_")[1].split("\\")[0]
except Exception:
rev = None
try:
serial = QueryValueEx(cd_rom, f'{num}')[0].split("&")[6]
except Exception:
serial = None
disks.update({num: {
"Vendor": ven,
"Product": prod,
"Revision": rev,
"SerialNumber": serial
}})
return disks if disks else False
return False
# Network Interface
def nic_winreg():
nic = dict()
loc = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards'
desc = []
if adapt_name := OpenKeyEx(HKEY_LOCAL_MACHINE, loc):
for idx in range(QueryInfoKey(adapt_name)[0]):
adapter = OpenKeyEx(adapt_name, EnumKey(adapt_name, idx))
nic[QueryValueEx(adapter, 'ServiceName')[0]] = dict()
nic[QueryValueEx(adapter, 'ServiceName')[0]].update({
"Description": QueryValueEx(adapter, 'Description')[0]
})
desc.append(QueryValueEx(adapter, 'Description')[0])
loc_adapt = r'SYSTEM\ControlSet001\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}'
cfg = []
if adapt := OpenKeyEx(HKEY_LOCAL_MACHINE, loc_adapt):
for idc in range(QueryInfoKey(adapt)[0]):
try:
adpt = OpenKeyEx(adapt, EnumKey(adapt, idc))
if QueryValueEx(adpt, 'DriverDesc')[0] in desc:
nic[QueryValueEx(adpt, 'NetCfgInstanceId')[0]].update({
"Description": QueryValueEx(adpt, 'DriverDesc')[0]
})
cfg.append(QueryValueEx(adpt, 'NetCfgInstanceId')[0])
except (FileNotFoundError, PermissionError):
continue
inter = r'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces'
if inter_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, inter):
for idb in range(QueryInfoKey(inter_cfg)[0]):
if EnumKey(inter_cfg, idb).upper() in cfg:
nic[EnumKey(inter_cfg, idb).upper()].update({
"NetCfgInstanceId": EnumKey(inter_cfg, idb).upper()})
intr = OpenKeyEx(inter_cfg, EnumKey(inter_cfg, idb))
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpDefaultGateway": QueryValueEx(intr, 'DhcpDefaultGateway')[0]})
except FileNotFoundError:
pass
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
except FileNotFoundError:
pass
try:
nic[EnumKey(inter_cfg, idb).upper()].update({
"DhcpIPAddress": QueryValueEx(intr, 'DhcpIPAddress')[0]})
except FileNotFoundError:
pass
netw = r'SYSTEM\ControlSet001\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}' + '\\' + \
EnumKey(inter_cfg, idb).upper() + '\\' + 'Connection'
if netw_cfg := OpenKeyEx(HKEY_LOCAL_MACHINE, netw):
nic[EnumKey(inter_cfg, idb).upper()].update({
"Name": QueryValueEx(netw_cfg, 'Name')[0]})
return nic if nic else False
wmic_info = ""
def print_wmic(part, dict_info):
global wmic_info
synonyms = {"ComputerName": "Имя компьютера", "Caption": "Название", "InstallDate": "Дата установки",
"LastBootUpTime": "Время последней загрузки", "Version": "Версия",
"WindowsDirectory": "Директория Windows", "TimeZone": "Часовой пояс", "UserName": "Имя пользователя",
"Manufacturer": "Производитель", "Name": "Название", "Product": "Изделие",
"MaxClockSpeed": "Максимальная тактовая частота", "SocketDesignation": "Название сокета",
"NumberOfPhysicalProcessors": "Количество физических процессоров", "VideoProcessor": "Видеопроцессор",
"NumberOfLogicalProcessors": "Количество логических процессоров", "Capacity": "Емкость",
"AdapterRAM": "Оперативная память адаптера", "CurrentRefreshRate": "Текущая частота обновления",
"Resolution": "Разрешение", "TotalPhysicalMemory": "Общий объем физической памяти", "Socket": "Сокет",
"ConfiguredClockSpeed": "Настроенная тактовая частота", "PartNumber": "Номер партии",
"SerialNumber": "Серийный номер", "DeviceID": "Идентификатор устройства", "MediaType": "Тип носителя",
"FirmwareRevision": "Ревизия прошивки", "Partitions": "Разделы", "Size": "Объем", "Drive": "Диск",
"VolumeName": "Имя тома", "VolumeSerialNumber": "Серийный номер тома", "MACAddress": "MAC-адрес",
"NetConnectionID": "Идентификатор сетевого подключения", "DHCPServer": "DHCP-сервер",
"IPAddress": "IP-адрес", "BuildNumber": "Номер сборки", "ID": "Идентификатор", "Status": "Статус",
"DefaultIPGateway": "IP-адрес шлюза по-умолчанию", "DNSHostName": "DNS Имя хоста",
"IPv4Address": "IPv4-адрес", "IPv6Address": "IPv6-адрес", "IPSubnet": "Маска подсети",
"ServiceName": "Название службы", "CurrentBuild": "Текущая сборка", "UBR": "Номер версии",
"RegisteredOwner": "Имя пользователя", "ActivateKey": "Ключ активации",
"SystemBiosVersion": "Версия Bios системы", "BIOSVendor": "Производитель", "BIOSVersion": "Версия",
"BIOSReleaseDate": "Дата выпуска релиза", "ShutdownTime": "Время выключения", "ProductName": "Название",
"EditionID": "Идентификатор редакции", "DisplayVersion": "Версия для отображения",
"SystemManufacturer": "Производитель", "SystemProductName": "Название сокета",
"CoreCount": "Количество ядер", "ProcessorNameString": "Название", "Identifier": "Идентификатор",
"VendorIdentifier": "Производитель", "~MHz": "Тактовая частота", "Vendor": "Производитель",
"Model": "Модель", "Revision": "Ревизия", "Description": "Название",
"NetCfgInstanceId": "Идентификатор", "DhcpDefaultGateway": "Шлюз по-умолчанию",
"DhcpIPAddress": "IP-адрес"}
part += f'{"-" * 50}\n'
for key in dict_info:
if type(dict_info[key]) == dict:
for item in dict_info[key]:
part += f'{synonyms[item]}: {dict_info[key][item]}\n'
part += "\n"
else:
part += f'{synonyms[key]}: {dict_info[key]}\n'
print(part)
wmic_info += f'{part}\n'
def main():
global wmic_info
t = time.monotonic()
document = Document()
document.add_heading(f'Сводная информация о компьютере: {node()}')
if os_info := winreg_os():
print_wmic("Информация об операционной системе\n", os_info)
if bios_info := bios_winreg():
print_wmic("Информация о BIOS\n", bios_info)
if mb_info := motherboard_winreg():
print_wmic("Информация о материнской плате\n", mb_info)
if cpu_info := cpu_winreg():
print_wmic("Информация о процессоре\n", cpu_info)
if gpu_info := gpu_winreg():
print_wmic("Информация о видеокарте\n", gpu_info)
if drive_info := hdd_ssd_winreg():
print_wmic("Информация о HDD и SSD\n", drive_info)
if cd_rom_info := cdrom_winreg():
print_wmic("Информация о CD/DVD-ROM\n", cd_rom_info)
if nic_info := nic_winreg():
print_wmic("Информация о физических сетевых интерфейсах\n", nic_info)
document.add_paragraph(wmic_info)
document.save(f'{node()}.docx')
print(f"Собранная информация сохранена в файл: {node()}.docx")
print(f'\nВремя работы скрипта: {time.monotonic() - t} с.')
if __name__ == "__main__":
main()
А на сегодня, пожалуй, все.
Спасибо за внимание. Надеюсь, данная информация будет вам полезна
Вложения
Последнее редактирование: