1 /* 2 * Unit tests to document shdocvw's 'Shell Instance Objects' features 3 * 4 * Copyright 2005 Michael Jung 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 /* At least since Windows 2000 it's possible to add FolderShortcut objects 22 * by creating some registry entries. Those objects, which refer to some 23 * point in the filesystem, can be registered in the shell namespace like other 24 * shell namespace extensions. Icons, names and filesystem location can be 25 * configured. This is documented at http://www.virtualplastic.net/html/ui_shell.html 26 * You can also google for a tool called "ShellObjectEditor" by "Tropical 27 * Technologies". This mechanism would be cool for wine, since we could 28 * map GNOME's virtual devices to FolderShortcuts and have them appear in the 29 * file dialogs. These unit tests are meant to document how this mechanism 30 * works on windows. 31 * 32 * Search MSDN for "Creating Shell Extensions with Shell Instance Objects" for 33 * more documentation.*/ 34 35 #include <stdarg.h> 36 37 #define COBJMACROS 38 39 #include "windef.h" 40 #include "winbase.h" 41 #include "winreg.h" 42 43 #include "initguid.h" 44 #include "shlobj.h" 45 #include "shobjidl.h" 46 #include "shlguid.h" 47 #include "ole2.h" 48 49 #include "wine/test.h" 50 51 /* The following definitions and helper functions are meant to make the de-/registration 52 * of the various necessary registry keys easier. */ 53 54 struct registry_value { 55 const char *szName; 56 const DWORD dwType; 57 const char *szValue; 58 const DWORD dwValue; 59 }; 60 61 #define REG_VALUE_ADDR(x) ((x->dwType==REG_SZ)?(const BYTE *)x->szValue:(const BYTE *)&x->dwValue) 62 #define REG_VALUE_SIZE(x) ((x->dwType==REG_SZ)?strlen(x->szValue)+1:sizeof(DWORD)) 63 64 struct registry_key { 65 const char *szName; 66 const struct registry_value *pValues; 67 const unsigned int cValues; 68 const struct registry_key *pSubKeys; 69 const unsigned int cSubKeys; 70 }; 71 72 static const struct registry_value ShellFolder_values[] = { 73 { "WantsFORPARSING", REG_SZ, "", 0 }, 74 { "Attributes", REG_DWORD, NULL, 0xF8000100 } 75 }; 76 77 static const struct registry_value Instance_values[] = { 78 { "CLSID", REG_SZ, "{0AFACED1-E828-11D1-9187-B532F1E9575D}", 0 } 79 }; 80 81 static const struct registry_value InitPropertyBag_values[] = { 82 { "Attributes", REG_DWORD, NULL, 0x00000015 }, 83 { "Target", REG_SZ, "C:\\", 0 } 84 }; 85 86 static const struct registry_key Instance_keys[] = { 87 { "InitPropertyBag", InitPropertyBag_values, 2, NULL, 0 } 88 }; 89 90 static const struct registry_value InProcServer32_values[] = { 91 { NULL, REG_SZ, "shdocvw.dll", 0 }, 92 { "ThreadingModel", REG_SZ, "Apartment", 0 } 93 }; 94 95 static const struct registry_value DefaultIcon_values[] = { 96 { NULL, REG_SZ,"shell32.dll,8", 0 } 97 }; 98 99 static const struct registry_key ShortcutCLSID_keys[] = { 100 { "DefaultIcon", DefaultIcon_values, 1, NULL, 0 }, 101 { "InProcServer32", InProcServer32_values, 2, NULL, 0 }, 102 { "Instance", Instance_values, 1, Instance_keys, 1 }, 103 { "ShellFolder", ShellFolder_values, 2, NULL, 0 } 104 }; 105 106 static const struct registry_value ShortcutCLSID_values[] = { 107 { NULL, REG_SZ, "WineTest", 0 } 108 }; 109 110 static const struct registry_key HKEY_CLASSES_ROOT_keys[] = { 111 { "CLSID\\{9B352EBF-2765-45C1-B4C6-85CC7F7ABC64}", ShortcutCLSID_values, 1, ShortcutCLSID_keys, 4} 112 }; 113 114 /* register_keys - helper function, which recursively creates the registry keys and values in 115 * parameter 'keys' in the registry under hRootKey. */ 116 static BOOL register_keys(HKEY hRootKey, const struct registry_key *keys, unsigned int numKeys) { 117 HKEY hKey; 118 unsigned int iKey, iValue; 119 120 for (iKey = 0; iKey < numKeys; iKey++) { 121 if (ERROR_SUCCESS == RegCreateKeyExA(hRootKey, keys[iKey].szName, 0, NULL, 0, 122 KEY_WRITE, NULL, &hKey, NULL)) 123 { 124 for (iValue = 0; iValue < keys[iKey].cValues; iValue++) { 125 const struct registry_value * value = &keys[iKey].pValues[iValue]; 126 if (ERROR_SUCCESS != RegSetValueExA(hKey, value->szName, 0, value->dwType, 127 REG_VALUE_ADDR(value), REG_VALUE_SIZE(value))) 128 { 129 RegCloseKey(hKey); 130 return FALSE; 131 } 132 } 133 134 if (!register_keys(hKey, keys[iKey].pSubKeys, keys[iKey].cSubKeys)) { 135 RegCloseKey(hKey); 136 return FALSE; 137 } 138 139 RegCloseKey(hKey); 140 } 141 } 142 143 return TRUE; 144 } 145 146 /* unregister_keys - clean up after register_keys */ 147 static void unregister_keys(HKEY hRootKey, const struct registry_key *keys, unsigned int numKeys) { 148 HKEY hKey; 149 unsigned int iKey; 150 151 for (iKey = 0; iKey < numKeys; iKey++) { 152 if (ERROR_SUCCESS == RegOpenKeyExA(hRootKey, keys[iKey].szName, 0, DELETE, &hKey)) { 153 unregister_keys(hKey, keys[iKey].pSubKeys, keys[iKey].cSubKeys); 154 RegCloseKey(hKey); 155 } 156 RegDeleteKeyA(hRootKey, keys[iKey].szName); 157 } 158 } 159 160 static void test_ShortcutFolder(void) { 161 LPSHELLFOLDER pDesktopFolder, pWineTestFolder; 162 IPersistFolder3 *pWineTestPersistFolder; 163 LPITEMIDLIST pidlWineTestFolder, pidlCurFolder; 164 HRESULT hr; 165 CLSID clsid; 166 const CLSID CLSID_WineTest = 167 { 0x9b352ebf, 0x2765, 0x45c1, { 0xb4, 0xc6, 0x85, 0xcc, 0x7f, 0x7a, 0xbc, 0x64 } }; 168 WCHAR wszWineTestFolder[] = { 169 ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-', 170 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 }; 171 172 /* First, we register all the necessary registry keys/values for our 'WineTest' 173 * shell object. */ 174 register_keys(HKEY_CLASSES_ROOT, HKEY_CLASSES_ROOT_keys, 1); 175 176 hr = SHGetDesktopFolder(&pDesktopFolder); 177 ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr); 178 if (FAILED(hr)) goto cleanup; 179 180 /* Convert the wszWineTestFolder string to an ITEMIDLIST. */ 181 hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL, 182 &pidlWineTestFolder, NULL); 183 todo_wine 184 { 185 ok (hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), 186 "Expected %08x, got %08x\n", HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), hr); 187 } 188 if (FAILED(hr)) { 189 IShellFolder_Release(pDesktopFolder); 190 goto cleanup; 191 } 192 193 /* FIXME: these tests are never run */ 194 195 /* Bind to a WineTest folder object. There has to be some support for this in shdocvw.dll. 196 * This isn't implemented in wine yet.*/ 197 hr = IShellFolder_BindToObject(pDesktopFolder, pidlWineTestFolder, NULL, &IID_IShellFolder, 198 (LPVOID*)&pWineTestFolder); 199 IShellFolder_Release(pDesktopFolder); 200 ILFree(pidlWineTestFolder); 201 ok (SUCCEEDED(hr), "IShellFolder::BindToObject(WineTestFolder) failed! hr = %08x\n", hr); 202 if (FAILED(hr)) goto cleanup; 203 204 hr = IShellFolder_QueryInterface(pWineTestFolder, &IID_IPersistFolder3, (LPVOID*)&pWineTestPersistFolder); 205 ok (SUCCEEDED(hr), "IShellFolder::QueryInterface(IPersistFolder3) failed! hr = %08x\n", hr); 206 IShellFolder_Release(pWineTestFolder); 207 if (FAILED(hr)) goto cleanup; 208 209 /* The resulting folder object has the FolderShortcut CLSID, instead of its own. */ 210 hr = IPersistFolder3_GetClassID(pWineTestPersistFolder, &clsid); 211 ok (SUCCEEDED(hr), "IPersist::GetClassID failed! hr = %08x\n", hr); 212 ok (IsEqualCLSID(&CLSID_FolderShortcut, &clsid), "GetClassId returned wrong CLSID!\n"); 213 214 pidlCurFolder = (LPITEMIDLIST)0xdeadbeef; 215 hr = IPersistFolder3_GetCurFolder(pWineTestPersistFolder, &pidlCurFolder); 216 ok (SUCCEEDED(hr), "IPersistFolder3::GetCurFolder failed! hr = %08x\n", hr); 217 ok (pidlCurFolder->mkid.cb == 20 && ((LPSHITEMID)((BYTE*)pidlCurFolder+20))->cb == 0 && 218 IsEqualCLSID(&CLSID_WineTest, (REFCLSID)((LPBYTE)pidlCurFolder+4)), 219 "GetCurFolder returned unexpected pidl!\n"); 220 221 ILFree(pidlCurFolder); 222 IPersistFolder3_Release(pWineTestPersistFolder); 223 224 cleanup: 225 unregister_keys(HKEY_CLASSES_ROOT, HKEY_CLASSES_ROOT_keys, 1); 226 } 227 228 START_TEST(shortcut) 229 { 230 OleInitialize(NULL); 231 test_ShortcutFolder(); 232 OleUninitialize(); 233 } 234