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