Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/cfgmgr32/devobject.c
12343 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 "cfgmgr32_private.h"
21
#include "devpkey.h"
22
23
WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
24
25
static int devproperty_compare( const void *p1, const void *p2 )
26
{
27
const DEVPROPCOMPKEY *key1 = &((DEVPROPERTY *)p1)->CompKey;
28
const DEVPROPCOMPKEY *key2 = &((DEVPROPERTY *)p2)->CompKey;
29
int cmp = memcmp( key1, key2, offsetof( DEVPROPCOMPKEY, LocaleName ));
30
31
if (cmp)
32
return cmp;
33
if (key1->LocaleName == key2->LocaleName)
34
return 0;
35
if (!key1->LocaleName)
36
return -1;
37
if (!key2->LocaleName)
38
return 1;
39
return wcsicmp( key1->LocaleName, key2->LocaleName );
40
}
41
42
static const char *debugstr_DEVPROP_OPERATOR( DEVPROP_OPERATOR op )
43
{
44
DWORD compare = op & DEVPROP_OPERATOR_MASK_EVAL;
45
DWORD list = op & DEVPROP_OPERATOR_MASK_LIST;
46
DWORD modifier = op & DEVPROP_OPERATOR_MASK_MODIFIER;
47
DWORD logical = op & DEVPROP_OPERATOR_MASK_LOGICAL;
48
DWORD array = op & DEVPROP_OPERATOR_MASK_ARRAY;
49
50
return wine_dbg_sprintf( "(%#lx|%#lx|%#lx|%#lx|%#lx)", list, array, modifier, compare, logical );
51
}
52
53
54
static const char *debugstr_DEVPROP_FILTER_EXPRESSION( const DEVPROP_FILTER_EXPRESSION *filter )
55
{
56
if (!filter) return "(null)";
57
return wine_dbg_sprintf( "{%s, {%s, %#lx, %lu, %p}}", debugstr_DEVPROP_OPERATOR( filter->Operator ),
58
debugstr_DEVPROPCOMPKEY( &filter->Property.CompKey ), filter->Property.Type,
59
filter->Property.BufferSize, filter->Property.Buffer );
60
}
61
62
/* Evaluate a filter expression containing comparison operator. */
63
static BOOL devprop_filter_eval_compare( const DEVPROPERTY *props, UINT props_len, const DEVPROP_FILTER_EXPRESSION *filter )
64
{
65
const DEVPROPERTY *cmp_prop = &filter->Property;
66
DEVPROP_OPERATOR op = filter->Operator;
67
const DEVPROPERTY *prop = NULL;
68
BOOL ret = FALSE;
69
70
TRACE( "(%p, %u, %s)\n", props, props_len, debugstr_DEVPROP_FILTER_EXPRESSION( filter ) );
71
72
switch (filter->Operator & DEVPROP_OPERATOR_MASK_EVAL)
73
{
74
case DEVPROP_OPERATOR_EXISTS:
75
prop = bsearch( &filter->Property.CompKey, props, props_len, sizeof(*props), devproperty_compare );
76
ret = prop && prop->Type != DEVPROP_TYPE_EMPTY;
77
break;
78
case DEVPROP_OPERATOR_EQUALS:
79
case DEVPROP_OPERATOR_LESS_THAN:
80
case DEVPROP_OPERATOR_GREATER_THAN:
81
case DEVPROP_OPERATOR_LESS_THAN_EQUALS:
82
case DEVPROP_OPERATOR_GREATER_THAN_EQUALS:
83
{
84
int cmp = 0;
85
86
prop = bsearch( &filter->Property.CompKey, props, props_len, sizeof(*props), devproperty_compare );
87
if (prop && cmp_prop->Type == prop->Type && cmp_prop->BufferSize == prop->BufferSize)
88
{
89
switch (prop->Type)
90
{
91
case DEVPROP_TYPE_STRING:
92
cmp = op & DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE ? wcsicmp( prop->Buffer, cmp_prop->Buffer )
93
: wcscmp( prop->Buffer, cmp_prop->Buffer );
94
TRACE( "%s vs %s -> %u\n", debugstr_w(prop->Buffer), debugstr_w(cmp_prop->Buffer), !!cmp );
95
break;
96
case DEVPROP_TYPE_GUID:
97
/* Any other comparison operator other than DEVPROP_OPERATOR_EQUALS with GUIDs evaluates to false. */
98
if (!(op & DEVPROP_OPERATOR_EQUALS)) break;
99
default:
100
cmp = memcmp( prop->Buffer, cmp_prop->Buffer, prop->BufferSize );
101
break;
102
}
103
if (op & DEVPROP_OPERATOR_EQUALS)
104
ret = !cmp;
105
else
106
ret = (op & DEVPROP_OPERATOR_LESS_THAN) ? cmp < 0 : cmp > 0;
107
}
108
break;
109
}
110
case DEVPROP_OPERATOR_BITWISE_AND:
111
case DEVPROP_OPERATOR_BITWISE_OR:
112
case DEVPROP_OPERATOR_BEGINS_WITH:
113
case DEVPROP_OPERATOR_ENDS_WITH:
114
case DEVPROP_OPERATOR_CONTAINS:
115
default:
116
FIXME( "Unsupported operator: %s", debugstr_DEVPROP_OPERATOR( filter->Operator & DEVPROP_OPERATOR_MASK_EVAL ) );
117
return FALSE;
118
}
119
120
if (op & DEVPROP_OPERATOR_MODIFIER_NOT) ret = !ret;
121
TRACE( "-> %u\n", ret );
122
return ret;
123
}
124
125
static const DEVPROP_FILTER_EXPRESSION *find_closing_filter( const DEVPROP_FILTER_EXPRESSION *filter, const DEVPROP_FILTER_EXPRESSION *end )
126
{
127
DWORD open = filter->Operator & DEVPROP_OPERATOR_MASK_LOGICAL, close = open + (DEVPROP_OPERATOR_AND_CLOSE - DEVPROP_OPERATOR_AND_OPEN), depth = 0;
128
129
for (const DEVPROP_FILTER_EXPRESSION *closing = filter + 1; closing < end; closing++)
130
{
131
DWORD logical = closing->Operator & DEVPROP_OPERATOR_MASK_LOGICAL;
132
if (logical == close && !depth--) return closing;
133
if (logical == open) depth++;
134
}
135
136
return NULL;
137
}
138
139
/* Return TRUE if the specified filter expressions match the object, FALSE if it doesn't. */
140
static BOOL devprop_filter_matches_properties( const DEVPROPERTY *props, UINT props_len, DEVPROP_OPERATOR op_outer_logical,
141
const DEVPROP_FILTER_EXPRESSION *filters, const DEVPROP_FILTER_EXPRESSION *end )
142
{
143
BOOL ret = TRUE;
144
145
TRACE( "(%p, %u, %#x, %p, %p)\n", props, props_len, op_outer_logical, filters, end );
146
147
for (const DEVPROP_FILTER_EXPRESSION *filter = filters; filter < end; filter++)
148
{
149
DEVPROP_OPERATOR op = filter->Operator;
150
151
if (op == DEVPROP_OPERATOR_NONE) ret = FALSE;
152
else if (op & (DEVPROP_OPERATOR_MASK_LIST | DEVPROP_OPERATOR_MASK_ARRAY))
153
{
154
FIXME( "Unsupported list/array operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) );
155
ret = FALSE;
156
}
157
else if (op & DEVPROP_OPERATOR_MASK_LOGICAL)
158
{
159
const DEVPROP_FILTER_EXPRESSION *closing = find_closing_filter( filter, end );
160
ret = devprop_filter_matches_properties( props, props_len, op & DEVPROP_OPERATOR_MASK_LOGICAL, filter + 1, closing );
161
filter = closing;
162
}
163
else if (op & DEVPROP_OPERATOR_MASK_EVAL)
164
{
165
ret = devprop_filter_eval_compare( props, props_len, filter );
166
}
167
168
/* See if we can short-circuit. */
169
switch (op_outer_logical)
170
{
171
/* {NOT_OPEN, ..., NOT_CLOSE} is the same as {NOT_OPEN, AND_OPEN, ..., AND_CLOSE, NOT_CLOSE}, so we can
172
* short circuit here as well. */
173
case DEVPROP_OPERATOR_NOT_OPEN:
174
case DEVPROP_OPERATOR_AND_OPEN:
175
if (!ret) goto done;
176
break;
177
case DEVPROP_OPERATOR_OR_OPEN:
178
if (ret) goto done;
179
break;
180
default:
181
assert( 0 );
182
break;
183
}
184
}
185
186
done:
187
if (op_outer_logical == DEVPROP_OPERATOR_NOT_OPEN) ret = !ret;
188
TRACE( "-> %u\n", ret );
189
return ret;
190
}
191
192
static BOOL devprop_type_validate( DEVPROPTYPE type, ULONG buf_size )
193
{
194
static const DWORD type_size[] = {
195
0, 0,
196
sizeof( BYTE ), sizeof( BYTE ),
197
sizeof( INT16 ), sizeof( INT16 ),
198
sizeof( INT32 ), sizeof( INT32 ),
199
sizeof( INT64 ), sizeof( INT64 ),
200
sizeof( FLOAT ), sizeof( DOUBLE ), sizeof( DECIMAL ),
201
sizeof( GUID ),
202
sizeof( CURRENCY ),
203
sizeof( DATE ),
204
sizeof( FILETIME ),
205
sizeof( DEVPROP_BOOLEAN ),
206
[DEVPROP_TYPE_DEVPROPKEY] = sizeof( DEVPROPKEY ),
207
[DEVPROP_TYPE_DEVPROPTYPE] = sizeof( DEVPROPTYPE ),
208
[DEVPROP_TYPE_ERROR] = sizeof( ULONG ),
209
[DEVPROP_TYPE_NTSTATUS] = sizeof( NTSTATUS )
210
};
211
DWORD mod = type & DEVPROP_MASK_TYPEMOD, size;
212
213
if (mod && mod != DEVPROP_TYPEMOD_ARRAY && mod != DEVPROP_TYPEMOD_LIST)
214
{
215
FIXME( "Unknown DEVPROPTYPE value: %#lx\n", type );
216
return FALSE;
217
}
218
219
switch (type & DEVPROP_MASK_TYPE)
220
{
221
case DEVPROP_TYPE_EMPTY:
222
case DEVPROP_TYPE_NULL:
223
return !mod;
224
case DEVPROP_TYPE_SECURITY_DESCRIPTOR:
225
case DEVPROP_TYPE_STRING_INDIRECT:
226
return !mod && !!buf_size;
227
228
case DEVPROP_TYPE_STRING:
229
case DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING:
230
return mod != DEVPROP_TYPEMOD_ARRAY && !!buf_size;
231
default:
232
/* The only valid modifier for the remaining types is DEVPROP_TYPEMOD_ARRAY */
233
if ((type & DEVPROP_MASK_TYPE) > DEVPROP_TYPE_NTSTATUS ||
234
(mod && mod != DEVPROP_TYPEMOD_ARRAY))
235
{
236
FIXME( "Unknown DEVPROPTYPE value: %#lx\n", type );
237
return FALSE;
238
}
239
size = type_size[type & DEVPROP_MASK_TYPE];
240
}
241
242
return mod == DEVPROP_TYPEMOD_ARRAY ? buf_size >= size : buf_size == size;
243
}
244
245
static BOOL devprop_filters_validate( const DEVPROP_FILTER_EXPRESSION *filters, const DEVPROP_FILTER_EXPRESSION *end )
246
{
247
const DEVPROP_FILTER_EXPRESSION *closing;
248
249
if (filters == end) return FALSE;
250
251
for (const DEVPROP_FILTER_EXPRESSION *filter = filters; filter < end; filter++)
252
{
253
const DEVPROPERTY *prop = &filter->Property;
254
DEVPROP_OPERATOR op = filter->Operator;
255
DWORD compare = op & DEVPROP_OPERATOR_MASK_EVAL;
256
DWORD list = op & DEVPROP_OPERATOR_MASK_LIST;
257
DWORD modifier = op & DEVPROP_OPERATOR_MASK_MODIFIER;
258
DWORD logical = op & DEVPROP_OPERATOR_MASK_LOGICAL;
259
DWORD array = op & DEVPROP_OPERATOR_MASK_ARRAY;
260
261
if ((compare && compare > DEVPROP_OPERATOR_CONTAINS)
262
|| (logical && (op & DEVPROP_OPERATOR_MASK_NOT_LOGICAL))
263
|| (array && (op != DEVPROP_OPERATOR_ARRAY_CONTAINS))
264
|| !!prop->Buffer != !!prop->BufferSize)
265
{
266
FIXME( "Unknown operator: %#x\n", op );
267
return FALSE;
268
}
269
if (!op) continue;
270
if (compare && compare != DEVPROP_OPERATOR_EXISTS
271
&& !devprop_type_validate( prop->Type, prop->BufferSize ))
272
return FALSE;
273
274
switch (modifier)
275
{
276
case DEVPROP_OPERATOR_NONE:
277
case DEVPROP_OPERATOR_MODIFIER_NOT:
278
case DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE:
279
break;
280
default:
281
return FALSE;
282
}
283
284
switch (list)
285
{
286
case DEVPROP_OPERATOR_NONE:
287
case DEVPROP_OPERATOR_LIST_CONTAINS:
288
case DEVPROP_OPERATOR_LIST_ELEMENT_BEGINS_WITH:
289
case DEVPROP_OPERATOR_LIST_ELEMENT_ENDS_WITH:
290
case DEVPROP_OPERATOR_LIST_ELEMENT_CONTAINS:
291
break;
292
default:
293
return FALSE;
294
}
295
296
switch (logical)
297
{
298
case DEVPROP_OPERATOR_NONE:
299
break;
300
case DEVPROP_OPERATOR_AND_OPEN:
301
case DEVPROP_OPERATOR_OR_OPEN:
302
case DEVPROP_OPERATOR_NOT_OPEN:
303
if (!(closing = find_closing_filter( filter, end ))) return FALSE;
304
if (!devprop_filters_validate( filter + 1, closing )) return FALSE;
305
filter = closing;
306
break;
307
default:
308
return FALSE;
309
}
310
}
311
312
return TRUE;
313
}
314
315
static LSTATUS copy_device_interface_property( HKEY hkey, const struct device_interface *iface, DEVPROPERTY *property )
316
{
317
struct property prop;
318
LSTATUS err;
319
320
init_property( &prop, &property->CompKey.Key, &property->Type, property->Buffer, &property->BufferSize );
321
if (!(err = query_device_interface_property( hkey, iface, &prop ))) return ERROR_SUCCESS;
322
if (err && err != ERROR_MORE_DATA) return ERROR_SUCCESS;
323
324
if (!(prop.buffer = malloc( property->BufferSize ))) return E_OUTOFMEMORY;
325
if ((err = query_device_interface_property( hkey, iface, &prop ))) free( prop.buffer );
326
else property->Buffer = prop.buffer;
327
328
return err;
329
}
330
331
static LSTATUS copy_device_interface_properties( HKEY hkey, const struct device_interface *iface, const DEVPROPCOMPKEY *keys, ULONG keys_len,
332
DEVPROPERTY *properties, ULONG *props_len )
333
{
334
const DEVPROPCOMPKEY *key, *end;
335
DEVPROPERTY *prop = properties;
336
LSTATUS err = ERROR_SUCCESS;
337
338
for (key = keys, end = keys + keys_len; !err && key < end; key++, prop++)
339
{
340
prop->CompKey = *key;
341
err = copy_device_interface_property( hkey, iface, prop );
342
}
343
*props_len = prop - properties;
344
345
return err;
346
}
347
348
static LSTATUS get_property_compare_keys( const DEVPROPKEY *keys, ULONG keys_len, const DEVPROPCOMPKEY **comp_keys, ULONG *comp_keys_len )
349
{
350
if (!(*comp_keys_len = keys_len)) return ERROR_SUCCESS;
351
if (!(*comp_keys = calloc( keys_len, sizeof(**comp_keys) ))) return ERROR_OUTOFMEMORY;
352
for (UINT i = 0; i < keys_len; ++i) ((DEVPROPCOMPKEY *)*comp_keys)[i].Key = keys[i];
353
return ERROR_SUCCESS;
354
}
355
356
static LSTATUS copy_device_interface_property_keys( HKEY hkey, const struct device_interface *iface, const DEVPROPCOMPKEY **comp_keys, ULONG *comp_keys_len )
357
{
358
DEVPROPKEY *tmp, *keys = NULL;
359
LSTATUS err;
360
361
for (;;)
362
{
363
if (!(err = enum_device_interface_property_keys( hkey, iface, keys, comp_keys_len ))) break;
364
if (err != ERROR_MORE_DATA) return err;
365
if (!(tmp = realloc( keys, *comp_keys_len * sizeof(*keys) ))) return ERROR_OUTOFMEMORY;
366
keys = tmp;
367
}
368
369
get_property_compare_keys( keys, *comp_keys_len, comp_keys, comp_keys_len );
370
free( keys );
371
return ERROR_SUCCESS;
372
}
373
374
typedef LSTATUS (*enum_device_object_cb)( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG *props_len, DEVPROPERTY **props, void *context );
375
376
static UINT select_property( const DEVPROPCOMPKEY *key, DEVPROPERTY *props, DEVPROPERTY *select_end, DEVPROPERTY *props_end )
377
{
378
for (DEVPROPERTY *prop = select_end; prop < props_end; prop++)
379
{
380
if (IsEqualDevPropCompKey( prop->CompKey, *key ))
381
{
382
DEVPROPERTY tmp = *select_end;
383
*select_end = *prop;
384
*prop = tmp;
385
return 1;
386
}
387
}
388
return 0;
389
}
390
391
static void select_properties( const DEVPROPCOMPKEY *keys, ULONG keys_len, DEVPROPERTY **props, ULONG *props_len )
392
{
393
DEVPROPERTY *select_end = *props, *props_end = *props + *props_len;
394
395
for (UINT i = 0; i < keys_len; i++) select_end += select_property( keys + i, *props, select_end, props_end );
396
*props_len = select_end - *props;
397
398
while (select_end < props_end) free( (select_end++)->Buffer );
399
if (!*props_len)
400
{
401
free( *props );
402
*props = NULL;
403
}
404
}
405
406
struct enum_dev_object_params
407
{
408
DEV_OBJECT_TYPE type;
409
const DEVPROPCOMPKEY *props;
410
ULONG props_len;
411
BOOL all_props;
412
const DEVPROP_FILTER_EXPRESSION *filters;
413
const DEVPROP_FILTER_EXPRESSION *filters_end;
414
enum_device_object_cb callback;
415
void *context;
416
};
417
418
static LSTATUS enum_dev_objects_device_interface( HKEY hkey, const void *object, const WCHAR *name, UINT name_len, void *context )
419
{
420
struct enum_dev_object_params *params = context;
421
ULONG keys_len = params->props_len, properties_len = 0;
422
const struct device_interface *iface = object;
423
const DEVPROPCOMPKEY *keys = params->props;
424
DEVPROPERTY *properties = NULL;
425
LSTATUS err = ERROR_SUCCESS;
426
BOOL matches;
427
428
TRACE( "hkey %p object %s name %s\n", hkey, debugstr_device_interface(iface), debugstr_w(name) );
429
430
/* If we're also filtering objects, get all properties for this object. Once the filters have been
431
* evaluated, free properties that have not been requested, and set cPropertyCount to comp_keys_len. */
432
if (params->all_props || params->filters) err = copy_device_interface_property_keys( hkey, iface, &keys, &keys_len );
433
if (!err)
434
{
435
if (keys_len && !(properties = calloc( keys_len, sizeof(*properties) ))) err = ERROR_OUTOFMEMORY;
436
else err = copy_device_interface_properties( hkey, iface, keys, keys_len, properties, &properties_len );
437
}
438
439
if (!err)
440
{
441
/* Sort properties by DEVPROPCOMPKEY for faster filter evaluation. */
442
if (params->filters) qsort( properties, properties_len, sizeof(*properties), devproperty_compare );
443
444
/* By default, the evaluation is performed by AND-ing all individual filter expressions. */
445
matches = devprop_filter_matches_properties( properties, properties_len, DEVPROP_OPERATOR_AND_OPEN, params->filters, params->filters_end );
446
447
/* Shrink properties to only the desired ones, unless DevQueryFlagAllProperties is set. */
448
if (!params->all_props) select_properties( params->props, params->props_len, &properties, &properties_len );
449
450
if (matches) err = params->callback( params->type, name, &properties_len, &properties, params->context );
451
}
452
453
DevFreeObjectProperties( properties_len, properties );
454
if (params->all_props || params->filters) free( (void *)keys );
455
456
return err;
457
}
458
459
static LSTATUS enum_dev_objects( DEV_OBJECT_TYPE type, const DEVPROPCOMPKEY *props, ULONG props_len, BOOL all_props,
460
const DEVPROP_FILTER_EXPRESSION *filters, const DEVPROP_FILTER_EXPRESSION *filters_end,
461
enum_device_object_cb callback, void *context )
462
{
463
struct enum_dev_object_params params =
464
{
465
.type = type,
466
.props = props,
467
.props_len = props_len,
468
.all_props = all_props,
469
.filters = filters,
470
.filters_end = filters_end,
471
.callback = callback,
472
.context = context,
473
};
474
475
switch (type)
476
{
477
case DevObjectTypeDeviceInterface:
478
case DevObjectTypeDeviceInterfaceDisplay:
479
return enum_device_interfaces( TRUE, enum_dev_objects_device_interface, &params );
480
481
case DevObjectTypeDeviceContainer:
482
case DevObjectTypeDevice:
483
case DevObjectTypeDeviceInterfaceClass:
484
case DevObjectTypeAEP:
485
case DevObjectTypeAEPContainer:
486
case DevObjectTypeDeviceInstallerClass:
487
case DevObjectTypeDeviceContainerDisplay:
488
case DevObjectTypeAEPService:
489
case DevObjectTypeDevicePanel:
490
case DevObjectTypeAEPProtocol:
491
FIXME("Unsupported DEV_OJBECT_TYPE: %d\n", type );
492
default:
493
return ERROR_SUCCESS;
494
}
495
}
496
497
struct objects_list
498
{
499
DEV_OBJECT *objects;
500
ULONG len;
501
};
502
503
static LSTATUS dev_objects_append( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG *props_len, DEVPROPERTY **props, void *context )
504
{
505
struct objects_list *list = context;
506
DEV_OBJECT *tmp, *obj;
507
508
if (!(tmp = realloc( list->objects, (list->len + 1) * sizeof(*list->objects) ))) return ERROR_OUTOFMEMORY;
509
list->objects = tmp;
510
511
obj = list->objects + list->len;
512
if (!(obj->pszObjectId = wcsdup( id ))) return ERROR_OUTOFMEMORY;
513
obj->ObjectType = type;
514
obj->cPropertyCount = *props_len;
515
obj->pProperties = *props;
516
*props_len = 0;
517
*props = NULL;
518
list->len++;
519
520
return ERROR_SUCCESS;
521
}
522
523
HRESULT WINAPI DevGetObjects( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
524
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG *objs_len,
525
const DEV_OBJECT **objs )
526
{
527
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, objs_len, objs );
528
return DevGetObjectsEx( type, flags, props_len, props, filters_len, filters, 0, NULL, objs_len, objs );
529
}
530
531
HRESULT WINAPI DevGetObjectsEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
532
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG params_len,
533
const DEV_QUERY_PARAMETER *params, ULONG *objs_len, const DEV_OBJECT **objs )
534
{
535
ULONG valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize;
536
struct objects_list objects = {0};
537
LSTATUS err;
538
539
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters,
540
params_len, params, objs_len, objs );
541
542
if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags)
543
|| (props_len && (flags & DevQueryFlagAllProperties))
544
|| (filters && !devprop_filters_validate( filters, filters + filters_len )))
545
return E_INVALIDARG;
546
if (params)
547
FIXME( "Query parameters are not supported!\n" );
548
549
*objs = NULL;
550
*objs_len = 0;
551
if ((err = enum_dev_objects( type, props, props_len, !!(flags & DevQueryFlagAllProperties), filters, filters + filters_len,
552
dev_objects_append, &objects )))
553
{
554
DevFreeObjects( objects.len, objects.objects );
555
return HRESULT_FROM_WIN32(err);
556
}
557
558
*objs = objects.objects;
559
*objs_len = objects.len;
560
return S_OK;
561
}
562
563
void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs )
564
{
565
DEV_OBJECT *objects = (DEV_OBJECT *)objs;
566
ULONG i;
567
568
TRACE( "(%lu, %p)\n", objs_len, objs );
569
570
for (i = 0; i < objs_len; i++)
571
{
572
DevFreeObjectProperties( objects[i].cPropertyCount, objects[i].pProperties );
573
free( (void *)objects[i].pszObjectId );
574
}
575
free( objects );
576
return;
577
}
578
579
struct device_iface_path
580
{
581
struct rb_entry entry;
582
WCHAR *path;
583
};
584
585
static int device_iface_path_cmp( const void *key, const struct rb_entry *entry )
586
{
587
const struct device_iface_path *iface = RB_ENTRY_VALUE( entry, struct device_iface_path, entry );
588
return wcsicmp( key, iface->path );
589
}
590
591
struct device_query_context
592
{
593
LONG ref;
594
DEV_OBJECT_TYPE type;
595
ULONG flags;
596
ULONG prop_keys_len;
597
DEVPROPCOMPKEY *prop_keys;
598
BOOL filters;
599
600
CRITICAL_SECTION cs;
601
PDEV_QUERY_RESULT_CALLBACK callback;
602
void *user_data;
603
DEV_QUERY_STATE state;
604
HANDLE closed;
605
struct rb_tree known_ifaces;
606
HCMNOTIFICATION notify;
607
};
608
609
static LSTATUS device_query_context_add_object( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG *props_len, DEVPROPERTY **props, void *data )
610
{
611
DEV_QUERY_RESULT_ACTION_DATA action_data =
612
{
613
.Action = DevQueryResultAdd,
614
.Data.DeviceObject =
615
{
616
.ObjectType = type,
617
.pszObjectId = id,
618
.cPropertyCount = *props_len,
619
.pProperties = *props,
620
},
621
};
622
struct device_query_context *ctx = data;
623
struct device_iface_path *iface_entry = NULL;
624
LSTATUS err = ERROR_SUCCESS;
625
626
TRACE( "(%s, %p)\n", debugstr_w( id ), data );
627
628
ctx->callback( (HDEVQUERY)ctx, ctx->user_data, &action_data );
629
630
EnterCriticalSection( &ctx->cs );
631
if (ctx->state == DevQueryStateClosed)
632
err = ERROR_CANCELLED;
633
else if (type == DevObjectTypeDeviceInterface || type == DevObjectTypeDeviceInterfaceDisplay)
634
{
635
if (!(iface_entry = calloc( 1, sizeof( *iface_entry ) )) || !(iface_entry->path = wcsdup( id )))
636
{
637
if (iface_entry) free( iface_entry->path );
638
free( iface_entry );
639
err = ERROR_OUTOFMEMORY;
640
}
641
else if (rb_put( &ctx->known_ifaces, iface_entry->path, &iface_entry->entry ))
642
{
643
free( iface_entry->path );
644
free( iface_entry );
645
}
646
}
647
LeaveCriticalSection( &ctx->cs );
648
649
return err;
650
}
651
652
static HRESULT device_query_context_create( struct device_query_context **query, DEV_OBJECT_TYPE type, ULONG flags,
653
ULONG props_len, const DEVPROPCOMPKEY *props,
654
PDEV_QUERY_RESULT_CALLBACK callback, void *user_data )
655
{
656
struct device_query_context *ctx;
657
ULONG i;
658
659
if (!(ctx = calloc( 1, sizeof( *ctx ))))
660
return E_OUTOFMEMORY;
661
ctx->ref = 1;
662
if (!(flags & DevQueryFlagAsyncClose))
663
{
664
ctx->closed = CreateEventW( NULL, FALSE, FALSE, NULL );
665
if (ctx->closed == INVALID_HANDLE_VALUE)
666
{
667
free( ctx );
668
return HRESULT_FROM_WIN32( GetLastError() );
669
}
670
}
671
ctx->prop_keys_len = props_len;
672
if (props_len && !(ctx->prop_keys = calloc( props_len, sizeof( *props ) )))
673
{
674
if (ctx->closed) CloseHandle( ctx->closed );
675
free( ctx );
676
return E_OUTOFMEMORY;
677
}
678
for (i = 0; i < props_len; i++)
679
{
680
ctx->prop_keys[i].Key = props[i].Key;
681
ctx->prop_keys[i].Store = props[i].Store;
682
}
683
InitializeCriticalSectionEx( &ctx->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO );
684
ctx->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": device_query_context.cs");
685
ctx->type = type;
686
ctx->flags = flags;
687
ctx->callback = callback;
688
ctx->user_data = user_data;
689
ctx->state = DevQueryStateInitialized;
690
rb_init( &ctx->known_ifaces, device_iface_path_cmp );
691
692
*query = ctx;
693
return S_OK;
694
}
695
696
static void device_query_context_addref( struct device_query_context *ctx )
697
{
698
InterlockedIncrement( &ctx->ref );
699
}
700
701
static void device_iface_path_free( struct rb_entry *entry, void *data )
702
{
703
struct device_iface_path *path = RB_ENTRY_VALUE( entry, struct device_iface_path, entry );
704
free( path->path );
705
free( path );
706
}
707
708
static void device_query_context_release( struct device_query_context *ctx )
709
{
710
if (!InterlockedDecrement( &ctx->ref ))
711
{
712
free( ctx->prop_keys );
713
ctx->cs.DebugInfo->Spare[0] = 0;
714
DeleteCriticalSection( &ctx->cs );
715
if (ctx->closed) CloseHandle( ctx->closed );
716
rb_destroy( &ctx->known_ifaces, device_iface_path_free, NULL );
717
free( ctx );
718
}
719
}
720
721
static void device_query_context_notify_state_change( struct device_query_context *ctx, DEV_QUERY_STATE state )
722
{
723
DEV_QUERY_RESULT_ACTION_DATA action_data = {0};
724
725
action_data.Action = DevQueryResultStateChange;
726
action_data.Data.State = state;
727
ctx->callback( (HDEVQUERY)ctx, ctx->user_data, &action_data );
728
}
729
730
static void CALLBACK device_query_context_notify_enum_completed_async( TP_CALLBACK_INSTANCE *instance, void *data )
731
{
732
device_query_context_notify_state_change( data, DevQueryStateEnumCompleted );
733
device_query_context_release( data );
734
}
735
736
static void CALLBACK device_query_context_notify_closed_async( TP_CALLBACK_INSTANCE *instance, void *data )
737
{
738
device_query_context_notify_state_change( data, DevQueryStateClosed );
739
device_query_context_release( data );
740
}
741
742
static void CALLBACK device_query_context_notify_aborted_async( TP_CALLBACK_INSTANCE *instance, void *data )
743
{
744
device_query_context_notify_state_change( data, DevQueryStateAborted );
745
device_query_context_release( data );
746
}
747
748
struct devquery_notify_data
749
{
750
DEV_QUERY_RESULT_ACTION_DATA action_data;
751
struct device_query_context *ctx;
752
};
753
754
static void CALLBACK device_query_notify_dev_async( TP_CALLBACK_INSTANCE *instance, void *notify_data )
755
{
756
struct devquery_notify_data *data = notify_data;
757
758
data->ctx->callback( (HDEVQUERY)data->ctx, data->ctx->user_data, &data->action_data );
759
if (data->action_data.Action != DevQueryResultStateChange)
760
DevFreeObjectProperties( data->action_data.Data.DeviceObject.cPropertyCount,
761
data->action_data.Data.DeviceObject.pProperties );
762
device_query_context_release( data->ctx );
763
free( data );
764
}
765
766
static const char *debugstr_CM_NOTIFY_ACTION( CM_NOTIFY_ACTION action )
767
{
768
static const char *str[] = {
769
"CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL",
770
"CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL",
771
"CM_NOTIFY_ACTION_DEVICEQUERYREMOVE",
772
"CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED",
773
"CM_NOTIFY_ACTION_DEVICEREMOVEPENDING",
774
"CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE",
775
"CM_NOTIFY_ACTION_DEVICECUSTOMEVENT",
776
"CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED",
777
"CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED",
778
"CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED",
779
};
780
781
return action < ARRAY_SIZE( str ) ? str[action] : wine_dbg_sprintf( "(unknown %d)", action );
782
}
783
784
static const char *debugstr_CM_NOTIFY_EVENT_DATA( const CM_NOTIFY_EVENT_DATA *event )
785
{
786
if (!event) return wine_dbg_sprintf( "(null)" );
787
switch (event->FilterType)
788
{
789
case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE:
790
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE %lu {{%s %s}}}", event->Reserved,
791
debugstr_guid( &event->u.DeviceInterface.ClassGuid ),
792
debugstr_w( event->u.DeviceInterface.SymbolicLink ) );
793
case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE:
794
if (event->u.DeviceHandle.NameOffset == -1)
795
{
796
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%s -1 %lu %p}}}", event->Reserved,
797
debugstr_guid( &event->u.DeviceHandle.EventGuid ),
798
event->u.DeviceHandle.DataSize, event->u.DeviceHandle.Data );
799
}
800
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%s %ld %lu %p %s}}}", event->Reserved,
801
debugstr_guid( &event->u.DeviceHandle.EventGuid ), event->u.DeviceHandle.NameOffset,
802
event->u.DeviceHandle.DataSize, event->u.DeviceHandle.Data,
803
debugstr_w( (WCHAR *)&event->u.DeviceHandle.Data[event->u.DeviceHandle.NameOffset] ) );
804
case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE:
805
return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE %lu %s}", event->Reserved,
806
debugstr_w( event->u.DeviceInstance.InstanceId ) );
807
default:
808
return wine_dbg_sprintf( "{(unknown %d) %lu}", event->FilterType, event->Reserved );
809
}
810
}
811
812
static DWORD CALLBACK device_query_context_cm_notify_callback( HCMNOTIFICATION notify, void *data,
813
CM_NOTIFY_ACTION action,
814
CM_NOTIFY_EVENT_DATA *event, DWORD event_size )
815
{
816
struct device_query_context *ctx = data;
817
const ULONG prop_flags = ctx->flags & (DevQueryFlagAllProperties | DevQueryFlagLocalize);
818
DEV_QUERY_RESULT_ACTION_DATA *action_data;
819
struct devquery_notify_data *notify_data;
820
struct device_iface_path *iface_entry;
821
const WCHAR *iface_path;
822
struct rb_entry *entry;
823
HRESULT hr = S_OK;
824
825
TRACE( "(%p, %p, %s, %s, %lu)\n", notify, data, debugstr_CM_NOTIFY_ACTION( action ),
826
debugstr_CM_NOTIFY_EVENT_DATA( event ), event_size );
827
828
if (action != CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL && action != CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL)
829
{
830
FIXME( "Unexpected CM_NOTIFY_ACTION value: %d\n", action );
831
return 0;
832
}
833
834
iface_path = event->u.DeviceInterface.SymbolicLink;
835
EnterCriticalSection( &ctx->cs );
836
if (ctx->state == DevQueryStateClosed || ctx->state == DevQueryStateAborted)
837
{
838
LeaveCriticalSection( &ctx->cs );
839
return 0;
840
}
841
842
if (!(notify_data = calloc( 1, sizeof ( *notify_data ) )))
843
goto abort;
844
action_data = &notify_data->action_data;
845
notify_data->ctx = ctx;
846
action_data->Data.DeviceObject.ObjectType = ctx->type;
847
848
/* Interface removal and arrival for objects that have already been enumerated. */
849
if ((entry = rb_get( &ctx->known_ifaces, iface_path )))
850
{
851
const DEVPROP_BOOLEAN enabled = action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ? DEVPROP_TRUE : DEVPROP_FALSE;
852
DEVPROPERTY *prop;
853
854
if (!(prop = calloc( 1, sizeof( *prop ))))
855
goto abort;
856
if (!(prop->Buffer = calloc( 1, sizeof( enabled ) )))
857
{
858
free( prop );
859
goto abort;
860
}
861
prop->CompKey.Key = DEVPKEY_DeviceInterface_Enabled;
862
prop->Type = DEVPROP_TYPE_BOOLEAN;
863
prop->BufferSize = sizeof( enabled );
864
memcpy( prop->Buffer, &enabled, sizeof( enabled ) );
865
iface_entry = RB_ENTRY_VALUE( entry, struct device_iface_path, entry );
866
action_data->Action = DevQueryResultUpdate;
867
action_data->Data.DeviceObject.cPropertyCount = 1;
868
action_data->Data.DeviceObject.pProperties = prop;
869
}
870
else
871
{
872
if (!(iface_entry = calloc( 1, sizeof( *iface_entry ) )))
873
goto abort;
874
if (!(iface_entry->path = wcsdup( iface_path )))
875
{
876
free( iface_entry );
877
goto abort;
878
}
879
rb_put( &ctx->known_ifaces, iface_path, &iface_entry->entry );
880
hr = DevGetObjectProperties( ctx->type, iface_path, prop_flags, ctx->prop_keys_len, ctx->prop_keys,
881
&action_data->Data.DeviceObject.cPropertyCount,
882
&action_data->Data.DeviceObject.pProperties );
883
if (FAILED( hr ))
884
goto abort;
885
action_data->Action = DevQueryResultAdd;
886
}
887
888
action_data->Data.DeviceObject.pszObjectId = iface_entry->path;
889
device_query_context_addref( ctx );
890
if (!TrySubmitThreadpoolCallback( device_query_notify_dev_async, notify_data, NULL ))
891
device_query_context_release( ctx );
892
LeaveCriticalSection( &ctx->cs );
893
return 0;
894
abort:
895
free( notify_data );
896
ctx->state = DevQueryStateAborted;
897
device_query_context_addref( ctx );
898
if (!TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL ))
899
device_query_context_release( ctx );
900
LeaveCriticalSection( &ctx->cs );
901
return 0;
902
}
903
904
static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *instance, void *data )
905
{
906
struct device_query_context *ctx = data;
907
LSTATUS err = ERROR_SUCCESS;
908
BOOL success;
909
910
if (!ctx->filters)
911
err = enum_dev_objects( ctx->type, ctx->prop_keys, ctx->prop_keys_len, !!(ctx->flags & DevQueryFlagAllProperties),
912
0, NULL, device_query_context_add_object, ctx );
913
914
EnterCriticalSection( &ctx->cs );
915
if (ctx->state == DevQueryStateClosed)
916
err = ERROR_CANCELLED;
917
918
switch (err)
919
{
920
case ERROR_SUCCESS:
921
ctx->state = DevQueryStateEnumCompleted;
922
success = TrySubmitThreadpoolCallback( device_query_context_notify_enum_completed_async, ctx, NULL );
923
if (ctx->filters || !(ctx->flags & DevQueryFlagUpdateResults))
924
break;
925
switch (ctx->type)
926
{
927
case DevObjectTypeDeviceInterface:
928
case DevObjectTypeDeviceInterfaceDisplay:
929
{
930
CM_NOTIFY_FILTER filter = { 0 };
931
CONFIGRET ret;
932
933
filter.cbSize = sizeof( filter );
934
filter.Flags = CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES;
935
filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
936
device_query_context_addref( ctx );
937
ret = CM_Register_Notification( &filter, ctx, device_query_context_cm_notify_callback, &ctx->notify );
938
if (ret)
939
{
940
ERR( "CM_Register_Notification failed: %lu\n", ret );
941
device_query_context_release( ctx );
942
goto abort;
943
}
944
break;
945
}
946
default:
947
FIXME( "Device updates not supported for object type %d\n", ctx->type );
948
break;
949
}
950
break;
951
case ERROR_CANCELLED:
952
if (!(ctx->flags & DevQueryFlagAsyncClose))
953
{
954
LeaveCriticalSection( &ctx->cs );
955
SetEvent( ctx->closed );
956
device_query_context_release( ctx );
957
return;
958
}
959
success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL );
960
break;
961
default:
962
goto abort;
963
}
964
965
LeaveCriticalSection( &ctx->cs );
966
if (!success)
967
device_query_context_release( ctx );
968
return;
969
abort:
970
ctx->state = DevQueryStateAborted;
971
success = TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL );
972
LeaveCriticalSection( &ctx->cs );
973
if (!success)
974
device_query_context_release( ctx );
975
}
976
977
HRESULT WINAPI DevCreateObjectQuery( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
978
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters,
979
PDEV_QUERY_RESULT_CALLBACK callback, void *user_data, HDEVQUERY *devquery )
980
{
981
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %p, %p, %p)\n", type, flags, props_len, props, filters_len, filters, callback,
982
user_data, devquery );
983
return DevCreateObjectQueryEx( type, flags, props_len, props, filters_len, filters, 0, NULL, callback, user_data,
984
devquery );
985
}
986
987
HRESULT WINAPI DevCreateObjectQueryEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG props_len, const DEVPROPCOMPKEY *props,
988
ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters, ULONG params_len,
989
const DEV_QUERY_PARAMETER *params, PDEV_QUERY_RESULT_CALLBACK callback,
990
void *user_data, HDEVQUERY *devquery )
991
{
992
ULONG valid_flags = DevQueryFlagUpdateResults | DevQueryFlagAllProperties | DevQueryFlagLocalize | DevQueryFlagAsyncClose;
993
struct device_query_context *ctx = NULL;
994
HRESULT hr;
995
996
TRACE( "(%d, %#lx, %lu, %p, %lu, %p, %lu, %p, %p, %p, %p)\n", type, flags, props_len, props, filters_len,
997
filters, params_len, params, callback, user_data, devquery );
998
999
if (!!props_len != !!props || !!filters_len != !!filters || !!params_len != !!params || (flags & ~valid_flags) || !callback
1000
|| (props_len && (flags & DevQueryFlagAllProperties))
1001
|| (filters && !devprop_filters_validate( filters, filters + filters_len )))
1002
return E_INVALIDARG;
1003
if (filters)
1004
FIXME( "Query filters are not supported!\n" );
1005
if (params)
1006
FIXME( "Query parameters are not supported!\n" );
1007
1008
hr = device_query_context_create( &ctx, type, flags, props_len, props, callback, user_data );
1009
if (FAILED( hr ))
1010
return hr;
1011
1012
ctx->filters = !!filters;
1013
device_query_context_addref( ctx );
1014
if (!TrySubmitThreadpoolCallback( device_query_enum_objects_async, ctx, NULL ))
1015
hr = HRESULT_FROM_WIN32( GetLastError() );
1016
if (FAILED( hr ))
1017
{
1018
device_query_context_release( ctx );
1019
ctx = NULL;
1020
}
1021
1022
*devquery = (HDEVQUERY)ctx;
1023
return hr;
1024
}
1025
1026
void WINAPI DevCloseObjectQuery( HDEVQUERY query )
1027
{
1028
struct device_query_context *ctx = (struct device_query_context *)query;
1029
BOOL async = ctx->flags & DevQueryFlagAsyncClose;
1030
DEV_QUERY_STATE old;
1031
1032
TRACE( "(%p)\n", query );
1033
1034
if (!query)
1035
return;
1036
1037
EnterCriticalSection( &ctx->cs );
1038
old = ctx->state;
1039
ctx->state = DevQueryStateClosed;
1040
1041
if (ctx->notify)
1042
{
1043
CM_Unregister_Notification( ctx->notify );
1044
device_query_context_release( ctx ); /* Reference held by CM_Register_Notification. */
1045
}
1046
if (async && old == DevQueryStateEnumCompleted)
1047
{
1048
/* No asynchronous operation involving this query is active, so we need to notify DevQueryStateClosed. */
1049
BOOL success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL );
1050
LeaveCriticalSection( &ctx->cs );
1051
if (success)
1052
return;
1053
}
1054
else if (!async && old == DevQueryStateInitialized)
1055
{
1056
LeaveCriticalSection( &ctx->cs );
1057
/* Wait for the active async operation to end. */
1058
WaitForSingleObject( ctx->closed, INFINITE );
1059
}
1060
else
1061
LeaveCriticalSection( &ctx->cs );
1062
1063
device_query_context_release( ctx );
1064
return;
1065
}
1066
1067
HRESULT WINAPI DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len,
1068
const DEVPROPCOMPKEY *props, ULONG *buf_len, const DEVPROPERTY **buf )
1069
{
1070
TRACE( "(%d, %s, %#lx, %lu, %p, %p, %p)\n", type, debugstr_w( id ), flags, props_len, props, buf_len, buf );
1071
return DevGetObjectPropertiesEx( type, id, flags, props_len, props, 0, NULL, buf_len, buf );
1072
}
1073
1074
HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len,
1075
const DEVPROPCOMPKEY *props, ULONG params_len,
1076
const DEV_QUERY_PARAMETER *params, ULONG *buf_len, const DEVPROPERTY **buf )
1077
{
1078
ULONG valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize;
1079
BOOL all_props = flags & DevQueryFlagAllProperties;
1080
LSTATUS err = ERROR_SUCCESS;
1081
1082
TRACE( "(%d, %s, %#lx, %lu, %p, %lu, %p, %p, %p)\n", type, debugstr_w( id ), flags, props_len, props,
1083
params_len, params, buf_len, buf );
1084
1085
if (flags & ~valid_flags)
1086
return E_INVALIDARG;
1087
if (type == DevObjectTypeUnknown || type > DevObjectTypeAEPProtocol)
1088
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
1089
if (!id || !!props_len != !!props || !!params_len != !!params
1090
|| (props_len && (flags & DevQueryFlagAllProperties)))
1091
return E_INVALIDARG;
1092
1093
switch (type)
1094
{
1095
case DevObjectTypeDeviceInterface:
1096
case DevObjectTypeDeviceInterfaceDisplay:
1097
{
1098
ULONG properties_len = 0, keys_len = props_len;
1099
const DEVPROPCOMPKEY *keys = props;
1100
DEVPROPERTY *properties = NULL;
1101
struct device_interface iface;
1102
HKEY hkey;
1103
1104
if ((err = init_device_interface( &iface, id ))) break;
1105
if ((err = open_device_interface_key( &iface, KEY_ALL_ACCESS, TRUE, &hkey ))) break;
1106
1107
if (all_props) err = copy_device_interface_property_keys( hkey, &iface, &keys, &keys_len );
1108
if (!err)
1109
{
1110
if ((properties_len = keys_len) && !(properties = calloc( keys_len, sizeof(*properties) ))) err = ERROR_OUTOFMEMORY;
1111
else err = copy_device_interface_properties( hkey, &iface, keys, keys_len, properties, &properties_len );
1112
}
1113
1114
*buf = properties;
1115
*buf_len = properties_len;
1116
1117
if (keys != props) free( (void *)keys );
1118
RegCloseKey( hkey );
1119
break;
1120
}
1121
default:
1122
FIXME( "Unsupported DEV_OBJECT_TYPE: %d\n", type );
1123
err = ERROR_FILE_NOT_FOUND;
1124
break;
1125
}
1126
1127
if (err) return HRESULT_FROM_WIN32(err);
1128
return S_OK;
1129
}
1130
1131
const DEVPROPERTY *WINAPI DevFindProperty( const DEVPROPKEY *key, DEVPROPSTORE store, const WCHAR *locale,
1132
ULONG props_len, const DEVPROPERTY *props )
1133
{
1134
DEVPROPCOMPKEY comp_key;
1135
ULONG i;
1136
1137
TRACE( "(%s, %d, %s, %lu, %p)\n", debugstr_DEVPROPKEY( key ), store, debugstr_w( locale ), props_len, props );
1138
1139
/* Windows does not validate parameters here. */
1140
comp_key.Key = *key;
1141
comp_key.Store = store;
1142
comp_key.LocaleName = locale;
1143
for (i = 0; i < props_len; i++)
1144
{
1145
if (IsEqualDevPropCompKey( comp_key, props[i].CompKey ))
1146
return &props[i];
1147
}
1148
return NULL;
1149
}
1150
1151
void WINAPI DevFreeObjectProperties( ULONG len, const DEVPROPERTY *props )
1152
{
1153
DEVPROPERTY *properties = (DEVPROPERTY *)props;
1154
ULONG i;
1155
1156
TRACE( "(%lu, %p)\n", len, props );
1157
1158
for (i = 0; i < len; i++)
1159
free( properties[i].Buffer );
1160
free( properties );
1161
}
1162
1163