Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378/* * PROJECT: ReactOS api tests * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Test for SHChangeNotify * COPYRIGHT: Copyright 2020-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */
// This program is used in SHChangeNotify and ShellExecCmdLine testcases.
#include "shelltest.h"#include "shell32_apitest_sub.h"#include <assert.h>
typedef enum DIRTYPE{ DIRTYPE_DESKTOP = 0, DIRTYPE_DESKTOP_DIR, DIRTYPE_DRIVES, DIRTYPE_PRINTERS, DIRTYPE_DIR1, DIRTYPE_MAX} DIRTYPE;
static HWND s_hMainWnd = NULL, s_hSubWnd = NULL;static LPITEMIDLIST s_pidl[DIRTYPE_MAX];static UINT s_uRegID = 0;static INT s_iStage = -1;
#define EVENTS (SHCNE_CREATE | SHCNE_DELETE | SHCNE_MKDIR | SHCNE_RMDIR | \ SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM)
inline LPITEMIDLIST DoGetPidl(INT iDir){ LPITEMIDLIST ret = NULL;
switch (iDir) { case DIRTYPE_DESKTOP: { SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &ret); break; } case DIRTYPE_DESKTOP_DIR: { WCHAR szPath1[MAX_PATH]; SHGetSpecialFolderPathW(NULL, szPath1, CSIDL_DESKTOPDIRECTORY, FALSE); ret = ILCreateFromPathW(szPath1); break; } case DIRTYPE_DRIVES: { SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &ret); break; } case DIRTYPE_PRINTERS: { SHGetSpecialFolderLocation(NULL, CSIDL_PRINTERS, &ret); break; } case DIRTYPE_DIR1: { WCHAR szPath1[MAX_PATH]; SHGetSpecialFolderPathW(NULL, szPath1, CSIDL_PERSONAL, FALSE); // My Documents PathAppendW(szPath1, L"_TESTDIR_1_"); ret = ILCreateFromPathW(szPath1); break; } default: { assert(0); break; } }
return ret;}
static BOOL OnCreate(HWND hwnd){ s_hSubWnd = hwnd;
for (INT i = 0; i < DIRTYPE_MAX; ++i) s_pidl[i] = DoGetPidl(i);
return TRUE;}
static BOOL InitSHCN(HWND hwnd){ assert(0 <= s_iStage); assert(s_iStage < NUM_STAGE);
SHChangeNotifyEntry entry; INT sources = 0; LONG events = EVENTS; switch (s_iStage) { case 0: { entry.fRecursive = FALSE; entry.pidl = NULL; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 1: { entry.fRecursive = TRUE; entry.pidl = NULL; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 2: { entry.fRecursive = FALSE; entry.pidl = s_pidl[DIRTYPE_DESKTOP]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 3: { entry.fRecursive = TRUE; entry.pidl = s_pidl[DIRTYPE_DESKTOP]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 4: { entry.fRecursive = TRUE; entry.pidl = s_pidl[DIRTYPE_DESKTOP_DIR]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 5: { entry.fRecursive = FALSE; entry.pidl = s_pidl[DIRTYPE_DRIVES]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 6: { entry.fRecursive = TRUE; entry.pidl = s_pidl[DIRTYPE_DRIVES]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 7: { entry.fRecursive = TRUE; entry.pidl = s_pidl[DIRTYPE_PRINTERS]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 8: { entry.fRecursive = FALSE; entry.pidl = s_pidl[DIRTYPE_DIR1]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel; events = EVENTS; break; } case 9: { entry.fRecursive = TRUE; entry.pidl = s_pidl[DIRTYPE_DIR1]; sources = SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel | SHCNRF_RecursiveInterrupt; events = EVENTS; break; } DEFAULT_UNREACHABLE; }
s_uRegID = SHChangeNotifyRegister(hwnd, sources, events, WM_SHELL_NOTIFY, 1, &entry); if (s_uRegID == 0) return FALSE;
return TRUE;}
static void UnInitSHCN(HWND hwnd){ if (s_uRegID) { SHChangeNotifyDeregister(s_uRegID); s_uRegID = 0; }}
static void OnCommand(HWND hwnd, UINT id){ switch (id) { case IDYES: // Start testing { s_hMainWnd = ::FindWindow(MAIN_CLASSNAME, MAIN_CLASSNAME); if (!s_hMainWnd) { ::DestroyWindow(hwnd); break; } s_iStage = 0; InitSHCN(hwnd); ::PostMessageW(s_hMainWnd, WM_COMMAND, IDYES, 0); break; } case IDRETRY: // New stage { UnInitSHCN(hwnd); ++s_iStage; InitSHCN(hwnd); ::PostMessageW(s_hMainWnd, WM_COMMAND, IDRETRY, 0); break; } case IDNO: // Quit { s_iStage = -1; UnInitSHCN(hwnd); ::DestroyWindow(hwnd); break; } }}
static void OnDestroy(HWND hwnd){ UnInitSHCN(hwnd);
for (auto& pidl : s_pidl) { CoTaskMemFree(pidl); pidl = NULL; }
::PostMessageW(s_hMainWnd, WM_COMMAND, IDNO, 0);
PostQuitMessage(0);}
static BOOL DoSendData(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2){ DWORD cbPidl1 = ILGetSize(pidl1), cbPidl2 = ILGetSize(pidl2); DWORD cbTotal = sizeof(lEvent) + sizeof(cbPidl1) + sizeof(cbPidl2) + cbPidl1 + cbPidl2; LPBYTE pbData = (LPBYTE)::LocalAlloc(LPTR, cbTotal); if (!pbData) return FALSE;
LPBYTE pb = pbData;
*(LONG*)pb = lEvent; pb += sizeof(lEvent);
*(DWORD*)pb = cbPidl1; pb += sizeof(cbPidl1);
*(DWORD*)pb = cbPidl2; pb += sizeof(cbPidl2);
CopyMemory(pb, pidl1, cbPidl1); pb += cbPidl1;
CopyMemory(pb, pidl2, cbPidl2); pb += cbPidl2;
assert(INT(pb - pbData) == INT(cbTotal));
COPYDATASTRUCT CopyData; CopyData.dwData = 0xBEEFCAFE; CopyData.cbData = cbTotal; CopyData.lpData = pbData; BOOL ret = (BOOL)::SendMessageW(s_hMainWnd, WM_COPYDATA, (WPARAM)s_hSubWnd, (LPARAM)&CopyData);
::LocalFree(pbData); return ret;}
static void DoShellNotify(HWND hwnd, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2, LONG lEvent){ if (s_iStage < 0) return;
DoSendData(lEvent, pidl1, pidl2);}
static INT_PTR OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam){ LONG lEvent; PIDLIST_ABSOLUTE *pidlAbsolute; HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &pidlAbsolute, &lEvent); if (hLock) { DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lEvent); SHChangeNotification_Unlock(hLock); } else { pidlAbsolute = (PIDLIST_ABSOLUTE *)wParam; DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lParam); } return TRUE;}
static LRESULT CALLBACK SubWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { case WM_CREATE: return (OnCreate(hwnd) ? 0 : -1);
case WM_COMMAND: OnCommand(hwnd, LOWORD(wParam)); break;
case WM_SHELL_NOTIFY: return OnShellNotify(hwnd, wParam, lParam);
case WM_DESTROY: OnDestroy(hwnd); break;
default: return ::DefWindowProcW(hwnd, uMsg, wParam, lParam); } return 0;}
INT APIENTRYwWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nCmdShow){ if (lstrcmpiW(lpCmdLine, L"") == 0 || lstrcmpiW(lpCmdLine, L"TEST") == 0) return 0;
WNDCLASSW wc = { 0, SubWindowProc }; wc.hInstance = hInstance; wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wc.lpszClassName = SUB_CLASSNAME; if (!RegisterClassW(&wc)) { assert(0); return -1; }
HWND hwnd = CreateWindowW(SUB_CLASSNAME, SUB_CLASSNAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 100, NULL, NULL, hInstance, NULL); if (!hwnd) { assert(0); return -2; }
ShowWindow(hwnd, SW_SHOWNORMAL); UpdateWindow(hwnd);
MSG msg; while (GetMessageW(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); }
return 0;}