1fe397602SKatayama Hirofumi MZ /*
2fe397602SKatayama Hirofumi MZ  * PROJECT:     ReactOS api tests
3fe397602SKatayama Hirofumi MZ  * LICENSE:     LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later)
4fe397602SKatayama Hirofumi MZ  * PURPOSE:     Test for SHChangeNotify
525e2f5f2SKatayama Hirofumi MZ  * COPYRIGHT:   Copyright 2020-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6fe397602SKatayama Hirofumi MZ  */
7fe397602SKatayama Hirofumi MZ 
825e2f5f2SKatayama Hirofumi MZ // NOTE: This testcase requires shell32_apitest_sub.exe.
990aff8ddSKatayama Hirofumi MZ 
10fe397602SKatayama Hirofumi MZ #include "shelltest.h"
1160b89da3SKatayama Hirofumi MZ #include "shell32_apitest_sub.h"
1225e2f5f2SKatayama Hirofumi MZ #include <assert.h>
13fe397602SKatayama Hirofumi MZ 
1425e2f5f2SKatayama Hirofumi MZ #define NUM_STEP        8
1525e2f5f2SKatayama Hirofumi MZ #define NUM_CHECKS      12
16d23573beSKatayama Hirofumi MZ #define INTERVAL        0
1725e2f5f2SKatayama Hirofumi MZ #define MAX_EVENT_TYPE  6
187e9cf359SKatayama Hirofumi MZ 
1925e2f5f2SKatayama Hirofumi MZ static HWND s_hMainWnd = NULL, s_hSubWnd = NULL;
2025e2f5f2SKatayama Hirofumi MZ static WCHAR s_szSubProgram[MAX_PATH]; // shell32_apitest_sub.exe
217e9cf359SKatayama Hirofumi MZ static HANDLE s_hThread = NULL;
2225e2f5f2SKatayama Hirofumi MZ static INT s_iStage = -1, s_iStep = -1;
2325e2f5f2SKatayama Hirofumi MZ static BYTE s_abChecks[NUM_CHECKS] = { 0 }; // Flags for testing
2425e2f5f2SKatayama Hirofumi MZ static BOOL s_bGotUpdateDir = FALSE; // Got SHCNE_UPDATEDIR?
25fe397602SKatayama Hirofumi MZ 
2625e2f5f2SKatayama Hirofumi MZ static BOOL DoCreateFile(LPCWSTR pszFileName)
27fe397602SKatayama Hirofumi MZ {
2825e2f5f2SKatayama Hirofumi MZ     HANDLE hFile = ::CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL,
2925e2f5f2SKatayama Hirofumi MZ                                  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
3025e2f5f2SKatayama Hirofumi MZ     ::CloseHandle(hFile);
3125e2f5f2SKatayama Hirofumi MZ     return hFile != INVALID_HANDLE_VALUE;
32fe397602SKatayama Hirofumi MZ }
33fe397602SKatayama Hirofumi MZ 
3425e2f5f2SKatayama Hirofumi MZ static void DoDeleteDirectory(LPCWSTR pszDir)
35fe397602SKatayama Hirofumi MZ {
3625e2f5f2SKatayama Hirofumi MZ     WCHAR szPath[MAX_PATH];
3725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(szPath, sizeof(szPath));
3825e2f5f2SKatayama Hirofumi MZ     StringCchCopyW(szPath, _countof(szPath), pszDir); // Double-NULL terminated
3925e2f5f2SKatayama Hirofumi MZ     SHFILEOPSTRUCTW FileOp = { NULL, FO_DELETE, szPath, NULL, FOF_NOCONFIRMATION | FOF_SILENT };
4025e2f5f2SKatayama Hirofumi MZ     SHFileOperation(&FileOp);
41fe397602SKatayama Hirofumi MZ }
42fe397602SKatayama Hirofumi MZ 
4325e2f5f2SKatayama Hirofumi MZ static INT GetEventType(LONG lEvent)
44fe397602SKatayama Hirofumi MZ {
4525e2f5f2SKatayama Hirofumi MZ     switch (lEvent)
4625e2f5f2SKatayama Hirofumi MZ     {
4725e2f5f2SKatayama Hirofumi MZ         case SHCNE_CREATE:          return 0;
4825e2f5f2SKatayama Hirofumi MZ         case SHCNE_DELETE:          return 1;
4925e2f5f2SKatayama Hirofumi MZ         case SHCNE_RENAMEITEM:      return 2;
5025e2f5f2SKatayama Hirofumi MZ         case SHCNE_MKDIR:           return 3;
5125e2f5f2SKatayama Hirofumi MZ         case SHCNE_RMDIR:           return 4;
5225e2f5f2SKatayama Hirofumi MZ         case SHCNE_RENAMEFOLDER:    return 5;
5325e2f5f2SKatayama Hirofumi MZ         C_ASSERT(5 + 1 == MAX_EVENT_TYPE);
5425e2f5f2SKatayama Hirofumi MZ         default:                    return -1;
5525e2f5f2SKatayama Hirofumi MZ     }
56fe397602SKatayama Hirofumi MZ }
57fe397602SKatayama Hirofumi MZ 
5825e2f5f2SKatayama Hirofumi MZ #define FILE_1  L"_TESTFILE_1_.txt"
5925e2f5f2SKatayama Hirofumi MZ #define FILE_2  L"_TESTFILE_2_.txt"
6025e2f5f2SKatayama Hirofumi MZ #define DIR_1   L"_TESTDIR_1_"
6125e2f5f2SKatayama Hirofumi MZ #define DIR_2   L"_TESTDIR_2_"
62fe397602SKatayama Hirofumi MZ 
6325e2f5f2SKatayama Hirofumi MZ static WCHAR s_szDir1[MAX_PATH];
6425e2f5f2SKatayama Hirofumi MZ static WCHAR s_szDir1InDir1[MAX_PATH];
6525e2f5f2SKatayama Hirofumi MZ static WCHAR s_szDir2InDir1[MAX_PATH];
6625e2f5f2SKatayama Hirofumi MZ static WCHAR s_szFile1InDir1InDir1[MAX_PATH];
6725e2f5f2SKatayama Hirofumi MZ static WCHAR s_szFile1InDir1[MAX_PATH];
6825e2f5f2SKatayama Hirofumi MZ static WCHAR s_szFile2InDir1[MAX_PATH];
697e9cf359SKatayama Hirofumi MZ 
707e9cf359SKatayama Hirofumi MZ static void DoDeleteFilesAndDirs(void)
717e9cf359SKatayama Hirofumi MZ {
7225e2f5f2SKatayama Hirofumi MZ     ::DeleteFileW(s_szFile1InDir1);
7325e2f5f2SKatayama Hirofumi MZ     ::DeleteFileW(s_szFile2InDir1);
7425e2f5f2SKatayama Hirofumi MZ     ::DeleteFileW(s_szFile1InDir1InDir1);
7525e2f5f2SKatayama Hirofumi MZ     DoDeleteDirectory(s_szDir1InDir1);
7625e2f5f2SKatayama Hirofumi MZ     DoDeleteDirectory(s_szDir2InDir1);
7725e2f5f2SKatayama Hirofumi MZ     DoDeleteDirectory(s_szDir1);
787e9cf359SKatayama Hirofumi MZ }
797e9cf359SKatayama Hirofumi MZ 
8025e2f5f2SKatayama Hirofumi MZ static void TEST_Quit(void)
817e9cf359SKatayama Hirofumi MZ {
8225e2f5f2SKatayama Hirofumi MZ     CloseHandle(s_hThread);
837e9cf359SKatayama Hirofumi MZ     s_hThread = NULL;
847e9cf359SKatayama Hirofumi MZ 
8525e2f5f2SKatayama Hirofumi MZ     PostMessageW(s_hSubWnd, WM_COMMAND, IDNO, 0); // Finish
8625e2f5f2SKatayama Hirofumi MZ     DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE); // Close sub-windows
877e9cf359SKatayama Hirofumi MZ 
887e9cf359SKatayama Hirofumi MZ     DoDeleteFilesAndDirs();
89fe397602SKatayama Hirofumi MZ }
90fe397602SKatayama Hirofumi MZ 
9125e2f5f2SKatayama Hirofumi MZ static BOOL FindSubProgram(void)
92302356bfSKatayama Hirofumi MZ {
935800ee5eSKatayama Hirofumi MZ     GetModuleFileNameW(NULL, s_szSubProgram, _countof(s_szSubProgram));
945800ee5eSKatayama Hirofumi MZ     PathRemoveFileSpecW(s_szSubProgram);
95fccea84dSKatayama Hirofumi MZ     PathAppendW(s_szSubProgram, L"shell32_apitest_sub.exe");
965800ee5eSKatayama Hirofumi MZ 
975800ee5eSKatayama Hirofumi MZ     if (!PathFileExistsW(s_szSubProgram))
985800ee5eSKatayama Hirofumi MZ     {
995800ee5eSKatayama Hirofumi MZ         PathRemoveFileSpecW(s_szSubProgram);
100fccea84dSKatayama Hirofumi MZ         PathAppendW(s_szSubProgram, L"testdata\\shell32_apitest_sub.exe");
1015800ee5eSKatayama Hirofumi MZ 
1025800ee5eSKatayama Hirofumi MZ         if (!PathFileExistsW(s_szSubProgram))
1035800ee5eSKatayama Hirofumi MZ             return FALSE;
1045800ee5eSKatayama Hirofumi MZ     }
1055800ee5eSKatayama Hirofumi MZ 
106302356bfSKatayama Hirofumi MZ     return TRUE;
107302356bfSKatayama Hirofumi MZ }
108302356bfSKatayama Hirofumi MZ 
10925e2f5f2SKatayama Hirofumi MZ static void DoBuildFilesAndDirs(void)
110302356bfSKatayama Hirofumi MZ {
11125e2f5f2SKatayama Hirofumi MZ     WCHAR szPath1[MAX_PATH];
11225e2f5f2SKatayama Hirofumi MZ     SHGetSpecialFolderPathW(NULL, szPath1, CSIDL_PERSONAL, FALSE); // My Documents
11325e2f5f2SKatayama Hirofumi MZ     PathAppendW(szPath1, DIR_1);
11425e2f5f2SKatayama Hirofumi MZ     StringCchCopyW(s_szDir1, _countof(s_szDir1), szPath1);
1157f766e94SKatayama Hirofumi MZ 
11625e2f5f2SKatayama Hirofumi MZ     PathAppendW(szPath1, DIR_1);
11725e2f5f2SKatayama Hirofumi MZ     StringCchCopyW(s_szDir1InDir1, _countof(s_szDir1InDir1), szPath1);
11825e2f5f2SKatayama Hirofumi MZ     PathRemoveFileSpecW(szPath1);
1197e9cf359SKatayama Hirofumi MZ 
12025e2f5f2SKatayama Hirofumi MZ     PathAppendW(szPath1, DIR_2);
12125e2f5f2SKatayama Hirofumi MZ     StringCchCopyW(s_szDir2InDir1, _countof(s_szDir2InDir1), szPath1);
12225e2f5f2SKatayama Hirofumi MZ     PathRemoveFileSpecW(szPath1);
12325e2f5f2SKatayama Hirofumi MZ 
12425e2f5f2SKatayama Hirofumi MZ     PathAppendW(szPath1, DIR_1);
12525e2f5f2SKatayama Hirofumi MZ     PathAppendW(szPath1, FILE_1);
12625e2f5f2SKatayama Hirofumi MZ     StringCchCopyW(s_szFile1InDir1InDir1, _countof(s_szFile1InDir1InDir1), szPath1);
12725e2f5f2SKatayama Hirofumi MZ     PathRemoveFileSpecW(szPath1);
12825e2f5f2SKatayama Hirofumi MZ     PathRemoveFileSpecW(szPath1);
12925e2f5f2SKatayama Hirofumi MZ 
13025e2f5f2SKatayama Hirofumi MZ     PathAppendW(szPath1, FILE_1);
13125e2f5f2SKatayama Hirofumi MZ     StringCchCopyW(s_szFile1InDir1, _countof(s_szFile1InDir1), szPath1);
13225e2f5f2SKatayama Hirofumi MZ     PathRemoveFileSpecW(szPath1);
13325e2f5f2SKatayama Hirofumi MZ 
13425e2f5f2SKatayama Hirofumi MZ     PathAppendW(szPath1, FILE_2);
13525e2f5f2SKatayama Hirofumi MZ     StringCchCopyW(s_szFile2InDir1, _countof(s_szFile2InDir1), szPath1);
13625e2f5f2SKatayama Hirofumi MZ     PathRemoveFileSpecW(szPath1);
13725e2f5f2SKatayama Hirofumi MZ 
13825e2f5f2SKatayama Hirofumi MZ #define TRACE_PATH(path) trace(#path ": %ls\n", path)
13925e2f5f2SKatayama Hirofumi MZ     TRACE_PATH(s_szDir1);
14025e2f5f2SKatayama Hirofumi MZ     TRACE_PATH(s_szDir1InDir1);
14125e2f5f2SKatayama Hirofumi MZ     TRACE_PATH(s_szFile1InDir1);
14225e2f5f2SKatayama Hirofumi MZ     TRACE_PATH(s_szFile1InDir1InDir1);
14325e2f5f2SKatayama Hirofumi MZ     TRACE_PATH(s_szFile2InDir1);
14425e2f5f2SKatayama Hirofumi MZ #undef TRACE_PATH
14525e2f5f2SKatayama Hirofumi MZ 
14625e2f5f2SKatayama Hirofumi MZ     DoDeleteFilesAndDirs();
14725e2f5f2SKatayama Hirofumi MZ 
14825e2f5f2SKatayama Hirofumi MZ     ::CreateDirectoryW(s_szDir1, NULL);
14925e2f5f2SKatayama Hirofumi MZ     ok_int(!!PathIsDirectoryW(s_szDir1), TRUE);
15025e2f5f2SKatayama Hirofumi MZ 
15125e2f5f2SKatayama Hirofumi MZ     DoDeleteDirectory(s_szDir1InDir1);
15225e2f5f2SKatayama Hirofumi MZ     ok_int(!PathIsDirectoryW(s_szDir1InDir1), TRUE);
1537e9cf359SKatayama Hirofumi MZ }
1547e9cf359SKatayama Hirofumi MZ 
15525e2f5f2SKatayama Hirofumi MZ static void DoTestEntry(LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
1567f766e94SKatayama Hirofumi MZ {
15725e2f5f2SKatayama Hirofumi MZ     WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH];
15825e2f5f2SKatayama Hirofumi MZ 
15925e2f5f2SKatayama Hirofumi MZ     szPath1[0] = szPath2[0] = 0;
16025e2f5f2SKatayama Hirofumi MZ     SHGetPathFromIDListW(pidl1, szPath1);
16125e2f5f2SKatayama Hirofumi MZ     SHGetPathFromIDListW(pidl2, szPath2);
16225e2f5f2SKatayama Hirofumi MZ 
16325e2f5f2SKatayama Hirofumi MZ     trace("(0x%lX, '%ls', '%ls')\n", lEvent, szPath1, szPath2);
16425e2f5f2SKatayama Hirofumi MZ 
16525e2f5f2SKatayama Hirofumi MZ     if (lEvent == SHCNE_UPDATEDIR)
16625e2f5f2SKatayama Hirofumi MZ     {
16725e2f5f2SKatayama Hirofumi MZ         trace("Got SHCNE_UPDATEDIR\n");
16825e2f5f2SKatayama Hirofumi MZ         s_bGotUpdateDir = TRUE;
169fe397602SKatayama Hirofumi MZ         return;
170fe397602SKatayama Hirofumi MZ     }
171fe397602SKatayama Hirofumi MZ 
17225e2f5f2SKatayama Hirofumi MZ     INT iEventType = GetEventType(lEvent);
17325e2f5f2SKatayama Hirofumi MZ     if (iEventType < 0)
17425e2f5f2SKatayama Hirofumi MZ         return;
17525e2f5f2SKatayama Hirofumi MZ 
17625e2f5f2SKatayama Hirofumi MZ     assert(iEventType < MAX_EVENT_TYPE);
17725e2f5f2SKatayama Hirofumi MZ 
17825e2f5f2SKatayama Hirofumi MZ     INT i = 0;
17925e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath1, L"") == 0) << iEventType;
18025e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szDir1) == 0) << iEventType;
18125e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szDir2InDir1) == 0) << iEventType;
18225e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szFile1InDir1) == 0) << iEventType;
18325e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szFile2InDir1) == 0) << iEventType;
18425e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath1, s_szFile1InDir1InDir1) == 0) << iEventType;
18525e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath2, L"") == 0) << iEventType;
18625e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szDir1) == 0) << iEventType;
18725e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szDir2InDir1) == 0) << iEventType;
18825e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szFile1InDir1) == 0) << iEventType;
18925e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szFile2InDir1) == 0) << iEventType;
19025e2f5f2SKatayama Hirofumi MZ     s_abChecks[i++] |= (lstrcmpiW(szPath2, s_szFile1InDir1InDir1) == 0) << iEventType;
19125e2f5f2SKatayama Hirofumi MZ     assert(i == NUM_CHECKS);
19225e2f5f2SKatayama Hirofumi MZ }
19325e2f5f2SKatayama Hirofumi MZ 
19425e2f5f2SKatayama Hirofumi MZ static LPCSTR StringFromChecks(void)
1957f766e94SKatayama Hirofumi MZ {
19625e2f5f2SKatayama Hirofumi MZ     static char s_sz[2 * NUM_CHECKS + 1];
19725e2f5f2SKatayama Hirofumi MZ 
19825e2f5f2SKatayama Hirofumi MZ     char *pch = s_sz;
19925e2f5f2SKatayama Hirofumi MZ     for (INT i = 0; i < NUM_CHECKS; ++i)
20025e2f5f2SKatayama Hirofumi MZ     {
20125e2f5f2SKatayama Hirofumi MZ         WCHAR sz[3];
20225e2f5f2SKatayama Hirofumi MZ         StringCchPrintfW(sz, _countof(sz), L"%02X", s_abChecks[i]);
20325e2f5f2SKatayama Hirofumi MZ         *pch++ = sz[0];
20425e2f5f2SKatayama Hirofumi MZ         *pch++ = sz[1];
20525e2f5f2SKatayama Hirofumi MZ     }
20625e2f5f2SKatayama Hirofumi MZ 
20725e2f5f2SKatayama Hirofumi MZ     assert((pch - s_sz) + 1 == sizeof(s_sz));
20825e2f5f2SKatayama Hirofumi MZ 
20925e2f5f2SKatayama Hirofumi MZ     *pch = 0;
21025e2f5f2SKatayama Hirofumi MZ     return s_sz;
21125e2f5f2SKatayama Hirofumi MZ }
21225e2f5f2SKatayama Hirofumi MZ 
21325e2f5f2SKatayama Hirofumi MZ struct TEST_ANSWER
21425e2f5f2SKatayama Hirofumi MZ {
21525e2f5f2SKatayama Hirofumi MZ     INT lineno;
21625e2f5f2SKatayama Hirofumi MZ     LPCSTR answer;
21725e2f5f2SKatayama Hirofumi MZ };
21825e2f5f2SKatayama Hirofumi MZ 
21925e2f5f2SKatayama Hirofumi MZ static void DoStepCheck(INT iStage, INT iStep, LPCSTR checks)
22025e2f5f2SKatayama Hirofumi MZ {
22125e2f5f2SKatayama Hirofumi MZ     assert(0 <= iStep);
22225e2f5f2SKatayama Hirofumi MZ     assert(iStep < NUM_STEP);
22325e2f5f2SKatayama Hirofumi MZ 
22425e2f5f2SKatayama Hirofumi MZ     assert(0 <= iStage);
22525e2f5f2SKatayama Hirofumi MZ     assert(iStage < NUM_STAGE);
22625e2f5f2SKatayama Hirofumi MZ 
22725e2f5f2SKatayama Hirofumi MZ     if (s_bGotUpdateDir)
22825e2f5f2SKatayama Hirofumi MZ     {
22925e2f5f2SKatayama Hirofumi MZ         ok_int(TRUE, TRUE);
230fe397602SKatayama Hirofumi MZ         return;
231fe397602SKatayama Hirofumi MZ     }
232fe397602SKatayama Hirofumi MZ 
23325e2f5f2SKatayama Hirofumi MZ     LPCSTR answer = NULL;
23425e2f5f2SKatayama Hirofumi MZ     INT lineno;
23525e2f5f2SKatayama Hirofumi MZ     switch (iStage)
236fe397602SKatayama Hirofumi MZ     {
23725e2f5f2SKatayama Hirofumi MZ         case 0:
23825e2f5f2SKatayama Hirofumi MZ         case 1:
239*8337df2bSKatayama Hirofumi MZ         case 3:
240*8337df2bSKatayama Hirofumi MZ         case 6:
241*8337df2bSKatayama Hirofumi MZ         case 9:
2427e9cf359SKatayama Hirofumi MZ         {
24325e2f5f2SKatayama Hirofumi MZ             static const TEST_ANSWER c_answers[] =
24425e2f5f2SKatayama Hirofumi MZ             {
24525e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000010000010000000000" }, // 0
24625e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000040000000000000400" }, // 1
24725e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000200020000000000" }, // 2
24825e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000000080000000000" }, // 3
24925e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000001010000000000" }, // 4
25025e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000002020000000000" }, // 5
25125e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000000000020000000" }, // 6
25225e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000010000000100000000000" }, // 7
25325e2f5f2SKatayama Hirofumi MZ             };
25425e2f5f2SKatayama Hirofumi MZ             C_ASSERT(_countof(c_answers) == NUM_STEP);
25525e2f5f2SKatayama Hirofumi MZ             lineno = c_answers[iStep].lineno;
25625e2f5f2SKatayama Hirofumi MZ             answer = c_answers[iStep].answer;
25725e2f5f2SKatayama Hirofumi MZ             break;
25825e2f5f2SKatayama Hirofumi MZ         }
25925e2f5f2SKatayama Hirofumi MZ         case 2:
260*8337df2bSKatayama Hirofumi MZ         case 4:
261*8337df2bSKatayama Hirofumi MZ         case 5:
262*8337df2bSKatayama Hirofumi MZ         case 7:
263*8337df2bSKatayama Hirofumi MZ         {
264*8337df2bSKatayama Hirofumi MZ             static const TEST_ANSWER c_answers[] =
265*8337df2bSKatayama Hirofumi MZ             {
266*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 0
267*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 1
268*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 2
269*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 3
270*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 4
271*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 5
272*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 6
273*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000000000000000000" }, // 7
274*8337df2bSKatayama Hirofumi MZ             };
275*8337df2bSKatayama Hirofumi MZ             C_ASSERT(_countof(c_answers) == NUM_STEP);
276*8337df2bSKatayama Hirofumi MZ             lineno = c_answers[iStep].lineno;
277*8337df2bSKatayama Hirofumi MZ             answer = c_answers[iStep].answer;
278*8337df2bSKatayama Hirofumi MZ             break;
279*8337df2bSKatayama Hirofumi MZ         }
280*8337df2bSKatayama Hirofumi MZ         case 8:
28125e2f5f2SKatayama Hirofumi MZ         {
28225e2f5f2SKatayama Hirofumi MZ             static const TEST_ANSWER c_answers[] =
28325e2f5f2SKatayama Hirofumi MZ             {
28425e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000010000010000000000" }, // 0
28525e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000040000000000000400" }, // 1
28625e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000200020000000000" }, // 2
28725e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000000080000000000" }, // 3
288*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000001010000000000" }, // 4 // Recursive case
289*8337df2bSKatayama Hirofumi MZ                 { __LINE__, "000000000002020000000000" }, // 5 // Recursive case
29025e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000000000000000020000000" }, // 6
29125e2f5f2SKatayama Hirofumi MZ                 { __LINE__, "000010000000100000000000" }, // 7
29225e2f5f2SKatayama Hirofumi MZ             };
29325e2f5f2SKatayama Hirofumi MZ             C_ASSERT(_countof(c_answers) == NUM_STEP);
29425e2f5f2SKatayama Hirofumi MZ             lineno = c_answers[iStep].lineno;
29525e2f5f2SKatayama Hirofumi MZ             answer = c_answers[iStep].answer;
29625e2f5f2SKatayama Hirofumi MZ             if (iStep == 4 || iStep == 5) // Recursive cases
29725e2f5f2SKatayama Hirofumi MZ             {
29825e2f5f2SKatayama Hirofumi MZ                 if (lstrcmpA(checks, "000000000000000000000000") == 0)
299*8337df2bSKatayama Hirofumi MZ                 {
300*8337df2bSKatayama Hirofumi MZ                     trace("Warning! Recursive cases...\n");
30125e2f5f2SKatayama Hirofumi MZ                     answer = "000000000000000000000000";
30225e2f5f2SKatayama Hirofumi MZ                 }
30325e2f5f2SKatayama Hirofumi MZ             }
30425e2f5f2SKatayama Hirofumi MZ             break;
30525e2f5f2SKatayama Hirofumi MZ         }
30625e2f5f2SKatayama Hirofumi MZ         default:
30725e2f5f2SKatayama Hirofumi MZ         {
30825e2f5f2SKatayama Hirofumi MZ             assert(0);
30925e2f5f2SKatayama Hirofumi MZ             break;
31025e2f5f2SKatayama Hirofumi MZ         }
31125e2f5f2SKatayama Hirofumi MZ     }
3127e9cf359SKatayama Hirofumi MZ 
31325e2f5f2SKatayama Hirofumi MZ     ok(lstrcmpA(checks, answer) == 0,
31425e2f5f2SKatayama Hirofumi MZ        "Line %d: '%s' vs '%s' at Stage %d, Step %d\n", lineno, checks, answer, iStage, iStep);
31525e2f5f2SKatayama Hirofumi MZ }
3167e9cf359SKatayama Hirofumi MZ 
31725e2f5f2SKatayama Hirofumi MZ static DWORD WINAPI StageThreadFunc(LPVOID arg)
31825e2f5f2SKatayama Hirofumi MZ {
31925e2f5f2SKatayama Hirofumi MZ     BOOL ret;
3207e9cf359SKatayama Hirofumi MZ 
32125e2f5f2SKatayama Hirofumi MZ     trace("Stage %d\n", s_iStage);
3227e9cf359SKatayama Hirofumi MZ 
32325e2f5f2SKatayama Hirofumi MZ     // 0: Create file1 in dir1
32425e2f5f2SKatayama Hirofumi MZ     s_iStep = 0;
32525e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
326*8337df2bSKatayama Hirofumi MZ     SHChangeNotify(0, SHCNF_PATHW | SHCNF_FLUSH, NULL, NULL);
32725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
32825e2f5f2SKatayama Hirofumi MZ     ret = DoCreateFile(s_szFile1InDir1);
32925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
330d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW | SHCNF_FLUSH, s_szFile1InDir1, 0);
33125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
33225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
3337e9cf359SKatayama Hirofumi MZ 
33425e2f5f2SKatayama Hirofumi MZ     // 1: Rename file1 as file2 in dir1
33525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
33625e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
33725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
33825e2f5f2SKatayama Hirofumi MZ     ret = MoveFileW(s_szFile1InDir1, s_szFile2InDir1);
33925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
340d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_PATHW | SHCNF_FLUSH, s_szFile1InDir1, s_szFile2InDir1);
34125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
34225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
3437e9cf359SKatayama Hirofumi MZ 
34425e2f5f2SKatayama Hirofumi MZ     // 2: Delete file2 in dir1
34525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
34625e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
34725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
34825e2f5f2SKatayama Hirofumi MZ     ret = DeleteFileW(s_szFile2InDir1);
34925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
350d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW | SHCNF_FLUSH, s_szFile2InDir1, NULL);
35125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
35225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
3537e9cf359SKatayama Hirofumi MZ 
35425e2f5f2SKatayama Hirofumi MZ     // 3: Create dir1 in dir1
35525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
35625e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
35725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
35825e2f5f2SKatayama Hirofumi MZ     ret = CreateDirectoryExW(s_szDir1, s_szDir1InDir1, NULL);
35925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
360d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, s_szDir1InDir1, NULL);
36125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
36225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
3637e9cf359SKatayama Hirofumi MZ 
36425e2f5f2SKatayama Hirofumi MZ     // 4: Create file1 in dir1 in dir1
36525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
36625e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
36725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
36825e2f5f2SKatayama Hirofumi MZ     ret = DoCreateFile(s_szFile1InDir1InDir1);
36925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
370d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW | SHCNF_FLUSH, s_szFile1InDir1InDir1, NULL);
37125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
37225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
3737e9cf359SKatayama Hirofumi MZ 
37425e2f5f2SKatayama Hirofumi MZ     // 5: Delete file1 in dir1 in dir1
37525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
37625e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
37725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
37825e2f5f2SKatayama Hirofumi MZ     ret = DeleteFileW(s_szFile1InDir1InDir1);
37925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
380d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW | SHCNF_FLUSH, s_szFile1InDir1InDir1, NULL);
38125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
38225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
38325e2f5f2SKatayama Hirofumi MZ 
38425e2f5f2SKatayama Hirofumi MZ     // 6: Rename dir1 as dir2 in dir1
38525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
38625e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
38725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
38825e2f5f2SKatayama Hirofumi MZ     ret = ::MoveFileW(s_szDir1InDir1, s_szDir2InDir1);
38925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
390d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_PATHW | SHCNF_FLUSH, s_szDir1InDir1, s_szDir2InDir1);
39125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
39225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
39325e2f5f2SKatayama Hirofumi MZ 
39425e2f5f2SKatayama Hirofumi MZ     // 7: Remove dir2 in dir1
39525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
39625e2f5f2SKatayama Hirofumi MZ     trace("Step %d\n", s_iStep);
39725e2f5f2SKatayama Hirofumi MZ     ZeroMemory(s_abChecks, sizeof(s_abChecks));
39825e2f5f2SKatayama Hirofumi MZ     ret = RemoveDirectoryW(s_szDir2InDir1);
39925e2f5f2SKatayama Hirofumi MZ     ok_int(ret, TRUE);
400d23573beSKatayama Hirofumi MZ     SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW | SHCNF_FLUSH, s_szDir2InDir1, NULL);
40125e2f5f2SKatayama Hirofumi MZ     ::Sleep(INTERVAL);
40225e2f5f2SKatayama Hirofumi MZ     DoStepCheck(s_iStage, s_iStep, StringFromChecks());
40325e2f5f2SKatayama Hirofumi MZ 
40425e2f5f2SKatayama Hirofumi MZ     // 8: Finish
40525e2f5f2SKatayama Hirofumi MZ     ++s_iStep;
40625e2f5f2SKatayama Hirofumi MZ     assert(s_iStep == NUM_STEP);
40725e2f5f2SKatayama Hirofumi MZ     C_ASSERT(NUM_STEP == 8);
40825e2f5f2SKatayama Hirofumi MZ     if (s_iStage + 1 < NUM_STAGE)
40925e2f5f2SKatayama Hirofumi MZ     {
41025e2f5f2SKatayama Hirofumi MZ         ::PostMessage(s_hSubWnd, WM_COMMAND, IDRETRY, 0); // Next stage
41125e2f5f2SKatayama Hirofumi MZ     }
41225e2f5f2SKatayama Hirofumi MZ     else
41325e2f5f2SKatayama Hirofumi MZ     {
41425e2f5f2SKatayama Hirofumi MZ         // Finish
41525e2f5f2SKatayama Hirofumi MZ         ::PostMessage(s_hSubWnd, WM_COMMAND, IDNO, 0);
41625e2f5f2SKatayama Hirofumi MZ         ::PostMessage(s_hMainWnd, WM_COMMAND, IDNO, 0);
41725e2f5f2SKatayama Hirofumi MZ     }
41825e2f5f2SKatayama Hirofumi MZ 
41925e2f5f2SKatayama Hirofumi MZ     s_iStep = -1;
4207e9cf359SKatayama Hirofumi MZ 
4217e9cf359SKatayama Hirofumi MZ     return 0;
4225800ee5eSKatayama Hirofumi MZ }
4235800ee5eSKatayama Hirofumi MZ 
42425e2f5f2SKatayama Hirofumi MZ // WM_COPYDATA
42525e2f5f2SKatayama Hirofumi MZ static BOOL OnCopyData(HWND hwnd, HWND hwndSender, COPYDATASTRUCT *pCopyData)
4265800ee5eSKatayama Hirofumi MZ {
42725e2f5f2SKatayama Hirofumi MZ     if (pCopyData->dwData != 0xBEEFCAFE)
42825e2f5f2SKatayama Hirofumi MZ         return FALSE;
4297e9cf359SKatayama Hirofumi MZ 
43025e2f5f2SKatayama Hirofumi MZ     LPBYTE pbData = (LPBYTE)pCopyData->lpData;
43125e2f5f2SKatayama Hirofumi MZ     LPBYTE pb = pbData;
4327e9cf359SKatayama Hirofumi MZ 
43325e2f5f2SKatayama Hirofumi MZ     LONG cbTotal = pCopyData->cbData;
43425e2f5f2SKatayama Hirofumi MZ     assert(cbTotal >= LONG(sizeof(LONG) + sizeof(DWORD) + sizeof(DWORD)));
43525e2f5f2SKatayama Hirofumi MZ 
43625e2f5f2SKatayama Hirofumi MZ     LONG lEvent = *(LONG*)pb;
43725e2f5f2SKatayama Hirofumi MZ     pb += sizeof(lEvent);
43825e2f5f2SKatayama Hirofumi MZ 
43925e2f5f2SKatayama Hirofumi MZ     DWORD cbPidl1 = *(DWORD*)pb;
44025e2f5f2SKatayama Hirofumi MZ     pb += sizeof(cbPidl1);
44125e2f5f2SKatayama Hirofumi MZ 
44225e2f5f2SKatayama Hirofumi MZ     DWORD cbPidl2 = *(DWORD*)pb;
44325e2f5f2SKatayama Hirofumi MZ     pb += sizeof(cbPidl2);
44425e2f5f2SKatayama Hirofumi MZ 
44525e2f5f2SKatayama Hirofumi MZ     LPITEMIDLIST pidl1 = NULL;
44625e2f5f2SKatayama Hirofumi MZ     if (cbPidl1)
44725e2f5f2SKatayama Hirofumi MZ     {
44825e2f5f2SKatayama Hirofumi MZ         pidl1 = (LPITEMIDLIST)CoTaskMemAlloc(cbPidl1);
44925e2f5f2SKatayama Hirofumi MZ         CopyMemory(pidl1, pb, cbPidl1);
45025e2f5f2SKatayama Hirofumi MZ         pb += cbPidl1;
45125e2f5f2SKatayama Hirofumi MZ     }
45225e2f5f2SKatayama Hirofumi MZ 
45325e2f5f2SKatayama Hirofumi MZ     LPITEMIDLIST pidl2 = NULL;
45425e2f5f2SKatayama Hirofumi MZ     if (cbPidl2)
45525e2f5f2SKatayama Hirofumi MZ     {
45625e2f5f2SKatayama Hirofumi MZ         pidl2 = (LPITEMIDLIST)CoTaskMemAlloc(cbPidl2);
45725e2f5f2SKatayama Hirofumi MZ         CopyMemory(pidl2, pb, cbPidl2);
45825e2f5f2SKatayama Hirofumi MZ         pb += cbPidl2;
45925e2f5f2SKatayama Hirofumi MZ     }
46025e2f5f2SKatayama Hirofumi MZ 
46125e2f5f2SKatayama Hirofumi MZ     assert((pb - pbData) == cbTotal);
46225e2f5f2SKatayama Hirofumi MZ 
46325e2f5f2SKatayama Hirofumi MZ     DoTestEntry(lEvent, pidl1, pidl2);
46425e2f5f2SKatayama Hirofumi MZ 
46525e2f5f2SKatayama Hirofumi MZ     CoTaskMemFree(pidl1);
46625e2f5f2SKatayama Hirofumi MZ     CoTaskMemFree(pidl2);
46725e2f5f2SKatayama Hirofumi MZ 
46825e2f5f2SKatayama Hirofumi MZ     return TRUE;
46925e2f5f2SKatayama Hirofumi MZ }
47025e2f5f2SKatayama Hirofumi MZ 
47125e2f5f2SKatayama Hirofumi MZ static LRESULT CALLBACK
47225e2f5f2SKatayama Hirofumi MZ MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
47325e2f5f2SKatayama Hirofumi MZ {
47425e2f5f2SKatayama Hirofumi MZ     switch (uMsg)
47525e2f5f2SKatayama Hirofumi MZ     {
47625e2f5f2SKatayama Hirofumi MZ         case WM_CREATE:
47725e2f5f2SKatayama Hirofumi MZ             s_hMainWnd = hwnd;
47825e2f5f2SKatayama Hirofumi MZ             return 0;
47925e2f5f2SKatayama Hirofumi MZ 
48025e2f5f2SKatayama Hirofumi MZ         case WM_COMMAND:
48125e2f5f2SKatayama Hirofumi MZ             switch (LOWORD(wParam))
48225e2f5f2SKatayama Hirofumi MZ             {
48325e2f5f2SKatayama Hirofumi MZ                 case IDYES: // Start testing
48425e2f5f2SKatayama Hirofumi MZ                 {
48525e2f5f2SKatayama Hirofumi MZ                     s_iStage = 0;
48625e2f5f2SKatayama Hirofumi MZ                     s_bGotUpdateDir = FALSE;
48725e2f5f2SKatayama Hirofumi MZ                     s_hThread = ::CreateThread(NULL, 0, StageThreadFunc, hwnd, 0, NULL);
48825e2f5f2SKatayama Hirofumi MZ                     if (!s_hThread)
48925e2f5f2SKatayama Hirofumi MZ                     {
49025e2f5f2SKatayama Hirofumi MZ                         skip("!s_hThread\n");
49125e2f5f2SKatayama Hirofumi MZ                         DestroyWindow(hwnd);
49225e2f5f2SKatayama Hirofumi MZ                     }
49325e2f5f2SKatayama Hirofumi MZ                     break;
49425e2f5f2SKatayama Hirofumi MZ                 }
49525e2f5f2SKatayama Hirofumi MZ                 case IDRETRY: // New stage
49625e2f5f2SKatayama Hirofumi MZ                 {
49725e2f5f2SKatayama Hirofumi MZ                     ::CloseHandle(s_hThread);
49825e2f5f2SKatayama Hirofumi MZ                     ++s_iStage;
49925e2f5f2SKatayama Hirofumi MZ                     s_bGotUpdateDir = FALSE;
50025e2f5f2SKatayama Hirofumi MZ                     s_hThread = ::CreateThread(NULL, 0, StageThreadFunc, hwnd, 0, NULL);
50125e2f5f2SKatayama Hirofumi MZ                     if (!s_hThread)
50225e2f5f2SKatayama Hirofumi MZ                     {
50325e2f5f2SKatayama Hirofumi MZ                         skip("!s_hThread\n");
50425e2f5f2SKatayama Hirofumi MZ                         DestroyWindow(hwnd);
50525e2f5f2SKatayama Hirofumi MZ                     }
50625e2f5f2SKatayama Hirofumi MZ                     break;
50725e2f5f2SKatayama Hirofumi MZ                 }
50825e2f5f2SKatayama Hirofumi MZ                 case IDNO: // Quit
50925e2f5f2SKatayama Hirofumi MZ                 {
51025e2f5f2SKatayama Hirofumi MZ                     s_iStage = -1;
51125e2f5f2SKatayama Hirofumi MZ                     DestroyWindow(hwnd);
51225e2f5f2SKatayama Hirofumi MZ                     break;
51325e2f5f2SKatayama Hirofumi MZ                 }
51425e2f5f2SKatayama Hirofumi MZ             }
51525e2f5f2SKatayama Hirofumi MZ             break;
51625e2f5f2SKatayama Hirofumi MZ 
51725e2f5f2SKatayama Hirofumi MZ         case WM_COPYDATA:
51825e2f5f2SKatayama Hirofumi MZ             if (s_iStage < 0 || s_iStep < 0)
51925e2f5f2SKatayama Hirofumi MZ                 break;
52025e2f5f2SKatayama Hirofumi MZ 
52125e2f5f2SKatayama Hirofumi MZ             OnCopyData(hwnd, (HWND)wParam, (COPYDATASTRUCT*)lParam);
52225e2f5f2SKatayama Hirofumi MZ             break;
52325e2f5f2SKatayama Hirofumi MZ 
52425e2f5f2SKatayama Hirofumi MZ         case WM_DESTROY:
52525e2f5f2SKatayama Hirofumi MZ             ::PostQuitMessage(0);
52625e2f5f2SKatayama Hirofumi MZ             break;
52725e2f5f2SKatayama Hirofumi MZ 
52825e2f5f2SKatayama Hirofumi MZ         default:
52925e2f5f2SKatayama Hirofumi MZ             return ::DefWindowProcW(hwnd, uMsg, wParam, lParam);
53025e2f5f2SKatayama Hirofumi MZ     }
53125e2f5f2SKatayama Hirofumi MZ     return 0;
53225e2f5f2SKatayama Hirofumi MZ }
53325e2f5f2SKatayama Hirofumi MZ 
53425e2f5f2SKatayama Hirofumi MZ static BOOL TEST_Init(void)
53525e2f5f2SKatayama Hirofumi MZ {
53625e2f5f2SKatayama Hirofumi MZ     if (!FindSubProgram())
5375800ee5eSKatayama Hirofumi MZ     {
538fccea84dSKatayama Hirofumi MZ         skip("shell32_apitest_sub.exe not found\n");
53925e2f5f2SKatayama Hirofumi MZ         return FALSE;
5405800ee5eSKatayama Hirofumi MZ     }
5415800ee5eSKatayama Hirofumi MZ 
54225e2f5f2SKatayama Hirofumi MZ     // close the SUB_CLASSNAME windows
54325e2f5f2SKatayama Hirofumi MZ     DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE);
54425e2f5f2SKatayama Hirofumi MZ 
54525e2f5f2SKatayama Hirofumi MZ     // Execute sub program
54625e2f5f2SKatayama Hirofumi MZ     HINSTANCE hinst = ShellExecuteW(NULL, NULL, s_szSubProgram, L"---", NULL, SW_SHOWNORMAL);
54725e2f5f2SKatayama Hirofumi MZ     if ((INT_PTR)hinst <= 32)
5487e9cf359SKatayama Hirofumi MZ     {
54925e2f5f2SKatayama Hirofumi MZ         skip("Unable to run shell32_apitest_sub.exe.\n");
55025e2f5f2SKatayama Hirofumi MZ         return FALSE;
55125e2f5f2SKatayama Hirofumi MZ     }
55225e2f5f2SKatayama Hirofumi MZ 
55325e2f5f2SKatayama Hirofumi MZ     // prepare for files and dirs
55425e2f5f2SKatayama Hirofumi MZ     DoBuildFilesAndDirs();
55525e2f5f2SKatayama Hirofumi MZ 
55625e2f5f2SKatayama Hirofumi MZ     // Register main window
55725e2f5f2SKatayama Hirofumi MZ     WNDCLASSW wc = { 0, MainWndProc };
55825e2f5f2SKatayama Hirofumi MZ     wc.hInstance = GetModuleHandleW(NULL);
55925e2f5f2SKatayama Hirofumi MZ     wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
56025e2f5f2SKatayama Hirofumi MZ     wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
56125e2f5f2SKatayama Hirofumi MZ     wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
56225e2f5f2SKatayama Hirofumi MZ     wc.lpszClassName = MAIN_CLASSNAME;
56325e2f5f2SKatayama Hirofumi MZ     if (!RegisterClassW(&wc))
56425e2f5f2SKatayama Hirofumi MZ     {
56525e2f5f2SKatayama Hirofumi MZ         skip("RegisterClassW failed\n");
56625e2f5f2SKatayama Hirofumi MZ         return FALSE;
56725e2f5f2SKatayama Hirofumi MZ     }
56825e2f5f2SKatayama Hirofumi MZ 
56925e2f5f2SKatayama Hirofumi MZ     // Create main window
57025e2f5f2SKatayama Hirofumi MZ     HWND hwnd = CreateWindowW(MAIN_CLASSNAME, MAIN_CLASSNAME, WS_OVERLAPPEDWINDOW,
57125e2f5f2SKatayama Hirofumi MZ                               CW_USEDEFAULT, CW_USEDEFAULT, 400, 100,
57225e2f5f2SKatayama Hirofumi MZ                               NULL, NULL, GetModuleHandleW(NULL), NULL);
57325e2f5f2SKatayama Hirofumi MZ     if (!hwnd)
57425e2f5f2SKatayama Hirofumi MZ     {
57525e2f5f2SKatayama Hirofumi MZ         skip("CreateWindowW failed\n");
57625e2f5f2SKatayama Hirofumi MZ         return FALSE;
57725e2f5f2SKatayama Hirofumi MZ     }
57825e2f5f2SKatayama Hirofumi MZ     ::ShowWindow(hwnd, SW_SHOWNORMAL);
57925e2f5f2SKatayama Hirofumi MZ     ::UpdateWindow(hwnd);
58025e2f5f2SKatayama Hirofumi MZ 
58125e2f5f2SKatayama Hirofumi MZ     // Find sub-window
58225e2f5f2SKatayama Hirofumi MZ     s_hSubWnd = DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, FALSE, FALSE);
58325e2f5f2SKatayama Hirofumi MZ     if (!s_hSubWnd)
58425e2f5f2SKatayama Hirofumi MZ     {
58525e2f5f2SKatayama Hirofumi MZ         skip("Unable to find sub-program window.\n");
58625e2f5f2SKatayama Hirofumi MZ         return FALSE;
58725e2f5f2SKatayama Hirofumi MZ     }
58825e2f5f2SKatayama Hirofumi MZ 
58925e2f5f2SKatayama Hirofumi MZ     // Start testing
59025e2f5f2SKatayama Hirofumi MZ     SendMessageW(s_hSubWnd, WM_COMMAND, IDYES, 0);
59125e2f5f2SKatayama Hirofumi MZ 
59225e2f5f2SKatayama Hirofumi MZ     return TRUE;
59325e2f5f2SKatayama Hirofumi MZ }
59425e2f5f2SKatayama Hirofumi MZ 
59525e2f5f2SKatayama Hirofumi MZ static void TEST_Main(void)
59625e2f5f2SKatayama Hirofumi MZ {
59725e2f5f2SKatayama Hirofumi MZ     if (!TEST_Init())
59825e2f5f2SKatayama Hirofumi MZ     {
59925e2f5f2SKatayama Hirofumi MZ         skip("Unable to start testing.\n");
60025e2f5f2SKatayama Hirofumi MZ         TEST_Quit();
6017e9cf359SKatayama Hirofumi MZ         return;
6027e9cf359SKatayama Hirofumi MZ     }
6037e9cf359SKatayama Hirofumi MZ 
60425e2f5f2SKatayama Hirofumi MZ     // Message loop
60525e2f5f2SKatayama Hirofumi MZ     MSG msg;
60625e2f5f2SKatayama Hirofumi MZ     while (GetMessageW(&msg, NULL, 0, 0))
60725e2f5f2SKatayama Hirofumi MZ     {
60825e2f5f2SKatayama Hirofumi MZ         ::TranslateMessage(&msg);
60925e2f5f2SKatayama Hirofumi MZ         ::DispatchMessage(&msg);
61025e2f5f2SKatayama Hirofumi MZ     }
6117e9cf359SKatayama Hirofumi MZ 
61225e2f5f2SKatayama Hirofumi MZ     TEST_Quit();
61325e2f5f2SKatayama Hirofumi MZ }
61425e2f5f2SKatayama Hirofumi MZ 
61525e2f5f2SKatayama Hirofumi MZ START_TEST(SHChangeNotify)
61625e2f5f2SKatayama Hirofumi MZ {
61725e2f5f2SKatayama Hirofumi MZ     trace("Please close all Explorer windows before testing.\n");
61825e2f5f2SKatayama Hirofumi MZ     trace("Please don't operate your PC while testing.\n");
61925e2f5f2SKatayama Hirofumi MZ 
62025e2f5f2SKatayama Hirofumi MZ     DWORD dwOldTick = GetTickCount();
62125e2f5f2SKatayama Hirofumi MZ     TEST_Main();
6227e9cf359SKatayama Hirofumi MZ     DWORD dwNewTick = GetTickCount();
62325e2f5f2SKatayama Hirofumi MZ 
6247e9cf359SKatayama Hirofumi MZ     DWORD dwTick = dwNewTick - dwOldTick;
6257e9cf359SKatayama Hirofumi MZ     trace("SHChangeNotify: Total %lu.%lu sec\n", (dwTick / 1000), (dwTick / 100 % 10));
626fe397602SKatayama Hirofumi MZ }
627