2 * Unit tests to document shdocvw's 'Shell Instance Objects' features
4 * Copyright 2005 Michael Jung
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.
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.
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
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
32 * Search MSDN for "Creating Shell Extensions with Shell Instance Objects" for
33 * more documentation.*/
37 #define WIN32_NO_STATUS
39 #define COM_NO_WINDOWS_H
47 //#include "initguid.h"
49 //#include "shobjidl.h"
50 //#include "shlguid.h"
53 #include <wine/test.h>
55 /* The following definitions and helper functions are meant to make the de-/registration
56 * of the various necessary registry keys easier. */
58 struct registry_value
{
65 #define REG_VALUE_ADDR(x) ((x->dwType==REG_SZ)?(const BYTE *)x->szValue:(const BYTE *)&x->dwValue)
66 #define REG_VALUE_SIZE(x) ((x->dwType==REG_SZ)?strlen(x->szValue)+1:sizeof(DWORD))
70 const struct registry_value
*pValues
;
71 const unsigned int cValues
;
72 const struct registry_key
*pSubKeys
;
73 const unsigned int cSubKeys
;
76 static const struct registry_value ShellFolder_values
[] = {
77 { "WantsFORPARSING", REG_SZ
, "", 0 },
78 { "Attributes", REG_DWORD
, NULL
, 0xF8000100 }
81 static const struct registry_value Instance_values
[] = {
82 { "CLSID", REG_SZ
, "{0AFACED1-E828-11D1-9187-B532F1E9575D}", 0 }
85 static const struct registry_value InitPropertyBag_values
[] = {
86 { "Attributes", REG_DWORD
, NULL
, 0x00000015 },
87 { "Target", REG_SZ
, "C:\\", 0 }
90 static const struct registry_key Instance_keys
[] = {
91 { "InitPropertyBag", InitPropertyBag_values
, 2, NULL
, 0 }
94 static const struct registry_value InProcServer32_values
[] = {
95 { NULL
, REG_SZ
, "shdocvw.dll", 0 },
96 { "ThreadingModel", REG_SZ
, "Apartment", 0 }
99 static const struct registry_value DefaultIcon_values
[] = {
100 { NULL
, REG_SZ
,"shell32.dll,8", 0 }
103 static const struct registry_key ShortcutCLSID_keys
[] = {
104 { "DefaultIcon", DefaultIcon_values
, 1, NULL
, 0 },
105 { "InProcServer32", InProcServer32_values
, 2, NULL
, 0 },
106 { "Instance", Instance_values
, 1, Instance_keys
, 1 },
107 { "ShellFolder", ShellFolder_values
, 2, NULL
, 0 }
110 static const struct registry_value ShortcutCLSID_values
[] = {
111 { NULL
, REG_SZ
, "WineTest", 0 }
114 static const struct registry_key HKEY_CLASSES_ROOT_keys
[] = {
115 { "CLSID\\{9B352EBF-2765-45C1-B4C6-85CC7F7ABC64}", ShortcutCLSID_values
, 1, ShortcutCLSID_keys
, 4}
118 /* register_keys - helper function, which recursively creates the registry keys and values in
119 * parameter 'keys' in the registry under hRootKey. */
120 static BOOL
register_keys(HKEY hRootKey
, const struct registry_key
*keys
, unsigned int numKeys
) {
122 unsigned int iKey
, iValue
;
124 for (iKey
= 0; iKey
< numKeys
; iKey
++) {
125 if (ERROR_SUCCESS
== RegCreateKeyExA(hRootKey
, keys
[iKey
].szName
, 0, NULL
, 0,
126 KEY_WRITE
, NULL
, &hKey
, NULL
))
128 for (iValue
= 0; iValue
< keys
[iKey
].cValues
; iValue
++) {
129 const struct registry_value
* value
= &keys
[iKey
].pValues
[iValue
];
130 if (ERROR_SUCCESS
!= RegSetValueExA(hKey
, value
->szName
, 0, value
->dwType
,
131 REG_VALUE_ADDR(value
), REG_VALUE_SIZE(value
)))
138 if (!register_keys(hKey
, keys
[iKey
].pSubKeys
, keys
[iKey
].cSubKeys
)) {
150 /* unregister_keys - clean up after register_keys */
151 static void unregister_keys(HKEY hRootKey
, const struct registry_key
*keys
, unsigned int numKeys
) {
155 for (iKey
= 0; iKey
< numKeys
; iKey
++) {
156 if (ERROR_SUCCESS
== RegOpenKeyExA(hRootKey
, keys
[iKey
].szName
, 0, DELETE
, &hKey
)) {
157 unregister_keys(hKey
, keys
[iKey
].pSubKeys
, keys
[iKey
].cSubKeys
);
160 RegDeleteKeyA(hRootKey
, keys
[iKey
].szName
);
164 static void test_ShortcutFolder(void) {
165 LPSHELLFOLDER pDesktopFolder
, pWineTestFolder
;
166 IPersistFolder3
*pWineTestPersistFolder
;
167 LPITEMIDLIST pidlWineTestFolder
, pidlCurFolder
;
170 const CLSID CLSID_WineTest
=
171 { 0x9b352ebf, 0x2765, 0x45c1, { 0xb4, 0xc6, 0x85, 0xcc, 0x7f, 0x7a, 0xbc, 0x64 } };
172 WCHAR wszWineTestFolder
[] = {
173 ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
174 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
176 /* First, we register all the necessary registry keys/values for our 'WineTest'
178 register_keys(HKEY_CLASSES_ROOT
, HKEY_CLASSES_ROOT_keys
, 1);
180 hr
= SHGetDesktopFolder(&pDesktopFolder
);
181 ok (SUCCEEDED(hr
), "SHGetDesktopFolder failed! hr = %08x\n", hr
);
182 if (FAILED(hr
)) goto cleanup
;
184 /* Convert the wszWineTestFolder string to an ITEMIDLIST. */
185 hr
= IShellFolder_ParseDisplayName(pDesktopFolder
, NULL
, NULL
, wszWineTestFolder
, NULL
,
186 &pidlWineTestFolder
, NULL
);
189 ok (hr
== HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER
),
190 "Expected %08x, got %08x\n", HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER
), hr
);
193 IShellFolder_Release(pDesktopFolder
);
197 /* FIXME: these tests are never run */
199 /* Bind to a WineTest folder object. There has to be some support for this in shdocvw.dll.
200 * This isn't implemented in wine yet.*/
201 hr
= IShellFolder_BindToObject(pDesktopFolder
, pidlWineTestFolder
, NULL
, &IID_IShellFolder
,
202 (LPVOID
*)&pWineTestFolder
);
203 IShellFolder_Release(pDesktopFolder
);
204 ILFree(pidlWineTestFolder
);
205 ok (SUCCEEDED(hr
), "IShellFolder::BindToObject(WineTestFolder) failed! hr = %08x\n", hr
);
206 if (FAILED(hr
)) goto cleanup
;
208 hr
= IShellFolder_QueryInterface(pWineTestFolder
, &IID_IPersistFolder3
, (LPVOID
*)&pWineTestPersistFolder
);
209 ok (SUCCEEDED(hr
), "IShellFolder::QueryInterface(IPersistFolder3) failed! hr = %08x\n", hr
);
210 IShellFolder_Release(pWineTestFolder
);
211 if (FAILED(hr
)) goto cleanup
;
213 /* The resulting folder object has the FolderShortcut CLSID, instead of its own. */
214 hr
= IPersistFolder3_GetClassID(pWineTestPersistFolder
, &clsid
);
215 ok (SUCCEEDED(hr
), "IPersist::GetClassID failed! hr = %08x\n", hr
);
216 ok (IsEqualCLSID(&CLSID_FolderShortcut
, &clsid
), "GetClassId returned wrong CLSID!\n");
218 pidlCurFolder
= (LPITEMIDLIST
)0xdeadbeef;
219 hr
= IPersistFolder3_GetCurFolder(pWineTestPersistFolder
, &pidlCurFolder
);
220 ok (SUCCEEDED(hr
), "IPersistFolder3::GetCurFolder failed! hr = %08x\n", hr
);
221 ok (pidlCurFolder
->mkid
.cb
== 20 && ((LPSHITEMID
)((BYTE
*)pidlCurFolder
+20))->cb
== 0 &&
222 IsEqualCLSID(&CLSID_WineTest
, (REFCLSID
)((LPBYTE
)pidlCurFolder
+4)),
223 "GetCurFolder returned unexpected pidl!\n");
225 ILFree(pidlCurFolder
);
226 IPersistFolder3_Release(pWineTestPersistFolder
);
229 unregister_keys(HKEY_CLASSES_ROOT
, HKEY_CLASSES_ROOT_keys
, 1);
235 test_ShortcutFolder();