mirror of
https://github.com/reactos/reactos.git
synced 2024-11-02 04:37:32 +00:00
8337df2bd1
Improve coverity and speed. JIRA issue: CORE-13950 - Move NUM_STAGE to the header file. - Add stages. - Add DIRTYPE_DESKTOP_DIR and DIRTYPE_PRINTERS directory types. - Improve speed more.
382 lines
9.2 KiB
C++
382 lines
9.2 KiB
C++
/*
|
|
* 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;
|
|
LONG 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:
|
|
{
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 APIENTRY
|
|
wWinMain(
|
|
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;
|
|
}
|