-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
WIP: rewrite Windows code
Showing
15 changed files
with
880 additions
and
509 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
/* | ||
* Fedora Media Writer | ||
* Copyright (C) 2024 Jan Grulich <jgrulichredhat.com> | ||
* Copyright (C) 2016 Martin Bříza <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/* | ||
* Fedora Media Writer | ||
* Copyright (C) 2022 Jan Grulich <[email protected]> | ||
* Copyright (C) 2022-2024 Jan Grulich <[email protected]> | ||
* Copyright (C) 2011-2022 Pete Batard <[email protected]> | ||
* Copyright (C) 2016 Martin Bříza <[email protected]> | ||
* | ||
|
@@ -22,262 +22,99 @@ | |
#include "windrivemanager.h" | ||
#include "notifications.h" | ||
|
||
#include <QDebug> | ||
#include <QStringList> | ||
#include <QTimer> | ||
|
||
#include <windows.h> | ||
#define INITGUID | ||
#include <guiddef.h> | ||
#include <dbt.h> | ||
|
||
#include <cmath> | ||
#include <cstring> | ||
|
||
const int maxPartitionCount = 16; | ||
|
||
DEFINE_GUID(PARTITION_MICROSOFT_DATA, 0xEBD0A0A2, 0xB9E5, 0x4433, 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7); | ||
#pragma comment(lib, "wbemuuid.lib") | ||
|
||
WinDriveProvider::WinDriveProvider(DriveManager *parent) | ||
: DriveProvider(parent) | ||
, m_wmi(std::make_unique<LibWMI>(this)) | ||
{ | ||
mDebug() << this->metaObject()->className() << "construction"; | ||
qApp->installNativeEventFilter(this); | ||
QTimer::singleShot(0, this, &WinDriveProvider::checkDrives); | ||
} | ||
|
||
void WinDriveProvider::checkDrives() | ||
bool WinDriveProvider::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) | ||
{ | ||
static bool firstRun = true; | ||
|
||
if (firstRun) | ||
mDebug() << this->metaObject()->className() << "Looking for the drives for the first time"; | ||
|
||
for (int i = 0; i < 64; i++) { | ||
bool present = describeDrive(i, firstRun); | ||
if (!present && m_drives.contains(i)) { | ||
emit driveRemoved(m_drives[i]); | ||
m_drives[i]->deleteLater(); | ||
m_drives.remove(i); | ||
Q_UNUSED(eventType); | ||
|
||
MSG *msg = static_cast<MSG *>(message); | ||
if ((msg->message == WM_DEVICECHANGE) && ((msg->wParam == DBT_DEVICEARRIVAL) || (msg->wParam == DBT_DEVICEREMOVECOMPLETE))) { | ||
mDebug() << "Recieved device change event"; | ||
*result = TRUE; | ||
for (const WinDrive *drive : m_drives) { | ||
// Ignore device change events when we are restoring or writting and schedule | ||
// re-check once we are done | ||
if (drive->busy()) { | ||
return true; | ||
} | ||
} | ||
QTimer::singleShot(0, this, &WinDriveProvider::checkDrives); | ||
return true; | ||
} | ||
|
||
if (firstRun) | ||
mDebug() << this->metaObject()->className() << "Finished looking for the drives for the first time"; | ||
firstRun = false; | ||
QTimer::singleShot(2500, this, &WinDriveProvider::checkDrives); | ||
} | ||
|
||
QString getPhysicalName(int driveNumber) | ||
{ | ||
return QString("\\\\.\\PhysicalDrive%0").arg(driveNumber); | ||
} | ||
|
||
HANDLE getPhysicalHandle(int driveNumber) | ||
{ | ||
HANDLE physicalHandle = INVALID_HANDLE_VALUE; | ||
QString physicalPath = getPhysicalName(driveNumber); | ||
physicalHandle = CreateFileA(physicalPath.toStdString().c_str(), GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | ||
return physicalHandle; | ||
return false; | ||
} | ||
|
||
bool WinDriveProvider::isMountable(int driveNumber) | ||
void WinDriveProvider::checkDrives() | ||
{ | ||
mDebug() << this->metaObject()->className() << "Checking whether " << getPhysicalName(driveNumber) << " is mountable"; | ||
|
||
HANDLE physicalHandle = getPhysicalHandle(driveNumber); | ||
if (physicalHandle == INVALID_HANDLE_VALUE) { | ||
mDebug() << this->metaObject()->className() << "Could not get physical handle for drive " << getPhysicalName(driveNumber); | ||
return false; | ||
} | ||
|
||
DWORD size; | ||
BYTE geometry[256]; | ||
bool ret = DeviceIoControl(physicalHandle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, geometry, sizeof(geometry), &size, NULL); | ||
if (!ret || size <= 0) { | ||
mDebug() << this->metaObject()->className() << "Could not get geometry for drive " << getPhysicalName(driveNumber); | ||
CloseHandle(physicalHandle); | ||
return false; | ||
} | ||
|
||
PDISK_GEOMETRY_EX diskGeometry = (PDISK_GEOMETRY_EX)(void *)geometry; | ||
// Drive info | ||
LONGLONG diskSize; | ||
DWORD sectorSize; | ||
DWORD sectorsPerTrack; | ||
DWORD firstDataSector; | ||
MEDIA_TYPE mediaType; | ||
|
||
diskSize = diskGeometry->DiskSize.QuadPart; | ||
sectorSize = diskGeometry->Geometry.BytesPerSector; | ||
firstDataSector = MAXDWORD; | ||
if (sectorSize < 512) { | ||
mDebug() << this->metaObject()->className() << "Warning: Drive " << getPhysicalName(driveNumber) << " reports a sector size of " << sectorSize << " - Correcting to 512 bytes."; | ||
sectorSize = 512; | ||
} | ||
sectorsPerTrack = diskGeometry->Geometry.SectorsPerTrack; | ||
mediaType = diskGeometry->Geometry.MediaType; | ||
|
||
BYTE layout[4096] = {0}; | ||
ret = DeviceIoControl(physicalHandle, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, layout, sizeof(layout), &size, NULL); | ||
if (!ret || size <= 0) { | ||
mDebug() << this->metaObject()->className() << "Could not get layout for drive " << getPhysicalName(driveNumber); | ||
CloseHandle(physicalHandle); | ||
return false; | ||
} | ||
|
||
PDRIVE_LAYOUT_INFORMATION_EX driveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void *)layout; | ||
|
||
switch (driveLayout->PartitionStyle) { | ||
case PARTITION_STYLE_MBR: | ||
mDebug() << this->metaObject()->className() << "MBR partition style"; | ||
for (int i = 0; i < driveLayout->PartitionCount; i++) { | ||
if (driveLayout->PartitionEntry[i].Mbr.PartitionType != PARTITION_ENTRY_UNUSED) { | ||
QVector<uint8_t> mbrMountable = {0x01, 0x04, 0x06, 0x07, 0x0b, 0x0c, 0x0e}; | ||
BYTE partType = driveLayout->PartitionEntry[i].Mbr.PartitionType; | ||
mDebug() << this->metaObject()->className() << "Partition type: " << partType; | ||
if (!mbrMountable.contains(partType)) { | ||
CloseHandle(physicalHandle); | ||
mDebug() << this->metaObject()->className() << getPhysicalName(driveNumber) << " is not mountable"; | ||
return false; | ||
} | ||
} | ||
} | ||
break; | ||
case PARTITION_STYLE_GPT: | ||
mDebug() << this->metaObject()->className() << "GPT partition style"; | ||
for (int i = 0; i < driveLayout->PartitionCount; i++) { | ||
if (memcmp(&driveLayout->PartitionEntry[i].Gpt.PartitionType, &PARTITION_MICROSOFT_DATA, sizeof(GUID)) != 0) { | ||
CloseHandle(physicalHandle); | ||
mDebug() << this->metaObject()->className() << getPhysicalName(driveNumber) << " is not mountable"; | ||
return false; | ||
mDebug() << this->metaObject()->className() << "Looking for the drives"; | ||
|
||
QMap<int, WinDrive *> drives; | ||
auto usbDeviceList = m_wmi->getUSBDeviceList(); | ||
for (auto it = usbDeviceList.cbegin(); it != usbDeviceList.cend(); it++) { | ||
bool noLogicalDevice = false; | ||
QStringList partitionList = m_wmi->getDevicePartitions(it.key()); | ||
for (const QString &partition : partitionList) { | ||
if (m_wmi->getLogicalDisks(partition).isEmpty()) { | ||
noLogicalDevice = true; | ||
break; | ||
} | ||
} | ||
break; | ||
default: | ||
mDebug() << this->metaObject()->className() << "Partition type: RAW"; | ||
break; | ||
} | ||
|
||
mDebug() << this->metaObject()->className() << getPhysicalName(driveNumber) << " is mountable"; | ||
|
||
CloseHandle(physicalHandle); | ||
return true; | ||
} | ||
|
||
bool WinDriveProvider::describeDrive(int nDriveNumber, bool verbose) | ||
{ | ||
BOOL removable; | ||
QString productVendor; | ||
QString productId; | ||
QString serialNumber; | ||
uint64_t deviceBytes; | ||
STORAGE_BUS_TYPE storageBus; | ||
|
||
BOOL bResult = FALSE; // results flag | ||
// DWORD dwRet = NO_ERROR; | ||
|
||
// Format physical drive path (may be '\\.\PhysicalDrive0', '\\.\PhysicalDrive1' and so on). | ||
QString strDrivePath = getPhysicalName(nDriveNumber); | ||
|
||
// Get a handle to physical drive | ||
HANDLE hDevice = ::CreateFile(strDrivePath.toStdWString().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); | ||
|
||
if (hDevice == INVALID_HANDLE_VALUE) | ||
return false; //::GetLastError(); | ||
|
||
if (verbose) | ||
mDebug() << this->metaObject()->className() << strDrivePath << "is present"; | ||
|
||
// Set the input data structure | ||
STORAGE_PROPERTY_QUERY storagePropertyQuery; | ||
ZeroMemory(&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY)); | ||
storagePropertyQuery.PropertyId = StorageDeviceProperty; | ||
storagePropertyQuery.QueryType = PropertyStandardQuery; | ||
|
||
// Get the necessary output buffer size | ||
STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader; | ||
ZeroMemory(&storageDescriptorHeader, sizeof(STORAGE_DESCRIPTOR_HEADER)); | ||
DWORD dwBytesReturned = 0; | ||
if (verbose) | ||
mDebug() << this->metaObject()->className() << strDrivePath << "IOCTL_STORAGE_QUERY_PROPERTY"; | ||
if (!::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY), &storageDescriptorHeader, sizeof(STORAGE_DESCRIPTOR_HEADER), &dwBytesReturned, NULL)) { | ||
// dwRet = ::GetLastError(); | ||
::CloseHandle(hDevice); | ||
return false; // dwRet; | ||
} | ||
auto diskDrive = m_wmi->getDiskDriveInformation(it.key(), it.value()); | ||
if (diskDrive->model().isEmpty() || !diskDrive->size() || diskDrive->serialNumber().isEmpty()) { | ||
continue; | ||
} | ||
|
||
// Alloc the output buffer | ||
const DWORD dwOutBufferSize = storageDescriptorHeader.Size; | ||
BYTE *pOutBuffer = new BYTE[dwOutBufferSize]; | ||
ZeroMemory(pOutBuffer, dwOutBufferSize); | ||
|
||
if (verbose) | ||
mDebug() << this->metaObject()->className() << strDrivePath << "IOCTL_STORAGE_QUERY_PROPERTY with a bigger buffer"; | ||
// Get the storage device descriptor | ||
if (!(bResult = ::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY), pOutBuffer, dwOutBufferSize, &dwBytesReturned, NULL))) { | ||
// dwRet = ::GetLastError(); | ||
delete[] pOutBuffer; | ||
::CloseHandle(hDevice); | ||
return false; // dwRet; | ||
WinDrive *currentDrive = new WinDrive(this, diskDrive->model(), diskDrive->size(), noLogicalDevice, diskDrive->index(), diskDrive->serialNumber()); | ||
drives[diskDrive->index()] = currentDrive; | ||
} | ||
|
||
// Now, the output buffer points to a STORAGE_DEVICE_DESCRIPTOR structure | ||
// followed by additional info like vendor ID, product ID, serial number, and so on. | ||
STORAGE_DEVICE_DESCRIPTOR *pDeviceDescriptor = (STORAGE_DEVICE_DESCRIPTOR *)pOutBuffer; | ||
removable = pDeviceDescriptor->RemovableMedia; | ||
if (pDeviceDescriptor->ProductIdOffset != 0) | ||
productId = QString((char *)pOutBuffer + pDeviceDescriptor->ProductIdOffset).trimmed(); | ||
if (pDeviceDescriptor->VendorIdOffset != 0) | ||
productVendor = QString((char *)pOutBuffer + pDeviceDescriptor->VendorIdOffset).trimmed(); | ||
if (pDeviceDescriptor->SerialNumberOffset != 0) | ||
serialNumber = QString((char *)pOutBuffer + pDeviceDescriptor->SerialNumberOffset).trimmed(); | ||
storageBus = pDeviceDescriptor->BusType; | ||
|
||
if (verbose) | ||
mDebug() << this->metaObject()->className() << strDrivePath << "detected:" << productVendor << productId << (removable ? ", removable" : ", nonremovable") << (storageBus == BusTypeUsb ? "USB" : "notUSB"); | ||
|
||
if (!removable && storageBus != BusTypeUsb) | ||
return false; | ||
|
||
DISK_GEOMETRY pdg; | ||
DWORD junk = 0; // discard results | ||
|
||
if (verbose) | ||
mDebug() << this->metaObject()->className() << strDrivePath << "IOCTL_DISK_GET_DRIVE_GEOMETRY"; | ||
bResult = DeviceIoControl(hDevice, // device to be queried | ||
IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform | ||
NULL, | ||
0, // no input buffer | ||
&pdg, | ||
sizeof(pdg), // output buffer | ||
&junk, // # bytes returned | ||
(LPOVERLAPPED)NULL); // synchronous I/O | ||
|
||
if (!bResult || pdg.MediaType == Unknown) | ||
return false; | ||
// Update our list of drives and notify about added and removed drives | ||
QList<int> driveIndexes = m_drives.keys(); | ||
for (auto it = drives.constBegin(); it != drives.constEnd(); it++) { | ||
if (m_drives.contains(it.key()) && *m_drives[it.key()] == *it.value()) { | ||
mDebug() << "Drive " << it.key() << " already exists"; | ||
it.value()->deleteLater(); | ||
driveIndexes.removeAll(it.key()); | ||
continue; | ||
} | ||
|
||
deviceBytes = pdg.Cylinders.QuadPart * pdg.TracksPerCylinder * pdg.SectorsPerTrack * pdg.BytesPerSector; | ||
if (m_drives.contains(it.key())) { | ||
mDebug() << "Replacing old drive in the list on index " << it.key(); | ||
emit driveRemoved(m_drives[it.key()]); | ||
m_drives[it.key()]->deleteLater(); | ||
m_drives.remove(it.key()); | ||
} | ||
|
||
// Do cleanup and return | ||
if (verbose) | ||
mDebug() << this->metaObject()->className() << strDrivePath << "cleanup, adding to the list"; | ||
delete[] pOutBuffer; | ||
::CloseHandle(hDevice); | ||
mDebug() << "Adding new drive to the list with index " << it.key(); | ||
m_drives[it.key()] = it.value(); | ||
emit driveConnected(it.value()); | ||
|
||
WinDrive *currentDrive = new WinDrive(this, productVendor + " " + productId, deviceBytes, !isMountable(nDriveNumber), nDriveNumber, serialNumber); | ||
if (m_drives.contains(nDriveNumber) && *m_drives[nDriveNumber] == *currentDrive) { | ||
currentDrive->deleteLater(); | ||
return true; | ||
driveIndexes.removeAll(it.key()); | ||
} | ||
|
||
if (m_drives.contains(nDriveNumber)) { | ||
emit driveRemoved(m_drives[nDriveNumber]); | ||
m_drives[nDriveNumber]->deleteLater(); | ||
// Remove our previously stored drives that were not present in the last check | ||
for (int index : driveIndexes) { | ||
mDebug() << "Removing old drive with index" << index; | ||
emit driveRemoved(m_drives[index]); | ||
m_drives[index]->deleteLater(); | ||
m_drives.remove(index); | ||
} | ||
|
||
m_drives[nDriveNumber] = currentDrive; | ||
emit driveConnected(currentDrive); | ||
|
||
return true; | ||
} | ||
|
||
WinDrive::WinDrive(WinDriveProvider *parent, const QString &name, uint64_t size, bool containsLive, int device, const QString &serialNumber) | ||
|
@@ -379,6 +216,11 @@ void WinDrive::restore() | |
m_child->start(QIODevice::ReadOnly); | ||
} | ||
|
||
bool WinDrive::busy() const | ||
{ | ||
return (m_child && m_child->state() == QProcess::Running); | ||
} | ||
|
||
QString WinDrive::serialNumber() const | ||
{ | ||
return m_serialNo; | ||
|
@@ -455,7 +297,7 @@ void WinDrive::onReadyRead() | |
Notifications::notify(tr("Finished!"), tr("Writing %1 was successful").arg(m_image->fullName())); | ||
} else { | ||
bool ok; | ||
qreal bytes = line.toLongLong(&ok); | ||
qint64 bytes = line.toLongLong(&ok); | ||
if (ok) { | ||
if (bytes < 0) | ||
m_progress->setValue(NAN); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
add_subdirectory(isomd5) | ||
if (WIN32) | ||
add_subdirectory(libwmi) | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
set(LIBWMI_SRCS | ||
libwmi.cpp | ||
) | ||
|
||
add_library(libwmi STATIC ${LIBWMI_SRCS}) | ||
|
||
target_link_libraries(libwmi | ||
Qt6::Core | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,361 @@ | ||
/* | ||
* Fedora Media Writer | ||
* Copyright (C) 2024 Jan Grulich <jgrulich@redhat.com> | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public License | ||
* as published by the Free Software Foundation; either version 2 | ||
* of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
#include "libwmi.h" | ||
|
||
#include <QDebug> | ||
|
||
#include <comdef.h> | ||
#include <dbt.h> | ||
|
||
#pragma comment(lib, "wbemuuid.lib") | ||
|
||
LibWMI::LibWMI(QObject *parent) | ||
: QObject(parent) | ||
{ | ||
HRESULT res = S_OK; | ||
// This needs to be initialized here before any RPC communication occurs | ||
res = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0); | ||
if (FAILED(res)) { | ||
_com_error err(res); | ||
qWarning() << "Failed to initialize security. Error = " << err.ErrorMessage(); | ||
return; | ||
} | ||
|
||
res = CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<void **>(&m_IWbemLocator)); | ||
if (FAILED(res)) { | ||
_com_error err(res); | ||
qWarning() << "Failed to create IWbemLocator object. Error = " << QString::fromWCharArray(err.ErrorMessage()); | ||
return; | ||
} | ||
|
||
res = m_IWbemLocator->ConnectServer(_bstr_t(L"root\\cimv2"), NULL, NULL, NULL, 0, NULL, NULL, &m_IWbemServices); | ||
if (FAILED(res)) { | ||
_com_error err(res); | ||
qWarning() << "Could not connect to WMI. Error = " << QString::fromWCharArray(err.ErrorMessage()); | ||
return; | ||
} | ||
|
||
initialized = true; | ||
} | ||
|
||
LibWMI::~LibWMI() | ||
{ | ||
if (m_IWbemLocator) { | ||
m_IWbemLocator->Release(); | ||
} | ||
if (m_IWbemServices) { | ||
m_IWbemServices->Release(); | ||
} | ||
CoUninitialize(); | ||
} | ||
|
||
QMap<quint32, QString> LibWMI::getUSBDeviceList() | ||
{ | ||
QMap<quint32, QString> result; | ||
if (!initialized) { | ||
return result; | ||
} | ||
|
||
HRESULT res = S_OK; | ||
IEnumWbemClassObject *pEnumDiskObjects = NULL; | ||
|
||
res = m_IWbemServices->ExecQuery(_bstr_t(L"WQL"), _bstr_t(L"SELECT * FROM Win32_DiskDrive WHERE InterfaceType = 'USB'"), WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumDiskObjects); | ||
if (FAILED(res)) { | ||
_com_error err(res); | ||
qWarning() << "WMI query failed. Error = " << QString::fromWCharArray(err.ErrorMessage()); | ||
return result; | ||
} | ||
|
||
while (true) { | ||
IWbemClassObject *pDiskObject = NULL; | ||
ULONG uReturn = 0; | ||
pEnumDiskObjects->Next(WBEM_INFINITE, 1, &pDiskObject, &uReturn); | ||
if (uReturn == 0) { | ||
break; | ||
} | ||
|
||
// Fetch disk information | ||
quint32 index; | ||
QString deviceID; | ||
VARIANT var; | ||
|
||
if ((pDiskObject->Get(_bstr_t(L"Index"), 0, &var, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (var.vt == VT_I4) { | ||
index = var.intVal; | ||
qDebug() << "Disk Index: " << index; | ||
} else if (var.vt == VT_UI4) { | ||
index = var.uintVal; | ||
qDebug() << "Disk Index: " << index; | ||
} | ||
VariantClear(&var); | ||
} | ||
|
||
if ((pDiskObject->Get(_bstr_t(L"DeviceID"), 0, &var, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (var.vt == VT_BSTR) { | ||
deviceID = QString::fromWCharArray(var.bstrVal); | ||
qDebug() << "Device ID" << deviceID; | ||
} | ||
VariantClear(&var); | ||
} | ||
pDiskObject->Release(); | ||
|
||
result.insert(index, deviceID); | ||
} | ||
pEnumDiskObjects->Release(); | ||
|
||
return result; | ||
} | ||
|
||
QStringList LibWMI::getDevicePartitions(quint32 index) | ||
{ | ||
QStringList result; | ||
if (!initialized) { | ||
return result; | ||
} | ||
|
||
HRESULT res = S_OK; | ||
IEnumWbemClassObject *pPartitionObjects = NULL; | ||
std::wstring partitionQuery = L"SELECT * FROM Win32_DiskPartition WHERE DiskIndex = " + std::to_wstring(index); | ||
|
||
res = m_IWbemServices->ExecQuery(_bstr_t(L"WQL"), _bstr_t(partitionQuery.c_str()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pPartitionObjects); | ||
if (FAILED(res)) { | ||
_com_error err(res); | ||
qWarning() << "Query for disk partitions failed. Error = " << QString::fromWCharArray(err.ErrorMessage()); | ||
return result; | ||
} | ||
|
||
while (true) { | ||
IWbemClassObject *pPartitionObject = NULL; | ||
ULONG uReturnPartition = 0; | ||
pPartitionObjects->Next(WBEM_INFINITE, 1, &pPartitionObject, &uReturnPartition); | ||
if (uReturnPartition == 0) { | ||
break; | ||
} | ||
|
||
QString deviceID; | ||
VARIANT partitionDeviceID; | ||
if ((pPartitionObject->Get(_bstr_t(L"DeviceID"), 0, &partitionDeviceID, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (partitionDeviceID.vt == VT_BSTR) { | ||
deviceID = QString::fromWCharArray(partitionDeviceID.bstrVal); | ||
qDebug() << "Partition device ID " << deviceID; | ||
} | ||
VariantClear(&partitionDeviceID); | ||
} | ||
pPartitionObject->Release(); | ||
if (!deviceID.isEmpty()) { | ||
result << deviceID; | ||
} | ||
} | ||
pPartitionObjects->Release(); | ||
|
||
return result; | ||
} | ||
|
||
QStringList LibWMI::getLogicalDisks(const QString &partitionID) | ||
{ | ||
QStringList result; | ||
if (!initialized) { | ||
return result; | ||
} | ||
|
||
HRESULT res = S_OK; | ||
std::wstring partitionToLogicalQuery = L"ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + partitionID.toStdWString() + L"'} WHERE AssocClass = Win32_LogicalDiskToPartition"; | ||
IEnumWbemClassObject *pLogicalDiskObjects = NULL; | ||
res = m_IWbemServices->ExecQuery(_bstr_t(L"WQL"), _bstr_t(partitionToLogicalQuery.c_str()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pLogicalDiskObjects); | ||
if (FAILED(res)) { | ||
_com_error err(res); | ||
qWarning() << "Query for logical disks failed. Error = " << QString::fromWCharArray(err.ErrorMessage()); | ||
return result; | ||
} | ||
|
||
while (true) { | ||
IWbemClassObject *pLogicalDiskObject = NULL; | ||
ULONG uReturnLogicalDisk = 0; | ||
pLogicalDiskObjects->Next(WBEM_INFINITE, 1, &pLogicalDiskObject, &uReturnLogicalDisk); | ||
if (uReturnLogicalDisk == 0) { | ||
break; | ||
} | ||
|
||
QString deviceID; | ||
VARIANT logicalDiskDeviceID; | ||
if ((pLogicalDiskObject->Get(_bstr_t(L"DeviceID"), 0, &logicalDiskDeviceID, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (logicalDiskDeviceID.vt == VT_BSTR) { | ||
deviceID = QString::fromWCharArray(logicalDiskDeviceID.bstrVal); | ||
qDebug() << "Logical disk device ID " << deviceID; | ||
} | ||
VariantClear(&logicalDiskDeviceID); | ||
} | ||
pLogicalDiskObject->Release(); | ||
if (!deviceID.isEmpty()) { | ||
result << deviceID; | ||
} | ||
} | ||
pLogicalDiskObjects->Release(); | ||
|
||
return result; | ||
} | ||
|
||
std::unique_ptr<LibWMIDiskDrive> LibWMI::getDiskDriveInformation(quint32 index, const QString &deviceID) | ||
{ | ||
std::unique_ptr<LibWMIDiskDrive> result = std::make_unique<LibWMIDiskDrive>(index, deviceID); | ||
if (!initialized) { | ||
return result; | ||
} | ||
|
||
HRESULT res = S_OK; | ||
IEnumWbemClassObject *pEnumDiskObjects = NULL; | ||
|
||
std::wstring deviceQuery = L"SELECT * FROM Win32_DiskDrive WHERE Index = " + std::to_wstring(index); | ||
res = m_IWbemServices->ExecQuery(_bstr_t(L"WQL"), _bstr_t(deviceQuery.c_str()), WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumDiskObjects); | ||
if (FAILED(res)) { | ||
_com_error err(res); | ||
qWarning() << "WMI query failed. Error = " << QString::fromWCharArray(err.ErrorMessage()); | ||
return result; | ||
} | ||
|
||
while (true) { | ||
IWbemClassObject *pDiskObject = NULL; | ||
ULONG uReturn = 0; | ||
pEnumDiskObjects->Next(WBEM_INFINITE, 1, &pDiskObject, &uReturn); | ||
if (uReturn == 0) { | ||
break; | ||
} | ||
|
||
VARIANT var; | ||
if (result->deviceID().isEmpty()) { | ||
if ((pDiskObject->Get(_bstr_t(L"DeviceID"), 0, &var, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (var.vt == VT_BSTR) { | ||
result->setDeviceID(QString::fromWCharArray(var.bstrVal)); | ||
qDebug() << "DeviceID " << result->deviceID(); | ||
} | ||
VariantClear(&var); | ||
} | ||
} | ||
|
||
if ((pDiskObject->Get(_bstr_t(L"Model"), 0, &var, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (var.vt == VT_BSTR) { | ||
result->setModel(QString::fromWCharArray(var.bstrVal)); | ||
qDebug() << "Disk model: " << result->model(); | ||
} | ||
VariantClear(&var); | ||
} | ||
|
||
if ((pDiskObject->Get(_bstr_t(L"Size"), 0, &var, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (var.vt == VT_BSTR) { | ||
result->setSize(QString::fromWCharArray(var.bstrVal).toULongLong()); | ||
qDebug() << "Size " << result->size(); | ||
} else if (var.vt == VT_I4) { | ||
result->setSize(var.intVal); | ||
qDebug() << "Size " << result->size(); | ||
} else if (var.vt == VT_UI4) { | ||
result->setSize(var.uintVal); | ||
qDebug() << "Size " << result->size(); | ||
} | ||
VariantClear(&var); | ||
} | ||
|
||
if ((pDiskObject->Get(_bstr_t(L"BytesPerSector"), 0, &var, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (var.vt == VT_BSTR) { | ||
result->setSectorSize(QString::fromWCharArray(var.bstrVal).toULongLong()); | ||
qDebug() << "Sector size " << result->sectorSize(); | ||
} else if (var.vt == VT_I4) { | ||
result->setSectorSize(var.intVal); | ||
qDebug() << "Sector size " << result->sectorSize(); | ||
} else if (var.vt == VT_UI4) { | ||
result->setSectorSize(var.uintVal); | ||
qDebug() << "Sector size " << result->sectorSize(); | ||
} | ||
VariantClear(&var); | ||
} | ||
|
||
if ((pDiskObject->Get(_bstr_t(L"SerialNumber"), 0, &var, 0, 0)) == WBEM_S_NO_ERROR) { | ||
if (var.vt == VT_BSTR) { | ||
result->setSerialNumber(QString::fromWCharArray(var.bstrVal)); | ||
qDebug() << "Serial number " << result->serialNumber(); | ||
} | ||
VariantClear(&var); | ||
} | ||
pDiskObject->Release(); | ||
} | ||
pEnumDiskObjects->Release(); | ||
|
||
return result; | ||
} | ||
|
||
LibWMIDiskDrive::LibWMIDiskDrive(quint32 index, const QString &deviceID) | ||
: m_index(index) | ||
, m_deviceID(deviceID) | ||
{ | ||
} | ||
|
||
quint32 LibWMIDiskDrive::index() const | ||
{ | ||
return m_index; | ||
} | ||
|
||
QString LibWMIDiskDrive::deviceID() const | ||
{ | ||
return m_deviceID; | ||
} | ||
|
||
void LibWMIDiskDrive::setDeviceID(const QString &deviceID) | ||
{ | ||
m_deviceID = deviceID; | ||
} | ||
|
||
QString LibWMIDiskDrive::model() const | ||
{ | ||
return m_model; | ||
} | ||
|
||
void LibWMIDiskDrive::setModel(const QString &model) | ||
{ | ||
m_model = model; | ||
} | ||
|
||
quint64 LibWMIDiskDrive::size() const | ||
{ | ||
return m_size; | ||
} | ||
|
||
void LibWMIDiskDrive::setSize(quint64 size) | ||
{ | ||
m_size = size; | ||
} | ||
|
||
QString LibWMIDiskDrive::serialNumber() const | ||
{ | ||
return m_serialNumber; | ||
} | ||
|
||
void LibWMIDiskDrive::setSerialNumber(const QString &serialNumber) | ||
{ | ||
m_serialNumber = serialNumber; | ||
} | ||
|
||
quint32 LibWMIDiskDrive::sectorSize() | ||
{ | ||
return m_sectorSize; | ||
} | ||
|
||
void LibWMIDiskDrive::setSectorSize(quint32 sectorSize) | ||
{ | ||
m_sectorSize = sectorSize; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
* Fedora Media Writer | ||
* Copyright (C) 2024 Jan Grulich <jgrulich@redhat.com> | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public License | ||
* as published by the Free Software Foundation; either version 2 | ||
* of the License, or (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
*/ | ||
|
||
#ifndef LIBWMI_H | ||
#define LIBWMI_H | ||
|
||
#include <QObject> | ||
#include <wbemidl.h> | ||
|
||
class LibWMIDiskDrive; | ||
|
||
class LibWMI : public QObject | ||
{ | ||
Q_OBJECT | ||
public: | ||
LibWMI(QObject *parent); | ||
~LibWMI(); | ||
|
||
// Returns a map<index, deviceID> with list of devices | ||
QMap<quint32, QString> getUSBDeviceList(); | ||
// Returns a list of partition IDs for device with given index | ||
QStringList getDevicePartitions(quint32 index); | ||
// Returns a list of logical disks IDs for given partition | ||
QStringList getLogicalDisks(const QString &partitionID); | ||
// Returns information about disk drive | ||
std::unique_ptr<LibWMIDiskDrive> getDiskDriveInformation(quint32 index, const QString &deviceID = QString()); | ||
|
||
private: | ||
bool initialized = false; | ||
IWbemLocator *m_IWbemLocator = NULL; | ||
IWbemServices *m_IWbemServices = NULL; | ||
}; | ||
|
||
class LibWMIDiskDrive | ||
{ | ||
public: | ||
LibWMIDiskDrive(quint32 index, const QString &deviceID = QString()); | ||
|
||
quint32 index() const; | ||
|
||
QString deviceID() const; | ||
void setDeviceID(const QString &deviceID); | ||
|
||
QString model() const; | ||
void setModel(const QString &model); | ||
|
||
quint64 size() const; | ||
void setSize(quint64 size); | ||
|
||
QString serialNumber() const; | ||
void setSerialNumber(const QString &serialNumber); | ||
|
||
quint32 sectorSize(); | ||
void setSectorSize(quint32 sectorSize); | ||
|
||
private: | ||
quint32 m_index = 0; | ||
quint32 m_sectorSize = 0; | ||
quint64 m_size = 0; | ||
QString m_deviceID; | ||
QString m_model; | ||
QString m_serialNumber; | ||
}; | ||
|
||
#endif // LIBWMI_H |