1 /* Unit test suite for various shell Association objects 2 * 3 * Copyright 2012 Detlef Riekenberg 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 #define COBJMACROS 21 22 #include <stdarg.h> 23 24 #include "shlwapi.h" 25 #include "shlguid.h" 26 #include "shobjidl.h" 27 28 #include "wine/heap.h" 29 #include "wine/test.h" 30 31 32 static void test_IQueryAssociations_QueryInterface(void) 33 { 34 IQueryAssociations *qa; 35 IQueryAssociations *qa2; 36 IUnknown *unk; 37 HRESULT hr; 38 39 hr = CoCreateInstance(&CLSID_QueryAssociations, NULL, CLSCTX_INPROC_SERVER, &IID_IQueryAssociations, (void*)&qa); 40 ok(hr == S_OK, "got 0x%08x\n", hr); 41 42 hr = IQueryAssociations_QueryInterface(qa, &IID_IQueryAssociations, (void**)&qa2); 43 ok(hr == S_OK, "QueryInterface (IQueryAssociations) returned 0x%x\n", hr); 44 if (SUCCEEDED(hr)) { 45 IQueryAssociations_Release(qa2); 46 } 47 48 hr = IQueryAssociations_QueryInterface(qa, &IID_IUnknown, (void**)&unk); 49 ok(hr == S_OK, "QueryInterface (IUnknown) returned 0x%x\n", hr); 50 if (SUCCEEDED(hr)) { 51 IUnknown_Release(unk); 52 } 53 54 hr = IQueryAssociations_QueryInterface(qa, &IID_IUnknown, NULL); 55 ok(hr == E_POINTER, "got 0x%x (expected E_POINTER)\n", hr); 56 57 IQueryAssociations_Release(qa); 58 } 59 60 61 static void test_IApplicationAssociationRegistration_QueryInterface(IApplicationAssociationRegistration *appreg) 62 { 63 IApplicationAssociationRegistration *appreg2; 64 IUnknown *unk; 65 HRESULT hr; 66 67 hr = IApplicationAssociationRegistration_QueryInterface(appreg, &IID_IApplicationAssociationRegistration, 68 (void**)&appreg2); 69 ok(hr == S_OK, "QueryInterface (IApplicationAssociationRegistration) returned 0x%x\n", hr); 70 if (SUCCEEDED(hr)) { 71 IApplicationAssociationRegistration_Release(appreg2); 72 } 73 74 hr = IApplicationAssociationRegistration_QueryInterface(appreg, &IID_IUnknown, (void**)&unk); 75 ok(hr == S_OK, "QueryInterface (IUnknown) returned 0x%x\n", hr); 76 if (SUCCEEDED(hr)) { 77 IUnknown_Release(unk); 78 } 79 80 hr = IApplicationAssociationRegistration_QueryInterface(appreg, &IID_IUnknown, NULL); 81 ok(hr == E_POINTER, "got 0x%x (expected E_POINTER)\n", hr); 82 } 83 84 struct assoc_getstring_test 85 { 86 const WCHAR *key; 87 ASSOCF flags; 88 ASSOCSTR str; 89 DWORD len; 90 HRESULT hr; 91 HRESULT brokenhr; 92 }; 93 94 static const WCHAR httpW[] = {'h','t','t','p',0}; 95 static const WCHAR badW[] = {'b','a','d','b','a','d',0}; 96 97 static struct assoc_getstring_test getstring_tests[] = 98 { 99 { httpW, 0, ASSOCSTR_EXECUTABLE, 2, 0x8007007a /* E_NOT_SUFFICIENT_BUFFER */, S_OK }, 100 { httpW, ASSOCF_NOTRUNCATE, ASSOCSTR_EXECUTABLE, 2, E_POINTER }, 101 { NULL } 102 }; 103 104 static void getstring_test(LPCWSTR assocName, HKEY progIdKey, ASSOCSTR str, LPCWSTR expected_string, int line) 105 { 106 IQueryAssociations *assoc; 107 HRESULT hr; 108 WCHAR *buffer = NULL; 109 DWORD len; 110 111 hr = CoCreateInstance(&CLSID_QueryAssociations, NULL, CLSCTX_INPROC_SERVER, &IID_IQueryAssociations, (void*)&assoc); 112 ok_(__FILE__, line)(hr == S_OK, "failed to create IQueryAssociations, 0x%x\n", hr); 113 hr = IQueryAssociations_Init(assoc, ASSOCF_NONE, assocName, progIdKey, NULL); 114 ok_(__FILE__, line)(hr == S_OK, "IQueryAssociations::Init failed, 0x%x\n", hr); 115 116 hr = IQueryAssociations_GetString(assoc, ASSOCF_NONE, str, NULL, NULL, &len); 117 if (expected_string) { 118 ok_(__FILE__, line)(hr == S_FALSE, "GetString returned 0x%x, expected S_FALSE\n", hr); 119 if (hr != S_FALSE) { 120 /* don't try to allocate memory using uninitialized len */ 121 IQueryAssociations_Release(assoc); 122 return; 123 } 124 125 buffer = heap_alloc(len * sizeof(WCHAR)); 126 ok_(__FILE__, line)(buffer != NULL, "out of memory\n"); 127 hr = IQueryAssociations_GetString(assoc, 0, str, NULL, buffer, &len); 128 ok_(__FILE__, line)(hr == S_OK, "GetString returned 0x%x, expected S_OK\n", hr); 129 130 ok_(__FILE__, line)(lstrcmpW(buffer, expected_string) == 0, "GetString returned %s, expected %s\n", 131 wine_dbgstr_w(buffer), wine_dbgstr_w(expected_string)); 132 } else { 133 ok_(__FILE__, line)(FAILED(hr), "GetString returned 0x%x, expected failure\n", hr); 134 } 135 136 IQueryAssociations_Release(assoc); 137 heap_free(buffer); 138 } 139 140 static void test_IQueryAssociations_GetString(void) 141 { 142 static WCHAR test_extensionW[] = {'.','t','e','s','t',0}; 143 static WCHAR test_progidW[] = {'t','e','s','t','f','i','l','e',0}; 144 static WCHAR DefaultIconW[] = {'D','e','f','a','u','l','t','I','c','o','n',0}; 145 /* folder.ico, why not */ 146 static WCHAR test_iconW[] = {'s','h','e','l','l','3','2','.','d','l','l',',','1',0}; 147 HKEY test_extension_key; 148 HKEY test_progid_key; 149 HKEY test_defaulticon_key; 150 LRESULT r; 151 152 struct assoc_getstring_test *ptr = getstring_tests; 153 IQueryAssociations *assoc; 154 HRESULT hr; 155 DWORD len; 156 int i = 0; 157 158 r = RegCreateKeyExW(HKEY_CLASSES_ROOT, test_extensionW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &test_extension_key, NULL); 159 if (r == ERROR_ACCESS_DENIED) 160 { 161 win_skip("Not enough permissions to create a test key.\n"); 162 return; 163 } 164 165 ok(r == ERROR_SUCCESS, "RegCreateKeyExW(HKCR, \".test\") failed: 0x%lx\n", r); 166 r = RegSetValueExW(test_extension_key, NULL, 0, REG_SZ, (PBYTE)test_progidW, sizeof(test_progidW)); 167 ok(r == ERROR_SUCCESS, "RegSetValueExW(HKCR\\.test, NULL, \"testfile\") failed: 0x%lx\n", r); 168 169 /* adding progid key with no information should fail to return information */ 170 r = RegCreateKeyExW(HKEY_CLASSES_ROOT, test_progidW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &test_progid_key, NULL); 171 ok(r == ERROR_SUCCESS, "RegCreateKeyExW(HKCR, \"testfile\") failed: 0x%lx\n", r); 172 getstring_test(test_extensionW, NULL, ASSOCSTR_DEFAULTICON, NULL, __LINE__); 173 getstring_test(test_progidW, NULL, ASSOCSTR_DEFAULTICON, NULL, __LINE__); 174 getstring_test(NULL, test_progid_key, ASSOCSTR_DEFAULTICON, NULL, __LINE__); 175 176 /* adding information to the progid should return that information */ 177 r = RegCreateKeyExW(test_progid_key, DefaultIconW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &test_defaulticon_key, NULL); 178 ok(r == ERROR_SUCCESS, "RegCreateKeyExW(HKCR\\testfile\\DefaultIcon) failed: 0x%lx\n", r); 179 r = RegSetValueExW(test_defaulticon_key, NULL, 0, REG_SZ, (PBYTE)test_iconW, sizeof(test_iconW)); 180 ok(r == ERROR_SUCCESS, "RegSetValueExW(HKCR\\testfile\\DefaultIcon, NULL, \"folder.ico\") failed: 0x%lx\n", r); 181 getstring_test(test_extensionW, NULL, ASSOCSTR_DEFAULTICON, test_iconW, __LINE__); 182 getstring_test(test_progidW, NULL, ASSOCSTR_DEFAULTICON, test_iconW, __LINE__); 183 getstring_test(NULL, test_progid_key, ASSOCSTR_DEFAULTICON, test_iconW, __LINE__); 184 185 RegDeleteKeyW(test_progid_key, DefaultIconW); 186 RegDeleteKeyW(HKEY_CLASSES_ROOT, test_progidW); 187 RegDeleteKeyW(HKEY_CLASSES_ROOT, test_extensionW); 188 189 hr = CoCreateInstance(&CLSID_QueryAssociations, NULL, CLSCTX_INPROC_SERVER, &IID_IQueryAssociations, (void*)&assoc); 190 ok(hr == S_OK, "failed to create object, 0x%x\n", hr); 191 192 hr = IQueryAssociations_Init(assoc, ASSOCF_NONE, httpW, NULL, NULL); 193 ok(hr == S_OK, "Init failed, 0x%x\n", hr); 194 195 len = 0; 196 hr = IQueryAssociations_GetString(assoc, ASSOCF_NONE, ASSOCSTR_EXECUTABLE, NULL, NULL, &len); 197 ok(hr == S_FALSE, "got 0x%08x\n", hr); 198 ok(len > 0, "got wrong needed length, %d\n", len); 199 200 while (ptr->key) 201 { 202 WCHAR buffW[MAX_PATH]; 203 DWORD len; 204 205 hr = IQueryAssociations_Init(assoc, ASSOCF_NONE, ptr->key, NULL, NULL); 206 ok(hr == S_OK, "%d: Init failed, 0x%x\n", i, hr); 207 208 len = ptr->len; 209 buffW[0] = ptr->flags & ASSOCF_NOTRUNCATE ? 0x1 : 0; 210 hr = IQueryAssociations_GetString(assoc, ptr->flags, ptr->str, NULL, buffW, &len); 211 if (hr != ptr->hr) 212 ok(broken(hr == ptr->brokenhr), "%d: GetString failed, 0x%08x\n", i, hr); 213 else 214 { 215 ok(hr == ptr->hr, "%d: GetString failed, 0x%08x\n", i, hr); 216 ok(len > ptr->len, "%d: got needed length %d\n", i, len); 217 } 218 219 /* even with ASSOCF_NOTRUNCATE it's null terminated */ 220 if ((ptr->flags & ASSOCF_NOTRUNCATE) && (ptr->len > 0)) 221 ok(buffW[0] == 0 || broken(buffW[0] == 0x1) /* pre win7 */, "%d: got %x\n", i, buffW[0]); 222 223 if (!(ptr->flags & ASSOCF_NOTRUNCATE) && ptr->len && FAILED(ptr->hr)) 224 ok(buffW[0] != 0, "%d: got %x\n", i, buffW[0]); 225 226 i++; 227 ptr++; 228 } 229 230 IQueryAssociations_Release(assoc); 231 } 232 233 static void test_IQueryAssociations_Init(void) 234 { 235 IQueryAssociations *assoc; 236 HRESULT hr; 237 DWORD len; 238 239 hr = CoCreateInstance(&CLSID_QueryAssociations, NULL, CLSCTX_INPROC_SERVER, &IID_IQueryAssociations, (void*)&assoc); 240 ok(hr == S_OK, "failed to create object, 0x%x\n", hr); 241 242 hr = IQueryAssociations_Init(assoc, ASSOCF_NONE, NULL, NULL, NULL); 243 ok(hr == E_INVALIDARG, "Init failed, 0x%08x\n", hr); 244 245 hr = IQueryAssociations_Init(assoc, ASSOCF_NONE, httpW, NULL, NULL); 246 ok(hr == S_OK, "Init failed, 0x%08x\n", hr); 247 248 hr = IQueryAssociations_Init(assoc, ASSOCF_NONE, badW, NULL, NULL); 249 ok(hr == S_OK || broken(hr == S_FALSE) /* pre-vista */, "Init failed, 0x%08x\n", hr); 250 251 len = 0; 252 hr = IQueryAssociations_GetString(assoc, ASSOCF_NONE, ASSOCSTR_EXECUTABLE, NULL, NULL, &len); 253 ok(hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) || broken(hr == E_FAIL) /* pre-vista */, "got 0x%08x\n", hr); 254 255 IQueryAssociations_Release(assoc); 256 } 257 258 static void test_IApplicationAssociationRegistration_QueryCurrentDefault(IApplicationAssociationRegistration *appreg) 259 { 260 static const WCHAR emptyW[] = {0}; 261 static const WCHAR txtW[] = {'.','t','x','t',0}; 262 static const WCHAR spacetxtW[] = {' ','.','t','x','t',0}; 263 HRESULT hr; 264 LPWSTR assocprog = NULL; 265 266 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, emptyW, AT_URLPROTOCOL, AL_EFFECTIVE, &assocprog); 267 ok(hr == E_INVALIDARG, "got 0x%x\n", hr); 268 269 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, emptyW, AT_FILEEXTENSION, AL_EFFECTIVE, &assocprog); 270 ok(hr == E_INVALIDARG, "got 0x%x\n", hr); 271 272 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, spacetxtW, AT_FILEEXTENSION, AL_EFFECTIVE, &assocprog); 273 ok(hr == E_INVALIDARG || hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /*Win8*/, "got 0x%x\n", hr); 274 275 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, httpW, AT_URLPROTOCOL, AL_EFFECTIVE, NULL); 276 ok(hr == E_INVALIDARG, "got 0x%x\n", hr); 277 278 /* AT_FILEEXTENSION must start with a period */ 279 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, txtW, AT_FILEEXTENSION, AL_EFFECTIVE, &assocprog); 280 ok(hr == S_OK, "got 0x%x\n", hr); 281 trace("%s\n", wine_dbgstr_w(assocprog)); 282 CoTaskMemFree(assocprog); 283 284 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, emptyW, AT_STARTMENUCLIENT, AL_EFFECTIVE, &assocprog); 285 ok(hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION), "got 0x%x\n", hr); 286 287 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, emptyW, AT_MIMETYPE, AL_EFFECTIVE, &assocprog); 288 ok(hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION), "got 0x%x\n", hr); 289 290 hr = IApplicationAssociationRegistration_QueryCurrentDefault(appreg, httpW, AT_URLPROTOCOL, AL_EFFECTIVE, &assocprog); 291 todo_wine ok(hr == S_OK, "got 0x%x\n", hr); 292 trace("%s\n", wine_dbgstr_w(assocprog)); 293 294 CoTaskMemFree(assocprog); 295 } 296 297 START_TEST(assoc) 298 { 299 IQueryAssociations *qa; 300 IApplicationAssociationRegistration *appreg = NULL; 301 HRESULT hr; 302 303 CoInitialize(NULL); 304 305 /* this works since XP */ 306 hr = CoCreateInstance(&CLSID_QueryAssociations, NULL, CLSCTX_INPROC_SERVER, &IID_IQueryAssociations, (void*)&qa); 307 if (hr == S_OK) 308 { 309 test_IQueryAssociations_QueryInterface(); 310 test_IQueryAssociations_GetString(); 311 test_IQueryAssociations_Init(); 312 313 IQueryAssociations_Release(qa); 314 } 315 else 316 win_skip("IQueryAssociations not supported, 0x%x\n", hr); 317 318 /* this works since Vista */ 319 hr = CoCreateInstance(&CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC_SERVER, 320 &IID_IApplicationAssociationRegistration, (LPVOID*)&appreg); 321 if (hr == S_OK) 322 { 323 test_IApplicationAssociationRegistration_QueryInterface(appreg); 324 test_IApplicationAssociationRegistration_QueryCurrentDefault(appreg); 325 326 IApplicationAssociationRegistration_Release(appreg); 327 } 328 else 329 win_skip("IApplicationAssociationRegistration not supported: 0x%x\n", hr); 330 331 CoUninitialize(); 332 } 333