-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathplatform_win.cpp
262 lines (222 loc) · 11 KB
/
platform_win.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
////////////////////////////////////////////////////////////////////////////////
// This file contains Windows implementation of platform-dependent functions
#include "common.h"
#include "usbdevice.h"
#include <QApplication>
#include <QMessageBox>
// Several WinAPI COM specific macros for keeping the code clean
// Runs the COM request specified, checks for return value and throws an exception
// with descriptive error message if it's not OK
#define CHECK_OK(code, msg) \
{ \
HRESULT res = code; \
if (res != S_OK) \
{ \
throw formatErrorMessageFromCode(msg, res); \
} \
}
// Releases the COM object and nullifies the pointer
#define SAFE_RELEASE(obj) \
{ \
if (obj != NULL) \
{ \
obj->Release(); \
obj = NULL; \
} \
}
// Allocated a BSTR string using the specified text, checks for successful memory allocation
// and throws an exception with descriptive error message if unsuccessful
#define ALLOC_BSTR(name, str) \
{ \
name = SysAllocString(str); \
if (name == NULL) \
{ \
throw QObject::tr("Memory allocation for %1 failed.").arg(#name); \
} \
}
// Releases the BSTR string and nullifies the pointer
#define FREE_BSTR(str) \
{ \
SysFreeString(str); \
str = NULL; \
}
bool platformEnumFlashDevices(AddFlashDeviceCallbackProc callback, void* cbParam)
{
// Using WMI for enumerating the USB devices
// Namespace of the WMI classes
BSTR strNamespace = NULL;
// "WQL" - the query language we're gonna use (the only possible, actually)
BSTR strQL = NULL;
// Query string for requesting physical devices
BSTR strQueryDisks = NULL;
// Query string for requesting partitions for each of the the physical devices
BSTR strQueryPartitions = NULL;
// Query string for requesting logical disks for each of the partitions
BSTR strQueryLetters = NULL;
// Various COM objects for executing the queries, enumerating lists and retrieving properties
IWbemLocator* pIWbemLocator = NULL;
IWbemServices* pWbemServices = NULL;
IEnumWbemClassObject* pEnumDisksObject = NULL;
IEnumWbemClassObject* pEnumPartitionsObject = NULL;
IEnumWbemClassObject* pEnumLettersObject = NULL;
IWbemClassObject* pDiskObject = NULL;
IWbemClassObject* pPartitionObject = NULL;
IWbemClassObject* pLetterObject = NULL;
// Temporary object for attaching data to the combobox entries
UsbDevice* deviceData = NULL;
try
{
// Start with allocating the fixed strings
ALLOC_BSTR(strNamespace, L"root\\cimv2");
ALLOC_BSTR(strQL, L"WQL");
ALLOC_BSTR(strQueryDisks, L"SELECT * FROM Win32_DiskDrive WHERE InterfaceType = \"USB\"");
// Create the IWbemLocator and execute the first query (list of physical disks attached via USB)
CHECK_OK(CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IUnknown, reinterpret_cast<void**>(&pIWbemLocator)), QObject::tr("CoCreateInstance(WbemAdministrativeLocator) failed."));
CHECK_OK(pIWbemLocator->ConnectServer(strNamespace, NULL, NULL, NULL, 0, NULL, NULL, &pWbemServices), QObject::tr("ConnectServer failed."));
CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryDisks, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumDisksObject), QObject::tr("Failed to query USB flash devices."));
// Enumerate the received list of devices
for (;;)
{
// Get the next available device or exit the loop
ULONG uReturned;
pEnumDisksObject->Next(WBEM_INFINITE, 1, &pDiskObject, &uReturned);
if (uReturned == 0)
break;
VARIANT val;
// Fetch the required properties and store them in the UsbDevice object
UsbDevice* deviceData = new UsbDevice;
// User-friendly name of the device
if (pDiskObject->Get(L"Model", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
{
if (val.vt == VT_BSTR)
{
deviceData->m_VisibleName = QString::fromWCharArray(val.bstrVal);
}
VariantClear(&val);
}
// System name of the device
if (pDiskObject->Get(L"DeviceID", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
{
if (val.vt == VT_BSTR)
{
deviceData->m_PhysicalDevice = QString::fromWCharArray(val.bstrVal);
}
VariantClear(&val);
}
// Size of the devifce
if (pDiskObject->Get(L"Size", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
{
if (val.vt == VT_BSTR)
{
deviceData->m_Size = QString::fromWCharArray(val.bstrVal).toULongLong();
}
VariantClear(&val);
}
// Sector size of the devifce
if (pDiskObject->Get(L"BytesPerSector", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
{
if (val.vt == VT_I4)
{
deviceData->m_SectorSize = val.intVal;
}
VariantClear(&val);
}
// The device object is no longer needed, release it
SAFE_RELEASE(pDiskObject);
// Construct the request for listing the partitions on the current disk
QString qstrQueryPartitions = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + deviceData->m_PhysicalDevice + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
ALLOC_BSTR(strQueryPartitions, reinterpret_cast<const wchar_t*>(qstrQueryPartitions.utf16()));
// Execute the query
CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryPartitions, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumPartitionsObject), QObject::tr("Failed to query list of partitions."));
// Enumerate the received list of partitions
for (;;)
{
// Get the next available partition or exit the loop
pEnumPartitionsObject->Next(WBEM_INFINITE, 1, &pPartitionObject, &uReturned);
if (uReturned == 0)
break;
// Fetch the DeviceID property and store it for using in the next request
QString qstrQueryLetters = "";
if (pPartitionObject->Get(L"DeviceID", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
{
if (val.vt == VT_BSTR)
{
qstrQueryLetters = QString::fromWCharArray(val.bstrVal);
}
VariantClear(&val);
}
// The partition object is no longer needed, release it
SAFE_RELEASE(pPartitionObject);
// If DeviceID was fetched proceed to the logical disks
if (qstrQueryLetters != "")
{
// Construct the request for listing the logical disks related to the current partition
qstrQueryLetters = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + qstrQueryLetters + "'} WHERE AssocClass = Win32_LogicalDiskToPartition";
ALLOC_BSTR(strQueryLetters, reinterpret_cast<const wchar_t*>(qstrQueryLetters.utf16()));
// Execute the query
CHECK_OK(pWbemServices->ExecQuery(strQL, strQueryLetters, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumLettersObject), QObject::tr("Failed to query list of logical disks."));
// Enumerate the received list of logical disks
for (;;)
{
// Get the next available logical disk or exit the loop
pEnumLettersObject->Next(WBEM_INFINITE, 1, &pLetterObject, &uReturned);
if (uReturned == 0)
break;
// Fetch the disk letter and add it to the list of volumes in the UsbDevice object
if (pLetterObject->Get(L"Caption", 0, &val, 0, 0) == WBEM_S_NO_ERROR)
{
if (val.vt == VT_BSTR)
{
deviceData->m_Volumes << QString::fromWCharArray(val.bstrVal);
}
VariantClear(&val);
}
// The logical disk object is no longer needed, release it
SAFE_RELEASE(pLetterObject);
}
// Release the logical disks enumerator object and the corresponding query string
SAFE_RELEASE(pEnumLettersObject);
FREE_BSTR(strQueryLetters);
}
}
// Release the partitions enumerator object and the corresponding query string
SAFE_RELEASE(pEnumPartitionsObject);
FREE_BSTR(strQueryPartitions);
// The device information is now complete, append the entry
callback(cbParam, deviceData);
// The object is now under the GUI control, nullify the pointer
deviceData = NULL;
}
}
catch (QString errMessage)
{
// Something bad happened
QMessageBox::critical(
QApplication::activeWindow(),
ApplicationTitle,
errMessage
);
}
// The cleanup stage
if (deviceData != NULL)
delete deviceData;
SAFE_RELEASE(pLetterObject);
SAFE_RELEASE(pPartitionObject);
SAFE_RELEASE(pDiskObject);
SAFE_RELEASE(pEnumDisksObject);
SAFE_RELEASE(pEnumPartitionsObject);
SAFE_RELEASE(pEnumLettersObject);
SAFE_RELEASE(pWbemServices);
SAFE_RELEASE(pIWbemLocator);
FREE_BSTR(strNamespace);
FREE_BSTR(strQL);
FREE_BSTR(strQueryDisks);
FREE_BSTR(strQueryPartitions);
FREE_BSTR(strQueryLetters);
return true;
}
bool ensureElevated()
{
// In Windows the manifest already ensures elevated privileges
return true;
}