Reactos
1/*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Tests for IsQSForward
5 * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7
8#include <apitest.h>
9#include <docobj.h>
10#include <shlobj.h>
11#include <shlwapi.h>
12#include <shlwapi_undoc.h>
13#include <pseh/pseh2.h>
14#include <versionhelpers.h>
15
16static const BOOL g_bWin8 = IsWindows8OrGreater();
17
18static HRESULT
19IsQSForwardMockup(_In_opt_ const GUID *pguidCmdGroup, _In_ ULONG cCmds, _In_ OLECMD *prgCmds)
20{
21 DWORD cmdFlags = 0;
22 OLECMDID cmdID;
23 ULONG iCmd;
24 enum {
25 CMD_FLAG_SUPPORTED_BASIC = 0x1,
26 CMD_FLAG_SUPPORTED_ADVANCED = 0x2,
27 CMD_FLAG_NOT_SUPPORTED = 0x4,
28 };
29
30 //TRACE("(%s, %lu, %p)\n", wine_dbgstr_guid(pguidCmdGroup), cCmds, prgCmds);
31
32 if ((LONG)cCmds <= 0)
33 return OLECMDERR_E_NOTSUPPORTED;
34
35 if (!pguidCmdGroup)
36 {
37 for (iCmd = 0; iCmd < cCmds; ++iCmd)
38 {
39 cmdID = (OLECMDID)prgCmds[iCmd].cmdID;
40 if (cmdID <= OLECMDID_PROPERTIES)
41 {
42 cmdFlags |= CMD_FLAG_NOT_SUPPORTED; // Not supported
43 continue;
44 }
45
46 if (cmdID <= OLECMDID_PASTE || cmdID == OLECMDID_SELECTALL)
47 {
48 cmdFlags |= CMD_FLAG_SUPPORTED_BASIC;
49 continue;
50 }
51
52 if (cmdID <= OLECMDID_UPDATECOMMANDS ||
53 (OLECMDID_HIDETOOLBARS <= cmdID && cmdID != OLECMDID_ENABLE_INTERACTION))
54 {
55 cmdFlags |= CMD_FLAG_NOT_SUPPORTED; // Not supported
56 continue;
57 }
58
59 cmdFlags |= CMD_FLAG_SUPPORTED_ADVANCED;
60 }
61 }
62 else
63 {
64 if (!IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
65 {
66 if (g_bWin8)
67 return OLECMDERR_E_UNKNOWNGROUP;
68 else
69 return OLECMDERR_E_NOTSUPPORTED;
70 }
71
72 for (iCmd = 0; iCmd < cCmds; ++iCmd)
73 {
74 cmdID = (OLECMDID)prgCmds[iCmd].cmdID;
75 if (cmdID == OLECMDID_SELECTALL ||
76 (OLECMDID_SHOWFIND <= cmdID && cmdID <= OLECMDID_SHOWPRINT))
77 {
78 cmdFlags |= CMD_FLAG_SUPPORTED_BASIC;
79 break;
80 }
81 }
82 }
83
84 if (!cmdFlags || (cmdFlags & CMD_FLAG_NOT_SUPPORTED))
85 return OLECMDERR_E_NOTSUPPORTED; // Not supported
86
87 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, cmdFlags);
88}
89
90static HRESULT
91MayQSForwardMockup(
92 _In_ IUnknown *lpUnknown,
93 _In_ INT nUnknown,
94 _In_opt_ const GUID *riidCmdGrp,
95 _In_ ULONG cCmds,
96 _Inout_ OLECMD *prgCmds,
97 _Inout_ OLECMDTEXT *pCmdText)
98{
99 //TRACE("(%p, %d, %s, %d, %p, %p)\n",
100 // lpUnknown, nUnknown, wine_dbgstr_guid(riidCmdGrp), cCmds, prgCmds, pCmdText);
101
102 HRESULT hr = IsQSForwardMockup(riidCmdGrp, cCmds, prgCmds);
103 if (FAILED(hr) || !HRESULT_CODE(hr) || nUnknown <= 0)
104 return OLECMDERR_E_NOTSUPPORTED;
105
106 return IUnknown_QueryStatus(lpUnknown, *riidCmdGrp, cCmds, prgCmds, pCmdText);
107}
108
109static HRESULT
110MayExecForwardMockup(
111 _In_ IUnknown *lpUnknown,
112 _In_ INT nUnknown,
113 _In_opt_ const GUID *pguidCmdGroup,
114 _In_ DWORD nCmdID,
115 _In_ DWORD nCmdexecopt,
116 _In_ VARIANT *pvaIn,
117 _Inout_ VARIANT *pvaOut)
118{
119 //TRACE("(%p, %d, %s, %d, %d, %p, %p)\n", lpUnknown, nUnknown,
120 // wine_dbgstr_guid(pguidCmdGroup), nCmdID, nCmdexecopt, pvaIn, pvaOut);
121
122 OLECMD cmd = { nCmdID };
123 HRESULT hr = IsQSForwardMockup(pguidCmdGroup, 1, &cmd);
124 if (FAILED(hr) || !HRESULT_CODE(hr) || nUnknown <= 0)
125 return OLECMDERR_E_NOTSUPPORTED;
126
127 return IUnknown_Exec(lpUnknown, *pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
128}
129
130static BOOL g_bQueryStatus = FALSE;
131static BOOL g_bExec = FALSE;
132
133class CDummyClass : public IOleCommandTarget
134{
135public:
136 CDummyClass() { }
137 virtual ~CDummyClass() { }
138
139 // ** IUnknown methods **
140 STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override
141 {
142 if (!ppvObj)
143 return E_POINTER;
144 *ppvObj = NULL;
145 if (riid == IID_IUnknown || riid == IID_IOleCommandTarget)
146 *ppvObj = this;
147 if (*ppvObj)
148 {
149 AddRef();
150 return S_OK;
151 }
152 return E_NOINTERFACE;
153 }
154 STDMETHODIMP_(ULONG) AddRef() override
155 {
156 return 2;
157 }
158 STDMETHODIMP_(ULONG) Release() override
159 {
160 return 2;
161 }
162
163 // ** IOleCommandTarget methods **
164 STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) override
165 {
166 g_bQueryStatus = TRUE;
167 return 0xBEEFCAFE;
168 }
169 STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) override
170 {
171 g_bExec = TRUE;
172 return 0x2BAD2BAD;
173 }
174
175 static void *operator new(size_t size)
176 {
177 return LocalAlloc(LPTR, size);
178 }
179 static void operator delete(void *ptr)
180 {
181 LocalFree(ptr);
182 }
183 static void operator delete(void *ptr, size_t size)
184 {
185 LocalFree(ptr);
186 }
187};
188
189static VOID TEST_IsQSForward(VOID)
190{
191 OLECMD cmds[2];
192 LONG cmdID, cmdID2;
193 HRESULT ret1, ret2;
194 ULONG cCmds;
195 BOOL bExcept1, bExcept2;
196 const GUID *pGUID;
197 enum { LOW_VALUE = -1, HIGH_VALUE = OLECMDID_CLOSE };
198
199 cmds[0].cmdf = 0;
200
201 cCmds = 0;
202 pGUID = NULL;
203 for (cmdID = LOW_VALUE; cmdID <= HIGH_VALUE; ++cmdID)
204 {
205 cmds[0].cmdID = cmdID;
206
207 ret1 = IsQSForward(*pGUID, cCmds, cmds);
208 ret2 = IsQSForwardMockup(pGUID, cCmds, cmds);
209 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
210
211 ret1 = IsQSForward(*pGUID, cCmds, NULL);
212 ret2 = IsQSForwardMockup(pGUID, cCmds, NULL);
213 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
214 }
215
216 cCmds = 1;
217 for (cmdID = LOW_VALUE; cmdID <= HIGH_VALUE; ++cmdID)
218 {
219 cmds[0].cmdID = cmdID;
220
221 pGUID = NULL;
222 ret1 = IsQSForward(*pGUID, cCmds, cmds);
223 ret2 = IsQSForwardMockup(pGUID, cCmds, cmds);
224 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
225
226 ret1 = ret2 = 0xDEADFACE;
227 bExcept1 = bExcept2 = FALSE;
228
229 _SEH2_TRY
230 {
231 ret1 = IsQSForward(*pGUID, cCmds, NULL);
232 }
233 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
234 {
235 bExcept1 = TRUE;
236 }
237 _SEH2_END;
238
239 _SEH2_TRY
240 {
241 ret2 = IsQSForwardMockup(pGUID, cCmds, NULL);
242 }
243 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
244 {
245 bExcept2 = TRUE;
246 }
247 _SEH2_END;
248
249 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
250 ok((bExcept1 && bExcept2), "cmdID: %ld (%d vs %d)\n", cmdID, bExcept1, bExcept2);
251
252 pGUID = &CGID_Explorer;
253 ret1 = IsQSForward(*pGUID, cCmds, cmds);
254 ret2 = IsQSForwardMockup(pGUID, cCmds, cmds);
255 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
256
257 pGUID = &IID_IUnknown;
258 ret1 = IsQSForward(*pGUID, cCmds, cmds);
259 ret2 = IsQSForwardMockup(pGUID, cCmds, cmds);
260 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
261 }
262
263 cCmds = 2;
264 for (cmdID = LOW_VALUE; cmdID <= HIGH_VALUE; ++cmdID)
265 {
266 cmds[0].cmdID = cmdID;
267
268 pGUID = NULL;
269 for (cmdID2 = LOW_VALUE; cmdID2 <= HIGH_VALUE; ++cmdID2)
270 {
271 cmds[1].cmdID = cmdID2;
272 ret1 = IsQSForward(*pGUID, cCmds, cmds);
273 ret2 = IsQSForwardMockup(pGUID, cCmds, cmds);
274 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
275 }
276
277 pGUID = &CGID_Explorer;
278 for (cmdID2 = LOW_VALUE; cmdID2 <= HIGH_VALUE; ++cmdID2)
279 {
280 cmds[1].cmdID = cmdID2;
281 ret1 = IsQSForward(*pGUID, cCmds, cmds);
282 ret2 = IsQSForwardMockup(pGUID, cCmds, cmds);
283 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
284 }
285 }
286}
287
288static VOID TEST_MayQSForwardMockup(VOID)
289{
290 CDummyClass dummy;
291 LONG cmdID;
292 OLECMD cmds[1];
293 OLECMDTEXT cmdText;
294 HRESULT ret1, ret2;
295
296 // Testing OLECMDID_PROPERTIES support
297 cmds[0].cmdID = cmdID = OLECMDID_PROPERTIES;
298
299 g_bQueryStatus = g_bExec = FALSE;
300 ret1 = MayQSForward(&dummy, 1, CGID_Explorer, _countof(cmds), cmds, &cmdText);
301 ok_int(g_bQueryStatus, FALSE);
302 ok_int(g_bExec, FALSE);
303
304 g_bQueryStatus = g_bExec = FALSE;
305 ret2 = MayQSForwardMockup(&dummy, 1, &CGID_Explorer, _countof(cmds), cmds, &cmdText);
306 ok_int(g_bQueryStatus, FALSE);
307 ok_int(g_bExec, FALSE);
308
309 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
310
311 // Testing OLECMDID_SHOWFIND support
312 cmds[0].cmdID = cmdID = OLECMDID_SHOWFIND;
313
314 g_bQueryStatus = g_bExec = FALSE;
315 ret1 = MayQSForward(&dummy, 1, CGID_Explorer, _countof(cmds), cmds, &cmdText);
316 ok_int(g_bQueryStatus, TRUE);
317 ok_int(g_bExec, FALSE);
318
319 g_bQueryStatus = g_bExec = FALSE;
320 ret2 = MayQSForwardMockup(&dummy, 1, &CGID_Explorer, _countof(cmds), cmds, &cmdText);
321 ok_int(g_bQueryStatus, TRUE);
322 ok_int(g_bExec, FALSE);
323
324 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
325}
326
327static VOID TEST_MayExecForwardMockup(VOID)
328{
329 CDummyClass dummy;
330 LONG cmdID;
331 HRESULT ret1, ret2;
332
333 // Testing OLECMDID_PROPERTIES support
334 cmdID = OLECMDID_PROPERTIES;
335
336 g_bQueryStatus = g_bExec = FALSE;
337 ret1 = MayExecForward(&dummy, 1, CGID_Explorer, cmdID, 0, NULL, NULL);
338 ok_int(g_bQueryStatus, FALSE);
339 ok_int(g_bExec, FALSE);
340
341 g_bQueryStatus = g_bExec = FALSE;
342 ret2 = MayExecForwardMockup(&dummy, 1, &CGID_Explorer, cmdID, 0, NULL, NULL);
343 ok_int(g_bQueryStatus, FALSE);
344 ok_int(g_bExec, FALSE);
345
346 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
347
348 // Testing OLECMDID_SHOWFIND support
349 cmdID = OLECMDID_SHOWFIND;
350
351 g_bQueryStatus = g_bExec = FALSE;
352 ret1 = MayExecForward(&dummy, 1, CGID_Explorer, cmdID, 0, NULL, NULL);
353 ok_int(g_bQueryStatus, FALSE);
354 ok_int(g_bExec, TRUE);
355
356 g_bQueryStatus = g_bExec = FALSE;
357 ret2 = MayExecForwardMockup(&dummy, 1, &CGID_Explorer, cmdID, 0, NULL, NULL);
358 ok_int(g_bQueryStatus, FALSE);
359 ok_int(g_bExec, TRUE);
360
361 ok(ret1 == ret2, "cmdID: %ld (%ld vs %ld)\n", cmdID, ret1, ret2);
362}
363
364START_TEST(IsQSForward)
365{
366 TEST_IsQSForward();
367 TEST_MayQSForwardMockup();
368 TEST_MayExecForwardMockup();
369}