ReactOS 0.4.17-dev-653-g1d6dae3
IQueryAssociations.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS API tests
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Tests for IQueryAssociations
5 * COPYRIGHT: Copyright 2026 Whindmar Saksit <whindsaks@proton.me>
6 */
7
8#include "shelltest.h"
9#include <shellutils.h>
10
11#define UNIQUEEXT L".RosTestExtShell32"
12#define UNIQUEPROGID L"88D6FB7F96C34598A94D7C1CBB37E70B"
13#define UNIQUEPROGID2 L"990B516360E4451EA6BE2156CB3819E4"
14#define UNIQUEGUID L"{FF9A10E8-E0E8-47B9-A2BC-36C5E435E5E1}"
15
16#define HKCR HKEY_CLASSES_ROOT
17#define HKCU HKEY_CURRENT_USER
18#define E_ANY ((0xFF << 24) | 42)
19#define S_ANY (~UINT(0) >> 24)
20enum
21{
32};
33
34struct RESULT
35{
40};
41
42static void Cleanup()
43{
47}
48
49static HRESULT InitTest(IQueryAssociations **ppQA = NULL)
50{
51 Cleanup();
52 return ppQA ? AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, ppQA)) : S_FALSE;
53}
54
55static HRESULT Init(UINT Flags, LPCWSTR pszAssoc, CComPtr<IQueryAssociations> &pQA, HKEY hProgId = NULL)
56{
57 if (pQA)
58 pQA = NULL;
59 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pQA));
60 return SUCCEEDED(hr) ? pQA->Init((ASSOCF)Flags, pszAssoc, hProgId, NULL) : hr;
61}
62
63static HRESULT GetString(ASSOCSTR Str, UINT Flags, LPCWSTR pszAssoc, LPCWSTR pszExtra, PWSTR Out, HKEY hProgId = NULL)
64{
65 CComPtr<IQueryAssociations> pQA;
66 HRESULT hr = Init(ASSOCF(Flags & AF_INITMASK), pszAssoc, pQA, hProgId);
68 lstrcpyW(Out, L"!InvalidData!");
69 return SUCCEEDED(hr) ? pQA->GetString(ASSOCF(Flags & ~AF_INITMASK), Str, pszExtra, Out, &cch) : hr;
70}
71
72static void CheckHelper(RESULT &r, UINT check, UINT type, UINT flags, LPCWSTR initstr, HKEY initprogid,
73 LPCWSTR extra, HKEY rhk, LPCWSTR rpath, LPCWSTR rname)
74{
75 CComPtr<IQueryAssociations> pQA;
76 r.hr = Init(ASSOCF(flags & AF_INITMASK), initstr, pQA, initprogid);
77 flags &= ~AF_INITMASK;
78 ZeroMemory(r.querydata, sizeof(r.querydata));
79
80 ZeroMemory(r.regdata, sizeof(r.regdata));
81 DWORD cbReg = sizeof(r.regdata);
83 r.reg = SHRegGetValueW(rhk, rpath, rname, srrf, NULL, r.regdata, &cbReg);
84
85 if (SUCCEEDED(r.hr) && r.reg == ERROR_SUCCESS)
86 {
87 if (check <= REG_EXPAND_SZ)
88 {
89 lstrcpyW(r.querydata, L"!InvalidData!");
90 DWORD cch = _countof(r.querydata);
91 r.hr = pQA->GetString(ASSOCF(flags), ASSOCSTR(type), extra, r.querydata, &cch);
92 r.match = SUCCEEDED(r.hr) && !lstrcmpiW(r.querydata, r.regdata);
93 }
94 else
95 {
96 *(UINT64*)&r.querydata = 0xBAAAAAAAAAAAAAADull;
97 DWORD cb = sizeof(r.querydata);
98 r.hr = pQA->GetData(ASSOCF(flags), ASSOCDATA(type), extra, r.querydata, &cb);
99 r.match = SUCCEEDED(r.hr) && cb == cbReg && !memcmp(r.querydata, r.regdata, cb);
100 }
101 }
102 r.match |= (FAILED(r.hr) && r.reg != ERROR_SUCCESS);
103}
104
105#define CheckRegValue(hrexpect, reg, type, flags, initstr, initprogid, extra, rhk, rpath, rname) do \
106{ \
107 RESULT r; \
108 CheckHelper(r, (reg), (type), (flags), (initstr), (initprogid), (extra), (rhk), (rpath), (rname)); \
109 if ((hrexpect) == E_ANY) \
110 { \
111 ok(FAILED(r.hr), "Expected failure, got %#x\n", r.hr); \
112 } \
113 else \
114 { \
115 ok_hr(r.hr, (hrexpect)); \
116 if (SUCCEEDED(r.hr)) \
117 ok(r.match, "ASSOC %d did not match %ls |%ls|%ls|\n", (type), RegNameDisp(rname), r.querydata, r.regdata); \
118 } \
119} while(0)
120
121#define CheckRegStr(hrexpect, type, flags, initstr, initprogid, extra, rhk, rpath, rname) \
122 CheckRegValue((hrexpect), REG_SZ, (type), (flags), (initstr), (initprogid), (extra), (rhk), (rpath), (rname))
123#define CheckRegData(hrexpect, type, flags, initstr, initprogid, extra, rhk, rpath, rname) \
124 CheckRegValue((hrexpect), REG_BINARY, (type), (flags), (initstr), (initprogid), (extra), (rhk), (rpath), (rname))
125
126START_TEST(IQueryAssociations)
127{
128 CCoInit ComInit;
129 BOOL nt5 = LOBYTE(GetVersion()) < 6, patch;
130 RESULT r;
131 LPCWSTR pszPath, pszValue;
133
134 CComPtr<IQueryAssociations> pQA;
135 HRESULT hr = InitTest(&pQA);
136 if (FAILED(hr))
137 {
138 skip("Could not create IQueryAssociations\n");
139 return;
140 }
141
142 hr = Init(AF_EXT, L".exe", pQA);
143 ok_hr(hr, S_OK);
144
145 hr = Init(AF_EXT, UNIQUEEXT, pQA);
146 ok_hr(hr, nt5 ? S_FALSE : S_OK); // Initialized but it has no HKEYs
147
148 CheckRegStr(S_OK, ASSOCSTR_COMMAND, AF_EXT, L".bat", NULL, NULL, HKCR, L"batfile\\shell\\open\\command", NULL);
149 CheckRegStr(S_OK, ASSOCSTR_CONTENTTYPE, AF_EXT, L".bmp", NULL, NULL, HKCR, L".bmp", L"Content Type");
150 CheckRegStr(S_OK, ASSOCSTR_DEFAULTICON, AF_EXT, L".bat", NULL, NULL, HKCR, L"batfile\\DefaultIcon", NULL);
151 CheckRegData(S_OK, ASSOCDATA_EDITFLAGS, AF_EXT, L".bat", NULL, NULL, HKCR, L"batfile", L"EditFlags");
152
153 // ASSOCF_INIT_DEFAULTTOFOLDER
154 patch = is_reactos() && SHRegGetValueW(HKCR, L"Folder", L"EditFlags", SRRF_RT_ANY, NULL, NULL, NULL);
155 if (patch)
156 RegSetString(HKCR, L"Folder", L"EditFlags", L"FixBrokenRos"); // Temporary fix
157 CheckRegData(S_OK, ASSOCDATA_EDITFLAGS, AF_PID | AF_DEFFOLDER, UNIQUEPROGID L"NotExist", NULL, NULL, HKCR, L"Folder", L"EditFlags");
158 if (patch)
159 SHDeleteValueW(HKCR, L"Folder", L"EditFlags");
160
161 // ASSOCDATA_VALUE in .ext without ProgId
162 pszPath = L"Software\\Classes\\" UNIQUEEXT, pszValue = L"TestVal";
163 RegSetString(HKCU, pszPath, pszValue, L"Ext1234");
165 ok(!r.hr && !lstrcmpiW(r.querydata, r.regdata), "VALUE mismatch\n");
166
167 // ASSOCDATA_VALUE in .ext with non-existing ProgId
168 RegSetString(HKCU, L"Software\\Classes\\" UNIQUEEXT, NULL, UNIQUEPROGID);
170 ok(!r.hr && !lstrcmpiW(r.querydata, r.regdata), "VALUE mismatch\n");
171
172 // ASSOCDATA_VALUE in .ext without value in ProgId
173 RegSetString(HKCU, L"Software\\Classes\\" UNIQUEPROGID, L"IgnoreMe", L"");
175 ok(FAILED(r.hr), "Should not fall back to .ext\n");
176
177 // ASSOCDATA_VALUE in ProgId
178 pszPath = L"Software\\Classes\\" UNIQUEPROGID;
179 RegSetString(HKCU, pszPath, pszValue, L"Pid1234");
181 ok(!r.hr && !lstrcmpiW(r.querydata, r.regdata), "VALUE mismatch\n");
182
183 // ASSOCF_INIT_DEFAULTTOSTAR
184 pszValue = L"AlwaysShowExt";
186
188 ok(!r.hr && !lstrcmpiW(r.querydata, r.regdata), "VALUE mismatch\n");
189
190 // ASSOCF_REMAPRUNDLL
191 pszValue = L"%SystemRoot%\\System32\\rundll32.exe \"%SystemRoot%\\System32\\shell32.dll\", Func %1";
192 RegSetString(HKCU, L"Software\\Classes\\" UNIQUEEXT, NULL, UNIQUEPROGID);
193 RegSetStringEx(HKCU, L"Software\\Classes\\" UNIQUEPROGID L"\\shell\\open\\command", NULL, pszValue, REG_EXPAND_SZ);
194
196 ok(!hr && StrStrIW(buf, L"\\rundll32.exe"), "REMAPRUNDLL is not implied for COMMAND, got %ls\n", buf);
197
199 ok(!hr && StrStrIW(buf, L"\\rundll32.exe"), "REMAPRUNDLL has no effect on COMMAND, got %ls\n", buf);
200
201 // ASSOCSTR_EXECUTABLE
203 ok(!hr && !StrStrIW(buf, L"\\rundll32.exe") && StrStrIW(buf, L".dll"), "REMAPRUNDLL implied, got %ls\n", buf);
204
205 pszValue = L"\"%SystemRoot%\\System32\\rundll32.exe\" \"%SystemRoot%\\System32\\shell32.dll\", Func %1"; // Quotes
206 RegSetStringEx(HKCU, L"Software\\Classes\\" UNIQUEPROGID L"\\shell\\open\\command", NULL, pszValue, REG_EXPAND_SZ);
208 ok(!hr && !StrStrIW(buf, L"\\rundll32.exe") && StrStrIW(buf, L".dll"), "REMAPRUNDLL implied, got %ls\n", buf);
209
210 pszValue = L"rundll32.exe shell32.dll, Func %1";
211 RegSetStringEx(HKCU, L"Software\\Classes\\" UNIQUEPROGID L"\\shell\\open\\command", NULL, pszValue, REG_EXPAND_SZ);
213 ok(!hr && !StrStrIW(buf, L"\\rundll32.exe") && StrStrIW(buf, L".dll"), "REMAPRUNDLL implied, got %ls\n", buf);
214
215 pszValue = L"%SystemRoot%\\System32\\rundll32.exe";
216 RegSetStringEx(HKCU, L"Software\\Classes\\" UNIQUEPROGID L"\\shell\\open\\command", NULL, pszValue, REG_EXPAND_SZ);
218 ok_hr(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); // A bit surprising but true
219
220 pszValue = L"\"%SystemRoot%\\System32\\cmd.exe\" /C \"%SystemRoot%\\System32\\rundll32.exe\"";
221 RegSetStringEx(HKCU, L"Software\\Classes\\" UNIQUEPROGID L"\\shell\\open\\command", NULL, pszValue, REG_EXPAND_SZ);
223 ok_hr(hr, S_OK);
224 ok(!hr && StrStrIW(buf, L"\\cmd.exe") && !StrStrIW(buf, L"\\rundll32.exe"), "Just the first path, got %ls\n", buf);
225
226 // ASSOCSTR_INFOTIP ProgId
227 pszPath = L"Software\\Classes\\" UNIQUEPROGID, pszValue = L"InfoTip";
228 RegSetString(HKCU, pszPath, pszValue, L"Pid1234");
230
231 // CLSID
232 pszPath = L"Software\\Classes\\CLSID\\" UNIQUEGUID, pszValue = L"InfoTip";
233 RegSetString(HKCU, pszPath, pszValue, L"CLSID1234");
235
236 // CLSID ignore ProgId redirection
237 RegSetString(HKCU, L"Software\\Classes\\CLSID\\" UNIQUEGUID L"\\ProgId", NULL, UNIQUEPROGID);
238 RegSetString(HKCU, L"Software\\Classes\\" UNIQUEPROGID, pszValue, L"Pid1234");
240
241 // ASSOCF_INIT_NOREMAPCLSID seems to be implied on XP+?
243
244 // ProgId CurVer redirection
245 pszPath = L"Software\\Classes\\" UNIQUEPROGID2, pszValue = L"InfoTip";
246 RegSetString(HKCU, L"Software\\Classes\\" UNIQUEPROGID L"\\CurVer", NULL, UNIQUEPROGID2);
247 RegSetString(HKCU, L"Software\\Classes\\" UNIQUEPROGID2 L"\\shell", L"JustWantTheKeyToExist", NULL);
248 RegSetString(HKCU, pszPath, pszValue, L"CV1234");
250
251 // ASSOCSTR_INFOTIP .ext without ProgId
252 Cleanup();
253 pszPath = L"Software\\Classes\\" UNIQUEEXT, pszValue = L"InfoTip";
254 RegSetString(HKCU, pszPath, pszValue, L"Ext1234");
256
257 Cleanup();
258}
#define ok_hr(status, expected)
Definition: ACListISF.cpp:31
#define HKCU
static void CheckHelper(RESULT &r, UINT check, UINT type, UINT flags, LPCWSTR initstr, HKEY initprogid, LPCWSTR extra, HKEY rhk, LPCWSTR rpath, LPCWSTR rname)
#define CheckRegStr(hrexpect, type, flags, initstr, initprogid, extra, rhk, rpath, rname)
#define CheckRegData(hrexpect, type, flags, initstr, initprogid, extra, rhk, rpath, rname)
static void Cleanup()
#define HKCR
#define UNIQUEEXT
#define E_ANY
#define UNIQUEPROGID2
static HRESULT InitTest(IQueryAssociations **ppQA=NULL)
static HRESULT GetString(ASSOCSTR Str, UINT Flags, LPCWSTR pszAssoc, LPCWSTR pszExtra, PWSTR Out, HKEY hProgId=NULL)
@ HR_NOASSOC
@ AF_INITMASK
@ AF_DEFFOLDER
@ HR_ACCDENIED
@ AF_DEFSTAR
#define UNIQUEGUID
#define UNIQUEPROGID
COMPILER_DEPENDENT_UINT64 UINT64
Definition: actypes.h:131
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define START_TEST(x)
Definition: atltest.h:75
HRESULT hr
Definition: delayimp.cpp:582
#define ERROR_SUCCESS
Definition: deptool.c:10
@ Out
#define NULL
Definition: types.h:112
LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
Definition: string.c:380
#define MAX_PATH
Definition: compat.h:34
#define lstrcpyW
Definition: compat.h:749
#define ERROR_ACCESS_DENIED
Definition: compat.h:97
int WINAPI lstrcmpiW(LPCWSTR str1, LPCWSTR str2)
Definition: locale.c:4171
DWORD WINAPI GetVersion(void)
Definition: version.c:1458
static MonoProfilerRuntimeShutdownBeginCallback cb
Definition: metahost.c:118
_ACRTIMP int __cdecl memcmp(const void *, const void *, size_t)
Definition: string.c:2807
DWORD WINAPI SHDeleteValueW(HKEY hkey, const WCHAR *subkey, const WCHAR *value)
Definition: main.c:1906
DWORD WINAPI SHDeleteKeyW(HKEY hkey, const WCHAR *subkey)
Definition: main.c:1886
static DWORD RegSetString(HKEY hKey, LPCWSTR Name, LPCWSTR Str, DWORD Type OPTIONAL_(REG_SZ))
Definition: utils.h:81
HRESULT WINAPI AssocCreate(CLSID clsid, REFIID refiid, void **lpInterface)
Definition: assoc.c:213
#define check(expected, result)
Definition: dplayx.c:32
#define L(x)
Definition: resources.c:13
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
GLdouble GLdouble GLdouble r
Definition: gl.h:2055
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
GLbitfield flags
Definition: glext.h:7161
unsigned int UINT
Definition: sysinfo.c:13
@ extra
Definition: id3.c:95
#define S_OK
Definition: intsafe.h:52
#define SUCCEEDED(hr)
Definition: intsafe.h:50
#define FAILED(hr)
Definition: intsafe.h:51
#define LOBYTE(W)
Definition: jmemdos.c:487
#define is_reactos()
Definition: kmt_test.h:534
#define ZeroMemory
Definition: minwinbase.h:31
#define ERROR_FILE_NOT_FOUND
Definition: disk.h:79
#define REG_BINARY
Definition: nt_native.h:1499
#define REG_EXPAND_SZ
Definition: nt_native.h:1497
short WCHAR
Definition: pedump.c:58
long LONG
Definition: pedump.c:60
ASSOCDATA
Definition: shlwapi.h:926
@ ASSOCDATA_EDITFLAGS
Definition: shlwapi.h:932
@ ASSOCDATA_VALUE
Definition: shlwapi.h:933
ASSOCSTR
Definition: shlwapi.h:885
@ ASSOCSTR_CONTENTTYPE
Definition: shlwapi.h:900
@ ASSOCSTR_COMMAND
Definition: shlwapi.h:886
@ ASSOCSTR_INFOTIP
Definition: shlwapi.h:896
@ ASSOCSTR_EXECUTABLE
Definition: shlwapi.h:887
@ ASSOCSTR_DEFAULTICON
Definition: shlwapi.h:901
@ ASSOCF_REMAPRUNDLL
Definition: shlwapi.h:867
@ ASSOCF_NOFIXUPS
Definition: shlwapi.h:868
@ ASSOCF_INIT_DEFAULTTOSTAR
Definition: shlwapi.h:862
@ ASSOCF_INIT_DEFAULTTOFOLDER
Definition: shlwapi.h:863
@ ASSOCF_INIT_NOREMAPCLSID
Definition: shlwapi.h:859
@ ASSOCF_INIT_IGNOREUNKNOWN
Definition: shlwapi.h:870
@ ASSOCF_NOTRUNCATE
Definition: shlwapi.h:865
INT SRRF
Definition: shlwapi.h:717
#define SRRF_RT_REG_SZ
Definition: shlwapi.h:698
DWORD ASSOCF
Definition: shlwapi.h:967
#define SRRF_RT_ANY
Definition: shlwapi.h:704
_In_opt_ LPCSTR _In_opt_ LPCSTR pszValue
Definition: shlwapi.h:783
_In_ UINT _In_ UINT cch
Definition: shellapi.h:432
static UINT RegSetStringEx(HKEY hKey, LPCWSTR Path, LPCWSTR Name, LPCWSTR Str, DWORD Type)
Definition: shelltest.h:27
#define _countof(array)
Definition: sndvol32.h:70
WCHAR querydata[_countof(regdata)]
WCHAR regdata[MAX_PATH *2]
uint16_t * PWSTR
Definition: typedefs.h:56
const uint16_t * LPCWSTR
Definition: typedefs.h:57
WINSHLWAPI LSTATUS WINAPI SHRegGetValueW(HKEY, LPCWSTR, LPCWSTR, SRRF, LPDWORD, LPVOID, LPDWORD)
#define S_FALSE
Definition: winerror.h:3451
static HRESULT HRESULT_FROM_WIN32(unsigned int x)
Definition: winerror.h:210
#define ERROR_NO_ASSOCIATION
Definition: winerror.h:1001
#define ERROR_NOT_FOUND
Definition: winerror.h:1014
#define HKEY_CLASSES_ROOT
Definition: winreg.h:10
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
#define IID_PPV_ARG(Itype, ppType)