File indexing completed on 2024-04-14 05:41:27

0001 /*
0002     SPDX-FileCopyrightText: 2016 ROSA
0003     SPDX-License-Identifier: GPL-3.0-or-later
0004 */
0005 
0006 ////////////////////////////////////////////////////////////////////////////////
0007 // This file contains Windows implementation of platform-dependent functions
0008 
0009 #include "common.h"
0010 #include "usbdevice.h"
0011 
0012 #include <QApplication>
0013 #include <QMessageBox>
0014 
0015 
0016 // Several WinAPI COM specific macros for keeping the code clean
0017 
0018 // Runs the COM request specified, checks for return value and throws an exception
0019 // with descriptive error message if it's not OK
0020 #define CHECK_OK(code, msg)                             \
0021     {                                                   \
0022         HRESULT res = code;                             \
0023         if (res != S_OK)                                \
0024         {                                               \
0025             throw formatErrorMessageFromCode(msg, res); \
0026         }                                               \
0027     }
0028 
0029 // Releases the COM object and nullifies the pointer
0030 #define SAFE_RELEASE(obj)   \
0031     {                       \
0032         if (obj != NULL)    \
0033         {                   \
0034             obj->Release(); \
0035             obj = NULL;     \
0036         }                   \
0037     }
0038 
0039 // Allocated a BSTR string using the specified text, checks for successful memory allocation
0040 // and throws an exception with descriptive error message if unsuccessful
0041 #define ALLOC_BSTR(name, str)                                                 \
0042     {                                                                         \
0043         name = SysAllocString(str);                                           \
0044         if (name == NULL)                                                     \
0045         {                                                                     \
0046             throw "Memory allocation for string failed."; \
0047         }                                                                     \
0048     }
0049 
0050 // Releases the BSTR string and nullifies the pointer
0051 #define FREE_BSTR(str)      \
0052     {                       \
0053         SysFreeString(str); \
0054         str = NULL;         \
0055     }
0056 
0057 
0058 bool platformEnumFlashDevices(AddFlashDeviceCallbackProc callback, void* cbParam)
0059 {
0060     // Using WMI for enumerating the USB devices
0061 
0062     // Namespace of the WMI classes
0063     BSTR strNamespace       = NULL;
0064     // "WQL" - the query language we're gonna use (the only possible, actually)
0065     BSTR strQL              = NULL;
0066     // Query string for requesting physical devices
0067     BSTR strQueryDisks      = NULL;
0068     // Query string for requesting partitions for each of the physical devices
0069     BSTR strQueryPartitions = NULL;
0070     // Query string for requesting logical disks for each of the partitions
0071     BSTR strQueryLetters    = NULL;
0072 
0073     // Various COM objects for executing the queries, enumerating lists and retrieving properties
0074     IWbemLocator*         pIWbemLocator         = NULL;
0075     IWbemServices*        pWbemServices         = NULL;
0076     IEnumWbemClassObject* pEnumDisksObject      = NULL;
0077     IEnumWbemClassObject* pEnumPartitionsObject = NULL;
0078     IEnumWbemClassObject* pEnumLettersObject    = NULL;
0079     IWbemClassObject*     pDiskObject           = NULL;
0080     IWbemClassObject*     pPartitionObject      = NULL;
0081     IWbemClassObject*     pLetterObject         = NULL;
0082 
0083     // Temporary object for attaching data to the combobox entries
0084     UsbDevice* deviceData = NULL;
0085 
0086     try
0087     {
0088         // Start with allocating the fixed strings
0089         ALLOC_BSTR(strNamespace, L"root\\cimv2");
0090         ALLOC_BSTR(strQL, L"WQL");
0091         ALLOC_BSTR(strQueryDisks, L"SELECT * FROM Win32_DiskDrive WHERE InterfaceType = \"USB\"");
0092 
0093         // Create the IWbemLocator and execute the first query (list of physical disks attached via USB)
0094         CHECK_OK(CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IUnknown, reinterpret_cast<void**>(&pIWbemLocator)), i18n("CoCreateInstance(WbemAdministrativeLocator) failed."));
0095         CHECK_OK(pIWbemLocator->ConnectServer(strNamespace, NULL, NULL, NULL, 0, NULL, NULL, &pWbemServices), i18n("ConnectServer failed."));
0096         CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryDisks, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumDisksObject), i18n("Failed to query USB flash devices."));
0097 
0098         // Enumerate the received list of devices
0099         for (;;)
0100         {
0101             // Get the next available device or exit the loop
0102             ULONG uReturned;
0103             pEnumDisksObject->Next(WBEM_INFINITE, 1, &pDiskObject, &uReturned);
0104             if (uReturned == 0)
0105                 break;
0106 
0107             VARIANT val;
0108 
0109             // Fetch the required properties and store them in the UsbDevice object
0110             UsbDevice* deviceData = new UsbDevice;
0111 
0112             // User-friendly name of the device
0113             if (pDiskObject->Get(L"Model", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
0114             {
0115                 if (val.vt == VT_BSTR)
0116                 {
0117                     deviceData->m_VisibleName = QString::fromWCharArray(val.bstrVal);
0118                 }
0119                 VariantClear(&val);
0120             }
0121 
0122             // System name of the device
0123             if (pDiskObject->Get(L"DeviceID", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
0124             {
0125                 if (val.vt == VT_BSTR)
0126                 {
0127                     deviceData->m_PhysicalDevice = QString::fromWCharArray(val.bstrVal);
0128                 }
0129                 VariantClear(&val);
0130             }
0131 
0132             // Size of the devifce
0133             if (pDiskObject->Get(L"Size", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
0134             {
0135                 if (val.vt == VT_BSTR)
0136                 {
0137                     deviceData->m_Size = QString::fromWCharArray(val.bstrVal).toULongLong();
0138                 }
0139                 VariantClear(&val);
0140             }
0141 
0142             // Sector size of the devifce
0143             if (pDiskObject->Get(L"BytesPerSector", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
0144             {
0145                 if (val.vt == VT_I4)
0146                 {
0147                     deviceData->m_SectorSize = val.intVal;
0148                 }
0149                 VariantClear(&val);
0150             }
0151 
0152             // The device object is no longer needed, release it
0153             SAFE_RELEASE(pDiskObject);
0154 
0155             // Construct the request for listing the partitions on the current disk
0156             QString qstrQueryPartitions = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + deviceData->m_PhysicalDevice + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
0157             ALLOC_BSTR(strQueryPartitions, reinterpret_cast<const wchar_t*>(qstrQueryPartitions.utf16()));
0158 
0159             // Execute the query
0160             CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryPartitions, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumPartitionsObject), i18n("Failed to query list of partitions."));
0161 
0162             // Enumerate the received list of partitions
0163             for (;;)
0164             {
0165                 // Get the next available partition or exit the loop
0166                 pEnumPartitionsObject->Next(WBEM_INFINITE, 1, &pPartitionObject, &uReturned);
0167                 if (uReturned == 0)
0168                     break;
0169 
0170                 // Fetch the DeviceID property and store it for using in the next request
0171                 QString qstrQueryLetters = "";
0172                 if (pPartitionObject->Get(L"DeviceID", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
0173                 {
0174                     if (val.vt == VT_BSTR)
0175                     {
0176                         qstrQueryLetters = QString::fromWCharArray(val.bstrVal);
0177                     }
0178                     VariantClear(&val);
0179                 }
0180 
0181                 // The partition object is no longer needed, release it
0182                 SAFE_RELEASE(pPartitionObject);
0183 
0184                 // If DeviceID was fetched proceed to the logical disks
0185                 if (!qstrQueryLetters.isEmpty())
0186                 {
0187                     // Construct the request for listing the logical disks related to the current partition
0188                     qstrQueryLetters = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + qstrQueryLetters + "'} WHERE AssocClass = Win32_LogicalDiskToPartition";
0189                     ALLOC_BSTR(strQueryLetters, reinterpret_cast<const wchar_t*>(qstrQueryLetters.utf16()));
0190 
0191                     // Execute the query
0192                     CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryLetters, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumLettersObject), i18n("Failed to query list of logical disks."));
0193 
0194                     // Enumerate the received list of logical disks
0195                     for (;;)
0196                     {
0197                         // Get the next available logical disk or exit the loop
0198                         pEnumLettersObject->Next(WBEM_INFINITE, 1, &pLetterObject, &uReturned);
0199                         if (uReturned == 0)
0200                             break;
0201 
0202                         // Fetch the disk letter and add it to the list of volumes in the UsbDevice object
0203                         if (pLetterObject->Get(L"Caption", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
0204                         {
0205                             if (val.vt == VT_BSTR)
0206                             {
0207                                 deviceData->m_Volumes << QString::fromWCharArray(val.bstrVal);
0208                             }
0209                             VariantClear(&val);
0210                         }
0211 
0212                         // The logical disk object is no longer needed, release it
0213                         SAFE_RELEASE(pLetterObject);
0214                     }
0215 
0216                     // Release the logical disks enumerator object and the corresponding query string
0217                     SAFE_RELEASE(pEnumLettersObject);
0218                     FREE_BSTR(strQueryLetters);
0219                 }
0220             }
0221 
0222             // Release the partitions enumerator object and the corresponding query string
0223             SAFE_RELEASE(pEnumPartitionsObject);
0224             FREE_BSTR(strQueryPartitions);
0225 
0226             // The device information is now complete, append the entry
0227             callback(cbParam, deviceData);
0228             // The object is now under the GUI control, nullify the pointer
0229             deviceData = NULL;
0230         }
0231     }
0232     catch (QString errMessage)
0233     {
0234         // Something bad happened
0235         QMessageBox::critical(
0236             QApplication::activeWindow(),
0237             ApplicationTitle,
0238             errMessage
0239         );
0240     }
0241 
0242     // The cleanup stage
0243     if (deviceData != NULL)
0244         delete deviceData;
0245 
0246     SAFE_RELEASE(pLetterObject);
0247     SAFE_RELEASE(pPartitionObject);
0248     SAFE_RELEASE(pDiskObject);
0249     SAFE_RELEASE(pEnumDisksObject);
0250     SAFE_RELEASE(pEnumPartitionsObject);
0251     SAFE_RELEASE(pEnumLettersObject);
0252     SAFE_RELEASE(pWbemServices);
0253     SAFE_RELEASE(pIWbemLocator);
0254 
0255     FREE_BSTR(strNamespace);
0256     FREE_BSTR(strQL);
0257     FREE_BSTR(strQueryDisks);
0258     FREE_BSTR(strQueryPartitions);
0259     FREE_BSTR(strQueryLetters);
0260     return true;
0261 }
0262 
0263 bool ensureElevated()
0264 {
0265     // In Windows the manifest already ensures elevated privileges
0266     return true;
0267 }