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);