| /* |
| * Unit tests to document shdocvw's 'Shell Instance Objects' features |
| * |
| * Copyright 2005 Michael Jung |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| /* At least since Windows 2000 it's possible to add FolderShortcut objects |
| * by creating some registry entries. Those objects, which refer to some |
| * point in the filesystem, can be registered in the shell namespace like other |
| * shell namespace extensions. Icons, names and filesystem location can be |
| * configured. This is documented at http://www.virtualplastic.net/html/ui_shell.html |
| * You can also google for a tool called "ShellObjectEditor" by "Tropical |
| * Technologies". This mechanism would be cool for wine, since we could |
| * map GNOME's virtual devices to FolderShortcuts and have them appear in the |
| * file dialogs. These unit tests are meant to document how this mechanism |
| * works on windows. |
| * |
| * Search MSDN for "Creating Shell Extensions with Shell Instance Objects" for |
| * more documentation.*/ |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winreg.h" |
| |
| #include "initguid.h" |
| #include "shlobj.h" |
| #include "shobjidl.h" |
| #include "shlguid.h" |
| #include "ole2.h" |
| |
| #include "wine/test.h" |
| |
| /* The following definitions and helper functions are meant to make the de-/registration |
| * of the various necessary registry keys easier. */ |
| |
| struct registry_value { |
| const char *szName; |
| const DWORD dwType; |
| const char *szValue; |
| const DWORD dwValue; |
| }; |
| |
| #define REG_VALUE_ADDR(x) ((x->dwType==REG_SZ)?(const BYTE *)x->szValue:(const BYTE *)&x->dwValue) |
| #define REG_VALUE_SIZE(x) ((x->dwType==REG_SZ)?strlen(x->szValue)+1:sizeof(DWORD)) |
| |
| struct registry_key { |
| const char *szName; |
| const struct registry_value *pValues; |
| const unsigned int cValues; |
| const struct registry_key *pSubKeys; |
| const unsigned int cSubKeys; |
| }; |
| |
| static const struct registry_value ShellFolder_values[] = { |
| { "WantsFORPARSING", REG_SZ, "", 0 }, |
| { "Attributes", REG_DWORD, NULL, 0xF8000100 } |
| }; |
| |
| static const struct registry_value Instance_values[] = { |
| { "CLSID", REG_SZ, "{0AFACED1-E828-11D1-9187-B532F1E9575D}", 0 } |
| }; |
| |
| static const struct registry_value InitPropertyBag_values[] = { |
| { "Attributes", REG_DWORD, NULL, 0x00000015 }, |
| { "Target", REG_SZ, "C:\\", 0 } |
| }; |
| |
| static const struct registry_key Instance_keys[] = { |
| { "InitPropertyBag", InitPropertyBag_values, 2, NULL, 0 } |
| }; |
| |
| static const struct registry_value InProcServer32_values[] = { |
| { NULL, REG_SZ, "shdocvw.dll", 0 }, |
| { "ThreadingModel", REG_SZ, "Apartment", 0 } |
| }; |
| |
| static const struct registry_value DefaultIcon_values[] = { |
| { NULL, REG_SZ,"shell32.dll,8", 0 } |
| }; |
| |
| static const struct registry_key ShortcutCLSID_keys[] = { |
| { "DefaultIcon", DefaultIcon_values, 1, NULL, 0 }, |
| { "InProcServer32", InProcServer32_values, 2, NULL, 0 }, |
| { "Instance", Instance_values, 1, Instance_keys, 1 }, |
| { "ShellFolder", ShellFolder_values, 2, NULL, 0 } |
| }; |
| |
| static const struct registry_value ShortcutCLSID_values[] = { |
| { NULL, REG_SZ, "WineTest", 0 } |
| }; |
| |
| static const struct registry_key HKEY_CLASSES_ROOT_keys[] = { |
| { "CLSID\\{9B352EBF-2765-45C1-B4C6-85CC7F7ABC64}", ShortcutCLSID_values, 1, ShortcutCLSID_keys, 4} |
| }; |
| |
| /* register_keys - helper function, which recursively creates the registry keys and values in |
| * parameter 'keys' in the registry under hRootKey. */ |
| static BOOL register_keys(HKEY hRootKey, const struct registry_key *keys, unsigned int numKeys) { |
| HKEY hKey; |
| unsigned int iKey, iValue; |
| |
| for (iKey = 0; iKey < numKeys; iKey++) { |
| if (ERROR_SUCCESS == RegCreateKeyExA(hRootKey, keys[iKey].szName, 0, NULL, 0, |
| KEY_WRITE, NULL, &hKey, NULL)) |
| { |
| for (iValue = 0; iValue < keys[iKey].cValues; iValue++) { |
| const struct registry_value * value = &keys[iKey].pValues[iValue]; |
| if (ERROR_SUCCESS != RegSetValueExA(hKey, value->szName, 0, value->dwType, |
| REG_VALUE_ADDR(value), REG_VALUE_SIZE(value))) |
| { |
| RegCloseKey(hKey); |
| return FALSE; |
| } |
| } |
| |
| if (!register_keys(hKey, keys[iKey].pSubKeys, keys[iKey].cSubKeys)) { |
| RegCloseKey(hKey); |
| return FALSE; |
| } |
| |
| RegCloseKey(hKey); |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| /* unregister_keys - clean up after register_keys */ |
| static void unregister_keys(HKEY hRootKey, const struct registry_key *keys, unsigned int numKeys) { |
| HKEY hKey; |
| unsigned int iKey; |
| |
| for (iKey = 0; iKey < numKeys; iKey++) { |
| if (ERROR_SUCCESS == RegOpenKeyExA(hRootKey, keys[iKey].szName, 0, DELETE, &hKey)) { |
| unregister_keys(hKey, keys[iKey].pSubKeys, keys[iKey].cSubKeys); |
| RegCloseKey(hKey); |
| } |
| RegDeleteKeyA(hRootKey, keys[iKey].szName); |
| } |
| } |
| |
| static void test_ShortcutFolder(void) { |
| LPSHELLFOLDER pDesktopFolder, pWineTestFolder; |
| IPersistFolder3 *pWineTestPersistFolder; |
| LPITEMIDLIST pidlWineTestFolder, pidlCurFolder; |
| HRESULT hr; |
| CLSID clsid; |
| const CLSID CLSID_WineTest = |
| { 0x9b352ebf, 0x2765, 0x45c1, { 0xb4, 0xc6, 0x85, 0xcc, 0x7f, 0x7a, 0xbc, 0x64 } }; |
| WCHAR wszWineTestFolder[] = { |
| ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-', |
| 'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 }; |
| |
| /* First, we register all the necessary registry keys/values for our 'WineTest' |
| * shell object. */ |
| register_keys(HKEY_CLASSES_ROOT, HKEY_CLASSES_ROOT_keys, 1); |
| |
| hr = SHGetDesktopFolder(&pDesktopFolder); |
| ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr); |
| if (FAILED(hr)) goto cleanup; |
| |
| /* Convert the wszWineTestFolder string to an ITEMIDLIST. */ |
| hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL, |
| &pidlWineTestFolder, NULL); |
| todo_wine |
| { |
| ok (hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), |
| "Expected %08x, got %08x\n", HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), hr); |
| } |
| if (FAILED(hr)) { |
| IShellFolder_Release(pDesktopFolder); |
| goto cleanup; |
| } |
| |
| /* FIXME: these tests are never run */ |
| |
| /* Bind to a WineTest folder object. There has to be some support for this in shdocvw.dll. |
| * This isn't implemented in wine yet.*/ |
| hr = IShellFolder_BindToObject(pDesktopFolder, pidlWineTestFolder, NULL, &IID_IShellFolder, |
| (LPVOID*)&pWineTestFolder); |
| IShellFolder_Release(pDesktopFolder); |
| ILFree(pidlWineTestFolder); |
| ok (SUCCEEDED(hr), "IShellFolder::BindToObject(WineTestFolder) failed! hr = %08x\n", hr); |
| if (FAILED(hr)) goto cleanup; |
| |
| hr = IShellFolder_QueryInterface(pWineTestFolder, &IID_IPersistFolder3, (LPVOID*)&pWineTestPersistFolder); |
| ok (SUCCEEDED(hr), "IShellFolder::QueryInterface(IPersistFolder3) failed! hr = %08x\n", hr); |
| IShellFolder_Release(pWineTestFolder); |
| if (FAILED(hr)) goto cleanup; |
| |
| /* The resulting folder object has the FolderShortcut CLSID, instead of its own. */ |
| hr = IPersistFolder3_GetClassID(pWineTestPersistFolder, &clsid); |
| ok (SUCCEEDED(hr), "IPersist::GetClassID failed! hr = %08x\n", hr); |
| ok (IsEqualCLSID(&CLSID_FolderShortcut, &clsid), "GetClassId returned wrong CLSID!\n"); |
| |
| pidlCurFolder = (LPITEMIDLIST)0xdeadbeef; |
| hr = IPersistFolder3_GetCurFolder(pWineTestPersistFolder, &pidlCurFolder); |
| ok (SUCCEEDED(hr), "IPersistFolder3::GetCurFolder failed! hr = %08x\n", hr); |
| ok (pidlCurFolder->mkid.cb == 20 && ((LPSHITEMID)((BYTE*)pidlCurFolder+20))->cb == 0 && |
| IsEqualCLSID(&CLSID_WineTest, (REFCLSID)((LPBYTE)pidlCurFolder+4)), |
| "GetCurFolder returned unexpected pidl!\n"); |
| |
| ILFree(pidlCurFolder); |
| IPersistFolder3_Release(pWineTestPersistFolder); |
| |
| cleanup: |
| unregister_keys(HKEY_CLASSES_ROOT, HKEY_CLASSES_ROOT_keys, 1); |
| } |
| |
| START_TEST(shortcut) |
| { |
| OleInitialize(NULL); |
| test_ShortcutFolder(); |
| OleUninitialize(); |
| } |