Skip to content

Commit d1c91ce

Browse files
committed
[SHELL32_APITEST] Extend ShellState API tests
- Skip registry tests if the key is missing, but keep testing - Add tests for different bitmasks and compare amount of set bytes - Add inter-process tests that verify shell behavior All tests are confirmed to pass on Windows 2003, 7 and 10. CORE-20585
1 parent ebbd660 commit d1c91ce

3 files changed

Lines changed: 249 additions & 8 deletions

File tree

‎modules/rostests/apitests/shell32/ShellState.cpp‎

Lines changed: 223 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
* PURPOSE: Test for SHELLSTATE
55
* PROGRAMMERS: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
66
*/
7+
78
#include "shelltest.h"
9+
#include "shell32_apitest_sub.h"
810

911
#define NDEBUG
1012
#include <debug.h>
@@ -22,30 +24,37 @@ typedef struct REGSHELLSTATE
2224
SHELLSTATE ss;
2325
} REGSHELLSTATE, *PREGSHELLSTATE;
2426

25-
static void dump(const char *name, const void *ptr, size_t siz)
27+
static const LPCWSTR s_pszExplorer =
28+
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer";
29+
static const LPCWSTR s_pszShellState =
30+
L"ShellState";
31+
32+
static ULONG dump(const char *name, const void *ptr, size_t siz)
2633
{
2734
char buf[256], sz[16];
35+
ULONG ret = 0;
2836

2937
StringCbCopyA(buf, sizeof(buf), name);
3038
StringCbCatA(buf, sizeof(buf), ": ");
3139

3240
const BYTE *pb = reinterpret_cast<const BYTE *>(ptr);
3341
while (siz--)
3442
{
43+
if (*pb)
44+
ret++;
3545
StringCbPrintfA(sz, sizeof(sz), "%02X ", *pb++);
3646
StringCbCatA(buf, sizeof(buf), sz);
3747
}
3848

3949
trace("%s\n", buf);
50+
return ret;
4051
}
4152

4253
static int read_key(REGSHELLSTATE *prss)
4354
{
4455
HKEY hKey;
4556
LONG result;
4657
DWORD cb;
47-
static const LPCWSTR s_pszExplorer =
48-
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer";
4958

5059
memset(prss, 0, sizeof(*prss));
5160

@@ -60,7 +69,7 @@ static int read_key(REGSHELLSTATE *prss)
6069
}
6170

6271
cb = sizeof(*prss);
63-
result = RegQueryValueExW(hKey, L"ShellState", NULL, NULL, reinterpret_cast<LPBYTE>(prss), &cb);
72+
result = RegQueryValueExW(hKey, s_pszShellState, NULL, NULL, reinterpret_cast<LPBYTE>(prss), &cb);
6473
RegCloseKey(hKey);
6574

6675
ok(result == ERROR_SUCCESS, "result was %ld\n", result);
@@ -151,18 +160,204 @@ static int read_advanced_key(SHELLSTATE* pss)
151160

152161
static int dump_pss(SHELLSTATE *pss)
153162
{
154-
dump("SHELLSTATE", pss, sizeof(*pss));
163+
dump("SHELLSTATE KEY", pss, sizeof(*pss));
155164
return 0;
156165
}
157166

167+
static HWND start_sub(void)
168+
{
169+
int retries = 50;
170+
WCHAR s_szSubProgram[MAX_PATH]; // shell32_apitest_sub.exe
171+
if (!FindSubProgram(s_szSubProgram, _countof(s_szSubProgram)))
172+
{
173+
skip("shell32_apitest_sub.exe not found\n");
174+
return 0;
175+
}
176+
177+
// Close the SUB_CLASSNAME windows
178+
DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE);
179+
180+
// Execute sub program
181+
HINSTANCE hinst = ShellExecuteW(NULL, NULL, s_szSubProgram, L"HIDE", NULL, SW_SHOWNORMAL);
182+
if ((INT_PTR)hinst <= 32)
183+
{
184+
skip("Unable to run shell32_apitest_sub.exe.\n");
185+
return 0;
186+
}
187+
188+
Retry:
189+
HWND s_hSubWnd = DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, FALSE, FALSE);
190+
if (!s_hSubWnd)
191+
{
192+
if (--retries > 0)
193+
{
194+
Sleep(100);
195+
goto Retry;
196+
}
197+
skip("Unable to find sub-program window.\n");
198+
return 0;
199+
}
200+
201+
return s_hSubWnd;
202+
}
203+
204+
static void stop_sub(HWND s_hSubWnd)
205+
{
206+
PostMessageW(s_hSubWnd, WM_COMMAND, IDNO, 0); // Finish
207+
DoWaitForWindow(SUB_CLASSNAME, SUB_CLASSNAME, TRUE, TRUE); // Close sub-windows
208+
}
209+
210+
static void process_test(SHELLSTATE *pss)
211+
{
212+
SHELLSTATE bak, ss1, ss2;
213+
SHELLSTATE_SUB sub;
214+
HKEY hKey;
215+
LONG result;
216+
217+
memset(&sub, 0, sizeof(sub));
218+
COPYDATASTRUCT copyData = { ID_SHSTATE, sizeof(sub), &sub };
219+
220+
// Backup current state
221+
memset(&bak, 0, sizeof(bak));
222+
SHGetSetSettings(&bak, MAXDWORD, FALSE);
223+
224+
HWND s_hSubWnd = start_sub();
225+
if (!s_hSubWnd)
226+
return;
227+
228+
memset(&ss1, 0, sizeof(ss1));
229+
SHGetSetSettings(&ss1, MAXDWORD, FALSE);
230+
231+
// Test 1: Read one field, then write another
232+
sub.dwMask = SSF_DOUBLECLICKINWEBVIEW;
233+
sub.bSet = FALSE;
234+
SendMessageW(s_hSubWnd, WM_COPYDATA, 0, (LPARAM)&copyData);
235+
236+
sub.ss.fNoConfirmRecycle = !ss1.fNoConfirmRecycle;
237+
ss1.fNoConfirmRecycle = sub.ss.fNoConfirmRecycle;
238+
sub.dwMask = SSF_NOCONFIRMRECYCLE;
239+
sub.bSet = TRUE;
240+
SendMessageW(s_hSubWnd, WM_COPYDATA, 0, (LPARAM)&copyData);
241+
242+
memset(&ss2, 0, sizeof(ss2));
243+
SHGetSetSettings(&ss2, MAXDWORD, FALSE);
244+
245+
#define CHECK_BITS(x) ok(ss1.x == ss2.x, "ss2.%s expected %d, was %d\n", #x, (int)ss1.x, (int)ss2.x)
246+
CHECK_BITS(fNoConfirmRecycle);
247+
CHECK_BITS(fDoubleClickInWebView);
248+
CHECK_BITS(fDesktopHTML);
249+
CHECK_BITS(fWin95Classic);
250+
CHECK_BITS(lParamSort);
251+
CHECK_BITS(iSortDirection);
252+
CHECK_BITS(fStartPanelOn);
253+
CHECK_BITS(fShowCompColor);
254+
CHECK_BITS(fShowAttribCol);
255+
CHECK_BITS(fShowInfoTip);
256+
257+
stop_sub(s_hSubWnd);
258+
259+
// Test 2: Only write a field
260+
s_hSubWnd = start_sub();
261+
if (!s_hSubWnd)
262+
goto Cleanup;
263+
264+
sub.ss.fNoConfirmRecycle = !ss1.fNoConfirmRecycle;
265+
ss1.fNoConfirmRecycle = sub.ss.fNoConfirmRecycle;
266+
sub.dwMask = SSF_NOCONFIRMRECYCLE;
267+
sub.bSet = TRUE;
268+
SendMessageW(s_hSubWnd, WM_COPYDATA, 0, (LPARAM)&copyData);
269+
270+
memset(&ss2, 0, sizeof(ss2));
271+
SHGetSetSettings(&ss2, MAXDWORD, FALSE);
272+
273+
CHECK_BITS(fNoConfirmRecycle);
274+
CHECK_BITS(fDoubleClickInWebView);
275+
CHECK_BITS(fDesktopHTML);
276+
CHECK_BITS(fWin95Classic);
277+
CHECK_BITS(lParamSort);
278+
CHECK_BITS(iSortDirection);
279+
CHECK_BITS(fStartPanelOn);
280+
CHECK_BITS(fShowCompColor);
281+
CHECK_BITS(fShowAttribCol);
282+
CHECK_BITS(fShowInfoTip);
283+
284+
stop_sub(s_hSubWnd);
285+
286+
// Test 3: Check default state values
287+
#define FLIP_FLAG(x) ss1.x = !ss1.x
288+
FLIP_FLAG(fNoConfirmRecycle);
289+
FLIP_FLAG(fDoubleClickInWebView);
290+
FLIP_FLAG(fDesktopHTML);
291+
FLIP_FLAG(fWin95Classic);
292+
FLIP_FLAG(fStartPanelOn);
293+
FLIP_FLAG(fShowCompColor);
294+
FLIP_FLAG(fShowAttribCol);
295+
FLIP_FLAG(fShowInfoTip);
296+
SHGetSetSettings(&ss1, MAXDWORD, TRUE);
297+
298+
result = RegOpenKeyExW(HKEY_CURRENT_USER, s_pszExplorer, 0, KEY_SET_VALUE, &hKey);
299+
if (result != ERROR_SUCCESS || !hKey)
300+
{
301+
skip("process_test(): RegOpenKeyEx failed: %ld\n", result);
302+
goto Cleanup;
303+
}
304+
305+
result = RegDeleteValueW(hKey, s_pszShellState);
306+
RegCloseKey(hKey);
307+
308+
if (result != ERROR_SUCCESS)
309+
{
310+
skip("process_test(): RegDeleteValueW failed: %ld\n", result);
311+
goto Cleanup;
312+
}
313+
314+
s_hSubWnd = start_sub();
315+
if (!s_hSubWnd)
316+
goto Cleanup;
317+
318+
sub.dwMask = SSF_NOCONFIRMRECYCLE;
319+
sub.bSet = FALSE;
320+
SendMessageW(s_hSubWnd, WM_COPYDATA, 0, (LPARAM)&copyData);
321+
322+
sub.bSet = TRUE;
323+
SendMessageW(s_hSubWnd, WM_COPYDATA, 0, (LPARAM)&copyData);
324+
325+
memset(&ss2, 0, sizeof(ss2));
326+
SHGetSetSettings(&ss2, MAXDWORD, FALSE);
327+
328+
#define CHECK_TRUE(x) ok(ss2.x, "ss2.%s expected to be TRUE\n", #x)
329+
#define CHECK_FALSE(x) ok(!ss2.x, "ss2.%s expected to be FALSE\n", #x)
330+
if (GetNTVersion() < _WIN32_WINNT_WIN8)
331+
CHECK_FALSE(fNoConfirmRecycle); // Different between Windows versions
332+
else
333+
CHECK_TRUE(fNoConfirmRecycle);
334+
CHECK_TRUE(fDoubleClickInWebView);
335+
CHECK_FALSE(fDesktopHTML);
336+
CHECK_FALSE(fWin95Classic);
337+
CHECK_TRUE(fStartPanelOn);
338+
CHECK_BITS(fShowCompColor); // Keeps the state
339+
CHECK_FALSE(fShowAttribCol);
340+
CHECK_BITS(fShowInfoTip); // Keeps the state
341+
342+
stop_sub(s_hSubWnd);
343+
344+
Cleanup:
345+
// Restore current state
346+
SHGetSetSettings(&bak, MAXDWORD, TRUE);
347+
348+
return;
349+
}
350+
158351
START_TEST(ShellState)
159352
{
160353
OSVERSIONINFO osinfo;
161354
REGSHELLSTATE rss;
162355
SHELLSTATE ss, *pss;
163356
SHELLFLAGSTATE FlagState;
357+
BOOL GotReg = TRUE;
164358
LPBYTE pb;
165359
int ret;
360+
ULONG c1, c2;
166361

167362
trace("GetVersion(): 0x%08lX\n", GetVersion());
168363

@@ -200,7 +395,8 @@ START_TEST(ShellState)
200395
ret = read_key(&rss);
201396
if (ret)
202397
{
203-
return;
398+
GotReg = FALSE;
399+
goto SkipReg;
204400
}
205401

206402
dump_pss(pss);
@@ -242,6 +438,7 @@ START_TEST(ShellState)
242438
DUMP_BOOL(pss->fShowStatusBar);
243439
#endif
244440

441+
SkipReg:
245442
#define SSF_MASK \
246443
(SSF_SHOWALLOBJECTS | SSF_SHOWEXTENSIONS | SSF_NOCONFIRMRECYCLE | SSF_SHOWSYSFILES | \
247444
SSF_SHOWCOMPCOLOR | SSF_DOUBLECLICKINWEBVIEW | SSF_DESKTOPHTML | \
@@ -253,6 +450,8 @@ START_TEST(ShellState)
253450
/* Get the settings */
254451
memset(&ss, 0, sizeof(ss));
255452
SHGetSetSettings(&ss, SSF_MASK, FALSE);
453+
if (GotReg)
454+
{
256455
#define CHECK_REG_FLAG(x) ok(pss->x == ss.x, "ss.%s expected %d, was %d\n", #x, (int)pss->x, (int)ss.x)
257456
CHECK_REG_FLAG(fShowAllObjects);
258457
CHECK_REG_FLAG(fShowExtensions);
@@ -275,6 +474,14 @@ START_TEST(ShellState)
275474
#if NTDDI_VERSION >= NTDDI_WIN8 // for future use
276475
CHECK_REG_FLAG(fShowStatusBar);
277476
#endif
477+
}
478+
c1 = dump("SHELLSTATE SSF_MASK", &ss, sizeof(ss));
479+
480+
memset(&ss, 0, sizeof(ss));
481+
SHGetSetSettings(&ss, MAXDWORD, FALSE);
482+
c2 = dump("SHELLSTATE MAXDWORD", &ss, sizeof(ss));
483+
484+
ok(c2 >= c1, "Set bytes in MAXDWORD state expected to be greater than %u, was %u\n", c1, c2);
278485

279486
/* Get the flag settings */
280487
memset(&FlagState, 0, sizeof(FlagState));
@@ -297,6 +504,16 @@ START_TEST(ShellState)
297504
CHECK_FLAG(fAutoCheckSelect);
298505
CHECK_FLAG(fIconsOnly);
299506
#endif
507+
c1 = dump("SHELLFLAGSTATE SSF_MASK", &FlagState, sizeof(FlagState));
508+
509+
memset(&FlagState, 0, sizeof(FlagState));
510+
SHGetSettings(&FlagState, MAXDWORD);
511+
c2 = dump("SHELLFLAGSTATE MAXDWORD", &FlagState, sizeof(FlagState));
512+
513+
ok(c2 >= c1, "Set bytes in MAXDWORD flags expected to be greater than %u, was %u\n", c1, c2);
514+
515+
/* Inter-process tests */
516+
process_test(pss);
300517

301518
/* Structure alignment tests */
302519
#if 1

‎modules/rostests/apitests/shell32/shell32_apitest_sub.cpp‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* COPYRIGHT: Copyright 2020-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
66
*/
77

8-
// This program is used in SHChangeNotify and ShellExecCmdLine testcases.
8+
// This program is used in SHChangeNotify, ShellExecCmdLine, and ShellState testcases.
99

1010
#include "shelltest.h"
1111
#include "shell32_apitest_sub.h"
@@ -308,6 +308,18 @@ static INT_PTR OnShellNotify(HWND hwnd, WPARAM wParam, LPARAM lParam)
308308
return TRUE;
309309
}
310310

311+
static LRESULT OnCopyData(HWND hwnd, LPARAM lParam)
312+
{
313+
COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lParam;
314+
315+
if (hwnd != 0 || cds->dwData != ID_SHSTATE)
316+
return 0;
317+
318+
PSHELLSTATE_SUB sub = (PSHELLSTATE_SUB)cds->lpData;
319+
SHGetSetSettings(&sub->ss, sub->dwMask, sub->bSet);
320+
return 1;
321+
}
322+
311323
static LRESULT CALLBACK SubWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
312324
{
313325
switch (uMsg)
@@ -322,6 +334,9 @@ static LRESULT CALLBACK SubWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA
322334
case WM_SHELL_NOTIFY:
323335
return OnShellNotify(hwnd, wParam, lParam);
324336

337+
case WM_COPYDATA:
338+
return OnCopyData((HWND)wParam, lParam);
339+
325340
case WM_DESTROY:
326341
OnDestroy(hwnd);
327342
break;
@@ -363,7 +378,7 @@ wWinMain(
363378
return -2;
364379
}
365380

366-
ShowWindow(hwnd, SW_SHOWNORMAL);
381+
ShowWindow(hwnd, lstrcmpiW(lpCmdLine, L"HIDE") == 0 ? SW_HIDE : SW_SHOWNORMAL);
367382
UpdateWindow(hwnd);
368383

369384
MSG msg;

‎modules/rostests/apitests/shell32/shell32_apitest_sub.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212

1313
#define NUM_STAGE 10
1414

15+
#define ID_SHSTATE 0xFEEDDEAF
16+
17+
typedef struct SHELLSTATE_SUB
18+
{
19+
SHELLSTATE ss;
20+
DWORD dwMask;
21+
BOOL bSet;
22+
} SHELLSTATE_SUB, *PSHELLSTATE_SUB;
23+
1524
static inline BOOL FindSubProgram(LPWSTR pszSubProgram, DWORD cchSubProgram)
1625
{
1726
GetModuleFileNameW(NULL, pszSubProgram, cchSubProgram);

0 commit comments

Comments
 (0)