-- v2: cfgmgr32: Implement device updates for DevCreateObjectQuery. ntoskrnl.exe/test: Add tests for device updates in DevCreateObjectQuery. cfgmgr32: Implement DevGetObjectProperties for device interfaces. cfgmgr32: Add stubs for DevGetObjectProperties(Ex).
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/cfgmgr32.spec | 3 + dlls/cfgmgr32/main.c | 22 +++++ dlls/cfgmgr32/tests/cfgmgr32.c | 162 +++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+)
diff --git a/dlls/cfgmgr32/cfgmgr32.spec b/dlls/cfgmgr32/cfgmgr32.spec index a437eb72afc..2d4d6b23bec 100644 --- a/dlls/cfgmgr32/cfgmgr32.spec +++ b/dlls/cfgmgr32/cfgmgr32.spec @@ -191,6 +191,9 @@ @ stdcall DevCloseObjectQuery(ptr) @ stdcall DevCreateObjectQuery(long long long ptr long ptr ptr ptr ptr) @ stdcall DevCreateObjectQueryEx(long long long ptr long ptr long ptr ptr ptr ptr) +@ stdcall DevFreeObjectProperties(long ptr) @ stdcall DevFreeObjects(long ptr) +@ stdcall DevGetObjectProperties(long ptr long long ptr ptr ptr) +@ stdcall DevGetObjectPropertiesEx(long ptr long long ptr long ptr ptr ptr) @ stdcall DevGetObjects(long long long ptr long ptr ptr ptr) @ stdcall DevGetObjectsEx(long long long ptr long ptr long ptr ptr ptr) diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 4ba2aee672d..89302a282b5 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -829,3 +829,25 @@ void WINAPI DevCloseObjectQuery( HDEVQUERY query ) device_query_context_release( ctx ); return; } + +HRESULT WINAPI DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len, + const DEVPROPCOMPKEY *props, ULONG *buf_len, const DEVPROPERTY **buf ) +{ + TRACE( "(%d, %s, %#lx, %lu, %p, %p, %p)\n", type, debugstr_w( id ), flags, props_len, props, buf_len, buf ); + return DevGetObjectPropertiesEx( type, id, flags, props_len, props, 0, NULL, buf_len, buf ); +} + +HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, ULONG flags, ULONG props_len, + const DEVPROPCOMPKEY *props, ULONG params_len, + const DEV_QUERY_PARAMETER *params, ULONG *buf_len, const DEVPROPERTY **buf ) +{ + FIXME( "(%d, %s, %#lx, %lu, %p, %lu, %p, %p, %p): stub!\n", type, debugstr_w( id ), flags, props_len, props, + params_len, params, buf_len, buf ); + return E_NOTIMPL; +} + +void WINAPI DevFreeObjectProperties( ULONG len, const DEVPROPERTY *props ) +{ + FIXME( "(%lu, %p): stub!\n", len, props ); + return; +} diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index fbbd504d23e..5243bf05a70 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -25,6 +25,7 @@ #include "devguid.h" #include "initguid.h" #include "devpkey.h" +#include "propkey.h" #include "setupapi.h" #include "cfgmgr32.h" #include "ntddvdeo.h" @@ -549,6 +550,128 @@ struct test_property
DEFINE_DEVPROPKEY(DEVPKEY_dummy, 0xdeadbeef, 0xdead, 0xbeef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 1);
+static BOOL dev_property_val_equal( const DEVPROPERTY *p1, const DEVPROPERTY *p2 ) +{ + if (!(p1->Type == p2->Type && p1->BufferSize == p2->BufferSize)) + return FALSE; + switch (p1->Type) + { + case DEVPROP_TYPE_STRING: + return !wcsicmp( (WCHAR *)p1->Buffer, (WCHAR *)p2->Buffer ); + default: + return !memcmp( p1->Buffer, p2->Buffer, p1->BufferSize ); + } +} + +static const char *debugstr_DEVPROP_val( const DEVPROPERTY *prop ) +{ + switch (prop->Type) + { + case DEVPROP_TYPE_STRING: + return wine_dbg_sprintf( "{type=%#lx buf=%s buf_len=%lu}", prop->Type, debugstr_w( prop->Buffer ), prop->BufferSize ); + default: + return wine_dbg_sprintf( "{type=%#lx buf=%p buf_len=%lu}", prop->Type, prop->Buffer, prop->BufferSize ); + } +} + +static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, const DEVPROPERTY *exp_props, ULONG props_len ) +{ + static const DEVPROPKEY dummy_propkey = { { 0xdeadbeef, 0xdead, 0xbeef, { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } }, 0x1 }; + static const DEVPROPCOMPKEY dummy_propcompkey = { dummy_propkey, DEVPROP_STORE_SYSTEM, NULL }; + ULONG buf_len, rem_props = props_len, i; + const DEVPROPERTY *buf; + DEVPROPCOMPKEY *keys; + HRESULT hr; + + hr = DevGetObjectProperties( type, id, DevQueryFlagUpdateResults, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( type, id, DevQueryFlagAsyncClose, 0, NULL, &buf_len, &buf ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( type, id, DevQueryFlagAsyncClose, 0, NULL, &buf_len, &buf ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 1, NULL, &buf_len, &buf ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 0, (DEVPROPCOMPKEY *)0xdeadbeef, &buf_len, &buf ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + buf = NULL; + buf_len = 0; + hr = DevGetObjectProperties( type, id, DevQueryFlagAllProperties, 0, NULL, &buf_len, &buf ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok( buf_len == props_len, "%lu != %lu\n", buf_len, props_len ); + for (i = 0; i < props_len; i++) + { + ULONG j; + for (j = 0; j < buf_len; j++) + { + if (IsEqualDevPropKey( exp_props[i].CompKey.Key, buf[j].CompKey.Key ) && rem_props) + { + winetest_push_context( "{%s,%#lx}", debugstr_guid( &exp_props[i].CompKey.Key.fmtid ), + exp_props[i].CompKey.Key.pid ); + /* ItemNameDisplay for software devices has different values for properties obtained from DevGetObjects + * and DevGetObjectProperties. */ + if (!IsEqualDevPropKey(PKEY_ItemNameDisplay, buf[j].CompKey.Key)) + ok( dev_property_val_equal( &exp_props[i], &buf[j] ), "%s != %s\n", debugstr_DEVPROP_val( &buf[j] ), + debugstr_DEVPROP_val( &exp_props[i] ) ); + winetest_pop_context(); + rem_props--; + } + } + } + todo_wine ok( rem_props == 0, "got rem_props %lu\n", rem_props ); + DevFreeObjectProperties( buf_len, buf ); + + buf = (DEVPROPERTY *)0xdeadbeef; + buf_len = 0xdeadbeef; + rem_props = props_len; + hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 0, NULL, &buf_len, &buf ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok( buf_len == 0, "got buf_len %lu\n", buf_len ); + todo_wine ok( !buf, "got buf %p\n", buf ); + + buf = NULL; + buf_len = 0; + keys = calloc( props_len, sizeof( *keys ) ); + for (i = 0; i < props_len; i++) + keys[i] = exp_props[i].CompKey; + hr = DevGetObjectProperties( type, id, DevQueryFlagNone, props_len, keys, &buf_len, &buf ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok( buf_len == props_len, "%lu != %lu\n", buf_len, props_len ); + for (i = 0; i < props_len; i++) + { + ULONG j; + for (j = 0; j < buf_len; j++) + { + if (IsEqualDevPropKey( exp_props[i].CompKey.Key, buf[j].CompKey.Key )) + { + rem_props--; + break; + } + } + } + todo_wine ok( rem_props == 0, "got rem_props %lu\n", rem_props ); + DevFreeObjectProperties( buf_len, buf ); + + buf_len = 0; + buf = NULL; + hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 1, &dummy_propcompkey, &buf_len, &buf ); + todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); + todo_wine ok( !!buf, "got buf %p", buf ); + todo_wine ok( buf_len == 1, "got buf_len %lu\n", buf_len ); + if (buf) + { + ok( IsEqualDevPropKey( buf[0].CompKey.Key, dummy_propkey ), "got propkey {%s, %#lx}\n", + debugstr_guid( &buf[0].CompKey.Key.fmtid ), buf[0].CompKey.Key.pid ); + ok( buf[0].Type == DEVPROP_TYPE_EMPTY, "got Type %#lx\n", buf[0].Type ); + DevFreeObjectProperties( buf_len, buf ); + } + free( keys ); +} + static void test_dev_object_iface_props( int line, const DEV_OBJECT *obj, const struct test_property *exp_props, DWORD props_len ) { @@ -713,6 +836,9 @@ static void test_DevGetObjects( void ) winetest_push_context( "device %s", debugstr_w( obj->pszObjectId ) ); ok( obj->ObjectType == test_cases[i].object_type, "got ObjectType %d\n", obj->ObjectType ); test_dev_object_iface_props( __LINE__, obj, test_cases[i].exp_props, test_cases[i].props_len ); + winetest_push_context( "%d", __LINE__ ); + test_DevGetObjectProperties( obj->ObjectType, obj->pszObjectId, obj->pProperties, obj->cPropertyCount ); + winetest_pop_context(); winetest_pop_context(); } DevFreeObjects( len, objects ); @@ -920,6 +1046,41 @@ static void test_DevCreateObjectQuery( void ) CloseHandle( data.closed ); }
+static void test_DevGetObjectProperties_invalid( void ) +{ + HRESULT hr; + + hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeUnknown, L"", 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, DevQueryFlagAsyncClose, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", DevQueryFlagUpdateResults, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", 0xdeadbeef, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 1, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 0, (DEVPROPCOMPKEY *)0xdeadbeef, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeDeviceInterfaceDisplay, L"foobar", 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, NULL, 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); +} + START_TEST(cfgmgr32) { test_CM_MapCrToWin32Err(); @@ -928,4 +1089,5 @@ START_TEST(cfgmgr32) test_CM_Get_Device_Interface_List(); test_DevGetObjects(); test_DevCreateObjectQuery(); + test_DevGetObjectProperties_invalid(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 93 ++++++++++++++++++++++++---------- dlls/cfgmgr32/tests/cfgmgr32.c | 54 ++++++++++---------- 2 files changed, 93 insertions(+), 54 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 89302a282b5..87f158e6f10 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -316,15 +316,16 @@ static BOOL dev_properties_append( DEVPROPERTY **properties, ULONG *props_len, c return TRUE; }
-static HRESULT dev_object_iface_get_props( DEV_OBJECT *obj, HDEVINFO set, SP_DEVICE_INTERFACE_DATA *iface_data, - ULONG props_len, const DEVPROPCOMPKEY *props, BOOL all_props ) +static HRESULT dev_object_iface_get_props( ULONG *props_len, DEVPROPERTY **props, HDEVINFO set, + SP_DEVICE_INTERFACE_DATA *iface_data, ULONG propkeys_len, + const DEVPROPCOMPKEY *propkeys, BOOL all_props ) { DEVPROPKEY *all_keys = NULL; DWORD keys_len = 0, i = 0; HRESULT hr = S_OK;
- obj->cPropertyCount = 0; - obj->pProperties = NULL; + *props_len = 0; + *props = NULL; if (!props && !all_props) return S_OK;
@@ -345,25 +346,24 @@ static HRESULT dev_object_iface_get_props( DEV_OBJECT *obj, HDEVINFO set, SP_DEV } } else - keys_len = props_len; + keys_len = propkeys_len;
for (i = 0; i < keys_len; i++) { - const DEVPROPKEY *key = all_keys ? &all_keys[i] : &props[i].Key; + const DEVPROPKEY *key = all_keys ? &all_keys[i] : &propkeys[i].Key; DWORD req = 0, size; DEVPROPTYPE type; BYTE *buf;
- if (props && props[i].Store != DEVPROP_STORE_SYSTEM) + if (propkeys && propkeys[i].Store != DEVPROP_STORE_SYSTEM) { - FIXME( "Unsupported Store value: %d\n", props[i].Store ); + FIXME( "Unsupported Store value: %d\n", propkeys[i].Store ); continue; } if (SetupDiGetDeviceInterfacePropertyW( set, iface_data, key, &type, NULL, 0, &req, 0 ) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - if (props && !dev_properties_append( (DEVPROPERTY **)&obj->pProperties, &obj->cPropertyCount, key, - DEVPROP_TYPE_EMPTY, 0, NULL )) + if (propkeys && !dev_properties_append( props, props_len, key, DEVPROP_TYPE_EMPTY, 0, NULL )) { hr = E_OUTOFMEMORY; goto done; @@ -383,7 +383,7 @@ static HRESULT dev_object_iface_get_props( DEV_OBJECT *obj, HDEVINFO set, SP_DEV free( buf ); goto done; } - if (!dev_properties_append( (DEVPROPERTY **)&obj->pProperties, &obj->cPropertyCount, key, type, size, buf )) + if (!dev_properties_append( props, props_len, key, type, size, buf )) { free( buf ); hr = E_OUTOFMEMORY; @@ -395,11 +395,9 @@ done: free( all_keys ); if (FAILED( hr )) { - for (i = 0; i < obj->cPropertyCount; i++) - free( ( (DEVPROPERTY *)obj[i].pProperties )->Buffer ); - free( (DEVPROPERTY *)obj->pProperties ); - obj->cPropertyCount = 0; - obj->pProperties = NULL; + DevFreeObjectProperties( *props_len, *props ); + *props_len = 0; + *props = NULL; } return hr; } @@ -476,7 +474,9 @@ static HRESULT enum_dev_objects( DEV_OBJECT_TYPE type, ULONG props_len, const DE
obj.ObjectType = type; obj.pszObjectId = detail->DevicePath; - if (SUCCEEDED( (hr = dev_object_iface_get_props( &obj, set, &iface, props_len, props, all_props )) )) + hr = dev_object_iface_get_props( &obj.cPropertyCount, (DEVPROPERTY **)&obj.pProperties, set, &iface, props_len, + props, all_props ); + if (SUCCEEDED( hr )) hr = callback( obj, data ); }
@@ -565,13 +565,7 @@ void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs )
for (i = 0; i < objs_len; i++) { - DEVPROPERTY *props = (DEVPROPERTY *)objects[i].pProperties; - ULONG j; - - for (j = 0; j < objects[i].cPropertyCount; j++) - free( props[j].Buffer ); - free( props ); - + DevFreeObjectProperties( objects[i].cPropertyCount, objects[i].pProperties ); free( (void *)objects[i].pszObjectId ); } free( objects ); @@ -841,13 +835,58 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id, const DEVPROPCOMPKEY *props, ULONG params_len, const DEV_QUERY_PARAMETER *params, ULONG *buf_len, const DEVPROPERTY **buf ) { - FIXME( "(%d, %s, %#lx, %lu, %p, %lu, %p, %p, %p): stub!\n", type, debugstr_w( id ), flags, props_len, props, + HRESULT hr; + ULONG valid_flags = DevQueryFlagAllProperties | DevQueryFlagLocalize; + + TRACE( "(%d, %s, %#lx, %lu, %p, %lu, %p, %p, %p)\n", type, debugstr_w( id ), flags, props_len, props, params_len, params, buf_len, buf ); - return E_NOTIMPL; + + if (flags & ~valid_flags) + return E_INVALIDARG; + if (type == DevObjectTypeUnknown || type > DevObjectTypeAEPProtocol) + return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); + if (!id || !!props_len != !!props || !!params_len != !!params + || (props_len && (flags & DevQueryFlagAllProperties))) + return E_INVALIDARG; + + switch (type) + { + case DevObjectTypeDeviceInterface: + case DevObjectTypeDeviceInterfaceDisplay: + { + SP_DEVICE_INTERFACE_DATA iface = {0}; + HDEVINFO set; + + set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); + if (set == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32( GetLastError() ); + iface.cbSize = sizeof( iface ); + if (!SetupDiOpenDeviceInterfaceW( set, id, 0, &iface )) + { + DWORD err = GetLastError(); + SetupDiDestroyDeviceInfoList( set ); + return HRESULT_FROM_WIN32(err == ERROR_NO_SUCH_DEVICE_INTERFACE ? ERROR_FILE_NOT_FOUND : err); + } + + hr = dev_object_iface_get_props( buf_len, (DEVPROPERTY **)buf, set, &iface, props_len, props, + !!(flags & DevQueryFlagAllProperties) ); + + break; + } + default: + FIXME( "Unsupported DEV_OBJECT_TYPE: %d\n", type ); + hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); + } + + return hr; }
void WINAPI DevFreeObjectProperties( ULONG len, const DEVPROPERTY *props ) { - FIXME( "(%lu, %p): stub!\n", len, props ); + DEVPROPERTY *properties = (DEVPROPERTY *)props; + ULONG i; + + for (i = 0; i < len; i++) + free( properties[i].Buffer ); + free( properties ); return; } diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 5243bf05a70..5d98ee1f872 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -584,25 +584,25 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, HRESULT hr;
hr = DevGetObjectProperties( type, id, DevQueryFlagUpdateResults, 0, NULL, NULL, NULL ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjectProperties( type, id, DevQueryFlagAsyncClose, 0, NULL, &buf_len, &buf ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjectProperties( type, id, DevQueryFlagAsyncClose, 0, NULL, &buf_len, &buf ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 1, NULL, &buf_len, &buf ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 0, (DEVPROPCOMPKEY *)0xdeadbeef, &buf_len, &buf ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
buf = NULL; buf_len = 0; hr = DevGetObjectProperties( type, id, DevQueryFlagAllProperties, 0, NULL, &buf_len, &buf ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( buf_len == props_len, "%lu != %lu\n", buf_len, props_len ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( buf_len == props_len, "%lu != %lu\n", buf_len, props_len ); for (i = 0; i < props_len; i++) { ULONG j; @@ -622,16 +622,16 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, } } } - todo_wine ok( rem_props == 0, "got rem_props %lu\n", rem_props ); + ok( rem_props == 0, "got rem_props %lu\n", rem_props ); DevFreeObjectProperties( buf_len, buf );
buf = (DEVPROPERTY *)0xdeadbeef; buf_len = 0xdeadbeef; rem_props = props_len; hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 0, NULL, &buf_len, &buf ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( buf_len == 0, "got buf_len %lu\n", buf_len ); - todo_wine ok( !buf, "got buf %p\n", buf ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( buf_len == 0, "got buf_len %lu\n", buf_len ); + ok( !buf, "got buf %p\n", buf );
buf = NULL; buf_len = 0; @@ -639,8 +639,8 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, for (i = 0; i < props_len; i++) keys[i] = exp_props[i].CompKey; hr = DevGetObjectProperties( type, id, DevQueryFlagNone, props_len, keys, &buf_len, &buf ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( buf_len == props_len, "%lu != %lu\n", buf_len, props_len ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( buf_len == props_len, "%lu != %lu\n", buf_len, props_len ); for (i = 0; i < props_len; i++) { ULONG j; @@ -653,15 +653,15 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, } } } - todo_wine ok( rem_props == 0, "got rem_props %lu\n", rem_props ); + ok( rem_props == 0, "got rem_props %lu\n", rem_props ); DevFreeObjectProperties( buf_len, buf );
buf_len = 0; buf = NULL; hr = DevGetObjectProperties( type, id, DevQueryFlagNone, 1, &dummy_propcompkey, &buf_len, &buf ); - todo_wine ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine ok( !!buf, "got buf %p", buf ); - todo_wine ok( buf_len == 1, "got buf_len %lu\n", buf_len ); + ok( hr == S_OK, "got hr %#lx\n", hr ); + ok( !!buf, "got buf %p", buf ); + ok( buf_len == 1, "got buf_len %lu\n", buf_len ); if (buf) { ok( IsEqualDevPropKey( buf[0].CompKey.Key, dummy_propkey ), "got propkey {%s, %#lx}\n", @@ -1051,34 +1051,34 @@ static void test_DevGetObjectProperties_invalid( void ) HRESULT hr;
hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 0, NULL, NULL, NULL ); - todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeUnknown, L"", 0, 0, NULL, NULL, NULL ); - todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, DevQueryFlagAsyncClose, 0, NULL, NULL, NULL ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", DevQueryFlagUpdateResults, 0, NULL, NULL, NULL ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", 0xdeadbeef, 0, NULL, NULL, NULL ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 1, NULL, NULL, NULL ); - todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 0, (DEVPROPCOMPKEY *)0xdeadbeef, NULL, NULL ); - todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", 0, 0, NULL, NULL, NULL ); - todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeDeviceInterfaceDisplay, L"foobar", 0, 0, NULL, NULL, NULL ); - todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr );
hr = DevGetObjectProperties( DevObjectTypeDeviceInterface, NULL, 0, 0, NULL, NULL, NULL ); - todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); }
START_TEST(cfgmgr32)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/ntoskrnl.exe/tests/Makefile.in | 2 +- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 164 ++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-)
diff --git a/dlls/ntoskrnl.exe/tests/Makefile.in b/dlls/ntoskrnl.exe/tests/Makefile.in index 97dee8b25cf..f8fb0fa2ea9 100644 --- a/dlls/ntoskrnl.exe/tests/Makefile.in +++ b/dlls/ntoskrnl.exe/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = ntoskrnl.exe -IMPORTS = advapi32 crypt32 newdev setupapi user32 wintrust ws2_32 hid +IMPORTS = advapi32 cfgmgr32 crypt32 newdev setupapi user32 wintrust ws2_32 hid
driver_IMPORTS = winecrt0 ntoskrnl hal fltmgr driver_EXTRADLLFLAGS = -nodefaultlibs -nostartfiles -Wl,--subsystem,native diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 819d1497ff9..fec2c661e81 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -35,6 +35,8 @@ #include "mssip.h" #include "setupapi.h" #include "cfgmgr32.h" +#include "devfiltertypes.h" +#include "devquery.h" #include "newdev.h" #include "regstr.h" #include "dbt.h" @@ -1443,6 +1445,125 @@ static void pump_messages(void) } }
+struct devquery_callback_data +{ + HANDLE enum_completed_evt; + BOOL initial_enum_completed; + + HANDLE device_added_sem; + DWORD bus_dev_added; + DWORD child_dev_added; + + HANDLE device_removed_sem; + DWORD bus_dev_removed; + DWORD child_dev_removed; +}; + +static void CALLBACK devquery_notify_callback( HDEVQUERY devquery, void *user_data, + const DEV_QUERY_RESULT_ACTION_DATA *action_data ) +{ + struct devquery_callback_data *data = user_data; + + switch (action_data->Action) + { + case DevQueryResultStateChange: + if (action_data->Data.State == DevQueryStateEnumCompleted) + { + data->initial_enum_completed = TRUE; + SetEvent( data->enum_completed_evt ); + } + break; + case DevQueryResultAdd: + { + const DEV_OBJECT *obj = &action_data->Data.DeviceObject; + DEVPROP_BOOLEAN state = DEVPROP_FALSE; + GUID iface_guid = {0}; + ULONG i; + + if (!data->initial_enum_completed) + break; + if (obj->ObjectType != DevObjectTypeDeviceInterfaceDisplay) + break; + ok( obj->cPropertyCount == 2, "got cPropertyCount %lu\n", obj->cPropertyCount ); + ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); + if (!obj->pProperties) + return; + for (i = 0; i < obj->cPropertyCount; i++) + { + const DEVPROPERTY *prop = &obj->pProperties[i]; + if (IsEqualDevPropKey( prop->CompKey.Key, DEVPKEY_DeviceInterface_ClassGuid )) + { + ok( prop->Type == DEVPROP_TYPE_GUID, "got Type %#lx != %#x\n", prop->Type, DEVPROP_TYPE_GUID ); + ok( prop->BufferSize == sizeof( GUID ), "got BufferSize %lu != %lu\n", prop->BufferSize, + (DWORD)sizeof( GUID ) ); + if (prop->BufferSize == sizeof( GUID ) && prop->Type == DEVPROP_TYPE_GUID) + iface_guid = *(GUID *)prop->Buffer; + } + else if (IsEqualDevPropKey( prop->CompKey.Key, DEVPKEY_DeviceInterface_Enabled )) + { + ok( prop->Type == DEVPROP_TYPE_BOOLEAN, "got Type %#lx != %#x\n", prop->Type, DEVPROP_TYPE_BOOLEAN ); + ok( prop->BufferSize == sizeof( DEVPROP_BOOLEAN ), "got BufferSize %lu != %lu\n", prop->BufferSize, + (DWORD)sizeof( DEVPROP_BOOLEAN ) ); + if (prop->BufferSize == sizeof( DEVPROP_BOOLEAN ) && prop->Type == DEVPROP_TYPE_BOOLEAN) + state = *(DEVPROP_BOOLEAN *)prop->Buffer; + } + } + + ok ( IsEqualGUID( &iface_guid, &bus_class ) || IsEqualGUID( &iface_guid, &child_class ), "got iface_guid %s\n", + debugstr_guid( &iface_guid ) ); + if (IsEqualGUID( &iface_guid, &bus_class ) && state) + { + data->bus_dev_added++; + ok( !wcsicmp( obj->pszObjectId, L"\\?\ROOT#WINETEST#0#{deadbeef-29ef-4538-a5fd-b69573a362c1}" ), + "got pszObjectId %s\n", debugstr_w( obj->pszObjectId ) ); + ReleaseSemaphore( data->device_added_sem, 1, NULL ); + } + else if (IsEqualGUID( &iface_guid, &child_class ) && state) + { + data->child_dev_added++; + ok( !wcsicmp( obj->pszObjectId, L"\\?\Wine#Test#1#{deadbeef-29ef-4538-a5fd-b69573a362c2}" ), + "got pszObjectId %s\n", debugstr_w( obj->pszObjectId ) ); + ReleaseSemaphore( data->device_added_sem, 1, NULL ); + } + break; + } + case DevQueryResultUpdate: + { + const DEV_OBJECT *obj = &action_data->Data.DeviceObject; + const DEVPROPERTY *prop = &obj->pProperties[0]; + + if (!data->initial_enum_completed) + break; + if (obj->ObjectType != DevObjectTypeDeviceInterfaceDisplay) + break; + ok( !wcsicmp( obj->pszObjectId, L"\\?\ROOT#WINETEST#0#{deadbeef-29ef-4538-a5fd-b69573a362c1}" ) || + !wcsicmp( obj->pszObjectId, L"\\?\Wine#Test#1#{deadbeef-29ef-4538-a5fd-b69573a362c2}" ), + "got pszObjectId %s\n", debugstr_w( obj->pszObjectId ) ); + ok( obj->cPropertyCount == 1, "got cPropertyCount %lu\n", obj->cPropertyCount ); + ok( !!obj->pProperties, "got pProperties %p\n", obj->pProperties ); + if (!obj->pProperties) + return; + ok( IsEqualDevPropKey( prop->CompKey.Key, DEVPKEY_DeviceInterface_Enabled ), "got CompKey.Key {%s,%#lx}\n", + debugstr_guid( &prop->CompKey.Key.fmtid ), prop->CompKey.Key.pid ); + ok( prop->Type == DEVPROP_TYPE_BOOLEAN, "got Type %#lx != %#x\n", prop->Type, DEVPROP_TYPE_BOOLEAN ); + ok( prop->BufferSize == sizeof( DEVPROP_BOOLEAN ), "got BufferSize %lu != %lu\n", prop->BufferSize, + (DWORD)sizeof( DEVPROP_BOOLEAN ) ); + if (prop->BufferSize == sizeof( DEVPROP_BOOLEAN ) && prop->Type == DEVPROP_TYPE_BOOLEAN && + !*(DEVPROP_BOOLEAN *)prop->Buffer ) + { + if (!wcsicmp( obj->pszObjectId, L"\\?\ROOT#WINETEST#0#{deadbeef-29ef-4538-a5fd-b69573a362c1}" )) + data->bus_dev_removed++; + else if (!wcsicmp( obj->pszObjectId, L"\\?\Wine#Test#1#{deadbeef-29ef-4538-a5fd-b69573a362c2}" )) + data->child_dev_removed++; + ReleaseSemaphore( data->device_removed_sem, 1, NULL ); + } + break; + } + default: + break; + } +} + static void test_pnp_devices(void) { static const char expect_hardware_id[] = "winetest_hardware\0winetest_hardware_1\0"; @@ -1469,6 +1590,12 @@ static void test_pnp_devices(void) .lpszClassName = "ntoskrnl_test_wc", .lpfnWndProc = device_notify_proc, }; + struct devquery_callback_data devquery_data = {0}; + DEVPROPCOMPKEY iface_prop_keys[2] = + { + { DEVPKEY_DeviceInterface_ClassGuid, DEVPROP_STORE_SYSTEM, 0 }, + { DEVPKEY_DeviceInterface_Enabled, DEVPROP_STORE_SYSTEM, 0 } + }; HDEVNOTIFY notify_handle; DWORD size = 0, type, dword; HANDLE bus, child, tmp; @@ -1476,9 +1603,11 @@ static void test_pnp_devices(void) UNICODE_STRING string; OVERLAPPED ovl = {0}; IO_STATUS_BLOCK io; + HDEVQUERY query; HDEVINFO set; HWND window; LSTATUS status; + HRESULT hr; HKEY key; BOOL ret; int id; @@ -1490,6 +1619,15 @@ static void test_pnp_devices(void) notify_handle = RegisterDeviceNotificationA(window, &filter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); ok(!!notify_handle, "failed to register window, error %lu\n", GetLastError());
+ devquery_data.enum_completed_evt = CreateEventW( NULL, FALSE, FALSE, NULL ); + devquery_data.device_added_sem = CreateSemaphoreW( NULL, 0, 1, NULL ); + devquery_data.device_removed_sem = CreateSemaphoreW( NULL, 0, 1, NULL ); + hr = DevCreateObjectQuery( DevObjectTypeDeviceInterfaceDisplay, DevQueryFlagUpdateResults, 2, iface_prop_keys, 0, + NULL, devquery_notify_callback, &devquery_data, &query ); + ok(hr == S_OK, "Failed to create device query, hr %#lx\n", hr); + status = WaitForSingleObject(devquery_data.enum_completed_evt, 5000); + ok(!status, "WaitforSingleObject failed, error %lu\n", status); + set = SetupDiGetClassDevsA(&control_class, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#lx\n", GetLastError());
@@ -1569,6 +1707,10 @@ static void test_pnp_devices(void) pump_messages(); ok(got_bus_arrival == 1, "got %u bus arrival messages\n", got_bus_arrival); ok(!got_bus_removal, "got %u bus removal messages\n", got_bus_removal); + status = WaitForSingleObject(devquery_data.device_added_sem, 1000); + todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); + todo_wine ok(devquery_data.bus_dev_added == 1, "got %lu new bus device objects\n", devquery_data.bus_dev_added); + todo_wine_if ( !status ) ok(!devquery_data.bus_dev_removed, "got %lu bus device object removals\n", devquery_data.bus_dev_removed);
set = SetupDiGetClassDevsA(&bus_class, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#lx\n", GetLastError()); @@ -1585,6 +1727,10 @@ static void test_pnp_devices(void) pump_messages(); ok(got_bus_arrival == 1, "got %u bus arrival messages\n", got_bus_arrival); ok(got_bus_removal == 1, "got %u bus removal messages\n", got_bus_removal); + status = WaitForSingleObject(devquery_data.device_removed_sem, 1000); + todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); + todo_wine ok(devquery_data.bus_dev_added == 1, "got %lu new bus device objects\n", devquery_data.bus_dev_added); + todo_wine ok(devquery_data.bus_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.bus_dev_removed);
set = SetupDiGetClassDevsA(&bus_class, NULL, NULL, DIGCF_DEVICEINTERFACE); ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#lx\n", GetLastError()); @@ -1611,6 +1757,10 @@ static void test_pnp_devices(void) pump_messages(); ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival); ok(!got_child_removal, "got %u child removal messages\n", got_child_removal); + status = WaitForSingleObject(devquery_data.device_added_sem, 1000); + todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); + todo_wine ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); + todo_wine_if( !status ) ok(!devquery_data.child_dev_removed, "got %lu bus device object removals\n", devquery_data.child_dev_removed);
set = SetupDiGetClassDevsA(&child_class, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#lx\n", GetLastError()); @@ -1749,6 +1899,10 @@ static void test_pnp_devices(void) pump_messages(); ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival); ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal); + status = WaitForSingleObject(devquery_data.device_removed_sem, 1000); + todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); + todo_wine ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); + todo_wine ok(devquery_data.child_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.child_dev_removed);
ret = DeviceIoControl(child, IOCTL_WINETEST_CHILD_CHECK_REMOVED, NULL, 0, NULL, 0, &size, NULL); todo_wine ok(ret, "got error %lu\n", GetLastError()); @@ -1766,6 +1920,12 @@ static void test_pnp_devices(void) pump_messages(); ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival); ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal); + status = WaitForSingleObject(devquery_data.device_added_sem, 1000); + ok(status == WAIT_TIMEOUT, "got status %#lx\n", status); + status = WaitForSingleObject(devquery_data.device_removed_sem, 1000); + ok(status == WAIT_TIMEOUT, "got status %#lx\n", status); + todo_wine ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); + todo_wine ok(devquery_data.child_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.child_dev_removed);
ret = NtOpenFile(&tmp, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT); ok(ret == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", ret); @@ -1773,6 +1933,10 @@ static void test_pnp_devices(void) CloseHandle(bus);
UnregisterDeviceNotification(notify_handle); + DevCloseObjectQuery( query ); + CloseHandle( devquery_data.device_added_sem ); + CloseHandle( devquery_data.device_removed_sem ); + CloseHandle( devquery_data.enum_completed_evt ); DestroyWindow(window); UnregisterClassA("ntoskrnl_test_wc", GetModuleHandleA(NULL)); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 258 ++++++++++++++++++++++++++++- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 28 ++-- 2 files changed, 266 insertions(+), 20 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 87f158e6f10..2d298b1386b 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -18,6 +18,7 @@ */
#include "wine/debug.h" +#include "wine/rbtree.h" #include "winreg.h" #include "cfgmgr32.h" #include "winuser.h" @@ -572,6 +573,18 @@ void WINAPI DevFreeObjects( ULONG objs_len, const DEV_OBJECT *objs ) return; }
+struct device_iface_path +{ + struct rb_entry entry; + WCHAR *path; +}; + +static int device_iface_path_cmp( const void *key, const struct rb_entry *entry ) +{ + const struct device_iface_path *iface = RB_ENTRY_VALUE( entry, struct device_iface_path, entry ); + return wcsicmp( key, iface->path ); +} + struct device_query_context { LONG ref; @@ -579,12 +592,15 @@ struct device_query_context ULONG flags; ULONG prop_keys_len; DEVPROPCOMPKEY *prop_keys; + BOOL filters;
CRITICAL_SECTION cs; PDEV_QUERY_RESULT_CALLBACK callback; void *user_data; DEV_QUERY_STATE state; HANDLE closed; + struct rb_tree known_ifaces; + HCMNOTIFICATION notify; };
static HRESULT device_query_context_add_object( DEV_OBJECT obj, void *data ) @@ -592,9 +608,12 @@ static HRESULT device_query_context_add_object( DEV_OBJECT obj, void *data ) DEVPROPERTY *props = (DEVPROPERTY *)obj.pProperties; DEV_QUERY_RESULT_ACTION_DATA action_data = {0}; struct device_query_context *ctx = data; + struct device_iface_path *iface_entry = NULL; HRESULT hr = S_OK; ULONG i;
+ TRACE("{%s, %p}\n", debugstr_w(obj.pszObjectId), data); + action_data.Action = DevQueryResultAdd; action_data.Data.DeviceObject = obj; ctx->callback( (HDEVQUERY)ctx, ctx->user_data, &action_data ); @@ -602,6 +621,25 @@ static HRESULT device_query_context_add_object( DEV_OBJECT obj, void *data ) EnterCriticalSection( &ctx->cs ); if (ctx->state == DevQueryStateClosed) hr = E_CHANGED_STATE; + else if (obj.ObjectType == DevObjectTypeDeviceInterface || obj.ObjectType == DevObjectTypeDeviceInterfaceDisplay) + { + if ((iface_entry = calloc( 1, sizeof( *iface_entry ) ))) + { + if (!(iface_entry->path = wcsdup( obj.pszObjectId ))) + { + free( iface_entry ); + hr = E_OUTOFMEMORY; + } + else if (rb_put( &ctx->known_ifaces, iface_entry->path, &iface_entry->entry )) + { + free( (void *)obj.pszObjectId ); + free( iface_entry ); + } + } + else + hr = E_OUTOFMEMORY; + } + LeaveCriticalSection( &ctx->cs );
for (i = 0; i < obj.cPropertyCount; i++) @@ -648,6 +686,7 @@ static HRESULT device_query_context_create( struct device_query_context **query, ctx->callback = callback; ctx->user_data = user_data; ctx->state = DevQueryStateInitialized; + rb_init( &ctx->known_ifaces, device_iface_path_cmp );
*query = ctx; return S_OK; @@ -658,14 +697,23 @@ static void device_query_context_addref( struct device_query_context *ctx ) InterlockedIncrement( &ctx->ref ); }
+static void device_iface_path_free( struct rb_entry *entry, void *data ) +{ + struct device_iface_path *path = RB_ENTRY_VALUE( entry, struct device_iface_path, entry ); + free( path->path ); + free( path ); +} + static void device_query_context_release( struct device_query_context *ctx ) { if (!InterlockedDecrement( &ctx->ref )) { + if (ctx->notify) CM_Unregister_Notification( ctx->notify ); free( ctx->prop_keys ); ctx->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection( &ctx->cs ); if (ctx->closed) CloseHandle( ctx->closed ); + rb_destroy( &ctx->known_ifaces, device_iface_path_free, NULL ); free( ctx ); } } @@ -697,14 +745,176 @@ static void CALLBACK device_query_context_notify_aborted_async( TP_CALLBACK_INST device_query_context_release( data ); }
+struct devquery_notify_data +{ + DEV_QUERY_RESULT_ACTION_DATA action_data; + struct device_query_context *ctx; +}; + +static void CALLBACK device_query_notify_dev_async( TP_CALLBACK_INSTANCE *instance, void *notify_data ) +{ + struct devquery_notify_data *data = notify_data; + + data->ctx->callback( (HDEVQUERY)data->ctx, data->ctx->user_data, &data->action_data ); + if (data->action_data.Action != DevQueryResultStateChange) + DevFreeObjectProperties( data->action_data.Data.DeviceObject.cPropertyCount, + data->action_data.Data.DeviceObject.pProperties ); + device_query_context_release( data->ctx ); + free( data ); +} + +static const char *debugstr_CM_NOTIFY_ACTION( CM_NOTIFY_ACTION action ) +{ + static const char *str[] = { + "CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL", + "CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL", + "CM_NOTIFY_ACTION_DEVICEQUERYREMOVE", + "CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED", + "CM_NOTIFY_ACTION_DEVICEREMOVEPENDING", + "CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE", + "CM_NOTIFY_ACTION_DEVICECUSTOMEVENT", + "CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED", + "CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED", + "CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED", + }; + + return action < ARRAY_SIZE( str ) ? str[action] : wine_dbg_sprintf( "(unknown %d)", action ); +} + +static const char *debugstr_CM_NOTIFY_EVENT_DATA( const CM_NOTIFY_EVENT_DATA *event ) +{ + if (!event) return wine_dbg_sprintf( "(null)" ); + switch (event->FilterType) + { + case CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE: + return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE %lu {{%s %s}}}", event->Reserved, + debugstr_guid( &event->u.DeviceInterface.ClassGuid ), + debugstr_w( event->u.DeviceInterface.SymbolicLink ) ); + case CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE: + if (event->u.DeviceHandle.NameOffset == -1) + { + return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%s -1 %lu %p}}}", event->Reserved, + debugstr_guid( &event->u.DeviceHandle.EventGuid ), + event->u.DeviceHandle.DataSize, event->u.DeviceHandle.Data ); + } + return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE %lu {{%s %ld %lu %p %s}}}", event->Reserved, + debugstr_guid( &event->u.DeviceHandle.EventGuid ), event->u.DeviceHandle.NameOffset, + event->u.DeviceHandle.DataSize, event->u.DeviceHandle.Data, + debugstr_w( (WCHAR *)&event->u.DeviceHandle.Data[event->u.DeviceHandle.NameOffset] ) ); + case CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE: + return wine_dbg_sprintf( "{CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE %lu %s}", event->Reserved, + debugstr_w( event->u.DeviceInstance.InstanceId ) ); + default: + return wine_dbg_sprintf( "{(unknown %d) %lu}", event->FilterType, event->Reserved ); + } +} + +static DWORD CALLBACK device_query_context_cm_notify_callback( HCMNOTIFICATION notify, void *data, + CM_NOTIFY_ACTION action, + CM_NOTIFY_EVENT_DATA *event, DWORD event_size ) +{ + struct device_query_context *ctx = data; + const ULONG prop_flags = ctx->flags & (DevQueryFlagAllProperties | DevQueryFlagLocalize); + DEV_QUERY_RESULT_ACTION_DATA *action_data; + struct devquery_notify_data *notify_data; + struct device_iface_path *iface_entry; + PTP_SIMPLE_CALLBACK callback; + const WCHAR *iface_path; + struct rb_entry *entry; + HRESULT hr = S_OK; + BOOL success; + void *param; + + TRACE( "(%p, %p, %s, %s, %lu)\n", notify, data, debugstr_CM_NOTIFY_ACTION( action ), + debugstr_CM_NOTIFY_EVENT_DATA( event ), event_size ); + + if (action != CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL && action != CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) + { + FIXME( "Unexpected CM_NOTIFY_ACTION value: %d\n", action ); + return 0; + } + + iface_path = event->u.DeviceInterface.SymbolicLink; + EnterCriticalSection( &ctx->cs ); + if (ctx->state == DevQueryStateClosed || ctx->state == DevQueryStateAborted) + { + LeaveCriticalSection( &ctx->cs ); + return 0; + } + + if (!(notify_data = calloc( 1, sizeof ( *notify_data ) ))) + goto abort; + action_data = ¬ify_data->action_data; + notify_data->ctx = ctx; + action_data->Data.DeviceObject.ObjectType = ctx->type; + + /* Interface removal and arrival for objects that have already been enumerated. */ + if ((entry = rb_get( &ctx->known_ifaces, iface_path ))) + { + const DEVPROP_BOOLEAN enabled = action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ? DEVPROP_TRUE : DEVPROP_FALSE; + DEVPROPERTY *prop; + + if (!(prop = calloc( 1, sizeof( *prop )))) + goto abort; + if (!(prop->Buffer = calloc( 1, sizeof( enabled )))) + { + free( prop ); + goto abort; + } + prop->CompKey.Key = DEVPKEY_DeviceInterface_Enabled; + prop->Type = DEVPROP_TYPE_BOOLEAN; + prop->BufferSize = sizeof( enabled ); + memcpy( prop->Buffer, &enabled, sizeof( enabled ) ); + iface_entry = RB_ENTRY_VALUE( entry, struct device_iface_path, entry ); + action_data->Action = DevQueryResultUpdate; + action_data->Data.DeviceObject.cPropertyCount = 1; + action_data->Data.DeviceObject.pProperties = prop; + } + else + { + if (!(iface_entry = calloc( 1, sizeof( *iface_entry ) ))) + goto abort; + if (!(iface_entry->path = wcsdup( iface_path ))) + { + free( iface_entry ); + goto abort; + } + rb_put( &ctx->known_ifaces, iface_path, &iface_entry->entry ); + hr = DevGetObjectProperties( ctx->type, iface_path, prop_flags, ctx->prop_keys_len, ctx->prop_keys, + &action_data->Data.DeviceObject.cPropertyCount, + &action_data->Data.DeviceObject.pProperties ); + if (FAILED( hr )) + goto abort; + action_data->Action = DevQueryResultAdd; + } + + action_data->Data.DeviceObject.pszObjectId = iface_entry->path; + callback = device_query_notify_dev_async; + param = notify_data; + goto done; +abort: + free( notify_data ); + ctx->state = DevQueryStateAborted; + callback = device_query_context_notify_aborted_async; + param = ctx; +done: + device_query_context_addref( ctx ); + success = TrySubmitThreadpoolCallback( callback, param, NULL ); + LeaveCriticalSection( &ctx->cs ); + if (!success) + device_query_context_release( ctx ); + return 0; +} + static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *instance, void *data ) { struct device_query_context *ctx = data; - BOOL success = TRUE; - HRESULT hr; + BOOL success = TRUE, abort = FALSE; + HRESULT hr = S_OK;
- hr = enum_dev_objects( ctx->type, ctx->prop_keys_len, ctx->prop_keys, !!(ctx->flags & DevQueryFlagAllProperties), - device_query_context_add_object, ctx ); + if (!ctx->filters) + hr = enum_dev_objects( ctx->type, ctx->prop_keys_len, ctx->prop_keys, !!(ctx->flags & DevQueryFlagAllProperties), + device_query_context_add_object, ctx );
EnterCriticalSection( &ctx->cs ); if (ctx->state == DevQueryStateClosed) @@ -715,7 +925,37 @@ static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *inst case S_OK: ctx->state = DevQueryStateEnumCompleted; success = TrySubmitThreadpoolCallback( device_query_context_notify_enum_completed_async, ctx, NULL ); - LeaveCriticalSection( &ctx->cs ); + if (ctx->filters || !(ctx->flags & DevQueryFlagUpdateResults)) + { + LeaveCriticalSection( &ctx->cs ); + break; + } + switch (ctx->type) + { + case DevObjectTypeDeviceInterface: + case DevObjectTypeDeviceInterfaceDisplay: + { + CM_NOTIFY_FILTER filter = { 0 }; + CONFIGRET ret; + + filter.cbSize = sizeof( filter ); + filter.Flags = CM_NOTIFY_FILTER_FLAG_ALL_INTERFACE_CLASSES; + filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE; + ret = CM_Register_Notification( &filter, ctx, device_query_context_cm_notify_callback, &ctx->notify ); + if (ret) + { + ERR( "CM_Register_Notification failed: %lu\n", ret ); + abort = TRUE; + break; + } + LeaveCriticalSection( &ctx->cs ); + break; + } + default: + LeaveCriticalSection( &ctx->cs ); + FIXME( "Device updates not supported for object type %d\n", ctx->type ); + break; + } break; case E_CHANGED_STATE: if (ctx->flags & DevQueryFlagAsyncClose) @@ -731,10 +971,15 @@ static void CALLBACK device_query_enum_objects_async( TP_CALLBACK_INSTANCE *inst } break; default: + abort = TRUE; + break; + } + + if (abort) + { ctx->state = DevQueryStateAborted; success = TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL ); LeaveCriticalSection( &ctx->cs ); - break; }
if (!success) @@ -775,6 +1020,7 @@ HRESULT WINAPI DevCreateObjectQueryEx( DEV_OBJECT_TYPE type, ULONG flags, ULONG if (FAILED( hr )) return hr;
+ ctx->filters = !!filters; device_query_context_addref( ctx ); if (!TrySubmitThreadpoolCallback( device_query_enum_objects_async, ctx, NULL )) hr = HRESULT_FROM_WIN32( GetLastError() ); diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index fec2c661e81..3adc7451cad 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -1708,9 +1708,9 @@ static void test_pnp_devices(void) ok(got_bus_arrival == 1, "got %u bus arrival messages\n", got_bus_arrival); ok(!got_bus_removal, "got %u bus removal messages\n", got_bus_removal); status = WaitForSingleObject(devquery_data.device_added_sem, 1000); - todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); - todo_wine ok(devquery_data.bus_dev_added == 1, "got %lu new bus device objects\n", devquery_data.bus_dev_added); - todo_wine_if ( !status ) ok(!devquery_data.bus_dev_removed, "got %lu bus device object removals\n", devquery_data.bus_dev_removed); + ok(!status, "WaitForSingleObject failed, error %lu\n", status); + ok(devquery_data.bus_dev_added == 1, "got %lu new bus device objects\n", devquery_data.bus_dev_added); + ok(!devquery_data.bus_dev_removed, "got %lu bus device object removals\n", devquery_data.bus_dev_removed);
set = SetupDiGetClassDevsA(&bus_class, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#lx\n", GetLastError()); @@ -1728,9 +1728,9 @@ static void test_pnp_devices(void) ok(got_bus_arrival == 1, "got %u bus arrival messages\n", got_bus_arrival); ok(got_bus_removal == 1, "got %u bus removal messages\n", got_bus_removal); status = WaitForSingleObject(devquery_data.device_removed_sem, 1000); - todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); - todo_wine ok(devquery_data.bus_dev_added == 1, "got %lu new bus device objects\n", devquery_data.bus_dev_added); - todo_wine ok(devquery_data.bus_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.bus_dev_removed); + ok(!status, "WaitForSingleObject failed, error %lu\n", status); + ok(devquery_data.bus_dev_added == 1, "got %lu new bus device objects\n", devquery_data.bus_dev_added); + ok(devquery_data.bus_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.bus_dev_removed);
set = SetupDiGetClassDevsA(&bus_class, NULL, NULL, DIGCF_DEVICEINTERFACE); ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#lx\n", GetLastError()); @@ -1758,9 +1758,9 @@ static void test_pnp_devices(void) ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival); ok(!got_child_removal, "got %u child removal messages\n", got_child_removal); status = WaitForSingleObject(devquery_data.device_added_sem, 1000); - todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); - todo_wine ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); - todo_wine_if( !status ) ok(!devquery_data.child_dev_removed, "got %lu bus device object removals\n", devquery_data.child_dev_removed); + ok(!status, "WaitForSingleObject failed, error %lu\n", status); + ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); + ok(!devquery_data.child_dev_removed, "got %lu bus device object removals\n", devquery_data.child_dev_removed);
set = SetupDiGetClassDevsA(&child_class, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); ok(set != INVALID_HANDLE_VALUE, "failed to get device list, error %#lx\n", GetLastError()); @@ -1900,9 +1900,9 @@ static void test_pnp_devices(void) ok(got_child_arrival == 1, "got %u child arrival messages\n", got_child_arrival); ok(got_child_removal == 1, "got %u child removal messages\n", got_child_removal); status = WaitForSingleObject(devquery_data.device_removed_sem, 1000); - todo_wine ok(!status, "WaitForSingleObject failed, error %lu\n", status); - todo_wine ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); - todo_wine ok(devquery_data.child_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.child_dev_removed); + ok(!status, "WaitForSingleObject failed, error %lu\n", status); + ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); + ok(devquery_data.child_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.child_dev_removed);
ret = DeviceIoControl(child, IOCTL_WINETEST_CHILD_CHECK_REMOVED, NULL, 0, NULL, 0, &size, NULL); todo_wine ok(ret, "got error %lu\n", GetLastError()); @@ -1924,8 +1924,8 @@ static void test_pnp_devices(void) ok(status == WAIT_TIMEOUT, "got status %#lx\n", status); status = WaitForSingleObject(devquery_data.device_removed_sem, 1000); ok(status == WAIT_TIMEOUT, "got status %#lx\n", status); - todo_wine ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); - todo_wine ok(devquery_data.child_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.child_dev_removed); + ok(devquery_data.child_dev_added == 1, "got %lu new bus device objects\n", devquery_data.child_dev_added); + ok(devquery_data.child_dev_removed == 1, "got %lu bus device object removals\n", devquery_data.child_dev_removed);
ret = NtOpenFile(&tmp, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT); ok(ret == STATUS_OBJECT_NAME_NOT_FOUND, "got %#x\n", ret);