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 }