-- v11: cfgmgr32: Implement device updates for DevCreateObjectQuery. ntoskrnl.exe/tests: Add tests for device updates in DevCreateObjectQuery. cfgmgr32: Implement DevGetObjectProperties for device interfaces. cfgmgr32: Implement DevFreeObjectProperties. cfgmgr32: Add stubs for DevGetObjectProperties(Ex).
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/cfgmgr32.spec | 3 + dlls/cfgmgr32/main.c | 21 ++++ dlls/cfgmgr32/tests/cfgmgr32.c | 179 +++++++++++++++++++++++++++++++++ 3 files changed, 203 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 6f3b058c237..e6147e0a81c 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -829,3 +829,24 @@ 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 ); +} diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 0107b81cca2..1cedd18c01a 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" @@ -123,6 +124,9 @@ void (WINAPI *pDevCloseObjectQuery)(HDEVQUERY); HRESULT (WINAPI *pDevGetObjects)(DEV_OBJECT_TYPE, ULONG, ULONG, const DEVPROPCOMPKEY*, ULONG, const DEVPROP_FILTER_EXPRESSION*, ULONG*, const DEV_OBJECT**); void (WINAPI *pDevFreeObjects)(ULONG, const DEV_OBJECT*); +HRESULT (WINAPI *pDevGetObjectProperties)(DEV_OBJECT_TYPE, const WCHAR *, ULONG, ULONG, const DEVPROPCOMPKEY *, ULONG *, + const DEVPROPERTY **); +void (WINAPI *pDevFreeObjectProperties)(ULONG, const DEVPROPERTY *);
DEFINE_DEVPROPKEY(DEVPROPKEY_GPU_LUID, 0x60b193cb, 0x5276, 0x4d0f, 0x96, 0xfc, 0xf1, 0x73, 0xab, 0xad, 0x3e, 0xc6, 2);
@@ -556,6 +560,134 @@ 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; + + if (!pDevGetObjectProperties || !pDevFreeObjectProperties) + { + win_skip( "Functions unavailable, skipping test. (%p %p)\n", pDevGetObjects, pDevFreeObjects ); + return; + } + + hr = pDevGetObjectProperties( type, id, DevQueryFlagUpdateResults, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( type, id, DevQueryFlagAsyncClose, 0, NULL, &buf_len, &buf ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( type, id, DevQueryFlagAsyncClose, 0, NULL, &buf_len, &buf ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( type, id, DevQueryFlagNone, 1, NULL, &buf_len, &buf ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 ); + pDevFreeObjectProperties( buf_len, buf ); + + buf = (DEVPROPERTY *)0xdeadbeef; + buf_len = 0xdeadbeef; + rem_props = props_len; + hr = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 ); + pDevFreeObjectProperties( buf_len, buf ); + + buf_len = 0; + buf = NULL; + hr = pDevGetObjectProperties( 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 ); + pDevFreeObjectProperties( 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 ) { @@ -726,6 +858,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(); } pDevFreeObjects( len, objects ); @@ -939,6 +1074,47 @@ static void test_DevCreateObjectQuery( void ) CloseHandle( data.closed ); }
+static void test_DevGetObjectProperties_invalid( void ) +{ + HRESULT hr; + + if (!pDevGetObjectProperties) + { + win_skip( "Functions unavailable, skipping test. (%p)\n", pDevGetObjectProperties ); + return; + } + + hr = pDevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( DevObjectTypeUnknown, L"", 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( DevObjectTypeUnknown, NULL, DevQueryFlagAsyncClose, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", DevQueryFlagUpdateResults, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( DevObjectTypeDeviceInterface, L"foobar", 0xdeadbeef, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( DevObjectTypeUnknown, NULL, 0, 1, NULL, NULL, NULL ); + todo_wine ok( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ), "got hr %#lx\n", hr ); + + hr = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( DevObjectTypeDeviceInterface, NULL, 0, 0, NULL, NULL, NULL ); + todo_wine ok( hr == E_INVALIDARG, "got hr %#lx\n", hr ); +} + START_TEST(cfgmgr32) { HMODULE mod = GetModuleHandleA("cfgmgr32.dll"); @@ -946,6 +1122,8 @@ START_TEST(cfgmgr32) pDevCloseObjectQuery = (void *)GetProcAddress(mod, "DevCloseObjectQuery"); pDevGetObjects = (void *)GetProcAddress(mod, "DevGetObjects"); pDevFreeObjects = (void *)GetProcAddress(mod, "DevFreeObjects"); + pDevGetObjectProperties = (void *)GetProcAddress(mod, "DevGetObjectProperties"); + pDevFreeObjectProperties = (void *)GetProcAddress(mod, "DevFreeObjectProperties");
test_CM_MapCrToWin32Err(); test_CM_Get_Device_ID_List(); @@ -953,4 +1131,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 | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index e6147e0a81c..cf05c8d8758 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -395,9 +395,7 @@ done: free( all_keys ); if (FAILED( hr )) { - for (i = 0; i < obj->cPropertyCount; i++) - free( ( (DEVPROPERTY *)obj[i].pProperties )->Buffer ); - free( (DEVPROPERTY *)obj->pProperties ); + DevFreeObjectProperties( obj->cPropertyCount, obj->pProperties ); obj->cPropertyCount = 0; obj->pProperties = NULL; } @@ -565,13 +563,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 ); @@ -595,11 +587,9 @@ struct device_query_context
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; HRESULT hr = S_OK; - ULONG i;
action_data.Action = DevQueryResultAdd; action_data.Data.DeviceObject = obj; @@ -610,9 +600,7 @@ static HRESULT device_query_context_add_object( DEV_OBJECT obj, void *data ) hr = E_CHANGED_STATE; LeaveCriticalSection( &ctx->cs );
- for (i = 0; i < obj.cPropertyCount; i++) - free( props[i].Buffer ); - free( props ); + DevFreeObjectProperties( obj.cPropertyCount, obj.pProperties ); return hr; }
@@ -848,5 +836,12 @@ HRESULT WINAPI DevGetObjectPropertiesEx( DEV_OBJECT_TYPE type, const WCHAR *id,
void WINAPI DevFreeObjectProperties( ULONG len, const DEVPROPERTY *props ) { - FIXME( "(%lu, %p): stub!\n", len, props ); + DEVPROPERTY *properties = (DEVPROPERTY *)props; + ULONG i; + + TRACE( "(%lu, %p)\n", len, props ); + + for (i = 0; i < len; i++) + free( properties[i].Buffer ); + free( properties ); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 44 +++++++++++++++++++++++++-- dlls/cfgmgr32/tests/cfgmgr32.c | 54 +++++++++++++++++----------------- 2 files changed, 69 insertions(+), 29 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index cf05c8d8758..a5d5c9046a7 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -829,9 +829,49 @@ 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 = {.cbSize = sizeof( iface )}; + DEV_OBJECT obj = {0}; + HDEVINFO set; + + set = SetupDiCreateDeviceInfoListExW( NULL, NULL, NULL, NULL ); + if (set == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32( GetLastError() ); + 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( &obj, set, &iface, props_len, props, !!(flags & DevQueryFlagAllProperties) ); + *buf = obj.pProperties; + *buf_len = obj.cPropertyCount; + 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 ) diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 1cedd18c01a..f328da04200 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -600,25 +600,25 @@ static void test_DevGetObjectProperties( DEV_OBJECT_TYPE type, const WCHAR *id, }
hr = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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; @@ -638,16 +638,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 ); pDevFreeObjectProperties( buf_len, buf );
buf = (DEVPROPERTY *)0xdeadbeef; buf_len = 0xdeadbeef; rem_props = props_len; hr = pDevGetObjectProperties( 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; @@ -655,8 +655,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 = pDevGetObjectProperties( 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; @@ -669,15 +669,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 ); pDevFreeObjectProperties( buf_len, buf );
buf_len = 0; buf = NULL; hr = pDevGetObjectProperties( 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", @@ -1085,34 +1085,34 @@ static void test_DevGetObjectProperties_invalid( void ) }
hr = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 = pDevGetObjectProperties( 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 | 264 +++++++++++++++++++++++++++- 2 files changed, 264 insertions(+), 2 deletions(-)
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 1822923e552..749a25ad9fe 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" @@ -58,6 +60,10 @@ static struct test_data *test_data; static BOOL (WINAPI *pCancelIoEx)(HANDLE, OVERLAPPED *); static BOOL (WINAPI *pSetFileCompletionNotificationModes)(HANDLE, UCHAR);
+static HRESULT (WINAPI *pDevCreateObjectQuery)(DEV_OBJECT_TYPE, ULONG, ULONG, const DEVPROPCOMPKEY*, ULONG, + const DEVPROP_FILTER_EXPRESSION*, PDEV_QUERY_RESULT_CALLBACK, void*, HDEVQUERY*); +static void (WINAPI *pDevCloseObjectQuery)(HDEVQUERY); + static void load_resource(const WCHAR *name, WCHAR *filename) { static WCHAR path[MAX_PATH]; @@ -1443,6 +1449,191 @@ 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 const char *debugstr_DEV_OBJECT_TYPE(DEV_OBJECT_TYPE type) +{ + static const char *str[] = { + "DevObjectTypeUnknown", + "DevObjectTypeDeviceInterface", + "DevObjectTypeDeviceContainer", + "DevObjectTypeDevice", + "DevObjectTypeDeviceInterfaceClass", + "DevObjectTypeAEP", + "DevObjectTypeAEPContainer", + "DevObjectTypeDeviceInstallerClass", + "DevObjectTypeDeviceInterfaceDisplay", + "DevObjectTypeDeviceContainerDisplay", + "DevObjectTypeAEPService", + "DevObjectTypeDevicePanel", + "DevObjectTypeAEPProtocol", + }; + if (type >= ARRAY_SIZE(str)) + return wine_dbg_sprintf("(unknown %d)", type); + return wine_dbg_sprintf("%s", str[type]); +} + +static const char *debugstr_DEV_QUERY_RESULT_ACTION_DATA(const DEV_QUERY_RESULT_ACTION_DATA *action_data) +{ + if (!action_data) return wine_dbg_sprintf("(null)"); + switch (action_data->Action) + { + case DevQueryResultStateChange: + { + static const char *str[] = { + "DevQueryStateInitialized", + "DevQueryStateEnumCompleted", + "DevQueryStateAborted", + "DevQueryStateClosed" + }; + + if (action_data->Data.State >= ARRAY_SIZE(str)) + return wine_dbg_sprintf("{ DevQueryResultStateChange { (unknown %d) } }", action_data->Data.State); + return wine_dbg_sprintf("{ DevQueryResultStateChange { %s } }", str[action_data->Data.State]); + } + case DevQueryResultAdd: + case DevQueryResultUpdate: + case DevQueryResultRemove: + { + const DEV_OBJECT *obj = &action_data->Data.DeviceObject; + static const char *str[] = { + "DevQueryResultAdd", + "DevQueryResultUpdate", + "DevQueryResultRemove", + }; + + return wine_dbg_sprintf("{ %s { %s %s %lu %p } }", str[action_data->Action - 1], + debugstr_DEV_OBJECT_TYPE(obj->ObjectType), debugstr_w(obj->pszObjectId), + obj->cPropertyCount, obj->pProperties); + } + default: + return wine_dbg_sprintf("{ (unknown %d) }", action_data->Action); + } +} + +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 (winetest_debug > 1) + trace("%s\n", debugstr_DEV_QUERY_RESULT_ACTION_DATA(action_data)); + + 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 (winetest_debug > 1) + trace("%s\n", debugstr_DEV_QUERY_RESULT_ACTION_DATA(action_data)); + 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 != %Iu\n", prop->BufferSize, 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 != %Iu\n", prop->BufferSize, + 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 (winetest_debug > 1) + trace("%s\n", debugstr_DEV_QUERY_RESULT_ACTION_DATA(action_data)); + 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 != %Iu\n", prop->BufferSize, + 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 GUID expect_container_id_guid = {0x12345678, 0x1234, 0x1234, {0x12, 0x34, 0x12, 0x34, 0x56, 0x78, 0x91, 0x23}}; @@ -1473,6 +1664,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; @@ -1480,11 +1677,13 @@ 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; + BOOL ret, have_devquery = pDevCreateObjectQuery && pDevCloseObjectQuery; int id;
ret = RegisterClassA(&class); @@ -1494,6 +1693,21 @@ 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());
+ if (!have_devquery) + win_skip("DevCreateObjectQuery, DevCloseObjectQuery unavailable.\n"); + + if (have_devquery) + { + 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 = pDevCreateObjectQuery(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());
@@ -1573,6 +1787,13 @@ 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); + if (have_devquery) + { + 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()); @@ -1589,6 +1810,13 @@ 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); + if (have_devquery) + { + 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()); @@ -1615,6 +1843,13 @@ 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); + if (have_devquery) + { + 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()); @@ -1783,6 +2018,13 @@ 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); + if (have_devquery) + { + 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()); @@ -1800,6 +2042,15 @@ 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); + if (have_devquery) + { + 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); @@ -1807,6 +2058,13 @@ static void test_pnp_devices(void) CloseHandle(bus);
UnregisterDeviceNotification(notify_handle); + if (have_devquery) + { + pDevCloseObjectQuery(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)); } @@ -1960,10 +2218,13 @@ START_TEST(ntoskrnl) BOOL ret, is_wow64; HANDLE mapping; DWORD written; + HMODULE cfgmgr32 = LoadLibraryA("cfgmgr32.dll");
pCancelIoEx = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "CancelIoEx"); pSetFileCompletionNotificationModes = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetFileCompletionNotificationModes"); + pDevCreateObjectQuery = (void *)GetProcAddress(cfgmgr32, "DevCreateObjectQuery"); + pDevCloseObjectQuery = (void *)GetProcAddress(cfgmgr32, "DevCloseObjectQuery");
if (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64) { @@ -2037,6 +2298,7 @@ START_TEST(ntoskrnl)
out: testsign_cleanup(&ctx); + FreeLibrary(cfgmgr32); UnmapViewOfFile(test_data); CloseHandle(mapping); CloseHandle(okfile);
From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 263 +++++++++++++++++++++++++++-- dlls/ntoskrnl.exe/tests/ntoskrnl.c | 28 +-- 2 files changed, 262 insertions(+), 29 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index a5d5c9046a7..5a1418a0f3c 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" @@ -570,6 +571,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; @@ -577,20 +590,26 @@ 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 ) { 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;
+ 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 ); @@ -598,6 +617,20 @@ 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 ) )) || !(iface_entry->path = wcsdup( obj.pszObjectId ))) + { + if (iface_entry) free( iface_entry->path ); + free( iface_entry ); + hr = E_OUTOFMEMORY; + } + else if (rb_put( &ctx->known_ifaces, iface_entry->path, &iface_entry->entry )) + { + free( iface_entry->path ); + free( iface_entry ); + } + } LeaveCriticalSection( &ctx->cs );
DevFreeObjectProperties( obj.cPropertyCount, obj.pProperties ); @@ -642,6 +675,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; @@ -652,6 +686,13 @@ 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 )) @@ -660,6 +701,7 @@ static void device_query_context_release( struct device_query_context *ctx ) 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 ); } } @@ -691,14 +733,171 @@ 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; + const WCHAR *iface_path; + struct rb_entry *entry; + HRESULT hr = S_OK; + + 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; + device_query_context_addref( ctx ); + if (!TrySubmitThreadpoolCallback( device_query_notify_dev_async, notify_data, NULL )) + device_query_context_release( ctx ); + LeaveCriticalSection( &ctx->cs ); + return 0; +abort: + free( notify_data ); + ctx->state = DevQueryStateAborted; + device_query_context_addref( ctx ); + if (!TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL )) + device_query_context_release( ctx ); + LeaveCriticalSection( &ctx->cs ); + 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; + 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) @@ -709,28 +908,56 @@ 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 ); - break; - case E_CHANGED_STATE: - if (ctx->flags & DevQueryFlagAsyncClose) + if (ctx->filters || !(ctx->flags & DevQueryFlagUpdateResults)) + break; + switch (ctx->type) { - success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL ); - LeaveCriticalSection( &ctx->cs ); + 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; + device_query_context_addref( ctx ); + ret = CM_Register_Notification( &filter, ctx, device_query_context_cm_notify_callback, &ctx->notify ); + if (ret) + { + ERR( "CM_Register_Notification failed: %lu\n", ret ); + device_query_context_release( ctx ); + goto abort; + } + break; } - else + default: + FIXME( "Device updates not supported for object type %d\n", ctx->type ); + break; + } + break; + case E_CHANGED_STATE: + if (!(ctx->flags & DevQueryFlagAsyncClose)) { LeaveCriticalSection( &ctx->cs ); SetEvent( ctx->closed ); device_query_context_release( ctx ); + return; } + success = TrySubmitThreadpoolCallback( device_query_context_notify_closed_async, ctx, NULL ); break; default: - ctx->state = DevQueryStateAborted; - success = TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL ); - LeaveCriticalSection( &ctx->cs ); - break; + goto abort; }
+ LeaveCriticalSection( &ctx->cs ); + if (!success) + device_query_context_release( ctx ); + return; +abort: + ctx->state = DevQueryStateAborted; + success = TrySubmitThreadpoolCallback( device_query_context_notify_aborted_async, ctx, NULL ); + LeaveCriticalSection( &ctx->cs ); if (!success) device_query_context_release( ctx ); } @@ -769,6 +996,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() ); @@ -797,6 +1025,11 @@ void WINAPI DevCloseObjectQuery( HDEVQUERY query ) old = ctx->state; ctx->state = DevQueryStateClosed;
+ if (ctx->notify) + { + CM_Unregister_Notification( ctx->notify ); + device_query_context_release( ctx ); /* Reference held by CM_Register_Notification. */ + } if (async && old == DevQueryStateEnumCompleted) { /* No asynchronous operation involving this query is active, so we need to notify DevQueryStateClosed. */ diff --git a/dlls/ntoskrnl.exe/tests/ntoskrnl.c b/dlls/ntoskrnl.exe/tests/ntoskrnl.c index 749a25ad9fe..6d3914c8c0b 100644 --- a/dlls/ntoskrnl.exe/tests/ntoskrnl.c +++ b/dlls/ntoskrnl.exe/tests/ntoskrnl.c @@ -1790,9 +1790,9 @@ static void test_pnp_devices(void) if (have_devquery) { 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); @@ -1813,9 +1813,9 @@ static void test_pnp_devices(void) if (have_devquery) { 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); @@ -1846,9 +1846,9 @@ static void test_pnp_devices(void) if (have_devquery) { 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); @@ -2021,9 +2021,9 @@ static void test_pnp_devices(void) if (have_devquery) { 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); @@ -2048,8 +2048,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);