On Windows, passing SHCNRF_InterruptLevel as a flag to SHChangeNotifyRegister results in shell notification messages being sent on filesystem changes, rather than on explicit SHChangeNotify calls. Wine doesn't currently implement this, resulting in some strange shell behavior when pasting or creating a new folder.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=30752 Signed-off-by: Nigel Baillie metreckk@gmail.com --- dlls/shell32/tests/shlfolder.c | 158 +++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+)
diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c index beee31f2ca..d964d77ae4 100644 --- a/dlls/shell32/tests/shlfolder.c +++ b/dlls/shell32/tests/shlfolder.c @@ -4978,6 +4978,163 @@ static void test_SHChangeNotify(BOOL test_new_delivery) ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError()); }
+static void test_SHCNRF_InterruptLevel() +{ + HWND wnd; + ULONG notifyID, i; + HRESULT hr; + HANDLE test_file_handle; + BOOL br, has_unicode; + SHChangeNotifyEntry entries[1]; + struct ChNotifyTest create_event = {"CREATE", 1, 1, SHCNE_CREATE, "C:\shell32_cn_test.txt", ""}; + struct ChNotifyTest create_event2 = {"CREATE", 1, 1, SHCNE_CREATE, "C:\shell32_cn_test_dir\file.txt", ""}; + struct ChNotifyTest rename_event = {"RENAMEITEM", 1, 1, SHCNE_RENAMEITEM, "C:\shell32_cn_test.txt", "C:\shell32.txt"}; + struct ChNotifyTest delete_event = {"DELETE", 1, 1, SHCNE_DELETE, "C:\shell32.txt", ""}; + const CHAR root_dirA[] = "C:\"; + const WCHAR root_dirW[] = {'C',':','\',0}; + const CHAR root_dir2A[] = "C:\shell32_cn_test_dir"; + const WCHAR root_dir2W[] = {'C',':','\','s','h','e','l','l','3','2','_','c','n','_','t','e','s','t','_','d','i','r',0}; + + trace("Testing SHChangeNotifyRegister with SHCNRF_InterruptLevel\n"); + + CreateDirectoryW(NULL, NULL); + has_unicode = !(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED); + + exp_data = &create_event; + test_new_delivery_flag = FALSE; + + /* create test window */ + wnd = CreateWindowExA(0, testwindow_class, testwindow_class, 0, + CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, + NULL, NULL, GetModuleHandleA(NULL), 0); + ok(wnd != NULL, "Failed to make a window\n"); + + /* register for notifications on C:\ */ + entries[0].pidl = NULL; + if(has_unicode) + hr = SHILCreateFromPath(root_dirW, (LPITEMIDLIST*)&entries[0].pidl, 0); + else + hr = SHILCreateFromPath((const void *)root_dirA, (LPITEMIDLIST*)&entries[0].pidl, 0); + ok(hr == S_OK, "SHILCreateFromPath failed: 0x%08x\n", hr); + entries[0].fRecursive = TRUE; + + notifyID = SHChangeNotifyRegister( + wnd, SHCNRF_InterruptLevel, SHCNE_CREATE | SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_DELETE, + WM_USER_NOTIFY, 1, entries); + ok(notifyID != 0, "Failed to register a window for change notifications\n"); + + /* create test file */ + trace("creating %s\n", create_event.path_1); + test_file_handle = CreateFileA(create_event.path_1, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ok(test_file_handle != INVALID_HANDLE_VALUE, "CreateFile failed: %d\n", GetLastError()); + + /* see if a notification gets sent or not */ + SleepEx(1000, FALSE); + do_events(); + ok(exp_data->missing_events < 1, "%s: Expected wndproc to be called\n", exp_data->id); + + /* rename test file */ + CloseHandle(test_file_handle); + exp_data = &rename_event; + trace("renaming %s to %s\n", rename_event.path_1, rename_event.path_2); + br = MoveFileA(rename_event.path_1, rename_event.path_2); + ok(br, "failed to rename %s to %s\n", rename_event.path_1, rename_event.path_2); + + /* see if a notification gets sent or not */ + SleepEx(1000, FALSE); + do_events(); + ok(exp_data->missing_events < 1, "%s: Expected wndproc to be called\n", exp_data->id); + + /* delete test file */ + exp_data = &delete_event; + trace("deleting %s\n", delete_event.path_1); + DeleteFileA(delete_event.path_1); + + /* see if a notification gets sent or not */ + SleepEx(1000, FALSE); + do_events(); + ok(exp_data->missing_events < 1, "%s: Expected wndproc to be called\n", exp_data->id); + + /* deregister */ + SHChangeNotifyDeregister(notifyID); + ILFree((LPITEMIDLIST)entries[0].pidl); + DeleteFileA(create_event.path_1); + + /* create and listen on filename (rather than directory/drive) */ + test_file_handle = CreateFileA(create_event.path_1, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ok(test_file_handle != INVALID_HANDLE_VALUE, "CreateFile failed: %d\n", GetLastError()); + + entries[0].pidl = ILCreateFromPathA(create_event.path_1); + ok(entries[0].pidl != NULL, "ILCreateFromPathA failed\n"); + entries[0].fRecursive = TRUE; + + notifyID = SHChangeNotifyRegister( + wnd, SHCNRF_InterruptLevel, SHCNE_RENAMEITEM, + WM_USER_NOTIFY, 1, entries); + ok(notifyID != 0, "Failed to register a window for change notifications\n"); + + /* rename the file again */ + CloseHandle(test_file_handle); + rename_event.missing_events = rename_event.notify_count; + exp_data = &rename_event; + trace("renaming %s to %s again\n", rename_event.path_1, rename_event.path_2); + br = MoveFileA(rename_event.path_1, rename_event.path_2); + ok(br, "failed to rename %s to %s\n", rename_event.path_1, rename_event.path_2); + + /* see if a notification gets sent or not */ + SleepEx(1000, FALSE); + do_events(); + ok(exp_data->missing_events < 1, "%s: Expected wndproc to be called\n", exp_data->id); + + /* deregister */ + SHChangeNotifyDeregister(notifyID); + ILFree((LPITEMIDLIST)entries[0].pidl); + DeleteFileA(rename_event.path_2); + + /* now, listen on a directory rather than a drive letter */ + trace("creating directory %s\n", root_dir2A); + if (has_unicode) + CreateDirectoryW(root_dir2W, NULL); + else + CreateDirectoryA(root_dir2A, NULL); + + entries[0].pidl = ILCreateFromPathA(root_dir2A); + ok(entries[0].pidl != NULL, "ILCreateFromPathA failed\n"); + entries[0].fRecursive = FALSE; + + notifyID = SHChangeNotifyRegister( + wnd, SHCNRF_InterruptLevel, SHCNE_CREATE, + WM_USER_NOTIFY, 1, entries); + ok(notifyID != 0, "Failed to register a window for change notifications\n"); + + /* create a file */ + exp_data = &create_event2; + trace("creating %s\n", create_event2.path_1); + test_file_handle = CreateFileA(create_event2.path_1, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + ok(test_file_handle != INVALID_HANDLE_VALUE, "CreateFile failed: %d\n", GetLastError()); + + /* see if a notification gets sent or not */ + SleepEx(1000, FALSE); + do_events(); + ok(exp_data->missing_events < 1, "%s: Expected wndproc to be called\n", exp_data->id); + + /* delete test file */ + trace("deleting %s\n", create_event2.path_1); + DeleteFileA(create_event2.path_1); + + /* final cleanup */ + SHChangeNotifyDeregister(notifyID); + DestroyWindow(wnd); + ILFree((LPITEMIDLIST)entries[0].pidl); + RemoveDirectoryA(root_dir2A); +} + static void test_SHCreateDefaultContextMenu(void) { HKEY keys[16]; @@ -5334,6 +5491,7 @@ START_TEST(shlfolder) test_ShellItemCompare(); test_SHChangeNotify(FALSE); test_SHChangeNotify(TRUE); + test_SHCNRF_InterruptLevel(); test_ShellItemBindToHandler(); test_ShellItemGetAttributes(); test_ShellItemArrayGetAttributes();