Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/cfgmgr32/main.c
4388 views
1
/*
2
* Copyright (C) 2023 Mohamad Al-Jaf
3
* Copyright (C) 2025 Vibhav Pant
4
*
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
9
*
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
14
*
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18
*/
19
20
#include "wine/debug.h"
21
#include "wine/rbtree.h"
22
#include "winreg.h"
23
#include "winternl.h"
24
#include "cfgmgr32.h"
25
#include "winuser.h"
26
#include "dbt.h"
27
#include "wine/plugplay.h"
28
#include "setupapi.h"
29
#include "devfiltertypes.h"
30
#include "devquery.h"
31
32
#include "initguid.h"
33
#include "devpkey.h"
34
35
WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
36
37
/***********************************************************************
38
* CM_MapCrToWin32Err (cfgmgr32.@)
39
*/
40
DWORD WINAPI CM_MapCrToWin32Err( CONFIGRET code, DWORD default_error )
41
{
42
TRACE( "code: %#lx, default_error: %ld\n", code, default_error );
43
44
switch (code)
45
{
46
case CR_SUCCESS: return ERROR_SUCCESS;
47
case CR_OUT_OF_MEMORY: return ERROR_NOT_ENOUGH_MEMORY;
48
case CR_INVALID_POINTER: return ERROR_INVALID_USER_BUFFER;
49
case CR_INVALID_FLAG: return ERROR_INVALID_FLAGS;
50
case CR_INVALID_DEVNODE:
51
case CR_INVALID_DEVICE_ID:
52
case CR_INVALID_MACHINENAME:
53
case CR_INVALID_PROPERTY:
54
case CR_INVALID_REFERENCE_STRING: return ERROR_INVALID_DATA;
55
case CR_NO_SUCH_DEVNODE:
56
case CR_NO_SUCH_VALUE:
57
case CR_NO_SUCH_DEVICE_INTERFACE: return ERROR_NOT_FOUND;
58
case CR_ALREADY_SUCH_DEVNODE: return ERROR_ALREADY_EXISTS;
59
case CR_BUFFER_SMALL: return ERROR_INSUFFICIENT_BUFFER;
60
case CR_NO_REGISTRY_HANDLE: return ERROR_INVALID_HANDLE;
61
case CR_REGISTRY_ERROR: return ERROR_REGISTRY_CORRUPT;
62
case CR_NO_SUCH_REGISTRY_KEY: return ERROR_FILE_NOT_FOUND;
63
case CR_REMOTE_COMM_FAILURE:
64
case CR_MACHINE_UNAVAILABLE:
65
case CR_NO_CM_SERVICES: return ERROR_SERVICE_NOT_ACTIVE;
66
case CR_ACCESS_DENIED: return ERROR_ACCESS_DENIED;
67
case CR_CALL_NOT_IMPLEMENTED: return ERROR_CALL_NOT_IMPLEMENTED;
68
}
69
70
return default_error;
71
}
72
73
struct cm_notify_context
74
{
75
HDEVNOTIFY notify;
76
void *user_data;
77
PCM_NOTIFY_CALLBACK callback;
78
};
79
80
CALLBACK DWORD devnotify_callback( HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header )
81
{
82
struct cm_notify_context *ctx = handle;
83
CM_NOTIFY_EVENT_DATA *event_data;
84
CM_NOTIFY_ACTION action;
85
DWORD size, ret;
86
87
TRACE( "(%p, %#lx, %p)\n", handle, flags, header );
88
89
switch (flags)
90
{
91
case DBT_DEVICEARRIVAL:
92
action = CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL;
93
break;
94
case DBT_DEVICEREMOVECOMPLETE:
95
FIXME( "CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE not implemented\n" );
96
action = CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL;
97
break;
98
case DBT_CUSTOMEVENT:
99
action = CM_NOTIFY_ACTION_DEVICECUSTOMEVENT;
100
break;
101
default:
102
FIXME( "Unexpected flags value: %#lx\n", flags );
103
return 0;
104
}
105
106
switch (header->dbch_devicetype)
107
{
108
case DBT_DEVTYP_DEVICEINTERFACE:
109
{
110
const DEV_BROADCAST_DEVICEINTERFACE_W *iface = (DEV_BROADCAST_DEVICEINTERFACE_W *)header;
111
UINT data_size = wcslen( iface->dbcc_name ) + 1;
112
113
size = offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceInterface.SymbolicLink[data_size] );
114
if (!(event_data = calloc( 1, size ))) return 0;
115
116
event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
117
event_data->u.DeviceInterface.ClassGuid = iface->dbcc_classguid;
118
memcpy( event_data->u.DeviceInterface.SymbolicLink, iface->dbcc_name, data_size * sizeof(WCHAR) );
119
break;
120
}
121
case DBT_DEVTYP_HANDLE:
122
{
123
const DEV_BROADCAST_HANDLE *handle = (DEV_BROADCAST_HANDLE *)header;
124
UINT data_size = handle->dbch_size - 2 * sizeof(WCHAR) - offsetof( DEV_BROADCAST_HANDLE, dbch_data );
125
126
size = offsetof( CM_NOTIFY_EVENT_DATA, u.DeviceHandle.Data[data_size] );
127
if (!(event_data = calloc( 1, size ))) return 0;
128
129
event_data->FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE;
130
event_data->u.DeviceHandle.EventGuid = handle->dbch_eventguid;
131
event_data->u.DeviceHandle.NameOffset = handle->dbch_nameoffset;
132
event_data->u.DeviceHandle.DataSize = data_size;
133
memcpy( event_data->u.DeviceHandle.Data, handle->dbch_data, data_size );
134
break;
135
}
136
default:
137
FIXME( "Unexpected devicetype value: %#lx\n", header->dbch_devicetype );
138
return 0;
139
}
140
141
ret = ctx->callback( ctx, ctx->user_data, action, event_data, size );
142
free( event_data );
143
return ret;
144
}
145
146
static const char *debugstr_CM_NOTIFY_FILTER( const CM_NOTIFY_FILTER *filter )
147
{
148
if (!filter) return "(null)";
149
switch (filter->FilterType)
150
{
151
case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE:
152
return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE %lu {{%s}}}", filter->cbSize,
153
filter->Flags, filter->Reserved,
154
debugstr_guid( &filter->u.DeviceInterface.ClassGuid ) );
155
case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE:
156
return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%p}}}", filter->cbSize,
157
filter->Flags, filter->Reserved, filter->u.DeviceHandle.hTarget );
158
case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE:
159
return wine_dbg_sprintf( "{%#lx %lx CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE %lu {{%s}}}", filter->cbSize,
160
filter->Flags, filter->Reserved, debugstr_w( filter->u.DeviceInstance.InstanceId ) );
161
default:
162
return wine_dbg_sprintf( "{%#lx %lx (unknown FilterType %d) %lu}", filter->cbSize, filter->Flags,
163
filter->FilterType, filter->Reserved );
164
}
165
}
166
167
static CONFIGRET create_notify_context( const CM_NOTIFY_FILTER *filter, HCMNOTIFICATION *notify_handle,
168
PCM_NOTIFY_CALLBACK callback, void *user_data )
169
{
170
union
171
{
172
DEV_BROADCAST_HDR header;
173
DEV_BROADCAST_DEVICEINTERFACE_W iface;
174
DEV_BROADCAST_HANDLE handle;
175
} notify_filter = {0};
176
struct cm_notify_context *ctx;
177
static const GUID GUID_NULL;
178
179
switch (filter->FilterType)
180
{
181
case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE:
182
notify_filter.iface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
183
if (filter->Flags & CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES)
184
{
185
if (!IsEqualGUID( &filter->u.DeviceInterface.ClassGuid, &GUID_NULL )) return CR_INVALID_DATA;
186
notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_classguid );
187
}
188
else
189
{
190
notify_filter.iface.dbcc_size = offsetof( DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name );
191
notify_filter.iface.dbcc_classguid = filter->u.DeviceInterface.ClassGuid;
192
}
193
break;
194
case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE:
195
notify_filter.handle.dbch_devicetype = DBT_DEVTYP_HANDLE;
196
notify_filter.handle.dbch_size = sizeof(notify_filter.handle);
197
notify_filter.handle.dbch_handle = filter->u.DeviceHandle.hTarget;
198
break;
199
case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE:
200
FIXME( "CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE is not supported!\n" );
201
return CR_CALL_NOT_IMPLEMENTED;
202
default:
203
return CR_INVALID_DATA;
204
}
205
206
if (!(ctx = calloc( 1, sizeof(*ctx) ))) return CR_OUT_OF_MEMORY;
207
208
ctx->user_data = user_data;
209
ctx->callback = callback;
210
if (!(ctx->notify = I_ScRegisterDeviceNotification( ctx, &notify_filter.header, devnotify_callback )))
211
{
212
free( ctx );
213
switch (GetLastError())
214
{
215
case ERROR_NOT_ENOUGH_MEMORY: return CR_OUT_OF_MEMORY;
216
case ERROR_INVALID_PARAMETER: return CR_INVALID_DATA;
217
default: return CR_FAILURE;
218
}
219
}
220
*notify_handle = ctx;
221
return CR_SUCCESS;
222
}
223
224
/***********************************************************************
225
* CM_Register_Notification (cfgmgr32.@)
226
*/
227
CONFIGRET WINAPI CM_Register_Notification( CM_NOTIFY_FILTER *filter, void *context,
228
PCM_NOTIFY_CALLBACK callback, HCMNOTIFICATION *notify_context )
229
{
230
TRACE( "(%s %p %p %p)\n", debugstr_CM_NOTIFY_FILTER( filter ), context, callback, notify_context );
231
232
if (!notify_context) return CR_FAILURE;
233
if (!filter || !callback || filter->cbSize != sizeof(*filter)) return CR_INVALID_DATA;
234
235
return create_notify_context( filter, notify_context, callback, context );
236
}
237
238
/***********************************************************************
239
* CM_Unregister_Notification (cfgmgr32.@)
240
*/
241
CONFIGRET WINAPI CM_Unregister_Notification( HCMNOTIFICATION notify )
242
{
243
struct cm_notify_context *ctx = notify;
244
245
TRACE( "(%p)\n", notify );
246
247
if (!notify) return CR_INVALID_DATA;
248
249
I_ScUnregisterDeviceNotification( ctx->notify );
250
free( ctx );
251
252
return CR_SUCCESS;
253
}
254
255
/***********************************************************************
256
* CM_Get_Device_Interface_PropertyW (cfgmgr32.@)
257
*/
258
CONFIGRET WINAPI CM_Get_Device_Interface_PropertyW( LPCWSTR device_interface, const DEVPROPKEY *property_key,
259
DEVPROPTYPE *property_type, BYTE *property_buffer,
260
ULONG *property_buffer_size, ULONG flags )
261
{
262
SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
263
HDEVINFO set;
264
DWORD err;
265
BOOL ret;
266
267
TRACE( "%s %p %p %p %p %ld.\n", debugstr_w(device_interface), property_key, property_type, property_buffer,
268
property_buffer_size, flags);
269
270
if (!property_key) return CR_FAILURE;
271
if (!device_interface || !property_type || !property_buffer_size) return CR_INVALID_POINTER;
272
if (*property_buffer_size && !property_buffer) return CR_INVALID_POINTER;
273
if (flags) return CR_INVALID_FLAG;
274
275
set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL );
276
if (set == INVALID_HANDLE_VALUE) return CR_OUT_OF_MEMORY;
277
if (!SetupDiOpenDeviceInterfaceW( set, device_interface, 0, &iface ))
278
{
279
SetupDiDestroyDeviceInfoList( set );
280
TRACE( "No interface %s, err %lu.\n", debugstr_w( device_interface ), GetLastError());
281
return CR_NO_SUCH_DEVICE_INTERFACE;
282
}
283
284
ret = SetupDiGetDeviceInterfacePropertyW( set, &iface, property_key, property_type, property_buffer,
285
*property_buffer_size, property_buffer_size, 0 );
286
err = ret ? 0 : GetLastError();
287
SetupDiDestroyDeviceInfoList( set );
288
switch (err)
289
{
290
case ERROR_SUCCESS:
291
return CR_SUCCESS;
292
case ERROR_INSUFFICIENT_BUFFER:
293
return CR_BUFFER_SMALL;
294
case ERROR_NOT_FOUND:
295
return CR_NO_SUCH_VALUE;
296
default:
297
return CR_FAILURE;
298
}
299
}
300
301
static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, const DEVPROPKEY *key, DEVPROPTYPE type,
302
ULONG buf_size, void *buf )
303
{
304
DEVPROPERTY *tmp;
305
306
if (!(tmp = realloc( *properties, (*props_len + 1) * sizeof( **properties ))))
307
return FALSE;
308
*properties = tmp;
309
310
tmp = &tmp[*props_len];
311
tmp->CompKey.Key = *key;
312
tmp->CompKey.Store = DEVPROP_STORE_SYSTEM;
313
tmp->CompKey.LocaleName = NULL;
314
tmp->Type = type;
315
tmp->BufferSize = buf_size;
316
tmp->Buffer = buf;
317
318
*props_len += 1;
319
return TRUE;
320
}
321
322
static const char *debugstr_DEVPROPKEY( const DEVPROPKEY *key )
323
{
324
if (!key) return "(null)";
325
return wine_dbg_sprintf( "{%s, %04lx}", debugstr_guid( &key->fmtid ), key->pid );
326
}
327
328
static const char *debugstr_DEVPROPCOMPKEY( const DEVPROPCOMPKEY *key )
329
{
330
if (!key) return "(null)";
331
return wine_dbg_sprintf( "{%s, %d, %s}", debugstr_DEVPROPKEY( &key->Key ), key->Store,
332
debugstr_w( key->LocaleName ) );
333
}
334
335
static const char *debugstr_DEV_OBJECT( const DEV_OBJECT *obj )
336
{
337
if (!obj) return "(null)";
338
return wine_dbg_sprintf( "{%d, %s, %lu, %p}", obj->ObjectType, debugstr_w( obj->pszObjectId ), obj->cPropertyCount,
339
obj->pProperties );
340
}
341
342
static int devproperty_compare( const void *p1, const void *p2 )
343
{
344
const DEVPROPCOMPKEY *key1 = &((DEVPROPERTY *)p1)->CompKey;
345
const DEVPROPCOMPKEY *key2 = &((DEVPROPERTY *)p2)->CompKey;
346
int cmp = memcmp( key1, key2, offsetof( DEVPROPCOMPKEY, LocaleName ));
347
348
if (cmp)
349
return cmp;
350
if (key1->LocaleName == key2->LocaleName)
351
return 0;
352
if (!key1->LocaleName)
353
return -1;
354
if (!key2->LocaleName)
355
return 1;
356
return wcsicmp( key1->LocaleName, key2->LocaleName );
357
}
358
359
static const char *debugstr_DEVPROP_OPERATOR( DEVPROP_OPERATOR op )
360
{
361
DWORD compare = op & DEVPROP_OPERATOR_MASK_EVAL;
362
DWORD list = op & DEVPROP_OPERATOR_MASK_LIST;
363
DWORD modifier = op & DEVPROP_OPERATOR_MASK_MODIFIER;
364
DWORD logical = op & DEVPROP_OPERATOR_MASK_LOGICAL;
365
DWORD array = op & DEVPROP_OPERATOR_MASK_ARRAY;
366
367
return wine_dbg_sprintf( "(%#lx|%#lx|%#lx|%#lx|%#lx)", list, array, modifier, compare, logical );
368
}
369
370
371
static const char *debugstr_DEVPROP_FILTER_EXPRESSION( const DEVPROP_FILTER_EXPRESSION *filter )
372
{
373
if (!filter) return "(null)";
374
return wine_dbg_sprintf( "{%s, {%s, %#lx, %lu, %p}}", debugstr_DEVPROP_OPERATOR( filter->Operator ),
375
debugstr_DEVPROPCOMPKEY( &filter->Property.CompKey ), filter->Property.Type,
376
filter->Property.BufferSize, filter->Property.Buffer );
377
}
378
379
/* Evaluate a filter expression containing comparison operator. */
380
static HRESULT devprop_filter_eval_compare( const DEV_OBJECT *obj, const DEVPROP_FILTER_EXPRESSION *filter )
381
{
382
const DEVPROPERTY *cmp_prop = &filter->Property;
383
DEVPROP_OPERATOR op = filter->Operator;
384
const DEVPROPERTY *prop = NULL;
385
BOOL ret = FALSE;
386
387
TRACE( "(%s, %s)\n", debugstr_DEV_OBJECT( obj ), debugstr_DEVPROP_FILTER_EXPRESSION( filter ) );
388
389
if ((op & DEVPROP_OPERATOR_MASK_MODIFIER) & ~(DEVPROP_OPERATOR_MODIFIER_NOT | DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE))
390
return E_INVALIDARG;
391
392
switch (filter->Operator & DEVPROP_OPERATOR_MASK_EVAL)
393
{
394
case DEVPROP_OPERATOR_EXISTS:
395
prop = bsearch( &filter->Property.CompKey, obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ),
396
devproperty_compare );
397
ret = prop && prop->Type != DEVPROP_TYPE_EMPTY;
398
break;
399
case DEVPROP_OPERATOR_EQUALS:
400
case DEVPROP_OPERATOR_LESS_THAN:
401
case DEVPROP_OPERATOR_GREATER_THAN:
402
case DEVPROP_OPERATOR_LESS_THAN_EQUALS:
403
case DEVPROP_OPERATOR_GREATER_THAN_EQUALS:
404
{
405
int cmp = 0;
406
407
prop = bsearch( &filter->Property.CompKey, obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ),
408
devproperty_compare );
409
if (prop && cmp_prop->Type == prop->Type && cmp_prop->BufferSize == prop->BufferSize)
410
{
411
switch (prop->Type)
412
{
413
case DEVPROP_TYPE_STRING:
414
cmp = op & DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE ? wcsicmp( prop->Buffer, cmp_prop->Buffer )
415
: wcscmp( prop->Buffer, cmp_prop->Buffer );
416
break;
417
default:
418
cmp = memcmp( prop->Buffer, cmp_prop->Buffer, prop->BufferSize );
419
break;
420
}
421
if (op == DEVPROP_OPERATOR_EQUALS)
422
ret = !cmp;
423
else if (op & DEVPROP_OPERATOR_EQUALS && !cmp)
424
ret = TRUE;
425
else
426
ret = (op & DEVPROP_OPERATOR_LESS_THAN) ? cmp < 0 : cmp > 0;
427
}
428
break;
429
}
430
case DEVPROP_OPERATOR_BITWISE_AND:
431
case DEVPROP_OPERATOR_BITWISE_OR:
432
case DEVPROP_OPERATOR_BEGINS_WITH:
433
case DEVPROP_OPERATOR_ENDS_WITH:
434
case DEVPROP_OPERATOR_CONTAINS:
435
default:
436
FIXME( "Unsupported operator: %s", debugstr_DEVPROP_OPERATOR( filter->Operator & DEVPROP_OPERATOR_MASK_EVAL ) );
437
return S_OK;
438
}
439
440
if (op & DEVPROP_OPERATOR_MODIFIER_NOT)
441
ret = !ret;
442
return ret ? S_OK : S_FALSE;
443
}
444
445
/* Return S_OK if the specified filter expressions match the object, S_FALSE if it doesn't. */
446
static HRESULT devprop_filter_matches_object( const DEV_OBJECT *obj, ULONG filters_len,
447
const DEVPROP_FILTER_EXPRESSION *filters )
448
{
449
HRESULT hr = S_OK;
450
ULONG i;
451
452
TRACE( "(%s, %lu, %p)\n", debugstr_DEV_OBJECT( obj ), filters_len, filters );
453
454
if (!filters_len)
455
return S_OK;
456
457
/* By default, the evaluation is performed by AND-ing all individual filter expressions. */
458
for (i = 0; i < filters_len; i++)
459
{
460
const DEVPROP_FILTER_EXPRESSION *filter = &filters[i];
461
DEVPROP_OPERATOR op = filter->Operator;
462
463
if (op == DEVPROP_OPERATOR_NONE)
464
{
465
hr = S_FALSE;
466
break;
467
}
468
if (op & (DEVPROP_OPERATOR_MASK_LIST | DEVPROP_OPERATOR_MASK_ARRAY))
469
{
470
FIXME( "Unsupported list/array operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) );
471
continue;
472
}
473
if (op & DEVPROP_OPERATOR_MASK_LOGICAL)
474
{
475
FIXME( "Unsupported logical operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) );
476
continue;
477
}
478
if (op & DEVPROP_OPERATOR_MASK_EVAL)
479
{
480
hr = devprop_filter_eval_compare( obj, filter );
481
if (FAILED( hr ) || hr == S_FALSE)
482
break;
483
}
484
}
485
486
return hr;
487
}
488
489
static HRESULT stack_push( DEVPROP_OPERATOR **stack, ULONG *len, DEVPROP_OPERATOR op )
490
{
491
DEVPROP_OPERATOR *tmp;
492
493
if (!(tmp = realloc( *stack, (*len + 1) * sizeof( op ) )))
494
return E_OUTOFMEMORY;
495
*stack = tmp;
496
tmp[*len] = op;
497
*len += 1;
498
return S_OK;
499
}
500
501
static DEVPROP_OPERATOR stack_pop( DEVPROP_OPERATOR **stack, ULONG *len )
502
{
503
DEVPROP_OPERATOR op = DEVPROP_OPERATOR_NONE;
504
505
if (*len)
506
{
507
op = (*stack)[*len - 1];
508
*len -= 1;
509
}
510
return op;
511
}
512
513
static BOOL devprop_type_validate( DEVPROPTYPE type, ULONG buf_size )
514
{
515
static const DWORD type_size[] = {
516
0, 0,
517
sizeof( BYTE ), sizeof( BYTE ),
518
sizeof( INT16 ), sizeof( INT16 ),
519
sizeof( INT32 ), sizeof( INT32 ),
520
sizeof( INT64 ), sizeof( INT64 ),
521
sizeof( FLOAT ), sizeof( DOUBLE ), sizeof( DECIMAL ),
522
sizeof( GUID ),
523
sizeof( CURRENCY ),
524
sizeof( DATE ),
525
sizeof( FILETIME ),
526
sizeof( DEVPROP_BOOLEAN ),
527
[DEVPROP_TYPE_DEVPROPKEY] = sizeof( DEVPROPKEY ),
528
[DEVPROP_TYPE_DEVPROPTYPE] = sizeof( DEVPROPTYPE ),
529
[DEVPROP_TYPE_ERROR] = sizeof( ULONG ),
530
[DEVPROP_TYPE_NTSTATUS] = sizeof( NTSTATUS )
531
};
532
DWORD mod = type & DEVPROP_MASK_TYPEMOD, size;
533
534
if (mod && mod != DEVPROP_TYPEMOD_ARRAY && mod != DEVPROP_TYPEMOD_LIST)
535
{
536
FIXME( "Unknown DEVPROPTYPE value: %#lx\n", type );
537
return FALSE;
538
}
539
540
switch (type & DEVPROP_MASK_TYPE)
541
{
542
case DEVPROP_TYPE_EMPTY:
543
case DEVPROP_TYPE_NULL:
544
return !mod;
545
case DEVPROP_TYPE_SECURITY_DESCRIPTOR:
546
case DEVPROP_TYPE_STRING_INDIRECT:
547
return !mod && !!buf_size;
548
549
case DEVPROP_TYPE_STRING:
550
case DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING:
551
return mod != DEVPROP_TYPEMOD_ARRAY && !!buf_size;
552
default:
553
/* The only valid modifier for the remaining types is DEVPROP_TYPEMOD_ARRAY */
554
if ((type & DEVPROP_MASK_TYPE) > DEVPROP_TYPE_NTSTATUS ||
555
(mod && mod != DEVPROP_TYPEMOD_ARRAY))
556
{
557
FIXME( "Unknown DEVPROPTYPE value: %#lx\n", type );
558
return FALSE;
559
}
560
size = type_size[type & DEVPROP_MASK_TYPE];
561
}
562
563
return mod == DEVPROP_TYPEMOD_ARRAY ? buf_size >= size : buf_size == size;
564
}
565
566
static HRESULT devprop_filters_validate( ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters )
567
{
568
DEVPROP_OPERATOR *stack = NULL;
569
ULONG i, logical_open = 0, stack_top = 0;
570
HRESULT hr = S_OK;
571
572
for (i = 0; i < filters_len; i++)
573
{
574
const DEVPROP_FILTER_EXPRESSION *filter = &filters[i];
575
const DEVPROPERTY *prop = &filter->Property;
576
DEVPROP_OPERATOR op = filter->Operator;
577
DWORD compare = op & DEVPROP_OPERATOR_MASK_EVAL;
578
DWORD list = op & DEVPROP_OPERATOR_MASK_LIST;
579
DWORD modifier = op & DEVPROP_OPERATOR_MASK_MODIFIER;
580
DWORD logical = op & DEVPROP_OPERATOR_MASK_LOGICAL;
581
DWORD array = op & DEVPROP_OPERATOR_MASK_ARRAY;
582
583
if ((compare && compare > DEVPROP_OPERATOR_CONTAINS)
584
|| (logical && (op & DEVPROP_OPERATOR_MASK_NOT_LOGICAL))
585
|| (array && (op != DEVPROP_OPERATOR_ARRAY_CONTAINS))
586
|| !!prop->Buffer != !!prop->BufferSize)
587
{
588
FIXME( "Unknown operator: %#x\n", op );
589
hr = E_INVALIDARG;
590
break;
591
}
592
if (!op) continue;
593
if (compare && compare != DEVPROP_OPERATOR_EXISTS
594
&& !devprop_type_validate( prop->Type, prop->BufferSize ))
595
{
596
hr = E_INVALIDARG;
597
break;
598
}
599
600
switch (modifier)
601
{
602
case DEVPROP_OPERATOR_NONE:
603
case DEVPROP_OPERATOR_MODIFIER_NOT:
604
case DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE:
605
break;
606
default:
607
hr = E_INVALIDARG;
608
break;
609
}
610
611
switch (list)
612
{
613
case DEVPROP_OPERATOR_NONE:
614
case DEVPROP_OPERATOR_LIST_CONTAINS:
615
case DEVPROP_OPERATOR_LIST_ELEMENT_BEGINS_WITH:
616
case DEVPROP_OPERATOR_LIST_ELEMENT_ENDS_WITH:
617
case DEVPROP_OPERATOR_LIST_ELEMENT_CONTAINS:
618
break;
619
default:
620
hr = E_INVALIDARG;
621
break;
622
}
623
624
switch (logical)
625
{
626
case DEVPROP_OPERATOR_NONE:
627
break;
628
case DEVPROP_OPERATOR_AND_OPEN:
629
case DEVPROP_OPERATOR_OR_OPEN:
630
case DEVPROP_OPERATOR_NOT_OPEN:
631
hr = stack_push( &stack, &stack_top, logical );
632
logical_open = i;
633
break;
634
case DEVPROP_OPERATOR_AND_CLOSE:
635
case DEVPROP_OPERATOR_OR_CLOSE:
636
case DEVPROP_OPERATOR_NOT_CLOSE:
637
{
638
DEVPROP_OPERATOR top = stack_pop( &stack, &stack_top );
639
/* The operator should be correct paired, and shouldn't be empty. */
640
if (logical - top != (DEVPROP_OPERATOR_AND_CLOSE - DEVPROP_OPERATOR_AND_OPEN) || logical_open == i - 1)
641
hr = E_INVALIDARG;
642
break;
643
}
644
default:
645
hr = E_INVALIDARG;
646
break;
647
}
648
649
if (FAILED( hr )) break;
650
}
651
652
if (stack_top)
653
hr = E_INVALIDARG;
654
free( stack );
655
return hr;
656
}
657
658
static HRESULT dev_object_iface_get_props( DEV_OBJECT *obj, HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface_data,
659
ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props, BOOL sort )
660
{
661
DEVPROPKEY *all_keys = NULL;
662
DWORD keys_len = 0, i = 0;
663
HRESULT hr = S_OK;
664
665
obj->cPropertyCount = 0;
666
obj->pProperties = NULL;
667
if (!props && !all_props)
668
return S_OK;
669
670
if (all_props)
671
{
672
DWORD req = 0;
673
if (SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, NULL, 0, &req, 0 )
674
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER)
675
return HRESULT_FROM_WIN32( GetLastError() );
676
677
keys_len = req;
678
if (!(all_keys = calloc( keys_len, sizeof( *all_keys ) )))
679
return E_OUTOFMEMORY;
680
if (!SetupDiGetDeviceInterfacePropertyKeys( set, iface_data, all_keys, keys_len, &req, 0 ))
681
{
682
free( all_keys );
683
return HRESULT_FROM_WIN32( GetLastError() );
684
}
685
}
686
else
687
keys_len = props_len;
688
689
for (i = 0; i < keys_len; i++)
690
{
691
const DEVPROPKEY *key = all_keys ? &all_keys[i] : &props[i].Key;
692
DWORD req = 0, size;
693
DEVPROPTYPE type;
694
BYTE *buf;
695
696
if (props && props[i].Store != DEVPROP_STORE_SYSTEM)
697
{
698
FIXME( "Unsupported Store value: %d\n", props[i].Store );
699
continue;
700
}
701
if (SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, NULL, 0, &req, 0 )
702
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER)
703
{
704
if (props && !dev_properties_append( (DEVPROPERTY **)&obj->pProperties, &obj->cPropertyCount, key,
705
DEVPROP_TYPE_EMPTY, 0, NULL ))
706
{
707
hr = E_OUTOFMEMORY;
708
goto done;
709
}
710
continue;
711
}
712
713
size = req;
714
if (!(buf = calloc( 1, size )))
715
{
716
hr = E_OUTOFMEMORY;
717
goto done;
718
}
719
if (!SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, buf, size, &req, 0 ))
720
{
721
hr = HRESULT_FROM_WIN32( GetLastError() );
722
free( buf );
723
goto done;
724
}
725
if (!dev_properties_append( (DEVPROPERTY **)&obj->pProperties, &obj->cPropertyCount, key, type, size, buf ))
726
{
727
free( buf );
728
hr = E_OUTOFMEMORY;
729
goto done;
730
}
731
}
732
733
done:
734
free( all_keys );
735
if (FAILED( hr ))
736
{
737
DevFreeObjectProperties( obj->cPropertyCount, obj->pProperties );
738
obj->cPropertyCount = 0;
739
obj->pProperties = NULL;
740
}
741
else if (sort) /* Sort properties by DEVPROPCOMPKEY for faster filter evaluation. */
742
qsort( (DEVPROPERTY *)obj->pProperties, obj->cPropertyCount, sizeof( *obj->pProperties ), devproperty_compare );
743
return hr;
744
}
745
746
typedef HRESULT (*enum_device_object_cb)( DEV_OBJECT object, void *context );
747
748
static void dev_object_remove_unwanted_props( DEV_OBJECT *obj, ULONG keys_len, const DEVPROPCOMPKEY *keys_wanted )
749
{
750
DEVPROPERTY *props = (DEVPROPERTY *)obj->pProperties;
751
ULONG i = 0, j;
752
753
if (!keys_len)
754
{
755
DevFreeObjectProperties( obj->cPropertyCount, obj->pProperties );
756
obj->cPropertyCount = 0;
757
obj->pProperties = NULL;
758
}
759
760
while (i < obj->cPropertyCount)
761
{
762
BOOL found = FALSE;
763
764
for (j = 0; j < keys_len; j++)
765
{
766
if (IsEqualDevPropCompKey( props[i].CompKey, keys_wanted[j] ))
767
{
768
found = TRUE;
769
break;
770
}
771
}
772
if (!found)
773
{
774
free( obj->pProperties[i].Buffer );
775
props[i] = props[obj->cPropertyCount - 1];
776
obj->cPropertyCount--;
777
}
778
else
779
i++;
780
}
781
}
782
783
static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props,
784
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters,
785
enum_device_object_cb callback, void *data )
786
{
787
HKEY iface_key;
788
HRESULT hr = S_OK;
789
790
DWORD i;
791
792
switch (type)
793
{
794
case DevObjectTypeDeviceInterface:
795
case DevObjectTypeDeviceInterfaceDisplay:
796
break;
797
case DevObjectTypeDeviceContainer:
798
case DevObjectTypeDevice:
799
case DevObjectTypeDeviceInterfaceClass:
800
case DevObjectTypeAEP:
801
case DevObjectTypeAEPContainer:
802
case DevObjectTypeDeviceInstallerClass:
803
case DevObjectTypeDeviceContainerDisplay:
804
case DevObjectTypeAEPService:
805
case DevObjectTypeDevicePanel:
806
case DevObjectTypeAEPProtocol:
807
FIXME("Unsupported DEV_OJBECT_TYPE: %d\n", type );
808
default:
809
return S_OK;
810
}
811
812
if (!(iface_key = SetupDiOpenClassRegKeyExW( NULL, KEY_ENUMERATE_SUB_KEYS, DIOCR_INTERFACE, NULL, NULL )))
813
return HRESULT_FROM_WIN32( GetLastError() );
814
815
for (i = 0; SUCCEEDED( hr ); i++)
816
{
817
char buffer[sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA_W ) + MAX_PATH * sizeof( WCHAR )];
818
SP_DEVICE_INTERFACE_DATA iface = {.cbSize = sizeof( iface )};
819
SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer;
820
HDEVINFO set = INVALID_HANDLE_VALUE;
821
WCHAR iface_guid_str[40];
822
DWORD ret, len, j;
823
GUID iface_class;
824
825
len = ARRAY_SIZE( iface_guid_str );
826
ret = RegEnumKeyExW( iface_key, i, iface_guid_str, &len, NULL, NULL, NULL, NULL );
827
if (ret)
828
{
829
hr = (ret == ERROR_NO_MORE_ITEMS) ? S_OK : HRESULT_FROM_WIN32( ret );
830
break;
831
}
832
833
iface_guid_str[37] = '\0';
834
if (!UuidFromStringW( &iface_guid_str[1], &iface_class ))
835
{
836
set = SetupDiGetClassDevsW( &iface_class, NULL, NULL, DIGCF_DEVICEINTERFACE );
837
if (set == INVALID_HANDLE_VALUE) hr = HRESULT_FROM_WIN32( GetLastError() );
838
}
839
else
840
{
841
ERR( "Could not parse device interface GUID %s\n", debugstr_w( iface_guid_str ) );
842
continue;
843
}
844
845
for (j = 0; SUCCEEDED( hr ) && SetupDiEnumDeviceInterfaces( set, NULL, &iface_class, j, &iface ); j++)
846
{
847
DEV_OBJECT obj = {0};
848
849
detail->cbSize = sizeof( *detail );
850
if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof( buffer ), NULL, NULL )) continue;
851
852
obj.ObjectType = type;
853
obj.pszObjectId = detail->DevicePath;
854
/* If we're also filtering objects, get all properties for this object. Once the filters have been
855
* evaluated, free properties that have not been requested, and set cPropertyCount to props_len. */
856
if (filters_len)
857
hr = dev_object_iface_get_props( &obj, set, &iface, 0, NULL, TRUE, TRUE );
858
else
859
hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props, FALSE );
860
if (SUCCEEDED( hr ))
861
{
862
if (filters_len)
863
{
864
hr = devprop_filter_matches_object( &obj, filters_len, filters );
865
/* Shrink pProperties to only the desired ones, unless DevQueryFlagAllProperties is set. */
866
if (!all_props)
867
dev_object_remove_unwanted_props( &obj, props_len, props );
868
}
869
if (hr == S_OK)
870
hr = callback( obj, data );
871
else
872
DevFreeObjectProperties( obj.cPropertyCount, obj.pProperties );
873
}
874
}
875
876
if (set != INVALID_HANDLE_VALUE)
877
SetupDiDestroyDeviceInfoList( set );
878
}
879
RegCloseKey( iface_key );
880
return SUCCEEDED( hr ) ? S_OK : hr;
881
}
882
883
struct objects_list
884
{
885
DEV_OBJECT *objects;
886
ULONG len;
887
};
888
889
static HRESULT dev_objects_append( DEV_OBJECT obj, void *data )
890
{
891
struct objects_list *objects = data;
892
WCHAR *id;
893
DEV_OBJECT *tmp;
894
895
if (!(id = wcsdup( obj.pszObjectId )))
896
return E_OUTOFMEMORY;
897
if (!(tmp = realloc( objects->objects, (objects->len + 1) * sizeof( obj ) )))
898
{
899
free( id );
900
return E_OUTOFMEMORY;
901
}
902
903
objects->objects = tmp;
904
tmp = &tmp[objects->len++];
905
*tmp = obj;
906
tmp->pszObjectId = id;
907
return S_OK;
908
}
909
910
HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
911
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG *objs_len,
912
const DEV_OBJECT **objs )
913
{
914
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, objs_len, objs );
915
return DevGetObjectsEx( type, flags, props_len, props, filters_len, filters, 0, NULL, objs_len, objs );
916
}
917
918
HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
919
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG params_len,
920
const DEV_QUERY_PARAMETER *params, ULONG *objs_len, const DEV_OBJECT **objs )
921
{
922
ULONG valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize;
923
struct objects_list objects = {0};
924
HRESULT hr = S_OK;
925
926
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters,
927
params_len, params, objs_len, objs );
928
929
if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags)
930
|| (props_len && (flags & DevQueryFlagAllProperties))
931
|| FAILED( devprop_filters_validate( filters_len, filters ) ))
932
return E_INVALIDARG;
933
if (params)
934
FIXME( "Query parameters are not supported!\n" );
935
936
*objs = NULL;
937
*objs_len = 0;
938
939
hr = enum_dev_objects( type, props_len, props, !!(flags & DevQueryFlagAllProperties), filters_len, filters,
940
dev_objects_append, &objects );
941
if (SUCCEEDED( hr ))
942
{
943
*objs = objects.objects;
944
*objs_len = objects.len;
945
}
946
else
947
DevFreeObjects( objects.len, objects.objects );
948
949
return hr;
950
}
951
952
void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs )
953
{
954
DEV_OBJECT *objects = (DEV_OBJECT *)objs;
955
ULONG i;
956
957
TRACE( "(%lu, %p)\n", objs_len, objs );
958
959
for (i = 0; i < objs_len; i++)
960
{
961
DevFreeObjectProperties( objects[i].cPropertyCount, objects[i].pProperties );
962
free( (void *)objects[i].pszObjectId );
963
}
964
free( objects );
965
return;
966
}
967
968
struct device_iface_path
969
{
970
struct rb_entry entry;
971
WCHAR *path;
972
};
973
974
static int device_iface_path_cmp( const void *key, const struct rb_entry *entry )
975
{
976
const struct device_iface_path *iface = RB_ENTRY_VALUE( entry, struct device_iface_path, entry );
977
return wcsicmp( key, iface->path );
978
}
979
980
struct device_query_context
981
{
982
LONG ref;
983
DEV_OBJECT_TYPE type;
984
ULONG flags;
985
ULONG prop_keys_len;
986
DEVPROPCOMPKEY *prop_keys;
987
BOOL filters;
988
989
CRITICAL_SECTION cs;
990
PDEV_QUERY_RESULT_CALLBACK callback;
991
void *user_data;
992
DEV_QUERY_STATE state;
993
HANDLE closed;
994
struct rb_tree known_ifaces;
995
HCMNOTIFICATION notify;
996
};
997
998
static HRESULT device_query_context_add_object( DEV_OBJECT obj, void *data )
999
{
1000
DEV_QUERY_RESULT_ACTION_DATA action_data = {0};
1001
struct device_query_context *ctx = data;
1002
struct device_iface_path *iface_entry = NULL;
1003
HRESULT hr = S_OK;
1004
1005
TRACE( "(%s, %p)\n", debugstr_w( obj.pszObjectId ), data );
1006
1007
action_data.Action = DevQueryResultAdd;
1008
action_data.Data.DeviceObject = obj;
1009
ctx->callback( (HDEVQUERY)ctx, ctx->user_data, &action_data );
1010
1011
EnterCriticalSection( &ctx->cs );
1012
if (ctx->state == DevQueryStateClosed)
1013
hr = E_CHANGED_STATE;
1014
else if (obj.ObjectType == DevObjectTypeDeviceInterface || obj.ObjectType == DevObjectTypeDeviceInterfaceDisplay)
1015
{
1016
if (!(iface_entry = calloc( 1, sizeof( *iface_entry ) )) || !(iface_entry->path = wcsdup( obj.pszObjectId )))
1017
{
1018
if (iface_entry) free( iface_entry->path );
1019
free( iface_entry );
1020
hr = E_OUTOFMEMORY;
1021
}
1022
else if (rb_put( &ctx->known_ifaces, iface_entry->path, &iface_entry->entry ))
1023
{
1024
free( iface_entry->path );
1025
free( iface_entry );
1026
}
1027
}
1028
LeaveCriticalSection( &ctx->cs );
1029
1030
DevFreeObjectProperties( obj.cPropertyCount, obj.pProperties );
1031
return hr;
1032
}
1033
1034
static HRESULT device_query_context_create( struct device_query_context **query, DEV_OBJECT_TYPE type, ULONG flags,
1035
ULONG props_len, const DEVPROPCOMPKEY *props,
1036
PDEV_QUERY_RESULT_CALLBACK callback, void *user_data )
1037
{
1038
struct device_query_context *ctx;
1039
ULONG i;
1040
1041
if (!(ctx = calloc( 1, sizeof( *ctx ))))
1042
return E_OUTOFMEMORY;
1043
ctx->ref = 1;
1044
if (!(flags & DevQueryFlagAsyncClose))
1045
{
1046
ctx->closed = CreateEventW( NULL, FALSE, FALSE, NULL );
1047
if (ctx->closed == INVALID_HANDLE_VALUE)
1048
{
1049
free( ctx );
1050
return HRESULT_FROM_WIN32( GetLastError() );
1051
}
1052
}
1053
ctx->prop_keys_len = props_len;
1054
if (props_len && !(ctx->prop_keys = calloc( props_len, sizeof( *props ) )))
1055
{
1056
if (ctx->closed) CloseHandle( ctx->closed );
1057
free( ctx );
1058
return E_OUTOFMEMORY;
1059
}
1060
for (i = 0; i < props_len; i++)
1061
{
1062
ctx->prop_keys[i].Key = props[i].Key;
1063
ctx->prop_keys[i].Store = props[i].Store;
1064
}
1065
InitializeCriticalSectionEx( &ctx->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO );
1066
ctx->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": device_query_context.cs");
1067
ctx->type = type;
1068
ctx->flags = flags;
1069
ctx->callback = callback;
1070
ctx->user_data = user_data;
1071
ctx->state = DevQueryStateInitialized;
1072
rb_init( &ctx->known_ifaces, device_iface_path_cmp );
1073
1074
*query = ctx;
1075
return S_OK;
1076
}
1077
1078
static void device_query_context_addref( struct device_query_context *ctx )
1079
{
1080
InterlockedIncrement( &ctx->ref );
1081
}
1082
1083
static void device_iface_path_free( struct rb_entry *entry, void *data )
1084
{
1085
struct device_iface_path *path = RB_ENTRY_VALUE( entry, struct device_iface_path, entry );
1086
free( path->path );
1087
free( path );
1088
}
1089
1090
static void device_query_context_release( struct device_query_context *ctx )
1091
{
1092
if (!InterlockedDecrement( &ctx->ref ))
1093
{
1094
free( ctx->prop_keys );
1095
ctx->cs.DebugInfo->Spare[0] = 0;
1096
DeleteCriticalSection( &ctx->cs );
1097
if (ctx->closed) CloseHandle( ctx->closed );
1098
rb_destroy( &ctx->known_ifaces, device_iface_path_free, NULL );
1099
free( ctx );
1100
}
1101
}
1102
1103
static void device_query_context_notify_state_change( struct device_query_context *ctx, DEV_QUERY_STATE state )
1104
{
1105
DEV_QUERY_RESULT_ACTION_DATA action_data = {0};
1106
1107
action_data.Action = DevQueryResultStateChange;
1108
action_data.Data.State = state;
1109
ctx->callback( (HDEVQUERY)ctx, ctx->user_data, &action_data );
1110
}
1111
1112
static void CALLBACK device_query_context_notify_enum_completed_async( TP_CALLBACK_INSTANCE *instance, void *data )
1113
{
1114
device_query_context_notify_state_change( data, DevQueryStateEnumCompleted );
1115
device_query_context_release( data );
1116
}
1117
1118
static void CALLBACK device_query_context_notify_closed_async( TP_CALLBACK_INSTANCE *instance, void *data )
1119
{
1120
device_query_context_notify_state_change( data, DevQueryStateClosed );
1121
device_query_context_release( data );
1122
}
1123
1124
static void CALLBACK device_query_context_notify_aborted_async( TP_CALLBACK_INSTANCE *instance, void *data )
1125
{
1126
device_query_context_notify_state_change( data, DevQueryStateAborted );
1127
device_query_context_release( data );
1128
}
1129
1130
struct devquery_notify_data
1131
{
1132
DEV_QUERY_RESULT_ACTION_DATA action_data;
1133
struct device_query_context *ctx;
1134
};
1135
1136
static void CALLBACK device_query_notify_dev_async( TP_CALLBACK_INSTANCE *instance, void *notify_data )
1137
{
1138
struct devquery_notify_data *data = notify_data;
1139
1140
data->ctx->callback( (HDEVQUERY)data->ctx, data->ctx->user_data, &data->action_data );
1141
if (data->action_data.Action != DevQueryResultStateChange)
1142
DevFreeObjectProperties( data->action_data.Data.DeviceObject.cPropertyCount,
1143
data->action_data.Data.DeviceObject.pProperties );
1144
device_query_context_release( data->ctx );
1145
free( data );
1146
}
1147
1148
static const char *debugstr_CM_NOTIFY_ACTION( CM_NOTIFY_ACTION action )
1149
{
1150
static const char *str[] = {
1151
"CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL",
1152
"CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL",
1153
"CM_NOTIFY_ACTION_DEVICEQUERYREMOVE",
1154
"CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED",
1155
"CM_NOTIFY_ACTION_DEVICEREMOVEPENDING",
1156
"CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE",
1157
"CM_NOTIFY_ACTION_DEVICECUSTOMEVENT",
1158
"CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED",
1159
"CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED",
1160
"CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED",
1161
};
1162
1163
return action < ARRAY_SIZE( str ) ? str[action] : wine_dbg_sprintf( "(unknown %d)", action );
1164
}
1165
1166
static const char *debugstr_CM_NOTIFY_EVENT_DATA( const CM_NOTIFY_EVENT_DATA *event )
1167
{
1168
if (!event) return wine_dbg_sprintf( "(null)" );
1169
switch (event->FilterType)
1170
{
1171
case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE:
1172
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE %lu {{%s %s}}}", event->Reserved,
1173
debugstr_guid( &event->u.DeviceInterface.ClassGuid ),
1174
debugstr_w( event->u.DeviceInterface.SymbolicLink ) );
1175
case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE:
1176
if (event->u.DeviceHandle.NameOffset == -1)
1177
{
1178
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%s -1 %lu %p}}}", event->Reserved,
1179
debugstr_guid( &event->u.DeviceHandle.EventGuid ),
1180
event->u.DeviceHandle.DataSize, event->u.DeviceHandle.Data );
1181
}
1182
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%s %ld %lu %p %s}}}", event->Reserved,
1183
debugstr_guid( &event->u.DeviceHandle.EventGuid ), event->u.DeviceHandle.NameOffset,
1184
event->u.DeviceHandle.DataSize, event->u.DeviceHandle.Data,
1185
debugstr_w( (WCHAR *)&event->u.DeviceHandle.Data[event->u.DeviceHandle.NameOffset] ) );
1186
case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE:
1187
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE %lu %s}", event->Reserved,
1188
debugstr_w( event->u.DeviceInstance.InstanceId ) );
1189
default:
1190
return wine_dbg_sprintf( "{(unknown %d) %lu}", event->FilterType, event->Reserved );
1191
}
1192
}
1193
1194
static DWORD CALLBACK device_query_context_cm_notify_callback( HCMNOTIFICATION notify, void *data,
1195
CM_NOTIFY_ACTION action,
1196
CM_NOTIFY_EVENT_DATA *event, DWORD event_size )
1197
{
1198
struct device_query_context *ctx = data;
1199
const ULONG prop_flags = ctx->flags & (DevQueryFlagAllProperties | DevQueryFlagLocalize);
1200
DEV_QUERY_RESULT_ACTION_DATA *action_data;
1201
struct devquery_notify_data *notify_data;
1202
struct device_iface_path *iface_entry;
1203
const WCHAR *iface_path;
1204
struct rb_entry *entry;
1205
HRESULT hr = S_OK;
1206
1207
TRACE( "(%p, %p, %s, %s, %lu)\n", notify, data, debugstr_CM_NOTIFY_ACTION( action ),
1208
debugstr_CM_NOTIFY_EVENT_DATA( event ), event_size );
1209
1210
if (action != CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL && action != CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL)
1211
{
1212
FIXME( "Unexpected CM_NOTIFY_ACTION value: %d\n", action );
1213
return 0;
1214
}
1215
1216
iface_path = event->u.DeviceInterface.SymbolicLink;
1217
EnterCriticalSection( &ctx->cs );
1218
if (ctx->state == DevQueryStateClosed || ctx->state == DevQueryStateAborted)
1219
{
1220
LeaveCriticalSection( &ctx->cs );
1221
return 0;
1222
}
1223
1224
if (!(notify_data = calloc( 1, sizeof ( *notify_data ) )))
1225
goto abort;
1226
action_data = &notify_data->action_data;
1227
notify_data->ctx = ctx;
1228
action_data->Data.DeviceObject.ObjectType = ctx->type;
1229
1230
/* Interface removal and arrival for objects that have already been enumerated. */
1231
if ((entry = rb_get( &ctx->known_ifaces, iface_path )))
1232
{
1233
const DEVPROP_BOOLEAN enabled = action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ? DEVPROP_TRUE : DEVPROP_FALSE;
1234
DEVPROPERTY *prop;
1235
1236
if (!(prop = calloc( 1, sizeof( *prop ))))
1237
goto abort;
1238
if (!(prop->Buffer = calloc( 1, sizeof( enabled ) )))
1239
{
1240
free( prop );
1241
goto abort;
1242
}
1243
prop->CompKey.Key = DEVPKEY_DeviceInterface_Enabled;
1244
prop->Type = DEVPROP_TYPE_BOOLEAN;
1245
prop->BufferSize = sizeof( enabled );
1246
memcpy( prop->Buffer, &enabled, sizeof( enabled ) );
1247
iface_entry = RB_ENTRY_VALUE( entry, struct device_iface_path, entry );
1248
action_data->Action = DevQueryResultUpdate;
1249
action_data->Data.DeviceObject.cPropertyCount = 1;
1250
action_data->Data.DeviceObject.pProperties = prop;
1251
}
1252
else
1253
{
1254
if (!(iface_entry = calloc( 1, sizeof( *iface_entry ) )))
1255
goto abort;
1256
if (!(iface_entry->path = wcsdup( iface_path )))
1257
{
1258
free( iface_entry );
1259
goto abort;
1260
}
1261
rb_put( &ctx->known_ifaces, iface_path, &iface_entry->entry );
1262
hr = DevGetObjectProperties( ctx->type, iface_path, prop_flags, ctx->prop_keys_len, ctx->prop_keys,
1263
&action_data->Data.DeviceObject.cPropertyCount,
1264
&action_data->Data.DeviceObject.pProperties );
1265
if (FAILED( hr ))
1266
goto abort;
1267
action_data->Action = DevQueryResultAdd;
1268
}
1269
1270
action_data->Data.DeviceObject.pszObjectId = iface_entry->path;
1271
device_query_context_addref( ctx );
1272
if (!TrySubmitThreadpoolCallback( device_query_notify_dev_async, notify_data, NULL ))
1273
device_query_context_release( ctx );
1274
LeaveCriticalSection( &ctx->cs );
1275
return 0;
1276
abort:
1277
free( notify_data );
1278
ctx->state = DevQueryStateAborted;
1279
device_query_context_addref( ctx );
1280
if (!TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL ))
1281
device_query_context_release( ctx );
1282
LeaveCriticalSection( &ctx->cs );
1283
return 0;
1284
}
1285
1286
static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *instance, void *data )
1287
{
1288
struct device_query_context *ctx = data;
1289
BOOL success;
1290
HRESULT hr = S_OK;
1291
1292
if (!ctx->filters)
1293
hr = enum_dev_objects( ctx->type, ctx->prop_keys_len, ctx->prop_keys, !!(ctx->flags & DevQueryFlagAllProperties),
1294
0, NULL, device_query_context_add_object, ctx );
1295
1296
EnterCriticalSection( &ctx->cs );
1297
if (ctx->state == DevQueryStateClosed)
1298
hr = E_CHANGED_STATE;
1299
1300
switch (hr)
1301
{
1302
case S_OK:
1303
ctx->state = DevQueryStateEnumCompleted;
1304
success = TrySubmitThreadpoolCallback( device_query_context_notify_enum_completed_async, ctx, NULL );
1305
if (ctx->filters || !(ctx->flags & DevQueryFlagUpdateResults))
1306
break;
1307
switch (ctx->type)
1308
{
1309
case DevObjectTypeDeviceInterface:
1310
case DevObjectTypeDeviceInterfaceDisplay:
1311
{
1312
CM_NOTIFY_FILTER filter = { 0 };
1313
CONFIGRET ret;
1314
1315
filter.cbSize = sizeof( filter );
1316
filter.Flags = CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES;
1317
filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
1318
device_query_context_addref( ctx );
1319
ret = CM_Register_Notification( &filter, ctx, device_query_context_cm_notify_callback, &ctx->notify );
1320
if (ret)
1321
{
1322
ERR( "CM_Register_Notification failed: %lu\n", ret );
1323
device_query_context_release( ctx );
1324
goto abort;
1325
}
1326
break;
1327
}
1328
default:
1329
FIXME( "Device updates not supported for object type %d\n", ctx->type );
1330
break;
1331
}
1332
break;
1333
case E_CHANGED_STATE:
1334
if (!(ctx->flags & DevQueryFlagAsyncClose))
1335
{
1336
LeaveCriticalSection( &ctx->cs );
1337
SetEvent( ctx->closed );
1338
device_query_context_release( ctx );
1339
return;
1340
}
1341
success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL );
1342
break;
1343
default:
1344
goto abort;
1345
}
1346
1347
LeaveCriticalSection( &ctx->cs );
1348
if (!success)
1349
device_query_context_release( ctx );
1350
return;
1351
abort:
1352
ctx->state = DevQueryStateAborted;
1353
success = TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL );
1354
LeaveCriticalSection( &ctx->cs );
1355
if (!success)
1356
device_query_context_release( ctx );
1357
}
1358
1359
HRESULT WINAPI DevCreateObjectQuery( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
1360
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters,
1361
PDEV_QUERY_RESULT_CALLBACK callback, void *user_data, HDEVQUERY *devquery )
1362
{
1363
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, callback,
1364
user_data, devquery );
1365
return DevCreateObjectQueryEx( type, flags, props_len, props, filters_len, filters, 0, NULL, callback, user_data,
1366
devquery );
1367
}
1368
1369
HRESULT WINAPI DevCreateObjectQueryEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
1370
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG params_len,
1371
const DEV_QUERY_PARAMETER *params, PDEV_QUERY_RESULT_CALLBACK callback,
1372
void *user_data, HDEVQUERY *devquery )
1373
{
1374
ULONG valid_flags = DevQueryFlagUpdateResults | DevQueryFlagAllProperties | DevQueryFlagLocalize | DevQueryFlagAsyncClose;
1375
struct device_query_context *ctx = NULL;
1376
HRESULT hr;
1377
1378
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p, %p)\n", type, flags, props_len, props, filters_len,
1379
filters, params_len, params, callback, user_data, devquery );
1380
1381
if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags) || !callback
1382
|| (props_len && (flags & DevQueryFlagAllProperties))
1383
|| FAILED( devprop_filters_validate( filters_len, filters ) ))
1384
return E_INVALIDARG;
1385
if (filters)
1386
FIXME( "Query filters are not supported!\n" );
1387
if (params)
1388
FIXME( "Query parameters are not supported!\n" );
1389
1390
hr = device_query_context_create( &ctx, type, flags, props_len, props, callback, user_data );
1391
if (FAILED( hr ))
1392
return hr;
1393
1394
ctx->filters = !!filters;
1395
device_query_context_addref( ctx );
1396
if (!TrySubmitThreadpoolCallback( device_query_enum_objects_async, ctx, NULL ))
1397
hr = HRESULT_FROM_WIN32( GetLastError() );
1398
if (FAILED( hr ))
1399
{
1400
device_query_context_release( ctx );
1401
ctx = NULL;
1402
}
1403
1404
*devquery = (HDEVQUERY)ctx;
1405
return hr;
1406
}
1407
1408
void WINAPI DevCloseObjectQuery( HDEVQUERY query )
1409
{
1410
struct device_query_context *ctx = (struct device_query_context *)query;
1411
BOOL async = ctx->flags & DevQueryFlagAsyncClose;
1412
DEV_QUERY_STATE old;
1413
1414
TRACE( "(%p)\n", query );
1415
1416
if (!query)
1417
return;
1418
1419
EnterCriticalSection( &ctx->cs );
1420
old = ctx->state;
1421
ctx->state = DevQueryStateClosed;
1422
1423
if (ctx->notify)
1424
{
1425
CM_Unregister_Notification( ctx->notify );
1426
device_query_context_release( ctx ); /* Reference held by CM_Register_Notification. */
1427
}
1428
if (async && old == DevQueryStateEnumCompleted)
1429
{
1430
/* No asynchronous operation involving this query is active, so we need to notify DevQueryStateClosed. */
1431
BOOL success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL );
1432
LeaveCriticalSection( &ctx->cs );
1433
if (success)
1434
return;
1435
}
1436
else if (!async && old == DevQueryStateInitialized)
1437
{
1438
LeaveCriticalSection( &ctx->cs );
1439
/* Wait for the active async operation to end. */
1440
WaitForSingleObject( ctx->closed, INFINITE );
1441
}
1442
else
1443
LeaveCriticalSection( &ctx->cs );
1444
1445
device_query_context_release( ctx );
1446
return;
1447
}
1448
1449
HRESULT WINAPI DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len,
1450
const DEVPROPCOMPKEY *props, ULONG *buf_len, const DEVPROPERTY **buf )
1451
{
1452
TRACE( "(%d, %s, %#lx, %lu, %p, %p, %p)\n", type, debugstr_w( id ), flags, props_len, props, buf_len, buf );
1453
return DevGetObjectPropertiesEx( type, id, flags, props_len, props, 0, NULL, buf_len, buf );
1454
}
1455
1456
HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len,
1457
const DEVPROPCOMPKEY *props, ULONG params_len,
1458
const DEV_QUERY_PARAMETER *params, ULONG *buf_len, const DEVPROPERTY **buf )
1459
{
1460
HRESULT hr;
1461
ULONG valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize;
1462
1463
TRACE( "(%d, %s, %#lx, %lu, %p, %lu, %p, %p, %p)\n", type, debugstr_w( id ), flags, props_len, props,
1464
params_len, params, buf_len, buf );
1465
1466
if (flags & ~valid_flags)
1467
return E_INVALIDARG;
1468
if (type == DevObjectTypeUnknown || type > DevObjectTypeAEPProtocol)
1469
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
1470
if (!id || !!props_len != !!props || !!params_len != !!params
1471
|| (props_len && (flags & DevQueryFlagAllProperties)))
1472
return E_INVALIDARG;
1473
1474
switch (type)
1475
{
1476
case DevObjectTypeDeviceInterface:
1477
case DevObjectTypeDeviceInterfaceDisplay:
1478
{
1479
SP_DEVICE_INTERFACE_DATA iface = {.cbSize = sizeof( iface )};
1480
DEV_OBJECT obj = {0};
1481
HDEVINFO set;
1482
1483
set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL );
1484
if (set == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32( GetLastError() );
1485
if (!SetupDiOpenDeviceInterfaceW( set, id, 0, &iface ))
1486
{
1487
DWORD err = GetLastError();
1488
SetupDiDestroyDeviceInfoList( set );
1489
return HRESULT_FROM_WIN32(err == ERROR_NO_SUCH_DEVICE_INTERFACE ? ERROR_FILE_NOT_FOUND : err);
1490
}
1491
1492
hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, !!(flags & DevQueryFlagAllProperties), FALSE );
1493
*buf = obj.pProperties;
1494
*buf_len = obj.cPropertyCount;
1495
break;
1496
}
1497
default:
1498
FIXME( "Unsupported DEV_OBJECT_TYPE: %d\n", type );
1499
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
1500
}
1501
1502
return hr;
1503
}
1504
1505
const DEVPROPERTY *WINAPI DevFindProperty( const DEVPROPKEY *key, DEVPROPSTORE store, const WCHAR *locale,
1506
ULONG props_len, const DEVPROPERTY *props )
1507
{
1508
DEVPROPCOMPKEY comp_key;
1509
ULONG i;
1510
1511
TRACE( "(%s, %d, %s, %lu, %p)\n", debugstr_DEVPROPKEY( key ), store, debugstr_w( locale ), props_len, props );
1512
1513
/* Windows does not validate parameters here. */
1514
comp_key.Key = *key;
1515
comp_key.Store = store;
1516
comp_key.LocaleName = locale;
1517
for (i = 0; i < props_len; i++)
1518
{
1519
if (IsEqualDevPropCompKey( comp_key, props[i].CompKey ))
1520
return &props[i];
1521
}
1522
return NULL;
1523
}
1524
1525
void WINAPI DevFreeObjectProperties( ULONG len, const DEVPROPERTY *props )
1526
{
1527
DEVPROPERTY *properties = (DEVPROPERTY *)props;
1528
ULONG i;
1529
1530
TRACE( "(%lu, %p)\n", len, props );
1531
1532
for (i = 0; i < len; i++)
1533
free( properties[i].Buffer );
1534
free( properties );
1535
}
1536
1537