1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4  * PURPOSE:     Test for SHChangeNotify
5  * COPYRIGHT:   Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 
8 #include "shelltest.h"
9 #include "shell32_apitest_sub.h"
10 
11 static HWND s_hwnd = NULL;
12 static UINT s_uRegID = 0;
13 static BOOL s_fRecursive = FALSE;
14 static DIRTYPE s_iWatchDir = DIRTYPE_NULL;
15 static INT s_nSources = 0;
16 static LPITEMIDLIST s_pidl = NULL;
17 static WCHAR s_path1[MAX_PATH], s_path2[MAX_PATH];
18 static BYTE s_counters[TYPE_MAX + 1];
19 static HANDLE s_hEvent = NULL;
20 
21 static BOOL
22 OnCreate(HWND hwnd)
23 {
24     s_hwnd = hwnd;
25     s_pidl = DoGetPidl(s_iWatchDir);
26 
27     SHChangeNotifyEntry entry;
28     entry.pidl = s_pidl;
29     entry.fRecursive = s_fRecursive;
30     s_uRegID = SHChangeNotifyRegister(hwnd, s_nSources, SHCNE_ALLEVENTS, WM_SHELL_NOTIFY, 1, &entry);
31     return s_uRegID != 0;
32 }
33 
34 static void
35 OnCommand(HWND hwnd, UINT id)
36 {
37     switch (id)
38     {
39         case IDOK:
40         case IDCANCEL:
41             DestroyWindow(hwnd);
42             break;
43     }
44 }
45 
46 static void
47 OnDestroy(HWND hwnd)
48 {
49     SHChangeNotifyDeregister(s_uRegID);
50     s_uRegID = 0;
51 
52     CoTaskMemFree(s_pidl);
53     s_pidl = NULL;
54 
55     PostQuitMessage(0);
56     s_hwnd = NULL;
57 }
58 
59 static BOOL DoPathes(PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2)
60 {
61     WCHAR path[MAX_PATH];
62     if (!SHGetPathFromIDListW(pidl1, path))
63     {
64         s_path1[0] = s_path2[0] = 0;
65         return FALSE;
66     }
67 
68     if (wcsstr(path, L"Recent") != NULL)
69         return FALSE;
70 
71     StringCchCopyW(s_path1, _countof(s_path1), path);
72 
73     if (!SHGetPathFromIDListW(pidl2, s_path2))
74         s_path2[0] = 0;
75 
76     return TRUE;
77 }
78 
79 static VOID DoPathesAndFlags(UINT type, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2)
80 {
81     if (DoPathes(pidl1, pidl2))
82     {
83         s_counters[type] = 1;
84         SetEvent(s_hEvent);
85     }
86 }
87 
88 static void
89 DoShellNotify(HWND hwnd, PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2, LONG lEvent)
90 {
91     switch (lEvent)
92     {
93         case SHCNE_RENAMEITEM:
94             DoPathesAndFlags(TYPE_RENAMEITEM, pidl1, pidl2);
95             break;
96         case SHCNE_CREATE:
97             DoPathesAndFlags(TYPE_CREATE, pidl1, pidl2);
98             break;
99         case SHCNE_DELETE:
100             DoPathesAndFlags(TYPE_DELETE, pidl1, pidl2);
101             break;
102         case SHCNE_MKDIR:
103             DoPathesAndFlags(TYPE_MKDIR, pidl1, pidl2);
104             break;
105         case SHCNE_RMDIR:
106             DoPathesAndFlags(TYPE_RMDIR, pidl1, pidl2);
107             break;
108         case SHCNE_MEDIAINSERTED:
109             break;
110         case SHCNE_MEDIAREMOVED:
111             break;
112         case SHCNE_DRIVEREMOVED:
113             break;
114         case SHCNE_DRIVEADD:
115             break;
116         case SHCNE_NETSHARE:
117             break;
118         case SHCNE_NETUNSHARE:
119             break;
120         case SHCNE_ATTRIBUTES:
121             break;
122         case SHCNE_UPDATEDIR:
123             DoPathesAndFlags(TYPE_UPDATEDIR, pidl1, pidl2);
124             break;
125         case SHCNE_UPDATEITEM:
126             break;
127         case SHCNE_SERVERDISCONNECT:
128             break;
129         case SHCNE_UPDATEIMAGE:
130             break;
131         case SHCNE_DRIVEADDGUI:
132             break;
133         case SHCNE_RENAMEFOLDER:
134             DoPathesAndFlags(TYPE_RENAMEFOLDER, pidl1, pidl2);
135             break;
136         case SHCNE_FREESPACE:
137             break;
138         case SHCNE_EXTENDED_EVENT:
139             break;
140         case SHCNE_ASSOCCHANGED:
141             break;
142         default:
143             break;
144     }
145 }
146 
147 static INT_PTR
148 OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam)
149 {
150     LONG lEvent;
151     PIDLIST_ABSOLUTE *pidlAbsolute;
152     HANDLE hLock = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &pidlAbsolute, &lEvent);
153     if (hLock)
154     {
155         DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lEvent);
156         SHChangeNotification_Unlock(hLock);
157     }
158     else
159     {
160         pidlAbsolute = (PIDLIST_ABSOLUTE *)wParam;
161         DoShellNotify(hwnd, pidlAbsolute[0], pidlAbsolute[1], lParam);
162     }
163     return TRUE;
164 }
165 
166 static LRESULT
167 OnGetNotifyFlags(HWND hwnd)
168 {
169     if (s_uRegID == 0)
170         return 0xFFFFFFFF;
171 
172     DWORD dwFlags = 0;
173     for (size_t i = 0; i < _countof(s_counters); ++i)
174     {
175         if (s_counters[i])
176             dwFlags |= (1 << i);
177     }
178     return dwFlags;
179 }
180 
181 static void
182 DoSetPaths(HWND hwnd)
183 {
184     WCHAR szText[MAX_PATH * 2];
185     StringCchCopyW(szText, _countof(szText), s_path1);
186     StringCchCatW(szText, _countof(szText), L"|");
187     StringCchCatW(szText, _countof(szText), s_path2);
188 
189     FILE *fp = _wfopen(TEMP_FILE, L"wb");
190     if (fp)
191     {
192         fwrite(szText, (wcslen(szText) + 1) * sizeof(WCHAR), 1, fp);
193         fflush(fp);
194         fclose(fp);
195     }
196 }
197 
198 static LRESULT CALLBACK
199 WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
200 {
201     switch (uMsg)
202     {
203         case WM_CREATE:
204             return (OnCreate(hwnd) ? 0 : -1);
205 
206         case WM_COMMAND:
207             OnCommand(hwnd, LOWORD(wParam));
208             break;
209 
210         case WM_SHELL_NOTIFY:
211             return OnShellNotify(hwnd, wParam, lParam);
212 
213         case WM_DESTROY:
214             OnDestroy(hwnd);
215             break;
216 
217         case WM_GET_NOTIFY_FLAGS:
218             return OnGetNotifyFlags(hwnd);
219 
220         case WM_CLEAR_FLAGS:
221             ZeroMemory(&s_counters, sizeof(s_counters));
222             s_path1[0] = s_path2[0] = 0;
223             break;
224 
225         case WM_SET_PATHS:
226             DoSetPaths(hwnd);
227             break;
228 
229         default:
230             return DefWindowProcW(hwnd, uMsg, wParam, lParam);
231     }
232     return 0;
233 }
234 
235 static BOOL ParseCommandLine(LPWSTR lpCmdLine)
236 {
237     LPWSTR pch = lpCmdLine; // fRecursive,iWatchDir,nSources
238     s_fRecursive = !!wcstoul(pch, NULL, 0);
239     pch = wcschr(pch, L',');
240     if (!pch)
241         return FALSE;
242     ++pch;
243 
244     s_iWatchDir = (DIRTYPE)wcstoul(pch, NULL, 0);
245     pch = wcschr(pch, L',');
246     if (!pch)
247         return FALSE;
248     ++pch;
249 
250     s_nSources = wcstoul(pch, NULL, 0);
251     return TRUE;
252 }
253 
254 INT APIENTRY
255 wWinMain(HINSTANCE hInstance,
256          HINSTANCE hPrevInstance,
257          LPWSTR    lpCmdLine,
258          INT       nCmdShow)
259 {
260     if (lstrcmpiW(lpCmdLine, L"") == 0 || lstrcmpiW(lpCmdLine, L"TEST") == 0)
261         return 0;
262 
263     if (!ParseCommandLine(lpCmdLine))
264         return -1;
265 
266     s_hEvent = OpenEventW(EVENT_ALL_ACCESS, TRUE, EVENT_NAME);
267 
268     WNDCLASSW wc;
269     ZeroMemory(&wc, sizeof(wc));
270     wc.lpfnWndProc = WindowProc;
271     wc.hInstance = GetModuleHandleW(NULL);
272     wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
273     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
274     wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
275     wc.lpszClassName = CLASSNAME;
276     if (!RegisterClassW(&wc))
277         return -1;
278 
279     HWND hwnd = CreateWindowW(CLASSNAME, CLASSNAME, WS_OVERLAPPEDWINDOW,
280                               CW_USEDEFAULT, CW_USEDEFAULT, 400, 100,
281                               NULL, NULL, GetModuleHandleW(NULL), NULL);
282     if (!hwnd)
283         return -1;
284 
285     ShowWindow(hwnd, SW_SHOWNORMAL);
286     UpdateWindow(hwnd);
287 
288     MSG msg;
289     while (GetMessageW(&msg, NULL, 0, 0))
290     {
291         TranslateMessage(&msg);
292         DispatchMessageW(&msg);
293     }
294 
295     CloseHandle(s_hEvent);
296 
297     return 0;
298 }
299