diff --git a/rostests/winetests/shell32/CMakeLists.txt b/rostests/winetests/shell32/CMakeLists.txt index 0433f2a7cdf..e69d17b2a6c 100644 --- a/rostests/winetests/shell32/CMakeLists.txt +++ b/rostests/winetests/shell32/CMakeLists.txt @@ -8,10 +8,16 @@ add_definitions( list(APPEND SOURCE appbar.c autocomplete.c + brsfolder.c + ebrowser.c generated.c progman_dde.c + recyclebin.c + shelldispatch.c shelllink.c + shellole.c shellpath.c + shfldr_special.c shlexec.c shlfileop.c shlfolder.c @@ -19,11 +25,10 @@ list(APPEND SOURCE string.c systray.c testlist.c - shfldr_special.c rsrc.rc) add_executable(shell32_winetest ${SOURCE}) target_link_libraries(shell32_winetest wine uuid) set_module_type(shell32_winetest win32cui) -add_importlibs(shell32_winetest shlwapi gdi32 shell32 ole32 oleaut32 user32 advapi32 msvcrt kernel32 ntdll) +add_importlibs(shell32_winetest shell32 ole32 oleaut32 user32 advapi32 msvcrt kernel32 ntdll) add_cd_file(TARGET shell32_winetest DESTINATION reactos/bin FOR all) diff --git a/rostests/winetests/shell32/autocomplete.c b/rostests/winetests/shell32/autocomplete.c index d4a84aeeeba..5ff88479c1a 100644 --- a/rostests/winetests/shell32/autocomplete.c +++ b/rostests/winetests/shell32/autocomplete.c @@ -33,11 +33,115 @@ static HWND hMainWnd, hEdit; static HINSTANCE hinst; static int killfocus_count; +static void test_invalid_init(void) +{ + HRESULT hr; + IAutoComplete *ac; + IUnknown *acSource; + HWND edit_control; + + /* AutoComplete instance */ + hr = CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, + &IID_IAutoComplete, (void **)&ac); + if (hr == REGDB_E_CLASSNOTREG) + { + win_skip("CLSID_AutoComplete is not registered\n"); + return; + } + ok(hr == S_OK, "no IID_IAutoComplete (0x%08x)\n", hr); + + /* AutoComplete source */ + hr = CoCreateInstance(&CLSID_ACLMulti, NULL, CLSCTX_INPROC_SERVER, + &IID_IACList, (void **)&acSource); + if (hr == REGDB_E_CLASSNOTREG) + { + win_skip("CLSID_ACLMulti is not registered\n"); + IAutoComplete_Release(ac); + return; + } + ok(hr == S_OK, "no IID_IACList (0x%08x)\n", hr); + + edit_control = CreateWindowExA(0, "EDIT", "Some text", 0, 10, 10, 300, 300, + hMainWnd, NULL, hinst, NULL); + ok(edit_control != NULL, "Can't create edit control\n"); + + /* The refcount of acSource would be incremented on older Windows. */ + hr = IAutoComplete_Init(ac, NULL, acSource, NULL, NULL); + ok(hr == E_INVALIDARG || + broken(hr == S_OK), /* Win2k/XP/Win2k3 */ + "Init returned 0x%08x\n", hr); + if (hr == E_INVALIDARG) + { + LONG ref; + + IUnknown_AddRef(acSource); + ref = IUnknown_Release(acSource); + ok(ref == 1, "Expected AutoComplete source refcount to be 1, got %d\n", ref); + } + +if (0) +{ + /* Older Windows versions never check the window handle, while newer + * versions only check for NULL. Subsequent attempts to initialize the + * object after this call succeeds would fail, because initialization + * state is determined by whether a non-NULL window handle is stored. */ + hr = IAutoComplete_Init(ac, (HWND)0xdeadbeef, acSource, NULL, NULL); + ok(hr == S_OK, "Init returned 0x%08x\n", hr); + + /* Tests crash on older Windows. */ + hr = IAutoComplete_Init(ac, NULL, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Init returned 0x%08x\n", hr); + + hr = IAutoComplete_Init(ac, edit_control, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Init returned 0x%08x\n", hr); +} + + /* bind to edit control */ + hr = IAutoComplete_Init(ac, edit_control, acSource, NULL, NULL); + ok(hr == S_OK, "Init returned 0x%08x\n", hr); + + /* try invalid parameters after successful initialization .*/ + hr = IAutoComplete_Init(ac, NULL, NULL, NULL, NULL); + ok(hr == E_INVALIDARG || + hr == E_FAIL, /* Win2k/XP/Win2k3 */ + "Init returned 0x%08x\n", hr); + + hr = IAutoComplete_Init(ac, NULL, acSource, NULL, NULL); + ok(hr == E_INVALIDARG || + hr == E_FAIL, /* Win2k/XP/Win2k3 */ + "Init returned 0x%08x\n", hr); + + hr = IAutoComplete_Init(ac, edit_control, NULL, NULL, NULL); + ok(hr == E_INVALIDARG || + hr == E_FAIL, /* Win2k/XP/Win2k3 */ + "Init returned 0x%08x\n", hr); + + /* try initializing twice on the same control */ + hr = IAutoComplete_Init(ac, edit_control, acSource, NULL, NULL); + ok(hr == E_FAIL, "Init returned 0x%08x\n", hr); + + /* try initializing with a different control */ + hr = IAutoComplete_Init(ac, hEdit, acSource, NULL, NULL); + ok(hr == E_FAIL, "Init returned 0x%08x\n", hr); + + DestroyWindow(edit_control); + + /* try initializing with a different control after + * destroying the original initialization control */ + hr = IAutoComplete_Init(ac, hEdit, acSource, NULL, NULL); + ok(hr == E_UNEXPECTED || + hr == E_FAIL, /* Win2k/XP/Win2k3 */ + "Init returned 0x%08x\n", hr); + + IUnknown_Release(acSource); + IAutoComplete_Release(ac); +} static IAutoComplete *test_init(void) { HRESULT r; - IAutoComplete* ac; + IAutoComplete *ac; IUnknown *acSource; + LONG_PTR user_data; /* AutoComplete instance */ r = CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, @@ -55,18 +159,20 @@ static IAutoComplete *test_init(void) if (r == REGDB_E_CLASSNOTREG) { win_skip("CLSID_ACLMulti is not registered\n"); + IAutoComplete_Release(ac); return NULL; } ok(r == S_OK, "no IID_IACList (0x%08x)\n", r); -if (0) -{ - /* crashes on native */ - r = IAutoComplete_Init(ac, hEdit, NULL, NULL, NULL); -} + user_data = GetWindowLongPtrA(hEdit, GWLP_USERDATA); + ok(user_data == 0, "Expected the edit control user data to be zero\n"); + /* bind to edit control */ r = IAutoComplete_Init(ac, hEdit, acSource, NULL, NULL); - ok(r == S_OK, "Init failed (0x%08x)\n", r); + ok(r == S_OK, "Init returned 0x%08x\n", r); + + user_data = GetWindowLongPtrA(hEdit, GWLP_USERDATA); + ok(user_data == 0, "Expected the edit control user data to be zero\n"); IUnknown_Release(acSource); @@ -134,6 +240,7 @@ START_TEST(autocomplete) ok(hMainWnd != NULL, "Failed to create parent window. Tests aborted.\n"); if (!hMainWnd) return; + test_invalid_init(); ac = test_init(); if (!ac) goto cleanup; diff --git a/rostests/winetests/shell32/brsfolder.c b/rostests/winetests/shell32/brsfolder.c new file mode 100644 index 00000000000..7b87967f46b --- /dev/null +++ b/rostests/winetests/shell32/brsfolder.c @@ -0,0 +1,365 @@ +/* + * Unit test of the SHBrowseForFolder function. + * + * Copyright 2009-2010 Michael Mc Donnell + * Copyright 2011 André Hentschel + * + * 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 + */ +#include +#include +#include +#include + +#include "wine/test.h" +#define IDD_MAKENEWFOLDER 0x3746 /* From "../shresdef.h" */ +#define TIMER_WAIT_MS 50 /* Should be long enough for slow systems */ + +static const char new_folder_name[] = "foo"; +static LPITEMIDLIST selected_folder_pidl; + +/* + * Returns the number of folders in a folder. + */ +static int get_number_of_folders(LPCSTR path) +{ + int number_of_folders = 0; + char path_search_string[MAX_PATH]; + WIN32_FIND_DATA find_data; + HANDLE find_handle; + + strncpy(path_search_string, path, MAX_PATH); + strncat(path_search_string, "*", 1); + + find_handle = FindFirstFile(path_search_string, &find_data); + if (find_handle == INVALID_HANDLE_VALUE) + return -1; + + do + { + if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + strcmp(find_data.cFileName, ".") != 0 && + strcmp(find_data.cFileName, "..") != 0) + { + number_of_folders++; + } + } + while (FindNextFile(find_handle, &find_data) != 0); + + FindClose(find_handle); + return number_of_folders; +} + +static BOOL does_folder_or_file_exist(LPCSTR folder_path) +{ + DWORD file_attributes = GetFileAttributesA(folder_path); + return !(file_attributes == INVALID_FILE_ATTRIBUTES); +} + +/* + * Timer callback used by test_click_make_new_folder_button. It simulates a user + * making a new folder and calling it "foo". + */ +static void CALLBACK make_new_folder_timer_callback(HWND hwnd, UINT uMsg, + UINT_PTR idEvent, DWORD dwTime) +{ + static int step = 0; + + switch (step++) + { + case 0: + /* Click "Make New Folder" button */ + PostMessage(hwnd, WM_COMMAND, IDD_MAKENEWFOLDER, 0); + break; + case 1: + /* Set the new folder name to foo by replacing text in edit control */ + SendMessage(GetFocus(), EM_REPLACESEL, 0, (LPARAM) new_folder_name); + SetFocus(hwnd); + break; + case 2: + /* + * The test does not trigger the correct state on Windows. This results + * in the new folder pidl not being returned. The result is as + * expected if the same steps are done manually. + * Sending the down key selects the new folder again which sets the + * correct state. This ensures that the correct pidl is returned. + */ + keybd_event(VK_DOWN, 0, 0, 0); + break; + case 3: + keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0); + break; + case 4: + KillTimer(hwnd, idEvent); + /* Close dialog box */ + SendMessage(hwnd, WM_COMMAND, IDOK, 0); + break; + default: + break; + } +} + +/* + * Callback used by test_click_make_new_folder_button. It sets up a timer to + * simulate user input. + */ +static int CALLBACK create_new_folder_callback(HWND hwnd, UINT uMsg, + LPARAM lParam, LPARAM lpData) +{ + switch (uMsg) + { + case BFFM_INITIALIZED: + /* User input is simulated in timer callback */ + SetTimer(hwnd, 0, TIMER_WAIT_MS, make_new_folder_timer_callback); + return TRUE; + default: + return FALSE; + } +} + +/* + * Tests if clicking the "Make New Folder" button in a SHBrowseForFolder + * dialog box creates a new folder. (Bug 17986). + * + * Here follows a description of what happens on W2K,Vista, W2K8, W7: + * When the "Make New Folder" button is clicked a new folder is created and + * inserted into the tree. The folder is given a default name that depends on + * the locale (e.g. "New Folder"). The folder name is selected and the dialog + * waits for the user to type in a new name. The folder is renamed when the user + * types in a name and presses enter. + * + * Note that XP and W2K3 do not select the folder name or wait for the user + * to type in a new folder name. This behavior is considered broken as most + * users would like to give the folder a name after creating it. The fact that + * it originally waited for the user to type in a new folder name(W2K), and then + * again was changed back wait for the new folder name(Vista, W2K8, W7), + * indicates that MS also believes that it was broken in XP and W2K3. + */ +static void test_click_make_new_folder_button(void) +{ + HRESULT resCoInit, hr; + BROWSEINFO bi; + LPITEMIDLIST pidl = NULL; + LPITEMIDLIST test_folder_pidl; + IShellFolder *test_folder_object; + char test_folder_path[MAX_PATH]; + WCHAR test_folder_pathW[MAX_PATH]; + CHAR new_folder_path[MAX_PATH]; + CHAR new_folder_pidl_path[MAX_PATH]; + char selected_folder[MAX_PATH]; + const CHAR title[] = "test_click_make_new_folder_button"; + int number_of_folders = -1; + SHFILEOPSTRUCT shfileop; + + if (does_folder_or_file_exist(title)) + { + skip("The test folder already exists.\n"); + return; + } + + /* Must initialize COM if using the NEWDIAlOGSTYLE according to MSDN. */ + resCoInit = CoInitialize(NULL); + if(!(resCoInit == S_OK || resCoInit == S_FALSE)) + { + skip("COM could not be initialized %u\n", GetLastError()); + return; + } + + /* Leave room for concatenating title, two backslashes, and an extra NULL. */ + if (!GetCurrentDirectoryA(MAX_PATH-strlen(title)-3, test_folder_path)) + { + skip("GetCurrentDirectoryA failed %u\n", GetLastError()); + } + strncat(test_folder_path, "\\", 1); + strncat(test_folder_path, title, MAX_PATH-1); + strncat(test_folder_path, "\\", 1); + + /* Avoid conflicts by creating a test folder. */ + if (!CreateDirectoryA(title, NULL)) + { + skip("CreateDirectoryA failed %u\n", GetLastError()); + return; + } + + /* Initialize browse info struct for SHBrowseForFolder */ + bi.hwndOwner = NULL; + bi.pszDisplayName = (LPTSTR) &selected_folder; + bi.lpszTitle = (LPTSTR) title; + bi.ulFlags = BIF_NEWDIALOGSTYLE; + bi.lpfn = create_new_folder_callback; + /* Use test folder as the root folder for dialog box */ + MultiByteToWideChar(CP_UTF8, 0, test_folder_path, MAX_PATH, + test_folder_pathW, MAX_PATH); + hr = SHGetDesktopFolder(&test_folder_object); + ok (SUCCEEDED(hr), "SHGetDesktopFolder failed with hr 0x%08x\n", hr); + if (FAILED(hr)) { + skip("SHGetDesktopFolder failed - skipping\n"); + return; + } + test_folder_object->lpVtbl->ParseDisplayName(test_folder_object, NULL, NULL, + test_folder_pathW, 0UL, &test_folder_pidl, 0UL); + bi.pidlRoot = test_folder_pidl; + + /* Display dialog box and let callback click the buttons */ + pidl = SHBrowseForFolder(&bi); + + number_of_folders = get_number_of_folders(test_folder_path); + ok(number_of_folders == 1 || broken(number_of_folders == 0) /* W95, W98 */, + "Clicking \"Make New Folder\" button did not result in a new folder.\n"); + + /* There should be a new folder foo inside the test folder */ + strcpy(new_folder_path, test_folder_path); + strcat(new_folder_path, new_folder_name); + ok(does_folder_or_file_exist(new_folder_path) + || broken(!does_folder_or_file_exist(new_folder_path)) /* W95, W98, XP, W2K3 */, + "The new folder did not get the name %s\n", new_folder_name); + + /* Dialog should return a pidl pointing to the new folder */ + ok(SHGetPathFromIDListA(pidl, new_folder_pidl_path), + "SHGetPathFromIDList failed for new folder.\n"); + ok(strcmp(new_folder_path, new_folder_pidl_path) == 0 + || broken(strcmp(new_folder_path, new_folder_pidl_path) != 0) /* earlier than Vista */, + "SHBrowseForFolder did not return the pidl for the new folder. " + "Expected '%s' got '%s'\n", new_folder_path, new_folder_pidl_path); + + /* Remove test folder and any subfolders created in this test */ + shfileop.hwnd = NULL; + shfileop.wFunc = FO_DELETE; + /* Path must be double NULL terminated */ + test_folder_path[strlen(test_folder_path)+1] = '\0'; + shfileop.pFrom = test_folder_path; + shfileop.pTo = NULL; + shfileop.fFlags = FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT; + SHFileOperation(&shfileop); + + if (pidl) + CoTaskMemFree(pidl); + if (test_folder_pidl) + CoTaskMemFree(test_folder_pidl); + test_folder_object->lpVtbl->Release(test_folder_object); + + CoUninitialize(); +} + + +/* + * Callback used by test_selection. + */ +static int CALLBACK selection_callback(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + DWORD ret; + + switch (uMsg) + { + case BFFM_INITIALIZED: + /* test with zero values */ + ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 0, 0); + ok(!ret, "SendMessage returned: %u\n", ret); + ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 0, 0); + ok(!ret, "SendMessage returned: %u\n", ret); + + ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 1, 0); + ok(!ret, "SendMessage returned: %u\n", ret); + + if(0) + { + /* Crashes on NT4 */ + ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 1, 0); + ok(!ret, "SendMessage returned: %u\n", ret); + } + + ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 0, (LPARAM)selected_folder_pidl); + ok(!ret, "SendMessage returned: %u\n", ret); + ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 0, (LPARAM)selected_folder_pidl); + ok(!ret, "SendMessage returned: %u\n", ret); + + ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 1, (LPARAM)selected_folder_pidl); + ok(!ret, "SendMessage returned: %u\n", ret); + ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)selected_folder_pidl); + ok(!ret, "SendMessage returned: %u\n", ret); + + ret = SendMessage(hwnd, BFFM_SETSELECTIONA, 1, (LPARAM)new_folder_name); + ok(!ret, "SendMessage returned: %u\n", ret); + ret = SendMessage(hwnd, BFFM_SETSELECTIONW, 1, (LPARAM)new_folder_name); + ok(!ret, "SendMessage returned: %u\n", ret); + + SendMessage(hwnd, WM_COMMAND, IDOK, 0); + return TRUE; + default: + return FALSE; + } +} + +static void test_selection(void) +{ + HRESULT resCoInit, hr; + BROWSEINFO bi; + LPITEMIDLIST pidl = NULL; + IShellFolder *desktop_object; + WCHAR selected_folderW[MAX_PATH]; + const CHAR title[] = "test_selection"; + + resCoInit = CoInitialize(NULL); + if(!(resCoInit == S_OK || resCoInit == S_FALSE)) + { + skip("COM could not be initialized %u\n", GetLastError()); + return; + } + + if (!GetCurrentDirectoryW(MAX_PATH, selected_folderW)) + { + skip("GetCurrentDirectoryW failed %u\n", GetLastError()); + } + + /* Initialize browse info struct for SHBrowseForFolder */ + bi.hwndOwner = NULL; + bi.pszDisplayName = NULL; + bi.lpszTitle = (LPTSTR) title; + bi.lpfn = selection_callback; + + hr = SHGetDesktopFolder(&desktop_object); + ok (SUCCEEDED(hr), "SHGetDesktopFolder failed with hr 0x%08x\n", hr); + if (FAILED(hr)) { + skip("SHGetDesktopFolder failed - skipping\n"); + return; + } + desktop_object->lpVtbl->ParseDisplayName(desktop_object, NULL, NULL, + selected_folderW, 0UL, &selected_folder_pidl, 0UL); + bi.pidlRoot = selected_folder_pidl; + + /* test without flags */ + bi.ulFlags = 0; + pidl = SHBrowseForFolder(&bi); + + if (pidl) + CoTaskMemFree(pidl); + + /* test with flag */ + bi.ulFlags = BIF_NEWDIALOGSTYLE; + pidl = SHBrowseForFolder(&bi); + + if (pidl) + CoTaskMemFree(pidl); + + desktop_object->lpVtbl->Release(desktop_object); + + CoUninitialize(); +} + +START_TEST(brsfolder) +{ + test_click_make_new_folder_button(); + test_selection(); +} diff --git a/rostests/winetests/shell32/ebrowser.c b/rostests/winetests/shell32/ebrowser.c new file mode 100644 index 00000000000..8149f8f95a4 --- /dev/null +++ b/rostests/winetests/shell32/ebrowser.c @@ -0,0 +1,1799 @@ +/* + * Unit tests for the Explorer Browser control + * + * Copyright 2010 David Hedberg + * + * 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 + */ + +#include + +#define COBJMACROS +#define CONST_VTABLE + +#include "shlobj.h" +#include "shlwapi.h" + +#include "wine/test.h" + +#include "initguid.h" +#include "mshtml.h" + +/********************************************************************** + * Some IIDs for test_SetSite. + */ +DEFINE_GUID(IID_IBrowserSettings, 0xDD1E21CC, 0xE2C7, 0x402C, 0xBF,0x05, 0x10,0x32,0x8D,0x3F,0x6B,0xAD); +DEFINE_GUID(IID_IShellBrowserService, 0xDFBC7E30, 0xF9E5, 0x455F, 0x88,0xF8, 0xFA,0x98,0xC1,0xE4,0x94,0xCA); +DEFINE_GUID(IID_IShellTaskScheduler, 0x6CCB7BE0, 0x6807, 0x11D0, 0xB8,0x10, 0x00,0xC0,0x4F,0xD7,0x06,0xEC); +DEFINE_GUID(IID_IBrowserWithActivationNotification, + 0x6DB89131, 0x7B4C, 0x4E1C, 0x8B,0x01, 0x5D,0x31,0x2C,0x9C,0x73,0x88); +DEFINE_GUID(IID_ILayoutModifier, 0x90B4135A, 0x95BA, 0x46EA, 0x8C,0xAA, 0xE0,0x5B,0x45,0xCD,0x80,0x1E); +DEFINE_GUID(CLSID_Desktop, 0x00021400, 0x0000, 0x0000, 0xC0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46); +DEFINE_GUID(IID_IFileDialogPrivate, 0xAC92FFC5, 0xF0E9, 0x455A, 0x90,0x6B, 0x4A,0x83,0xE7,0x4A,0x80,0x3B); +DEFINE_GUID(IID_IWebbrowserApp, 0x0002df05, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46); +DEFINE_GUID(IID_IBrowserSettings_Vista, 0xF81B80BC, 0x29D1, 0x4734, 0xB5,0x15, 0x77,0x24,0xBF,0xF1,0x60,0x01); +DEFINE_GUID(IID_IFolderTypeModifier, 0x04BA120E, 0xAD52, 0x4A2D, 0x98,0x07, 0x2D,0xA1,0x78,0xD0,0xC3,0xE1); +DEFINE_GUID(IID_IShellBrowserService_Vista, 0xF5A24314, 0x5B8B, 0x44FA, 0xBC,0x2E, 0x31,0x28,0x55,0x44,0xB5,0x20); +DEFINE_GUID(IID_IFileDialogPrivate_Vista, 0x2539E31C, 0x857F, 0x43C4, 0x88,0x72, 0x45,0xBD,0x6A,0x02,0x48,0x92); +DEFINE_GUID(SID_SMenuBandParent, 0x8C278EEC, 0x3EAB, 0x11D1, 0x8C,0xB0 ,0x00,0xC0,0x4F,0xD9,0x18,0xD0); +DEFINE_GUID(SID_SMenuPopup, 0xD1E7AFEB, 0x6A2E, 0x11D0, 0x8C,0x78, 0x00,0xC0,0x4F,0xD9,0x18,0xB4); +DEFINE_GUID(IID_IShellMenu, 0xEE1F7637, 0xE138, 0x11D1, 0x83,0x79, 0x00,0xC0,0x4F,0xD9,0x18,0xD0); + +DEFINE_GUID(IID_UnknownInterface1, 0x3934E4C2, 0x8143, 0x4E4C, 0xA1,0xDC, 0x71,0x8F,0x85,0x63,0xF3,0x37); +DEFINE_GUID(IID_UnknownInterface2, 0x3E24A11C, 0x15B2, 0x4F71, 0xB8,0x1E, 0x00,0x8F,0x77,0x99,0x8E,0x9F); +DEFINE_GUID(IID_UnknownInterface3, 0xE38FE0F3, 0x3DB0, 0x47EE, 0xA3,0x14, 0x25,0xCF,0x7F,0x4B,0xF5,0x21); +DEFINE_GUID(IID_UnknownInterface4, 0xFAD451C2, 0xAF58, 0x4161, 0xB9,0xFF, 0x57,0xAF,0xBB,0xED,0x0A,0xD2); +DEFINE_GUID(IID_UnknownInterface5, 0xF80C2137, 0x5829, 0x4CE9, 0x9F,0x81, 0xA9,0x5E,0x15,0x9D,0xD8,0xD5); +DEFINE_GUID(IID_UnknownInterface6, 0xD7F81F62, 0x491F, 0x49BC, 0x89,0x1D, 0x56,0x65,0x08,0x5D,0xF9,0x69); +DEFINE_GUID(IID_UnknownInterface7, 0x68A4FDBA, 0xA48A, 0x4A86, 0xA3,0x29, 0x1B,0x69,0xB9,0xB1,0x9E,0x89); +DEFINE_GUID(IID_UnknownInterface8, 0xD3B1CAF5, 0xEC4F, 0x4B2E, 0xBC,0xB0, 0x60,0xD7,0x15,0xC9,0x3C,0xB2); +DEFINE_GUID(IID_UnknownInterface9, 0x9536CA39, 0x1ACB, 0x4AE6, 0xAD,0x27, 0x24,0x03,0xD0,0x4C,0xA2,0x8F); +DEFINE_GUID(IID_UnknownInterface10, 0xB722BE00, 0x4E68, 0x101B, 0xA2,0xBC, 0x00,0xAA,0x00,0x40,0x47,0x70); + +static HWND hwnd; + +static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**); +static HRESULT (WINAPI *pSHParseDisplayName)(LPCWSTR,IBindCtx*,LPITEMIDLIST*,SFGAOF,SFGAOF*); + +static void init_function_pointers(void) +{ + HMODULE hmod; + + hmod = GetModuleHandleA("shell32.dll"); + pSHCreateShellItem = (void*)GetProcAddress(hmod, "SHCreateShellItem"); + pSHParseDisplayName = (void*)GetProcAddress(hmod, "SHParseDisplayName"); +} + +/********************************************************************* + * Some simple helpers + */ +static HRESULT ebrowser_instantiate(IExplorerBrowser **peb) +{ + return CoCreateInstance(&CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC_SERVER, + &IID_IExplorerBrowser, (void**)peb); +} + +static HRESULT ebrowser_initialize(IExplorerBrowser *peb) +{ + RECT rc; + rc.top = rc.left = 0; rc.bottom = rc.right = 500; + return IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); +} + +static HRESULT ebrowser_browse_to_desktop(IExplorerBrowser *peb) +{ + LPITEMIDLIST pidl_desktop; + HRESULT hr; + SHGetSpecialFolderLocation (hwnd, CSIDL_DESKTOP, &pidl_desktop); + hr = IExplorerBrowser_BrowseToIDList(peb, pidl_desktop, 0); + ILFree(pidl_desktop); + return hr; +} + +/* Process some messages */ +static void process_msgs(void) +{ + MSG msg; + while(PeekMessage( &msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +static void dbg_print_guid(const GUID *guid) { + WCHAR buf[MAX_PATH]; + + StringFromGUID2(guid, buf, MAX_PATH); + printf("guid:[%s]\n", wine_dbgstr_wn(buf, lstrlenW(buf))); +} + +/********************************************************************* + * IExplorerBrowserEvents implementation + */ +typedef struct { + IExplorerBrowserEvents IExplorerBrowserEvents_iface; + LONG ref; + UINT pending, created, completed, failed; +} IExplorerBrowserEventsImpl; + +static IExplorerBrowserEventsImpl ebev; + +static inline IExplorerBrowserEventsImpl *impl_from_IExplorerBrowserEvents(IExplorerBrowserEvents *iface) +{ + return CONTAINING_RECORD(iface, IExplorerBrowserEventsImpl, IExplorerBrowserEvents_iface); +} + +static HRESULT WINAPI IExplorerBrowserEvents_fnQueryInterface(IExplorerBrowserEvents *iface, + REFIID riid, void **ppvObj) +{ + ok(0, "Never called.\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI IExplorerBrowserEvents_fnAddRef(IExplorerBrowserEvents *iface) +{ + IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI IExplorerBrowserEvents_fnRelease(IExplorerBrowserEvents *iface) +{ + IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface); + return InterlockedDecrement(&This->ref); +} + +static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationPending(IExplorerBrowserEvents *iface, + PCIDLIST_ABSOLUTE pidlFolder) +{ + IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface); + This->pending++; + return S_OK; +} + +static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBrowserEvents *iface, + PCIDLIST_ABSOLUTE pidlFolder) +{ + IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface); + This->completed++; + return S_OK; +} +static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationFailed(IExplorerBrowserEvents *iface, + PCIDLIST_ABSOLUTE pidlFolder) +{ + IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface); + This->failed++; + return S_OK; +} +static HRESULT WINAPI IExplorerBrowserEvents_fnOnViewCreated(IExplorerBrowserEvents *iface, + IShellView *psv) +{ + IExplorerBrowserEventsImpl *This = impl_from_IExplorerBrowserEvents(iface); + This->created++; + return S_OK; +} + +static const IExplorerBrowserEventsVtbl ebevents = +{ + IExplorerBrowserEvents_fnQueryInterface, + IExplorerBrowserEvents_fnAddRef, + IExplorerBrowserEvents_fnRelease, + IExplorerBrowserEvents_fnOnNavigationPending, + IExplorerBrowserEvents_fnOnViewCreated, + IExplorerBrowserEvents_fnOnNavigationComplete, + IExplorerBrowserEvents_fnOnNavigationFailed +}; + +/********************************************************************* + * IExplorerPaneVisibility implementation + */ +typedef struct +{ + IExplorerPaneVisibility IExplorerPaneVisibility_iface; + LONG ref; + LONG count; + LONG np, cp, cp_o, cp_v, dp, pp, qp, aqp, unk; /* The panes */ +} IExplorerPaneVisibilityImpl; + +static inline IExplorerPaneVisibilityImpl *impl_from_IExplorerPaneVisibility(IExplorerPaneVisibility *iface) +{ + return CONTAINING_RECORD(iface, IExplorerPaneVisibilityImpl, IExplorerPaneVisibility_iface); +} + +static HRESULT WINAPI IExplorerPaneVisibility_fnQueryInterface(IExplorerPaneVisibility *iface, + REFIID riid, LPVOID *ppvObj) +{ + ok(0, "Not called.\n"); + trace("REFIID:"); dbg_print_guid(riid); + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI IExplorerPaneVisibility_fnAddRef(IExplorerPaneVisibility *iface) +{ + IExplorerPaneVisibilityImpl *This = impl_from_IExplorerPaneVisibility(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI IExplorerPaneVisibility_fnRelease(IExplorerPaneVisibility *iface) +{ + IExplorerPaneVisibilityImpl *This = impl_from_IExplorerPaneVisibility(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + if(!ref) + HeapFree(GetProcessHeap(), 0, This); + + return ref; +} + +static HRESULT WINAPI IExplorerPaneVisibility_fnGetPaneState(IExplorerPaneVisibility *iface, + REFEXPLORERPANE ep, + EXPLORERPANESTATE *peps) +{ + IExplorerPaneVisibilityImpl *This = impl_from_IExplorerPaneVisibility(iface); + This->count++; + + ok(ep != NULL, "ep is NULL.\n"); + ok(peps != NULL, "peps is NULL.\n"); + ok(*peps == 0, "got %d\n", *peps); + + *peps = EPS_FORCE; + if(IsEqualGUID(&EP_NavPane, ep)) This->np++; + else if(IsEqualGUID(&EP_Commands, ep)) This->cp++; + else if(IsEqualGUID(&EP_Commands_Organize, ep)) This->cp_o++; + else if(IsEqualGUID(&EP_Commands_View, ep)) This->cp_v++; + else if(IsEqualGUID(&EP_DetailsPane, ep)) This->dp++; + else if(IsEqualGUID(&EP_PreviewPane, ep)) This->pp++; + else if(IsEqualGUID(&EP_QueryPane, ep)) This->qp++; + else if(IsEqualGUID(&EP_AdvQueryPane, ep)) This->aqp++; + else + { + trace("Unknown explorer pane: "); dbg_print_guid(ep); + This->unk++; + } + + return S_OK; +} + +static const IExplorerPaneVisibilityVtbl epvvt = +{ + IExplorerPaneVisibility_fnQueryInterface, + IExplorerPaneVisibility_fnAddRef, + IExplorerPaneVisibility_fnRelease, + IExplorerPaneVisibility_fnGetPaneState +}; + +static IExplorerPaneVisibilityImpl *create_explorerpanevisibility(void) +{ + IExplorerPaneVisibilityImpl *epv; + + epv = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IExplorerPaneVisibilityImpl)); + epv->IExplorerPaneVisibility_iface.lpVtbl = &epvvt; + epv->ref = 1; + + return epv; +} + +/********************************************************************* + * ICommDlgBrowser3 implementation + */ +typedef struct +{ + ICommDlgBrowser3 ICommDlgBrowser3_iface; + LONG ref; + UINT OnDefaultCommand, OnStateChange, IncludeObject; + UINT Notify, GetDefaultMenuText, GetViewFlags; + UINT OnColumnClicked, GetCurrentFilter, OnPreviewCreated; +} ICommDlgBrowser3Impl; + +static inline ICommDlgBrowser3Impl *impl_from_ICommDlgBrowser3(ICommDlgBrowser3 *iface) +{ + return CONTAINING_RECORD(iface, ICommDlgBrowser3Impl, ICommDlgBrowser3_iface); +} + +static HRESULT WINAPI ICommDlgBrowser3_fnQueryInterface(ICommDlgBrowser3 *iface, REFIID riid, LPVOID *ppvObj) +{ + ok(0, "Not called.\n"); + trace("riid:"); dbg_print_guid(riid); + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI ICommDlgBrowser3_fnAddRef(ICommDlgBrowser3 *iface) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI ICommDlgBrowser3_fnRelease(ICommDlgBrowser3 *iface) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + if(!ref) + HeapFree(GetProcessHeap(), 0, This); + + return ref; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnOnDefaultCommand(ICommDlgBrowser3* iface, IShellView *shv) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->OnDefaultCommand++; + return E_NOTIMPL; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnOnStateChange( + ICommDlgBrowser3* iface, + IShellView *shv, + ULONG uChange) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->OnStateChange++; + return E_NOTIMPL; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnIncludeObject( + ICommDlgBrowser3* iface, + IShellView *shv, + LPCITEMIDLIST pidl) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->IncludeObject++; + return S_OK; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnNotify( + ICommDlgBrowser3* iface, + IShellView *ppshv, + DWORD dwNotifyType) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->Notify++; + return E_NOTIMPL; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnGetDefaultMenuText( + ICommDlgBrowser3* iface, + IShellView *ppshv, + LPWSTR pszText, + int cchMax) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->GetDefaultMenuText++; + return E_NOTIMPL; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnGetViewFlags( + ICommDlgBrowser3* iface, + DWORD *pdwFlags) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->GetViewFlags++; + return E_NOTIMPL; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnOnColumnClicked( + ICommDlgBrowser3* iface, + IShellView *ppshv, + int iColumn) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->OnColumnClicked++; + return E_NOTIMPL; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnGetCurrentFilter( + ICommDlgBrowser3* iface, + LPWSTR pszFileSpec, + int cchFileSpec) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->GetCurrentFilter++; + return E_NOTIMPL; +} + +static HRESULT WINAPI ICommDlgBrowser3_fnOnPreviewCreated( + ICommDlgBrowser3* iface, + IShellView *ppshv) +{ + ICommDlgBrowser3Impl *This = impl_from_ICommDlgBrowser3(iface); + This->OnPreviewCreated++; + return E_NOTIMPL; +} + +static const ICommDlgBrowser3Vtbl cdbvtbl = +{ + ICommDlgBrowser3_fnQueryInterface, + ICommDlgBrowser3_fnAddRef, + ICommDlgBrowser3_fnRelease, + ICommDlgBrowser3_fnOnDefaultCommand, + ICommDlgBrowser3_fnOnStateChange, + ICommDlgBrowser3_fnIncludeObject, + ICommDlgBrowser3_fnNotify, + ICommDlgBrowser3_fnGetDefaultMenuText, + ICommDlgBrowser3_fnGetViewFlags, + ICommDlgBrowser3_fnOnColumnClicked, + ICommDlgBrowser3_fnGetCurrentFilter, + ICommDlgBrowser3_fnOnPreviewCreated +}; + +static ICommDlgBrowser3Impl *create_commdlgbrowser3(void) +{ + ICommDlgBrowser3Impl *cdb; + + cdb = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ICommDlgBrowser3Impl)); + cdb->ICommDlgBrowser3_iface.lpVtbl = &cdbvtbl; + cdb->ref = 1; + + return cdb; +} + +/********************************************************************* + * IServiceProvider Implementation + */ +typedef struct { + IServiceProvider IServiceProvider_iface; + LONG ref; + struct services { + REFGUID service; + REFIID id; + int count; + void *punk; + } *interfaces; +} IServiceProviderImpl; + +static inline IServiceProviderImpl *impl_from_IServiceProvider(IServiceProvider *iface) +{ + return CONTAINING_RECORD(iface, IServiceProviderImpl, IServiceProvider_iface); +} + +static HRESULT WINAPI IServiceProvider_fnQueryInterface(IServiceProvider *iface, REFIID riid, LPVOID *ppvObj) +{ + *ppvObj = NULL; + if(IsEqualIID(riid, &IID_IServiceProvider)) + { + *ppvObj = iface; + IServiceProvider_AddRef(iface); + return S_OK; + } + + if(IsEqualIID(riid, &IID_IOleCommandTarget)) + { + /* Windows Vista. */ + return E_NOINTERFACE; + } + + ok(0, "Unexpected interface requested.\n"); + trace("riid: "); dbg_print_guid(riid); + return E_NOINTERFACE; +} + +static ULONG WINAPI IServiceProvider_fnAddRef(IServiceProvider *iface) +{ + IServiceProviderImpl *This = impl_from_IServiceProvider(iface); + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI IServiceProvider_fnRelease(IServiceProvider *iface) +{ + IServiceProviderImpl *This = impl_from_IServiceProvider(iface); + LONG ref = InterlockedDecrement(&This->ref); + + if(!ref) + HeapFree(GetProcessHeap(), 0, This); + + return ref; +} + +static HRESULT WINAPI IServiceProvider_fnQueryService(IServiceProvider *iface, + REFGUID guidService, + REFIID riid, + void **ppv) +{ + IServiceProviderImpl *This = impl_from_IServiceProvider(iface); + BOOL was_in_list = FALSE; + IUnknown *punk = NULL; + UINT i; + + *ppv = NULL; + for(i = 0; This->interfaces[i].service != NULL; i++) + { + if(IsEqualGUID(This->interfaces[i].service, guidService) && + IsEqualIID(This->interfaces[i].id, riid)) + { + was_in_list = TRUE; + This->interfaces[i].count++; + punk = This->interfaces[i].punk; + break; + } + } + + ok(was_in_list, "-- Unknown service requested --\n"); + if(!was_in_list) + { + trace("guidService: "); dbg_print_guid(guidService); + trace("riid: "); dbg_print_guid(riid); + } + + /* Give back an interface, if any. */ + if(punk) + { + *ppv = punk; + IUnknown_AddRef(punk); + return S_OK; + } + + return E_NOINTERFACE; +} + +static const IServiceProviderVtbl spvtbl = +{ + IServiceProvider_fnQueryInterface, + IServiceProvider_fnAddRef, + IServiceProvider_fnRelease, + IServiceProvider_fnQueryService +}; + +static IServiceProviderImpl *create_serviceprovider(void) +{ + IServiceProviderImpl *sp = HeapAlloc(GetProcessHeap(), 0, sizeof(IServiceProviderImpl)); + sp->IServiceProvider_iface.lpVtbl = &spvtbl; + sp->ref = 1; + return sp; +} + +static void test_QueryInterface(void) +{ + IExplorerBrowser *peb; + IUnknown *punk; + HRESULT hr; + LONG lres; + + hr = ebrowser_instantiate(&peb); + ok(hr == S_OK, "Got 0x%08x\n", hr); + +#define test_qinterface(iid, exp) \ + do { \ + hr = IExplorerBrowser_QueryInterface(peb, &iid, (void**)&punk); \ + ok(hr == exp, "(%s:)Expected (0x%08x), got (0x%08x)\n", \ + #iid, exp, hr); \ + if(SUCCEEDED(hr)) IUnknown_Release(punk); \ + } while(0) + + test_qinterface(IID_IUnknown, S_OK); + test_qinterface(IID_IExplorerBrowser, S_OK); + test_qinterface(IID_IShellBrowser, S_OK); + todo_wine test_qinterface(IID_IOleWindow, S_OK); + test_qinterface(IID_ICommDlgBrowser, S_OK); + test_qinterface(IID_ICommDlgBrowser2, S_OK); + test_qinterface(IID_ICommDlgBrowser3, S_OK); + todo_wine test_qinterface(IID_IServiceProvider, S_OK); + test_qinterface(IID_IObjectWithSite, S_OK); + todo_wine test_qinterface(IID_IConnectionPointContainer, S_OK); + test_qinterface(IID_IOleObject, E_NOINTERFACE); + test_qinterface(IID_IViewObject, E_NOINTERFACE); + test_qinterface(IID_IViewObject2, E_NOINTERFACE); + test_qinterface(IID_IViewObjectEx, E_NOINTERFACE); + test_qinterface(IID_IConnectionPoint, E_NOINTERFACE); + test_qinterface(IID_IShellView, E_NOINTERFACE); + test_qinterface(IID_INameSpaceTreeControlEvents, E_NOINTERFACE); + +#undef test_qinterface + + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got %d\n", lres); +} + +static void test_SB_misc(void) +{ + IExplorerBrowser *peb; + IShellBrowser *psb; + IUnknown *punk; + HRESULT hr; + HWND retHwnd; + LRESULT lres; + LONG ref; + + ebrowser_instantiate(&peb); + hr = IExplorerBrowser_QueryInterface(peb, &IID_IShellBrowser, (void**)&psb); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(FAILED(hr)) + { + skip("Failed to get IShellBrowser interface.\n"); + return; + } + + /* Some unimplemented methods */ + retHwnd = (HWND)0xDEADBEEF; + hr = IShellBrowser_GetControlWindow(psb, FCW_TOOLBAR, &retHwnd); + ok(hr == E_NOTIMPL, "got (0x%08x)\n", hr); + ok(retHwnd == (HWND)0xDEADBEEF, "HWND overwritten\n"); + + hr = IShellBrowser_GetControlWindow(psb, FCW_STATUS, &retHwnd); + ok(hr == E_NOTIMPL, "got (0x%08x)\n", hr); + ok(retHwnd == (HWND)0xDEADBEEF, "HWND overwritten\n"); + + hr = IShellBrowser_GetControlWindow(psb, FCW_TREE, &retHwnd); + ok(hr == E_NOTIMPL, "got (0x%08x)\n", hr); + ok(retHwnd == (HWND)0xDEADBEEF, "HWND overwritten\n"); + + hr = IShellBrowser_GetControlWindow(psb, FCW_PROGRESS, &retHwnd); + ok(hr == E_NOTIMPL, "got (0x%08x)\n", hr); + ok(retHwnd == (HWND)0xDEADBEEF, "HWND overwritten\n"); + + /* ::InsertMenuSB */ + hr = IShellBrowser_InsertMenusSB(psb, NULL, NULL); + ok(hr == E_NOTIMPL, "got (0x%08x)\n", hr); + + /* ::RemoveMenusSB */ + hr = IShellBrowser_RemoveMenusSB(psb, NULL); + ok(hr == E_NOTIMPL, "got (0x%08x)\n", hr); + + /* ::SetMenuSB */ + hr = IShellBrowser_SetMenuSB(psb, NULL, NULL, NULL); + ok(hr == E_NOTIMPL, "got (0x%08x)\n", hr); + + /***** Before EB::Initialize *****/ + + /* ::GetWindow */ + retHwnd = (HWND)0xDEADBEEF; + hr = IShellBrowser_GetWindow(psb, &retHwnd); + ok(hr == E_FAIL, "got (0x%08x)\n", hr); + ok(retHwnd == (HWND)0xDEADBEEF, "HWND overwritten\n"); + + todo_wine + { + + /* ::SendControlMsg */ + lres = 0xDEADBEEF; + hr = IShellBrowser_SendControlMsg(psb, FCW_STATUS, 0, 0, 0, &lres); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(lres == 0, "lres was %ld\n", lres); + + lres = 0xDEADBEEF; + hr = IShellBrowser_SendControlMsg(psb, FCW_TOOLBAR, TB_CHECKBUTTON, + FCIDM_TB_SMALLICON, TRUE, &lres); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(lres == 0, "lres was %ld\n", lres); + + hr = IShellBrowser_SendControlMsg(psb, FCW_STATUS, 0, 0, 0, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IShellBrowser_SendControlMsg(psb, FCW_TREE, 0, 0, 0, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IShellBrowser_SendControlMsg(psb, FCW_PROGRESS, 0, 0, 0, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + } + + /* ::QueryActiveShellView */ + hr = IShellBrowser_QueryActiveShellView(psb, (IShellView**)&punk); + ok(hr == E_FAIL, "got (0x%08x)\n", hr); + + /* Initialize ExplorerBrowser */ + ebrowser_initialize(peb); + + /***** After EB::Initialize *****/ + + /* ::GetWindow */ + hr = IShellBrowser_GetWindow(psb, &retHwnd); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(GetParent(retHwnd) == hwnd, "The HWND returned is not our child.\n"); + + todo_wine + { + /* ::SendControlMsg */ + hr = IShellBrowser_SendControlMsg(psb, FCW_STATUS, 0, 0, 0, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + lres = 0xDEADBEEF; + hr = IShellBrowser_SendControlMsg(psb, FCW_TOOLBAR, 0, 0, 0, &lres); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(lres == 0, "lres was %ld\n", lres); + + lres = 0xDEADBEEF; + hr = IShellBrowser_SendControlMsg(psb, FCW_STATUS, 0, 0, 0, &lres); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(lres == 0, "lres was %ld\n", lres); + + lres = 0xDEADBEEF; + hr = IShellBrowser_SendControlMsg(psb, 1234, 0, 0, 0, &lres); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(lres == 0, "lres was %ld\n", lres); + + /* Returns S_OK */ + hr = IShellBrowser_SetStatusTextSB(psb, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IShellBrowser_ContextSensitiveHelp(psb, FALSE); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IShellBrowser_EnableModelessSB(psb, TRUE); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IShellBrowser_SetToolbarItems(psb, NULL, 1, 1); + ok(hr == S_OK, "got (0x%08x)\n", hr); + } + + hr = IShellBrowser_QueryActiveShellView(psb, (IShellView**)&punk); + ok(hr == E_FAIL, "got (0x%08x)\n", hr); + + IShellBrowser_Release(psb); + IExplorerBrowser_Destroy(peb); + IExplorerBrowser_Release(peb); + + /* Browse to the desktop. */ + ebrowser_instantiate(&peb); + ebrowser_initialize(peb); + IExplorerBrowser_QueryInterface(peb, &IID_IShellBrowser, (void**)&psb); + + process_msgs(); + hr = ebrowser_browse_to_desktop(peb); + ok(hr == S_OK, "got (0x%08x)\n", hr); + process_msgs(); + + /****** After Browsing *****/ + + hr = IShellBrowser_QueryActiveShellView(psb, (IShellView**)&punk); + ok(hr == S_OK, "got (0x%08x)\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + IShellBrowser_Release(psb); + IExplorerBrowser_Destroy(peb); + ref = IExplorerBrowser_Release(peb); + ok(ref == 0, "Got %d\n", ref); +} + +static void test_initialization(void) +{ + IExplorerBrowser *peb; + IShellBrowser *psb; + HRESULT hr; + ULONG lres; + RECT rc; + + ebrowser_instantiate(&peb); + + if(0) + { + /* Crashes on Windows 7 */ + IExplorerBrowser_Initialize(peb, NULL, NULL, NULL); + IExplorerBrowser_Initialize(peb, hwnd, NULL, NULL); + } + + ZeroMemory(&rc, sizeof(RECT)); + + hr = IExplorerBrowser_Initialize(peb, NULL, &rc, NULL); + ok(hr == E_INVALIDARG, "got (0x%08x)\n", hr); + + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + /* Initialize twice */ + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == E_UNEXPECTED, "got (0x%08x)\n", hr); + + hr = IExplorerBrowser_Destroy(peb); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + /* Initialize again */ + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == E_UNEXPECTED, "got (0x%08x)\n", hr); + + /* Destroy again */ + hr = IExplorerBrowser_Destroy(peb); + ok(hr == S_OK, "got (0x%08x)\n", hr); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got %d\n", lres); + + /* Initialize with a few different rectangles */ + peb = NULL; + ebrowser_instantiate(&peb); + rc.left = 50; rc.top = 20; rc.right = 100; rc.bottom = 80; + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + hr = IExplorerBrowser_QueryInterface(peb, &IID_IShellBrowser, (void**)&psb); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + HWND eb_hwnd; + RECT eb_rc; + char buf[1024]; + LONG style, expected_style; + static const RECT exp_rc = {0, 0, 48, 58}; + + hr = IShellBrowser_GetWindow(psb, &eb_hwnd); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + GetClientRect(eb_hwnd, &eb_rc); + ok(EqualRect(&eb_rc, &exp_rc), "Got client rect (%d, %d)-(%d, %d)\n", + eb_rc.left, eb_rc.top, eb_rc.right, eb_rc.bottom); + + GetWindowRect(eb_hwnd, &eb_rc); + ok(eb_rc.right - eb_rc.left == 50, "Got window width %d\n", eb_rc.right - eb_rc.left); + ok(eb_rc.bottom - eb_rc.top == 60, "Got window height %d\n", eb_rc.bottom - eb_rc.top); + + buf[0] = '\0'; + GetClassNameA(eb_hwnd, buf, 1024); + ok(!lstrcmpA(buf, "ExplorerBrowserControl"), "Unexpected classname %s\n", buf); + + expected_style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER; + style = GetWindowLongPtrW(eb_hwnd, GWL_STYLE); + todo_wine ok(style == expected_style, "Got style 0x%08x, expected 0x%08x\n", style, expected_style); + + expected_style = WS_EX_CONTROLPARENT; + style = GetWindowLongPtrW(eb_hwnd, GWL_EXSTYLE); + ok(style == expected_style, "Got exstyle 0x%08x, expected 0x%08x\n", style, expected_style); + + ok(GetParent(eb_hwnd) == hwnd, "GetParent returns %p\n", GetParent(eb_hwnd)); + + /* ::Destroy() destroys the window. */ + ok(IsWindow(eb_hwnd), "eb_hwnd invalid.\n"); + IExplorerBrowser_Destroy(peb); + ok(!IsWindow(eb_hwnd), "eb_hwnd valid.\n"); + + IShellBrowser_Release(psb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got refcount %d\n", lres); + } + else + { + skip("Skipping some tests.\n"); + + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got refcount %d\n", lres); + } + + ebrowser_instantiate(&peb); + rc.left = 0; rc.top = 0; rc.right = 0; rc.bottom = 0; + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got refcount %d\n", lres); + + ebrowser_instantiate(&peb); + rc.left = -1; rc.top = -1; rc.right = 1; rc.bottom = 1; + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got refcount %d\n", lres); + + ebrowser_instantiate(&peb); + rc.left = 10; rc.top = 10; rc.right = 5; rc.bottom = 5; + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got refcount %d\n", lres); + + ebrowser_instantiate(&peb); + rc.left = 10; rc.top = 10; rc.right = 5; rc.bottom = 5; + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == S_OK, "got (0x%08x)\n", hr); + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got refcount %d\n", lres); +} + +static void test_SetSite(void) +{ + IExplorerBrowser *peb; + IServiceProviderImpl *spimpl = create_serviceprovider(); + ICommDlgBrowser3Impl *cdbimpl = create_commdlgbrowser3(); + IExplorerPaneVisibilityImpl *epvimpl = create_explorerpanevisibility(); + IObjectWithSite *pow; + HRESULT hr; + LONG ref; + UINT i; + struct services expected[] = { + /* Win 7 */ + { &SID_STopLevelBrowser, &IID_ICommDlgBrowser2, 0, cdbimpl }, + { &SID_STopLevelBrowser, &IID_IShellBrowserService, 0, NULL }, + { &SID_STopLevelBrowser, &IID_IShellBrowser, 0, NULL }, + { &SID_STopLevelBrowser, &IID_UnknownInterface8, 0, NULL }, + { &SID_STopLevelBrowser, &IID_IConnectionPointContainer, 0, NULL }, + { &SID_STopLevelBrowser, &IID_IProfferService, 0, NULL }, + { &SID_STopLevelBrowser, &IID_UnknownInterface9, 0, NULL }, + { &SID_ExplorerPaneVisibility, &IID_IExplorerPaneVisibility, 0, epvimpl }, + { &SID_SExplorerBrowserFrame, &IID_ICommDlgBrowser2, 0, cdbimpl }, + { &SID_SExplorerBrowserFrame, &IID_ICommDlgBrowser3, 0, cdbimpl }, + { &IID_IFileDialogPrivate, &IID_IFileDialogPrivate, 0, NULL }, + { &IID_IFileDialogPrivate, &IID_IFileDialog, 0, NULL }, + { &IID_IShellTaskScheduler, &IID_IShellTaskScheduler, 0, NULL }, + { &IID_IShellTaskScheduler, &IID_UnknownInterface2, 0, NULL }, + { &IID_IWebbrowserApp, &IID_IConnectionPointContainer, 0, NULL }, + { &IID_IFolderView, &IID_IFolderView, 0, NULL }, + { &IID_ILayoutModifier, &IID_ILayoutModifier, 0, NULL }, + { &IID_IBrowserSettings, &IID_IBrowserSettings, 0, NULL }, + { &CLSID_Desktop, &IID_IUnknown, 0, NULL }, + { &IID_UnknownInterface1, &IID_UnknownInterface1, 0, NULL }, + { &IID_UnknownInterface3, &IID_UnknownInterface3, 0, NULL }, + { &IID_UnknownInterface4, &IID_IUnknown, 0, NULL }, + { &IID_UnknownInterface6, &IID_UnknownInterface7, 0, NULL }, + { &IID_IBrowserWithActivationNotification, &IID_IBrowserWithActivationNotification, 0, NULL }, + + /* Other services requested in Vista, Windows 2008 but not in Windows 7 */ + { &IID_IBrowserSettings_Vista, &IID_IBrowserSettings_Vista, 0, NULL }, + { &IID_IFolderTypeModifier, &IID_IFolderTypeModifier, 0, NULL }, + { &SID_STopLevelBrowser, &IID_IShellBrowserService_Vista, 0, NULL }, + { &IID_UnknownInterface5, &IID_UnknownInterface5, 0, NULL }, + { &IID_ICommDlgBrowser, &IID_ICommDlgBrowser, 0, cdbimpl }, + { &IID_IFileDialogPrivate_Vista,&IID_IFileDialogPrivate_Vista, 0, NULL}, + { &IID_IFileDialogPrivate_Vista,&IID_IFileDialog, 0, NULL}, + { &IID_UnknownInterface10, &IID_IHTMLDocument2, 0, NULL}, + { &SID_SMenuBandParent, &IID_IOleCommandTarget, 0, NULL}, + { &SID_SMenuBandParent, &IID_IShellMenu, 0, NULL}, + { &SID_STopLevelBrowser, &IID_IOleWindow, 0, NULL}, + { &SID_SMenuPopup, &IID_IOleCommandTarget, 0, NULL}, + { NULL } + }; + + ebrowser_instantiate(&peb); + IExplorerBrowser_SetOptions(peb, EBO_SHOWFRAMES); + + hr = IExplorerBrowser_QueryInterface(peb, &IID_IObjectWithSite, (void**)&pow); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + spimpl->interfaces = expected; + + hr = IObjectWithSite_SetSite(pow, (IUnknown*)&spimpl->IServiceProvider_iface); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + if(FAILED(hr)) + IObjectWithSite_Release(pow); + } + + if(FAILED(hr)) + { + skip("Failed to set site.\n"); + + IServiceProvider_Release(&spimpl->IServiceProvider_iface); + ICommDlgBrowser3_Release(&cdbimpl->ICommDlgBrowser3_iface); + IExplorerPaneVisibility_Release(&epvimpl->IExplorerPaneVisibility_iface); + IExplorerBrowser_Destroy(peb); + ref = IExplorerBrowser_Release(peb); + ok(ref == 0, "Got ref %d\n", ref); + + return; + } + + ShowWindow(hwnd, TRUE); + ebrowser_initialize(peb); + ebrowser_browse_to_desktop(peb); + + for(i = 0; i < 10; i++) + { + Sleep(100); + process_msgs(); + } + ShowWindow(hwnd, FALSE); + + /* ICommDlgBrowser3 */ + ok(!cdbimpl->OnDefaultCommand, "Got %d\n", cdbimpl->OnDefaultCommand); + todo_wine ok(cdbimpl->OnStateChange, "Got %d\n", cdbimpl->OnStateChange); + ok(cdbimpl->IncludeObject, "Got %d\n", cdbimpl->IncludeObject); + ok(!cdbimpl->Notify, "Got %d\n", cdbimpl->Notify); + ok(!cdbimpl->GetDefaultMenuText, "Got %d\n", cdbimpl->GetDefaultMenuText); + todo_wine ok(cdbimpl->GetViewFlags, "Got %d\n", cdbimpl->GetViewFlags); + ok(!cdbimpl->OnColumnClicked, "Got %d\n", cdbimpl->OnColumnClicked); + ok(!cdbimpl->GetCurrentFilter, "Got %d\n", cdbimpl->GetCurrentFilter); + todo_wine ok(cdbimpl->OnPreviewCreated, "Got %d\n", cdbimpl->OnPreviewCreated); + + /* IExplorerPaneVisibility */ + ok(epvimpl->np, "Got %d\n", epvimpl->np); + todo_wine ok(epvimpl->cp, "Got %d\n", epvimpl->cp); + todo_wine ok(epvimpl->cp_o, "Got %d\n", epvimpl->cp_o); + todo_wine ok(epvimpl->cp_v, "Got %d\n", epvimpl->cp_v); + todo_wine ok(epvimpl->dp, "Got %d\n", epvimpl->dp); + todo_wine ok(epvimpl->pp, "Got %d\n", epvimpl->pp); + ok(!epvimpl->qp, "Got %d\n", epvimpl->qp); + ok(!epvimpl->aqp, "Got %d\n", epvimpl->aqp); + ok(!epvimpl->unk, "Got %d\n", epvimpl->unk); + + if(0) + { + for(i = 0; expected[i].service != NULL; i++) + if(!expected[i].count) trace("count %d was 0.\n", i); + } + + /* Test when IServiceProvider is released. */ + IServiceProvider_AddRef(&spimpl->IServiceProvider_iface); + ref = IServiceProvider_Release(&spimpl->IServiceProvider_iface); + ok(ref == 2, "Got ref %d\n", ref); + + hr = IObjectWithSite_SetSite(pow, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + IServiceProvider_AddRef(&spimpl->IServiceProvider_iface); + ref = IServiceProvider_Release(&spimpl->IServiceProvider_iface); + ok(ref == 1, "Got ref %d\n", ref); + + hr = IObjectWithSite_SetSite(pow, (IUnknown*)&spimpl->IServiceProvider_iface); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + IServiceProvider_AddRef(&spimpl->IServiceProvider_iface); + ref = IServiceProvider_Release(&spimpl->IServiceProvider_iface); + ok(ref == 2, "Got ref %d\n", ref); + + IExplorerBrowser_Destroy(peb); + + IServiceProvider_AddRef(&spimpl->IServiceProvider_iface); + ref = IServiceProvider_Release(&spimpl->IServiceProvider_iface); + ok(ref == 2, "Got ref %d\n", ref); + + IObjectWithSite_Release(pow); + ref = IExplorerBrowser_Release(peb); + ok(ref == 0, "Got ref %d\n", ref); + + ref = IServiceProvider_Release(&spimpl->IServiceProvider_iface); + ok(ref == 0, "Got ref %d\n", ref); + + ref = ICommDlgBrowser3_Release(&cdbimpl->ICommDlgBrowser3_iface); + ok(ref == 0, "Got ref %d\n", ref); + ref = IExplorerPaneVisibility_Release(&epvimpl->IExplorerPaneVisibility_iface); + ok(ref == 0, "Got ref %d\n", ref); +} + +static void test_basics(void) +{ + IExplorerBrowser *peb; + IShellBrowser *psb; + FOLDERSETTINGS fs; + ULONG lres; + DWORD flags; + HDWP hdwp; + RECT rc; + HRESULT hr; + static const WCHAR winetest[] = {'W','i','n','e','T','e','s','t',0}; + + ebrowser_instantiate(&peb); + ebrowser_initialize(peb); + + /* SetRect */ + rc.left = 0; rc.top = 0; rc.right = 0; rc.bottom = 0; + hr = IExplorerBrowser_SetRect(peb, NULL, rc); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + rc.left = 100; rc.top = 100; rc.right = 10; rc.bottom = 10; + hr = IExplorerBrowser_SetRect(peb, NULL, rc); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + /* SetRect with DeferWindowPos */ + rc.left = rc.top = 0; rc.right = rc.bottom = 10; + hdwp = BeginDeferWindowPos(1); + hr = IExplorerBrowser_SetRect(peb, &hdwp, rc); + ok(hr == S_OK, "got (0x%08x)\n", hr); + lres = EndDeferWindowPos(hdwp); + ok(lres, "EndDeferWindowPos failed.\n"); + + hdwp = NULL; + hr = IExplorerBrowser_SetRect(peb, &hdwp, rc); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(hdwp == NULL, "got %p\n", hdwp); + lres = EndDeferWindowPos(hdwp); + ok(!lres, "EndDeferWindowPos succeeded unexpectedly.\n"); + + /* Test positioning */ + rc.left = 10; rc.top = 20; rc.right = 50; rc.bottom = 50; + hr = IExplorerBrowser_SetRect(peb, NULL, rc); + ok(hr == S_OK, "got (0x%08x)\n", hr); + hr = IExplorerBrowser_QueryInterface(peb, &IID_IShellBrowser, (void**)&psb); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + HWND eb_hwnd; + RECT eb_rc; + static const RECT exp_rc = {11, 21, 49, 49}; + static const RECT exp_rc2 = {11, 21, 49, 24}; + + hr = IShellBrowser_GetWindow(psb, &eb_hwnd); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + GetClientRect(eb_hwnd, &eb_rc); + MapWindowPoints(eb_hwnd, hwnd, (POINT*)&eb_rc, 2); + ok(EqualRect(&eb_rc, &exp_rc), "Got rect (%d, %d) - (%d, %d)\n", + eb_rc.left, eb_rc.top, eb_rc.right, eb_rc.bottom); + + /* Try resizing with invalid hdwp */ + rc.bottom = 25; + hdwp = (HDWP)0xdeadbeef; + hr = IExplorerBrowser_SetRect(peb, &hdwp, rc); + ok(hr == E_FAIL, "Got 0x%08x\n", hr); + GetClientRect(eb_hwnd, &eb_rc); + MapWindowPoints(eb_hwnd, hwnd, (POINT*)&eb_rc, 2); + ok(EqualRect(&eb_rc, &exp_rc), "Got rect (%d, %d) - (%d, %d)\n", + eb_rc.left, eb_rc.top, eb_rc.right, eb_rc.bottom); + + hdwp = NULL; + hr = IExplorerBrowser_SetRect(peb, &hdwp, rc); + ok(hr == S_OK, "Got 0x%08x\n", hr); + GetClientRect(eb_hwnd, &eb_rc); + MapWindowPoints(eb_hwnd, hwnd, (POINT*)&eb_rc, 2); + ok(EqualRect(&eb_rc, &exp_rc2), "Got rect (%d, %d) - (%d, %d)\n", + eb_rc.left, eb_rc.top, eb_rc.right, eb_rc.bottom); + + IShellBrowser_Release(psb); + } + + IExplorerBrowser_Destroy(peb); + IExplorerBrowser_Release(peb); + + /* GetOptions/SetOptions*/ + ebrowser_instantiate(&peb); + + if(0) { + /* Crashes on Windows 7 */ + IExplorerBrowser_GetOptions(peb, NULL); + } + + hr = IExplorerBrowser_GetOptions(peb, &flags); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(flags == 0, "got (0x%08x)\n", flags); + + /* Settings preserved through Initialize. */ + hr = IExplorerBrowser_SetOptions(peb, 0xDEADBEEF); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + ebrowser_initialize(peb); + + hr = IExplorerBrowser_GetOptions(peb, &flags); + ok(flags == 0xDEADBEEF, "got (0x%08x)\n", flags); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + IExplorerBrowser_Destroy(peb); + IExplorerBrowser_Release(peb); + + ebrowser_instantiate(&peb); + ebrowser_initialize(peb); + + /* SetFolderSettings */ + hr = IExplorerBrowser_SetFolderSettings(peb, NULL); + ok(hr == E_INVALIDARG, "got (0x%08x)\n", hr); + fs.ViewMode = 0; fs.fFlags = 0; + hr = IExplorerBrowser_SetFolderSettings(peb, &fs); + todo_wine ok(hr == E_INVALIDARG, "got (0x%08x)\n", hr); + + /* SetPropertyBag */ + hr = IExplorerBrowser_SetPropertyBag(peb, NULL); + ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr); + hr = IExplorerBrowser_SetPropertyBag(peb, winetest); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + /* TODO: Test after browsing somewhere. */ + + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got %d\n", lres); +} + +static void test_Advise(void) +{ + IExplorerBrowser *peb; + IExplorerBrowserEvents *pebe; + DWORD cookies[10]; + HRESULT hr; + UINT i, ref; + + /* Set up our IExplorerBrowserEvents implementation */ + ebev.IExplorerBrowserEvents_iface.lpVtbl = &ebevents; + pebe = &ebev.IExplorerBrowserEvents_iface; + + ebrowser_instantiate(&peb); + + if(0) + { + /* Crashes on Windows 7 */ + IExplorerBrowser_Advise(peb, pebe, NULL); + IExplorerBrowser_Advise(peb, NULL, &cookies[0]); + } + + /* Using Unadvise with a cookie that has yet to be given out + * results in E_INVALIDARG */ + hr = IExplorerBrowser_Unadvise(peb, 11); + ok(hr == E_INVALIDARG, "got (0x%08x)\n", hr); + + /* Add some before initialization */ + for(i = 0; i < 5; i++) + { + hr = IExplorerBrowser_Advise(peb, pebe, &cookies[i]); + ok(hr == S_OK, "got (0x%08x)\n", hr); + } + + ebrowser_initialize(peb); + + /* Add some after initialization */ + for(i = 5; i < 10; i++) + { + hr = IExplorerBrowser_Advise(peb, pebe, &cookies[i]); + ok(hr == S_OK, "got (0x%08x)\n", hr); + } + + ok(ebev.ref == 10, "Got %d\n", ebev.ref); + + ebev.completed = 0; + ebrowser_browse_to_desktop(peb); + process_msgs(); + ok(ebev.completed == 10, "Got %d\n", ebev.completed); + + /* Remove a bunch somewhere in the middle */ + for(i = 4; i < 8; i++) + { + hr = IExplorerBrowser_Unadvise(peb, cookies[i]); + ok(hr == S_OK, "got (0x%08x)\n", hr); + } + + ebev.completed = 0; + ebrowser_browse_to_desktop(peb); + process_msgs(); + ok(ebev.completed == 6, "Got %d\n", ebev.completed); + + if(0) + { + /* Using unadvise with a previously unadvised cookie results + * in a crash. */ + IExplorerBrowser_Unadvise(peb, cookies[5]); + } + + /* Remove the rest. */ + for(i = 0; i < 10; i++) + { + if(i<4||i>7) + { + hr = IExplorerBrowser_Unadvise(peb, cookies[i]); + ok(hr == S_OK, "%d: got (0x%08x)\n", i, hr); + } + } + + ok(ebev.ref == 0, "Got %d\n", ebev.ref); + + ebev.completed = 0; + ebrowser_browse_to_desktop(peb); + process_msgs(); + ok(ebev.completed == 0, "Got %d\n", ebev.completed); + + /* ::Destroy implies ::Unadvise. */ + hr = IExplorerBrowser_Advise(peb, pebe, &cookies[0]); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(ebev.ref == 1, "Got %d\n", ebev.ref); + + hr = IExplorerBrowser_Destroy(peb); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(ebev.ref == 0, "Got %d\n", ebev.ref); + + ref = IExplorerBrowser_Release(peb); + ok(!ref, "Got %d\n", ref); +} + +/* Based on PathAddBackslashW from dlls/shlwapi/path.c */ +static LPWSTR myPathAddBackslashW( LPWSTR lpszPath ) +{ + size_t iLen; + + if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH) + return NULL; + + if (iLen) + { + lpszPath += iLen; + if (lpszPath[-1] != '\\') + { + *lpszPath++ = '\\'; + *lpszPath = '\0'; + } + } + return lpszPath; +} + +static void test_browse_pidl_(IExplorerBrowser *peb, IExplorerBrowserEventsImpl *ebev, + LPITEMIDLIST pidl, UINT uFlags, + HRESULT hr_exp, UINT pending, UINT created, UINT failed, UINT completed, + const char *file, int line) +{ + HRESULT hr; + ebev->completed = ebev->created = ebev->pending = ebev->failed = 0; + + hr = IExplorerBrowser_BrowseToIDList(peb, pidl, uFlags); + ok_(file, line) (hr == hr_exp, "BrowseToIDList returned 0x%08x\n", hr); + process_msgs(); + + ok_(file, line) + (ebev->pending == pending && ebev->created == created && + ebev->failed == failed && ebev->completed == completed, + "Events occurred: %d, %d, %d, %d\n", + ebev->pending, ebev->created, ebev->failed, ebev->completed); +} +#define test_browse_pidl(peb, ebev, pidl, uFlags, hr, p, cr, f, co) \ + test_browse_pidl_(peb, ebev, pidl, uFlags, hr, p, cr, f, co, __FILE__, __LINE__) + +static void test_browse_pidl_sb_(IExplorerBrowser *peb, IExplorerBrowserEventsImpl *ebev, + LPITEMIDLIST pidl, UINT uFlags, + HRESULT hr_exp, UINT pending, UINT created, UINT failed, UINT completed, + const char *file, int line) +{ + IShellBrowser *psb; + HRESULT hr; + + hr = IExplorerBrowser_QueryInterface(peb, &IID_IShellBrowser, (void**)&psb); + ok_(file, line) (hr == S_OK, "QueryInterface returned 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + ebev->completed = ebev->created = ebev->pending = ebev->failed = 0; + + hr = IShellBrowser_BrowseObject(psb, pidl, uFlags); + ok_(file, line) (hr == hr_exp, "BrowseObject returned 0x%08x\n", hr); + process_msgs(); + + ok_(file, line) + (ebev->pending == pending && ebev->created == created && + ebev->failed == failed && ebev->completed == completed, + "Events occurred: %d, %d, %d, %d\n", + ebev->pending, ebev->created, ebev->failed, ebev->completed); + + IShellBrowser_Release(psb); + } +} +#define test_browse_pidl_sb(peb, ebev, pidl, uFlags, hr, p, cr, f, co) \ + test_browse_pidl_sb_(peb, ebev, pidl, uFlags, hr, p, cr, f, co, __FILE__, __LINE__) + +static void test_navigation(void) +{ + IExplorerBrowser *peb, *peb2; + IFolderView *pfv; + IShellItem *psi; + IShellFolder *psf; + LPITEMIDLIST pidl_current, pidl_child; + DWORD cookie, cookie2; + HRESULT hr; + LONG lres; + WCHAR current_path[MAX_PATH]; + WCHAR child_path[MAX_PATH]; + static const WCHAR testfolderW[] = + {'w','i','n','e','t','e','s','t','f','o','l','d','e','r','\0'}; + + ok(pSHParseDisplayName != NULL, "pSHParseDisplayName unexpectedly missing.\n"); + ok(pSHCreateShellItem != NULL, "pSHCreateShellItem unexpectedly missing.\n"); + + GetCurrentDirectoryW(MAX_PATH, current_path); + if(!lstrlenW(current_path)) + { + skip("Failed to create test-directory.\n"); + return; + } + + lstrcpyW(child_path, current_path); + myPathAddBackslashW(child_path); + lstrcatW(child_path, testfolderW); + + CreateDirectoryW(child_path, NULL); + + pSHParseDisplayName(current_path, NULL, &pidl_current, 0, NULL); + pSHParseDisplayName(child_path, NULL, &pidl_child, 0, NULL); + + ebrowser_instantiate(&peb); + ebrowser_initialize(peb); + + ebrowser_instantiate(&peb2); + ebrowser_initialize(peb2); + + /* Set up our IExplorerBrowserEvents implementation */ + ebev.IExplorerBrowserEvents_iface.lpVtbl = &ebevents; + + IExplorerBrowser_Advise(peb, &ebev.IExplorerBrowserEvents_iface, &cookie); + IExplorerBrowser_Advise(peb2, &ebev.IExplorerBrowserEvents_iface, &cookie2); + + /* These should all fail */ + test_browse_pidl(peb, &ebev, 0, SBSP_ABSOLUTE | SBSP_RELATIVE, E_FAIL, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_ABSOLUTE | SBSP_RELATIVE, E_FAIL, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, 0, SBSP_ABSOLUTE, E_INVALIDARG, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_ABSOLUTE, E_INVALIDARG, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, 0, SBSP_RELATIVE, E_FAIL, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_RELATIVE, E_FAIL, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, 0, SBSP_PARENT, E_FAIL, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_PARENT, E_FAIL, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, 0, SBSP_NAVIGATEFORWARD, E_FAIL, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_NAVIGATEFORWARD, E_FAIL, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, 0, SBSP_NAVIGATEBACK, E_FAIL, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_NAVIGATEBACK, E_FAIL, 0, 0, 0, 0); + + /* "The first browse is synchronous" */ + test_browse_pidl(peb, &ebev, pidl_child, SBSP_ABSOLUTE, S_OK, 1, 1, 0, 1); + test_browse_pidl_sb(peb2, &ebev, pidl_child, SBSP_ABSOLUTE, S_OK, 1, 1, 0, 1); + + /* Navigate empty history */ + test_browse_pidl(peb, &ebev, 0, SBSP_NAVIGATEFORWARD, S_OK, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_NAVIGATEFORWARD, S_OK, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, 0, SBSP_NAVIGATEBACK, S_OK, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_NAVIGATEBACK, S_OK, 0, 0, 0, 0); + + /* Navigate history */ + test_browse_pidl(peb, &ebev, 0, SBSP_PARENT, S_OK, 1, 1, 0, 1); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_PARENT, S_OK, 1, 1, 0, 1); + test_browse_pidl(peb, &ebev, 0, SBSP_NAVIGATEBACK, S_OK, 1, 1, 0, 1); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_NAVIGATEBACK, S_OK, 1, 1, 0, 1); + test_browse_pidl(peb, &ebev, 0, SBSP_NAVIGATEFORWARD, S_OK, 1, 1, 0, 1); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_NAVIGATEFORWARD, S_OK, 1, 1, 0, 1); + test_browse_pidl(peb, &ebev, 0, SBSP_ABSOLUTE, S_OK, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, 0, SBSP_ABSOLUTE, S_OK, 0, 0, 0, 0); + + /* Relative navigation */ + test_browse_pidl(peb, &ebev, pidl_current, SBSP_ABSOLUTE, S_OK, 1, 0, 0, 1); + test_browse_pidl_sb(peb2, &ebev, pidl_current, SBSP_ABSOLUTE, S_OK, 1, 0, 0, 1); + + hr = IExplorerBrowser_GetCurrentView(peb, &IID_IFolderView, (void**)&pfv); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + LPITEMIDLIST pidl_relative; + + hr = IFolderView_GetFolder(pfv, &IID_IShellFolder, (void**)&psf); + ok(hr == S_OK, "Got 0x%08x\n", hr); + hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, (LPWSTR)testfolderW, + NULL, &pidl_relative, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + /* Browsing to another location here before using the + * pidl_relative would make ExplorerBrowser in Windows 7 show a + * not-available dialog. Also, passing a relative pidl without + * specifying SBSP_RELATIVE makes it look for the pidl on the + * desktop + */ + + test_browse_pidl(peb, &ebev, pidl_relative, SBSP_RELATIVE, S_OK, 1, 1, 0, 1); + test_browse_pidl_sb(peb2, &ebev, pidl_relative, SBSP_RELATIVE, S_OK, 1, 1, 0, 1); + + ILFree(pidl_relative); + /* IShellFolder_Release(psf); */ + IFolderView_Release(pfv); + } + + /* misc **/ + test_browse_pidl(peb, &ebev, NULL, SBSP_ABSOLUTE, S_OK, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, NULL, SBSP_ABSOLUTE, S_OK, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, NULL, SBSP_DEFBROWSER, S_OK, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, NULL, SBSP_DEFBROWSER, S_OK, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, pidl_current, SBSP_SAMEBROWSER, S_OK, 1, 1, 0, 1); + test_browse_pidl_sb(peb2, &ebev, pidl_current, SBSP_SAMEBROWSER, S_OK, 1, 1, 0, 1); + test_browse_pidl(peb, &ebev, pidl_current, SBSP_SAMEBROWSER, S_OK, 1, 0, 0, 1); + test_browse_pidl_sb(peb2, &ebev, pidl_current, SBSP_SAMEBROWSER, S_OK, 1, 0, 0, 1); + + test_browse_pidl(peb, &ebev, pidl_current, SBSP_EXPLOREMODE, E_INVALIDARG, 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, pidl_current, SBSP_EXPLOREMODE, E_INVALIDARG, 0, 0, 0, 0); + test_browse_pidl(peb, &ebev, pidl_current, SBSP_OPENMODE, S_OK, 1, 0, 0, 1); + test_browse_pidl_sb(peb2, &ebev, pidl_current, SBSP_OPENMODE, S_OK, 1, 0, 0, 1); + + /* SBSP_NEWBROWSER will return E_INVALIDARG, claims MSDN, but in + * reality it works as one would expect (Windows 7 only?). + */ + if(0) + { + IExplorerBrowser_BrowseToIDList(peb, NULL, SBSP_NEWBROWSER); + } + + hr = IExplorerBrowser_Unadvise(peb, cookie); + ok(hr == S_OK, "Got 0x%08x\n", hr); + IExplorerBrowser_Destroy(peb); + process_msgs(); + hr = IExplorerBrowser_Unadvise(peb2, cookie2); + ok(hr == S_OK, "Got 0x%08x\n", hr); + IExplorerBrowser_Destroy(peb2); + process_msgs(); + + /* Attempt browsing after destroyed */ + test_browse_pidl(peb, &ebev, pidl_child, SBSP_ABSOLUTE, HRESULT_FROM_WIN32(ERROR_BUSY), 0, 0, 0, 0); + test_browse_pidl_sb(peb2, &ebev, pidl_child, SBSP_ABSOLUTE, HRESULT_FROM_WIN32(ERROR_BUSY), 0, 0, 0, 0); + + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got lres %d\n", lres); + lres = IExplorerBrowser_Release(peb2); + ok(lres == 0, "Got lres %d\n", lres); + + /******************************************/ + /* Test some options that affect browsing */ + + ebrowser_instantiate(&peb); + hr = IExplorerBrowser_Advise(peb, &ebev.IExplorerBrowserEvents_iface, &cookie); + ok(hr == S_OK, "Got 0x%08x\n", hr); + hr = IExplorerBrowser_SetOptions(peb, EBO_NAVIGATEONCE); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ebrowser_initialize(peb); + + test_browse_pidl(peb, &ebev, pidl_current, 0, S_OK, 1, 1, 0, 1); + test_browse_pidl(peb, &ebev, pidl_current, 0, E_FAIL, 0, 0, 0, 0); + + hr = IExplorerBrowser_SetOptions(peb, 0); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + test_browse_pidl(peb, &ebev, pidl_current, 0, S_OK, 1, 0, 0, 1); + test_browse_pidl(peb, &ebev, pidl_current, 0, S_OK, 1, 0, 0, 1); + + /* Difference in behavior lies where? */ + hr = IExplorerBrowser_SetOptions(peb, EBO_ALWAYSNAVIGATE); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + test_browse_pidl(peb, &ebev, pidl_current, 0, S_OK, 1, 0, 0, 1); + test_browse_pidl(peb, &ebev, pidl_current, 0, S_OK, 1, 0, 0, 1); + + hr = IExplorerBrowser_Unadvise(peb, cookie); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got lres %d\n", lres); + + /* BrowseToObject tests */ + ebrowser_instantiate(&peb); + ebrowser_initialize(peb); + + /* Browse to the desktop by passing an IShellFolder */ + hr = SHGetDesktopFolder(&psf); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IExplorerBrowser_BrowseToObject(peb, (IUnknown*)psf, SBSP_DEFBROWSER); + ok(hr == S_OK, "got (0x%08x)\n", hr); + if(hr == S_OK) process_msgs(); + + IShellFolder_Release(psf); + } + + /* Browse to the current directory by passing a ShellItem */ + hr = pSHCreateShellItem(NULL, NULL, pidl_current, &psi); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IExplorerBrowser_BrowseToObject(peb, (IUnknown*)psi, SBSP_DEFBROWSER); + ok(hr == S_OK, "got (0x%08x)\n", hr); + process_msgs(); + + IShellItem_Release(psi); + } + + IExplorerBrowser_Destroy(peb); + lres = IExplorerBrowser_Release(peb); + ok(lres == 0, "Got lres %d\n", lres); + + /* Cleanup */ + RemoveDirectoryW(child_path); + ILFree(pidl_current); + ILFree(pidl_child); +} + +static void test_GetCurrentView(void) +{ + IExplorerBrowser *peb; + IUnknown *punk; + HRESULT hr; + + /* GetCurrentView */ + ebrowser_instantiate(&peb); + + if(0) + { + /* Crashes under Windows 7 */ + IExplorerBrowser_GetCurrentView(peb, NULL, NULL); + } + hr = IExplorerBrowser_GetCurrentView(peb, NULL, (void**)&punk); + ok(hr == E_FAIL, "Got 0x%08x\n", hr); + +#define test_gcv(iid, exp) \ + do { \ + hr = IExplorerBrowser_GetCurrentView(peb, &iid, (void**)&punk); \ + ok(hr == exp, "(%s:)Expected (0x%08x), got: (0x%08x)\n", \ + #iid ,exp, hr); \ + if(SUCCEEDED(hr)) IUnknown_Release(punk); \ + } while(0) + + test_gcv(IID_IUnknown, E_FAIL); + test_gcv(IID_IUnknown, E_FAIL); + test_gcv(IID_IShellView, E_FAIL); + test_gcv(IID_IShellView2, E_FAIL); + test_gcv(IID_IFolderView, E_FAIL); + test_gcv(IID_IPersistFolder, E_FAIL); + test_gcv(IID_IPersistFolder2, E_FAIL); + test_gcv(IID_ICommDlgBrowser, E_FAIL); + test_gcv(IID_ICommDlgBrowser2, E_FAIL); + test_gcv(IID_ICommDlgBrowser3, E_FAIL); + + ebrowser_initialize(peb); + ebrowser_browse_to_desktop(peb); + + test_gcv(IID_IUnknown, S_OK); + test_gcv(IID_IUnknown, S_OK); + test_gcv(IID_IShellView, S_OK); + test_gcv(IID_IShellView2, S_OK); + test_gcv(IID_IFolderView, S_OK); + todo_wine test_gcv(IID_IPersistFolder, S_OK); + test_gcv(IID_IPersistFolder2, E_NOINTERFACE); + test_gcv(IID_ICommDlgBrowser, E_NOINTERFACE); + test_gcv(IID_ICommDlgBrowser2, E_NOINTERFACE); + test_gcv(IID_ICommDlgBrowser3, E_NOINTERFACE); + +#undef test_gcv + + IExplorerBrowser_Destroy(peb); + IExplorerBrowser_Release(peb); +} + +static void test_InputObject(void) +{ + IExplorerBrowser *peb; + IShellFolder *psf; + IInputObject *pio; + HRESULT hr; + RECT rc; + UINT i; + WPARAM supported_key_accels_mode1[] = { + VK_BACK, VK_TAB, VK_RETURN, VK_PRIOR, VK_NEXT, VK_END, VK_HOME, + VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_DELETE, VK_F1, VK_F2, + VK_F5, VK_F6, VK_F10, 0 }; + WPARAM supported_key_accels_mode2[] = { + VK_RETURN, VK_PRIOR, VK_NEXT, VK_END, VK_HOME, + VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN, VK_DELETE, VK_F1, VK_F2, + VK_F10, 0 }; + WPARAM *key_accels; + MSG msg_a = { + hwnd, + WM_KEYDOWN, + VK_F5, 0, + GetTickCount(), + {5, 2} + }; + + ebrowser_instantiate(&peb); + hr = IExplorerBrowser_QueryInterface(peb, &IID_IInputObject, (void**)&pio); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(FAILED(hr)) + { + win_skip("IInputObject not supported.\n"); + return; + } + + /* Before initializing */ + hr = IInputObject_TranslateAcceleratorIO(pio, &msg_a); + todo_wine ok(hr == E_FAIL, "Got 0x%08x\n", hr); + + hr = IInputObject_HasFocusIO(pio); + todo_wine ok(hr == E_FAIL, "Got 0x%08x\n", hr); + + hr = IInputObject_UIActivateIO(pio, TRUE, &msg_a); + todo_wine ok(hr == S_OK, "Got 0x%08x\n", hr); + + hr = IInputObject_HasFocusIO(pio); + todo_wine ok(hr == E_FAIL, "Got 0x%08x\n", hr); + + hr = IInputObject_TranslateAcceleratorIO(pio, &msg_a); + todo_wine ok(hr == E_FAIL, "Got 0x%08x\n", hr); + + rc.left = 0; rc.top = 0; rc.right = 100; rc.bottom = 100; + hr = IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + hr = IInputObject_HasFocusIO(pio); + todo_wine ok(hr == E_FAIL, "Got 0x%08x\n", hr); + + hr = IInputObject_TranslateAcceleratorIO(pio, &msg_a); + todo_wine ok(hr == E_FAIL, "Got 0x%08x\n", hr); + + /* Browse to the desktop */ + SHGetDesktopFolder(&psf); + hr = IExplorerBrowser_BrowseToObject(peb, (IUnknown*)psf, SBSP_DEFBROWSER); + ok(hr == S_OK, "Got 0x%08x\n", hr); + IShellFolder_Release(psf); + + hr = IInputObject_UIActivateIO(pio, TRUE, &msg_a); + todo_wine ok(hr == S_OK, "Got 0x%08x\n", hr); + + hr = IInputObject_HasFocusIO(pio); + todo_wine ok(hr == S_OK, "Got 0x%08x\n", hr); + + hr = IInputObject_UIActivateIO(pio, FALSE, &msg_a); + todo_wine ok(hr == S_OK, "Got 0x%08x\n", hr); + + hr = IInputObject_HasFocusIO(pio); + todo_wine ok(hr == S_OK, "Got 0x%08x\n", hr); + + hr = IInputObject_TranslateAcceleratorIO(pio, &msg_a); + if(hr == S_OK) + key_accels = supported_key_accels_mode1; + else + key_accels = supported_key_accels_mode2; + + for(i = 0; i < 0x100; i++) + { + BOOL found = FALSE; + UINT j; + for(j = 0; key_accels[j] != 0; j++) + if(key_accels[j] == i) + { + found = TRUE; + break; + } + + msg_a.wParam = i; + process_msgs(); + hr = IInputObject_TranslateAcceleratorIO(pio, &msg_a); + todo_wine ok(hr == (found ? S_OK : S_FALSE), "Got 0x%08x (%04x)\n", hr, i); + if(i == VK_F5) + Sleep(1000); /* Needed for w2k8 (64bit) */ + } + + process_msgs(); + + IInputObject_Release(pio); + IExplorerBrowser_Destroy(peb); + IExplorerBrowser_Release(peb); +} + +static BOOL test_instantiate_control(void) +{ + IExplorerBrowser *peb; + HRESULT hr; + + hr = ebrowser_instantiate(&peb); + ok(hr == S_OK || hr == REGDB_E_CLASSNOTREG, "Got (0x%08x)\n", hr); + if(FAILED(hr)) + return FALSE; + + IExplorerBrowser_Release(peb); + return TRUE; +} + +static void setup_window(void) +{ + WNDCLASSW wc; + static const WCHAR ebtestW[] = {'e','b','t','e','s','t',0}; + + ZeroMemory(&wc, sizeof(WNDCLASSW)); + wc.lpfnWndProc = DefWindowProcW; + wc.lpszClassName = ebtestW; + RegisterClassW(&wc); + hwnd = CreateWindowExW(0, ebtestW, NULL, 0, + 0, 0, 500, 500, + NULL, 0, 0, NULL); + ok(hwnd != NULL, "Failed to create window for tests.\n"); +} + +START_TEST(ebrowser) +{ + OleInitialize(NULL); + + if(!test_instantiate_control()) + { + win_skip("No ExplorerBrowser control..\n"); + OleUninitialize(); + return; + } + + setup_window(); + init_function_pointers(); + + test_QueryInterface(); + test_SB_misc(); + test_initialization(); + test_basics(); + test_Advise(); + test_navigation(); + test_GetCurrentView(); + test_SetSite(); + test_InputObject(); + + DestroyWindow(hwnd); + OleUninitialize(); +} diff --git a/rostests/winetests/shell32/progman_dde.c b/rostests/winetests/shell32/progman_dde.c index d7bf4326a30..1e3239da818 100644 --- a/rostests/winetests/shell32/progman_dde.c +++ b/rostests/winetests/shell32/progman_dde.c @@ -200,14 +200,22 @@ static void init_strings(void) WCHAR module[MAX_PATH]; WCHAR module_expanded[MAX_PATH]; WCHAR localized[MAX_PATH]; + HRESULT hr; int id; MultiByteToWideChar(CP_ACP, 0, startup, -1, startupW, sizeof(startupW)/sizeof(WCHAR)); - pSHGetLocalizedName(startupW, module, MAX_PATH, &id); - ExpandEnvironmentStringsW(module, module_expanded, MAX_PATH); - LoadStringW(GetModuleHandleW(module_expanded), id, localized, MAX_PATH); + hr = pSHGetLocalizedName(startupW, module, MAX_PATH, &id); + todo_wine ok(hr == S_OK, "got 0x%08x\n", hr); + /* check to be removed when SHGetLocalizedName is implemented */ + if (hr == S_OK) + { + ExpandEnvironmentStringsW(module, module_expanded, MAX_PATH); + LoadStringW(GetModuleHandleW(module_expanded), id, localized, MAX_PATH); - WideCharToMultiByte(CP_ACP, 0, localized, -1, StartupTitle, sizeof(StartupTitle), NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, localized, -1, StartupTitle, sizeof(StartupTitle), NULL, NULL); + } + else + lstrcpyA(StartupTitle, (strrchr(startup, '\\') + 1)); } else { diff --git a/rostests/winetests/shell32/recyclebin.c b/rostests/winetests/shell32/recyclebin.c new file mode 100644 index 00000000000..391e0254990 --- /dev/null +++ b/rostests/winetests/shell32/recyclebin.c @@ -0,0 +1,107 @@ +/* + * Tests for recycle bin functions + * + * Copyright 2011 Jay Yang + * + * 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 + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include "shellapi.h" + +#include +#include "wine/test.h" + +static int (WINAPI *pSHQueryRecycleBinA)(LPCSTR,LPSHQUERYRBINFO); +static int (WINAPI *pSHFileOperationA)(LPSHFILEOPSTRUCTA); + +static char int64_buffer[65]; +/* Note: This function uses a single buffer for the return value.*/ +static const char* str_from_int64(__int64 ll) +{ + + if (sizeof(ll) > sizeof(unsigned long) && ll >> 32) + sprintf(int64_buffer,"%lx%08lx",(unsigned long)(ll >> 32),(unsigned long)ll); + else + sprintf(int64_buffer,"%lx",(unsigned long)ll); + return int64_buffer; +} + +static void setup_pointers(void) +{ + HMODULE hshell32 = GetModuleHandleA("shell32.dll"); + pSHQueryRecycleBinA = (void*)GetProcAddress(hshell32, "SHQueryRecycleBinA"); + pSHFileOperationA = (void*)GetProcAddress(hshell32, "SHFileOperationA"); +} + +static void test_query_recyclebin(void) +{ + SHQUERYRBINFO info1={sizeof(info1),0xdeadbeef,0xdeadbeef}; + SHQUERYRBINFO info2={sizeof(info2),0xdeadbeef,0xdeadbeef}; + UINT written; + HRESULT hr; + HANDLE file; + SHFILEOPSTRUCTA shfo; + const CHAR *name="test.txt"; + CHAR buf[MAX_PATH+strlen(name)+2]; + if(!pSHQueryRecycleBinA) + { + skip("SHQueryRecycleBinA does not exist\n"); + return; + } + if(!pSHFileOperationA) + { + skip("SHFileOperationA does not exist\n"); + return; + } + GetCurrentDirectoryA(MAX_PATH, buf); + strcat(buf,"\\"); + strcat(buf,name); + buf[strlen(buf) + 1] = '\0'; + hr = pSHQueryRecycleBinA(buf,&info1); + ok(hr == S_OK, "SHQueryRecycleBinA failed with error 0x%x\n", hr); + ok(info1.i64Size!=0xdeadbeef,"i64Size not set\n"); + ok(info1.i64NumItems!=0xdeadbeef,"i64NumItems not set\n"); + /*create and send a file to the recycle bin*/ + file = CreateFileA(name,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL); + ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n",name); + WriteFile(file,name,strlen(name),&written,NULL); + CloseHandle(file); + shfo.hwnd = NULL; + shfo.wFunc = FO_DELETE; + shfo.pFrom = buf; + shfo.pTo = NULL; + shfo.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_SILENT | FOF_ALLOWUNDO; + shfo.hNameMappings = NULL; + shfo.lpszProgressTitle = NULL; + ok(!pSHFileOperationA(&shfo), "Deletion was not successful\n"); + hr = pSHQueryRecycleBinA(buf,&info2); + ok(hr == S_OK, "SHQueryRecycleBinW failed with error 0x%x\n", hr); + if(info2.i64Size!=info1.i64Size || info2.i64NumItems!=info1.i64NumItems) { + ok(info2.i64Size==info1.i64Size+written,"Expected recycle bin to have 0x%s bytes\n",str_from_int64(info1.i64Size+written)); + ok(info2.i64NumItems==info1.i64NumItems+1,"Expected recycle bin to have 0x%s items\n",str_from_int64(info1.i64NumItems+1)); + } else todo_wine { + ok(info2.i64Size==info1.i64Size+written,"Expected recycle bin to have 0x%s bytes\n",str_from_int64(info1.i64Size+written)); + ok(info2.i64NumItems==info1.i64NumItems+1,"Expected recycle bin to have 0x%s items\n",str_from_int64(info1.i64NumItems+1)); + } +} + + +START_TEST(recyclebin) +{ + setup_pointers(); + test_query_recyclebin(); +} diff --git a/rostests/winetests/shell32/shell32.rbuild b/rostests/winetests/shell32/shell32.rbuild index 9f9d037be52..a503d9c6225 100644 --- a/rostests/winetests/shell32/shell32.rbuild +++ b/rostests/winetests/shell32/shell32.rbuild @@ -17,10 +17,16 @@ ntdll appbar.c autocomplete.c + brsfolder.c + ebrowser.c generated.c progman_dde.c + recyclebin.c + shelldispatch.c shelllink.c + shellole.c shellpath.c + shfldr_special.c shlexec.c shlfileop.c shlfolder.c @@ -28,7 +34,6 @@ string.c systray.c testlist.c - shfldr_special.c rsrc.rc diff --git a/rostests/winetests/shell32/shell32_test.h b/rostests/winetests/shell32/shell32_test.h index 64a2be1ef62..42a74fb2d5f 100755 --- a/rostests/winetests/shell32/shell32_test.h +++ b/rostests/winetests/shell32/shell32_test.h @@ -1,7 +1,7 @@ /* * Unit test suite for shell32 functions * - * Copyright 2005 Francois Gougett for CodeWeavers + * Copyright 2005 Francois Gouget for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/rostests/winetests/shell32/shelldispatch.c b/rostests/winetests/shell32/shelldispatch.c new file mode 100644 index 00000000000..2ccc364a2be --- /dev/null +++ b/rostests/winetests/shell32/shelldispatch.c @@ -0,0 +1,319 @@ +/* + * Unit tests for IShellDispatch + * + * Copyright 2010 Alexander Morozov for Etersoft + * + * 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 + */ + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include "shldisp.h" +#include "shlobj.h" +#include "shlwapi.h" +#include "wine/test.h" + +static HRESULT (WINAPI *pSHGetFolderPathW)(HWND, int, HANDLE, DWORD, LPWSTR); +static HRESULT (WINAPI *pSHGetNameFromIDList)(PCIDLIST_ABSOLUTE,SIGDN,PWSTR*); +static HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *); +static DWORD (WINAPI *pGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD); + +static void init_function_pointers(void) +{ + HMODULE hshell32, hkernel32; + + hshell32 = GetModuleHandleA("shell32.dll"); + hkernel32 = GetModuleHandleA("kernel32.dll"); + pSHGetFolderPathW = (void*)GetProcAddress(hshell32, "SHGetFolderPathW"); + pSHGetNameFromIDList = (void*)GetProcAddress(hshell32, "SHGetNameFromIDList"); + pSHGetSpecialFolderLocation = (void*)GetProcAddress(hshell32, + "SHGetSpecialFolderLocation"); + pGetLongPathNameW = (void*)GetProcAddress(hkernel32, "GetLongPathNameW"); +} + +static void test_namespace(void) +{ + static const WCHAR winetestW[] = {'w','i','n','e','t','e','s','t',0}; + static const WCHAR backslashW[] = {'\\',0}; + static const WCHAR clsidW[] = { + ':',':','{','6','4','5','F','F','0','4','0','-','5','0','8','1','-', + '1','0','1','B','-','9','F','0','8','-', + '0','0','A','A','0','0','2','F','9','5','4','E','}',0}; + + static WCHAR tempW[MAX_PATH], curW[MAX_PATH]; + WCHAR *long_pathW = NULL; + HRESULT r; + IShellDispatch *sd; + Folder *folder; + Folder2 *folder2; + FolderItem *item; + VARIANT var; + BSTR title, item_path; + int len; + + r = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, + &IID_IShellDispatch, (LPVOID*)&sd); + if (r == REGDB_E_CLASSNOTREG) /* NT4 */ + { + win_skip("skipping IShellDispatch tests\n"); + return; + } + ok(SUCCEEDED(r), "CoCreateInstance failed: %08x\n", r); + if (FAILED(r)) + return; + + VariantInit(&var); + folder = (void*)0xdeadbeef; + r = IShellDispatch_NameSpace(sd, var, &folder); + ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r); + ok(folder == NULL, "expected NULL, got %p\n", folder); + + V_VT(&var) = VT_I4; + V_I4(&var) = -1; + folder = (void*)0xdeadbeef; + r = IShellDispatch_NameSpace(sd, var, &folder); + todo_wine { + ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r); + ok(folder == NULL, "got %p\n", folder); +} + V_VT(&var) = VT_I4; + V_I4(&var) = ssfPROGRAMFILES; + r = IShellDispatch_NameSpace(sd, var, &folder); + ok(r == S_OK || + broken(r == S_FALSE), /* NT4 */ + "IShellDispatch::NameSpace failed: %08x\n", r); + if (r == S_OK) + { + static WCHAR path[MAX_PATH]; + + if (pSHGetFolderPathW) + { + r = pSHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES, NULL, + SHGFP_TYPE_CURRENT, path); + ok(r == S_OK, "SHGetFolderPath failed: %08x\n", r); + } + r = Folder_get_Title(folder, &title); + todo_wine + ok(r == S_OK, "Folder::get_Title failed: %08x\n", r); + if (r == S_OK) + { + /* On Win2000-2003 title is equal to program files directory name in + HKLM\Software\Microsoft\Windows\CurrentVersion\ProgramFilesDir. + On newer Windows it seems constant and is not changed + if the program files directory name is changed */ + if (pSHGetSpecialFolderLocation && pSHGetNameFromIDList) + { + LPITEMIDLIST pidl; + PWSTR name; + + r = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidl); + ok(r == S_OK, "SHGetSpecialFolderLocation failed: %08x\n", r); + r = pSHGetNameFromIDList(pidl, SIGDN_NORMALDISPLAY, &name); + ok(r == S_OK, "SHGetNameFromIDList failed: %08x\n", r); + todo_wine + ok(!lstrcmpW(title, name), "expected %s, got %s\n", + wine_dbgstr_w(name), wine_dbgstr_w(title)); + CoTaskMemFree(name); + CoTaskMemFree(pidl); + } + else if (pSHGetFolderPathW) + { + WCHAR *p; + + p = path + lstrlenW(path); + while (path < p && *(p - 1) != '\\') + p--; + ok(!lstrcmpW(title, p), "expected %s, got %s\n", + wine_dbgstr_w(p), wine_dbgstr_w(title)); + } + else skip("skipping Folder::get_Title test\n"); + SysFreeString(title); + } + r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2); + ok(r == S_OK, "Folder::QueryInterface failed: %08x\n", r); + if (r == S_OK) + { + r = Folder2_get_Self(folder2, &item); + ok(r == S_OK, "Folder::get_Self failed: %08x\n", r); + if (r == S_OK) + { + r = FolderItem_get_Path(item, &item_path); + ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r); + if (pSHGetFolderPathW) + ok(!lstrcmpW(item_path, path), "expected %s, got %s\n", + wine_dbgstr_w(path), wine_dbgstr_w(item_path)); + SysFreeString(item_path); + FolderItem_Release(item); + } + Folder2_Release(folder2); + } + Folder_Release(folder); + } + + V_VT(&var) = VT_I4; + V_I4(&var) = ssfBITBUCKET; + r = IShellDispatch_NameSpace(sd, var, &folder); + ok(r == S_OK || + broken(r == S_FALSE), /* NT4 */ + "IShellDispatch::NameSpace failed: %08x\n", r); + if (r == S_OK) + { + r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2); + ok(r == S_OK || + broken(r == E_NOINTERFACE), /* NT4 */ + "Folder::QueryInterface failed: %08x\n", r); + if (r == S_OK) + { + r = Folder2_get_Self(folder2, &item); + ok(r == S_OK, "Folder::get_Self failed: %08x\n", r); + if (r == S_OK) + { + r = FolderItem_get_Path(item, &item_path); + todo_wine + ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r); + todo_wine + ok(!lstrcmpW(item_path, clsidW), "expected %s, got %s\n", + wine_dbgstr_w(clsidW), wine_dbgstr_w(item_path)); + SysFreeString(item_path); + FolderItem_Release(item); + } + Folder2_Release(folder2); + } + Folder_Release(folder); + } + + GetTempPathW(MAX_PATH, tempW); + GetCurrentDirectoryW(MAX_PATH, curW); + SetCurrentDirectoryW(tempW); + CreateDirectoryW(winetestW, NULL); + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(winetestW); + r = IShellDispatch_NameSpace(sd, var, &folder); + ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r); + SysFreeString(V_BSTR(&var)); + + GetFullPathNameW(winetestW, MAX_PATH, tempW, NULL); + if (pGetLongPathNameW) + { + len = pGetLongPathNameW(tempW, NULL, 0); + long_pathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (long_pathW) + pGetLongPathNameW(tempW, long_pathW, len); + } + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(tempW); + r = IShellDispatch_NameSpace(sd, var, &folder); + ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r); + if (r == S_OK) + { + r = Folder_get_Title(folder, &title); + ok(r == S_OK, "Folder::get_Title failed: %08x\n", r); + if (r == S_OK) + { + ok(!lstrcmpW(title, winetestW), "bad title: %s\n", + wine_dbgstr_w(title)); + SysFreeString(title); + } + r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2); + ok(r == S_OK || + broken(r == E_NOINTERFACE), /* NT4 */ + "Folder::QueryInterface failed: %08x\n", r); + if (r == S_OK) + { + r = Folder2_get_Self(folder2, &item); + ok(r == S_OK, "Folder::get_Self failed: %08x\n", r); + if (r == S_OK) + { + r = FolderItem_get_Path(item, &item_path); + ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r); + if (long_pathW) + ok(!lstrcmpW(item_path, long_pathW), + "expected %s, got %s\n", wine_dbgstr_w(long_pathW), + wine_dbgstr_w(item_path)); + SysFreeString(item_path); + FolderItem_Release(item); + } + Folder2_Release(folder2); + } + Folder_Release(folder); + } + SysFreeString(V_BSTR(&var)); + + len = lstrlenW(tempW); + if (len < MAX_PATH - 1) + { + lstrcatW(tempW, backslashW); + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(tempW); + r = IShellDispatch_NameSpace(sd, var, &folder); + ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r); + if (r == S_OK) + { + r = Folder_get_Title(folder, &title); + ok(r == S_OK, "Folder::get_Title failed: %08x\n", r); + if (r == S_OK) + { + ok(!lstrcmpW(title, winetestW), "bad title: %s\n", + wine_dbgstr_w(title)); + SysFreeString(title); + } + r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2); + ok(r == S_OK || + broken(r == E_NOINTERFACE), /* NT4 */ + "Folder::QueryInterface failed: %08x\n", r); + if (r == S_OK) + { + r = Folder2_get_Self(folder2, &item); + ok(r == S_OK, "Folder::get_Self failed: %08x\n", r); + if (r == S_OK) + { + r = FolderItem_get_Path(item, &item_path); + ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r); + if (long_pathW) + ok(!lstrcmpW(item_path, long_pathW), + "expected %s, got %s\n", wine_dbgstr_w(long_pathW), + wine_dbgstr_w(item_path)); + SysFreeString(item_path); + FolderItem_Release(item); + } + Folder2_Release(folder2); + } + Folder_Release(folder); + } + SysFreeString(V_BSTR(&var)); + } + + HeapFree(GetProcessHeap(), 0, long_pathW); + RemoveDirectoryW(winetestW); + SetCurrentDirectoryW(curW); + IShellDispatch_Release(sd); +} + +START_TEST(shelldispatch) +{ + HRESULT r; + + r = CoInitialize(NULL); + ok(SUCCEEDED(r), "CoInitialize failed: %08x\n", r); + if (FAILED(r)) + return; + + init_function_pointers(); + test_namespace(); + + CoUninitialize(); +} diff --git a/rostests/winetests/shell32/shelllink.c b/rostests/winetests/shell32/shelllink.c index 1b38b0dc82a..d28cff0ac27 100755 --- a/rostests/winetests/shell32/shelllink.c +++ b/rostests/winetests/shell32/shelllink.c @@ -168,15 +168,15 @@ static void test_get_set(void) CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&slW); - if (!slW) - skip("SetPath with NULL parameter crashes on Win9x\n"); + if (!slW /* Win9x */ || !pGetLongPathNameA /* NT4 */) + skip("SetPath with NULL parameter crashes on Win9x and some NT4\n"); else { IShellLinkW_Release(slW); r = IShellLinkA_SetPath(sl, NULL); ok(r==E_INVALIDARG || broken(r==S_OK), /* Some Win95 and NT4 */ - "SetPath failed (0x%08x)\n", r); + "SetPath returned wrong error (0x%08x)\n", r); } r = IShellLinkA_SetPath(sl, ""); @@ -204,16 +204,14 @@ static void test_get_set(void) /* Test the interaction of SetPath and SetIDList */ tmp_pidl=NULL; r = IShellLinkA_GetIDList(sl, &tmp_pidl); - todo_wine ok(r == S_OK, "GetIDList failed (0x%08x)\n", r); + ok(r == S_OK, "GetIDList failed (0x%08x)\n", r); if (r == S_OK) { BOOL ret; strcpy(buffer,"garbage"); ret = SHGetPathFromIDListA(tmp_pidl, buffer); - todo_wine { ok(ret, "SHGetPathFromIDListA failed\n"); - } if (ret) ok(lstrcmpi(buffer,str)==0, "GetIDList returned '%s'\n", buffer); pILFree(tmp_pidl); @@ -326,9 +324,7 @@ static void test_get_set(void) i=0xdeadbeef; strcpy(buffer,"garbage"); r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i); - todo_wine { ok(r == S_OK, "GetIconLocation failed (0x%08x)\n", r); - } ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer); ok(i==0, "GetIconLocation returned %d\n", i); @@ -435,7 +431,7 @@ void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails) if (0) { /* crashes on XP */ - r = IPersistFile_GetCurFile(pf, NULL); + IPersistFile_GetCurFile(pf, NULL); } /* test GetCurFile before ::Save */ @@ -685,6 +681,24 @@ static void test_load_save(void) create_lnk(lnkfile, &desc, 0); check_lnk(lnkfile, &desc, 0x0); + /* Test omitting .exe from an absolute path */ + p=strrchr(realpath, '.'); + if (p) + *p='\0'; + + desc.description="absolute path without .exe"; + desc.workdir=mydir; + desc.path=realpath; + desc.pidl=NULL; + desc.arguments="/option1 /option2 \"Some string\""; + desc.showcmd=SW_SHOWNORMAL; + desc.icon=mypath; + desc.icon_id=0; + desc.hotkey=0x1234; + create_lnk(lnkfile, &desc, 0); + strcat(realpath, ".exe"); + check_lnk(lnkfile, &desc, 0x4); + /* Overwrite the existing lnk file and test link to a command on the path */ desc.description="command on path"; desc.workdir=mypath; @@ -701,6 +715,22 @@ static void test_load_save(void) desc.path=realpath; check_lnk(lnkfile, &desc, 0x0); + /* Test omitting .exe from a command on the path */ + desc.description="command on path without .exe"; + desc.workdir=mypath; + desc.path="rundll32"; + desc.pidl=NULL; + desc.arguments="/option1 /option2 \"Some string\""; + desc.showcmd=SW_SHOWNORMAL; + desc.icon=mypath; + desc.icon_id=0; + desc.hotkey=0x1234; + create_lnk(lnkfile, &desc, 0); + /* Check that link is created to proper location */ + SearchPathA( NULL, "rundll32", NULL, MAX_PATH, realpath, NULL); + desc.path=realpath; + check_lnk(lnkfile, &desc, 0x4); + /* Create a temporary non-executable file */ r=GetTempPath(sizeof(mypath), mypath); ok(r +#include + +#include "winbase.h" +#include "shlobj.h" +#include "initguid.h" + +DEFINE_GUID(FMTID_Test,0x12345678,0x1234,0x1234,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12); +DEFINE_GUID(FMTID_NotExisting, 0x12345678,0x1234,0x1234,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x13); + +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT(func) \ + do { \ + CHECK_EXPECT2(func); \ + expect_ ## func = FALSE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +DEFINE_EXPECT(Create); +DEFINE_EXPECT(Delete); +DEFINE_EXPECT(Open); +DEFINE_EXPECT(ReadMultiple); +DEFINE_EXPECT(ReadMultipleCodePage); +DEFINE_EXPECT(Release); +DEFINE_EXPECT(Stat); +DEFINE_EXPECT(WriteMultiple); + +static HRESULT (WINAPI *pSHPropStgCreate)(IPropertySetStorage*, REFFMTID, const CLSID*, + DWORD, DWORD, DWORD, IPropertyStorage**, UINT*); +static HRESULT (WINAPI *pSHPropStgReadMultiple)(IPropertyStorage*, UINT, + ULONG, const PROPSPEC*, PROPVARIANT*); +static HRESULT (WINAPI *pSHPropStgWriteMultiple)(IPropertyStorage*, UINT*, + ULONG, const PROPSPEC*, PROPVARIANT*, PROPID); + +static void init(void) +{ + HMODULE hmod = GetModuleHandleA("shell32.dll"); + + pSHPropStgCreate = (void*)GetProcAddress(hmod, "SHPropStgCreate"); + pSHPropStgReadMultiple = (void*)GetProcAddress(hmod, "SHPropStgReadMultiple"); + pSHPropStgWriteMultiple = (void*)GetProcAddress(hmod, "SHPropStgWriteMultiple"); +} + +static HRESULT WINAPI PropertyStorage_QueryInterface(IPropertyStorage *This, + REFIID riid, void **ppvObject) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static ULONG WINAPI PropertyStorage_AddRef(IPropertyStorage *This) +{ + ok(0, "unexpected call\n"); + return 2; +} + +static ULONG WINAPI PropertyStorage_Release(IPropertyStorage *This) +{ + CHECK_EXPECT(Release); + return 1; +} + +static HRESULT WINAPI PropertyStorage_ReadMultiple(IPropertyStorage *This, ULONG cpspec, + const PROPSPEC *rgpspec, PROPVARIANT *rgpropvar) +{ + if(cpspec == 1) { + CHECK_EXPECT(ReadMultipleCodePage); + + ok(rgpspec != NULL, "rgpspec = NULL\n"); + ok(rgpropvar != NULL, "rgpropvar = NULL\n"); + + ok(rgpspec[0].ulKind == PRSPEC_PROPID, "rgpspec[0].ulKind = %d\n", rgpspec[0].ulKind); + ok(rgpspec[0].u.propid == PID_CODEPAGE, "rgpspec[0].propid = %d\n", rgpspec[0].u.propid); + + rgpropvar[0].vt = VT_I2; + rgpropvar[0].u.iVal = 1234; + } else { + CHECK_EXPECT(ReadMultiple); + + ok(cpspec == 10, "cpspec = %u\n", cpspec); + ok(rgpspec == (void*)0xdeadbeef, "rgpspec = %p\n", rgpspec); + ok(rgpropvar != NULL, "rgpropvar = NULL\n"); + + ok(rgpropvar[0].vt==0 || broken(rgpropvar[0].vt==VT_BSTR), "rgpropvar[0].vt = %d\n", rgpropvar[0].vt); + + rgpropvar[0].vt = VT_BSTR; + rgpropvar[0].u.bstrVal = (void*)0xdeadbeef; + rgpropvar[1].vt = VT_LPSTR; + rgpropvar[1].u.pszVal = (void*)0xdeadbeef; + rgpropvar[2].vt = VT_BYREF|VT_I1; + rgpropvar[2].u.pcVal = (void*)0xdeadbeef; + rgpropvar[3].vt = VT_BYREF|VT_VARIANT; + rgpropvar[3].u.pvarVal = (void*)0xdeadbeef; + } + + return S_OK; +} + +static HRESULT WINAPI PropertyStorage_WriteMultiple(IPropertyStorage *This, ULONG cpspec, + const PROPSPEC *rgpspec, const PROPVARIANT *rgpropvar, + PROPID propidNameFirst) +{ + CHECK_EXPECT(WriteMultiple); + + ok(cpspec == 20, "cpspec = %d\n", cpspec); + ok(rgpspec == (void*)0xdeadbeef, "rgpspec = %p\n", rgpspec); + ok(rgpropvar == (void*)0xdeadbeef, "rgpropvar = %p\n", rgpspec); + ok(propidNameFirst == PID_FIRST_USABLE, "propidNameFirst = %d\n", propidNameFirst); + return S_OK; +} + +static HRESULT WINAPI PropertyStorage_DeleteMultiple(IPropertyStorage *This, ULONG cpspec, + const PROPSPEC *rgpspec) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_ReadPropertyNames(IPropertyStorage *This, ULONG cpropid, + const PROPID *rgpropid, LPOLESTR *rglpwstrName) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_WritePropertyNames(IPropertyStorage *This, ULONG cpropid, + const PROPID *rgpropid, const LPOLESTR *rglpwstrName) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_DeletePropertyNames(IPropertyStorage *This, ULONG cpropid, + const PROPID *rgpropid) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_Commit(IPropertyStorage *This, DWORD grfCommitFlags) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_Revert(IPropertyStorage *This) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_Enum(IPropertyStorage *This, IEnumSTATPROPSTG **ppenum) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_SetTimes(IPropertyStorage *This, const FILETIME *pctime, + const FILETIME *patime, const FILETIME *pmtime) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_SetClass(IPropertyStorage *This, REFCLSID clsid) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI PropertyStorage_Stat(IPropertyStorage *This, STATPROPSETSTG *statpsstg) +{ + CHECK_EXPECT(Stat); + + memset(statpsstg, 0, sizeof(STATPROPSETSTG)); + memcpy(&statpsstg->fmtid, &FMTID_Test, sizeof(FMTID)); + statpsstg->grfFlags = PROPSETFLAG_ANSI; + return S_OK; +} + +static IPropertyStorageVtbl PropertyStorageVtbl = { + PropertyStorage_QueryInterface, + PropertyStorage_AddRef, + PropertyStorage_Release, + PropertyStorage_ReadMultiple, + PropertyStorage_WriteMultiple, + PropertyStorage_DeleteMultiple, + PropertyStorage_ReadPropertyNames, + PropertyStorage_WritePropertyNames, + PropertyStorage_DeletePropertyNames, + PropertyStorage_Commit, + PropertyStorage_Revert, + PropertyStorage_Enum, + PropertyStorage_SetTimes, + PropertyStorage_SetClass, + PropertyStorage_Stat +}; + +static IPropertyStorage PropertyStorage = { &PropertyStorageVtbl }; + +static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *This, + REFIID riid, void **ppvObject) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *This) +{ + ok(0, "unexpected call\n"); + return 2; +} + +static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *This) +{ + ok(0, "unexpected call\n"); + return 1; +} + +static HRESULT WINAPI PropertySetStorage_Create(IPropertySetStorage *This, + REFFMTID rfmtid, const CLSID *pclsid, DWORD grfFlags, + DWORD grfMode, IPropertyStorage **ppprstg) +{ + CHECK_EXPECT(Create); + ok(IsEqualGUID(rfmtid, &FMTID_Test) || IsEqualGUID(rfmtid, &FMTID_NotExisting), + "Incorrect rfmtid value\n"); + ok(pclsid == NULL, "pclsid != NULL\n"); + ok(grfFlags == PROPSETFLAG_ANSI, "grfFlags = %x\n", grfFlags); + ok(grfMode == STGM_READ, "grfMode = %x\n", grfMode); + + *ppprstg = &PropertyStorage; + return S_OK; +} + +static HRESULT WINAPI PropertySetStorage_Open(IPropertySetStorage *This, + REFFMTID rfmtid, DWORD grfMode, IPropertyStorage **ppprstg) +{ + CHECK_EXPECT(Open); + + if(IsEqualGUID(rfmtid, &FMTID_Test)) { + ok(grfMode == STGM_READ, "grfMode = %x\n", grfMode); + + *ppprstg = &PropertyStorage; + return S_OK; + } + + return STG_E_FILENOTFOUND; +} + +static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *This, + REFFMTID rfmtid) +{ + CHECK_EXPECT(Delete); + ok(IsEqualGUID(rfmtid, &FMTID_Test), "wrong rfmtid value\n"); + return S_OK; +} + +static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *This, + IEnumSTATPROPSETSTG **ppenum) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static IPropertySetStorageVtbl PropertySetStorageVtbl = { + PropertySetStorage_QueryInterface, + PropertySetStorage_AddRef, + PropertySetStorage_Release, + PropertySetStorage_Create, + PropertySetStorage_Open, + PropertySetStorage_Delete, + PropertySetStorage_Enum +}; + +static IPropertySetStorage PropertySetStorage = { &PropertySetStorageVtbl }; + +static void test_SHPropStg_functions(void) +{ + IPropertyStorage *property_storage; + UINT codepage; + PROPVARIANT read[10]; + HRESULT hres; + + if(!pSHPropStgCreate || !pSHPropStgReadMultiple || !pSHPropStgWriteMultiple) { + win_skip("SHPropStg* functions are missing\n"); + return; + } + + if(0) { + /* Crashes on Windows */ + pSHPropStgCreate(NULL, &FMTID_Test, NULL, PROPSETFLAG_DEFAULT, + STGM_READ, OPEN_EXISTING, &property_storage, &codepage); + pSHPropStgCreate(&PropertySetStorage, NULL, NULL, PROPSETFLAG_DEFAULT, + STGM_READ, OPEN_EXISTING, &property_storage, &codepage); + pSHPropStgCreate(&PropertySetStorage, &FMTID_Test, NULL, PROPSETFLAG_DEFAULT, + STGM_READ, OPEN_EXISTING, NULL, &codepage); + } + + SET_EXPECT(Open); + SET_EXPECT(ReadMultipleCodePage); + hres = pSHPropStgCreate(&PropertySetStorage, &FMTID_Test, NULL, PROPSETFLAG_DEFAULT, + STGM_READ, OPEN_EXISTING, &property_storage, &codepage); + ok(codepage == 1234, "codepage = %d\n", codepage); + ok(hres == S_OK, "hres = %x\n", hres); + CHECK_CALLED(Open); + CHECK_CALLED(ReadMultipleCodePage); + + SET_EXPECT(Open); + hres = pSHPropStgCreate(&PropertySetStorage, &FMTID_NotExisting, NULL, + PROPSETFLAG_DEFAULT, STGM_READ, OPEN_EXISTING, &property_storage, &codepage); + ok(hres == STG_E_FILENOTFOUND, "hres = %x\n", hres); + CHECK_CALLED(Open); + + SET_EXPECT(Open); + SET_EXPECT(Release); + SET_EXPECT(Delete); + SET_EXPECT(Create); + SET_EXPECT(ReadMultipleCodePage); + hres = pSHPropStgCreate(&PropertySetStorage, &FMTID_Test, NULL, PROPSETFLAG_ANSI, + STGM_READ, CREATE_ALWAYS, &property_storage, &codepage); + ok(codepage == 1234, "codepage = %d\n", codepage); + ok(hres == S_OK, "hres = %x\n", hres); + CHECK_CALLED(Open); + CHECK_CALLED(Release); + CHECK_CALLED(Delete); + CHECK_CALLED(Create); + CHECK_CALLED(ReadMultipleCodePage); + + SET_EXPECT(Open); + SET_EXPECT(Create); + SET_EXPECT(ReadMultipleCodePage); + hres = pSHPropStgCreate(&PropertySetStorage, &FMTID_NotExisting, NULL, PROPSETFLAG_ANSI, + STGM_READ, CREATE_ALWAYS, &property_storage, &codepage); + ok(codepage == 1234, "codepage = %d\n", codepage); + ok(hres == S_OK, "hres = %x\n", hres); + CHECK_CALLED(Open); + CHECK_CALLED(Create); + CHECK_CALLED(ReadMultipleCodePage); + + SET_EXPECT(Open); + hres = pSHPropStgCreate(&PropertySetStorage, &FMTID_Test, &FMTID_NotExisting, + PROPSETFLAG_DEFAULT, STGM_READ, OPEN_EXISTING, &property_storage, NULL); + ok(hres == S_OK, "hres = %x\n", hres); + CHECK_CALLED(Open); + + SET_EXPECT(Stat); + SET_EXPECT(ReadMultipleCodePage); + SET_EXPECT(WriteMultiple); + codepage = 0; + hres = pSHPropStgWriteMultiple(property_storage, &codepage, 20, (void*)0xdeadbeef, (void*)0xdeadbeef, PID_FIRST_USABLE); + ok(hres == S_OK, "hres = %x\n", hres); + ok(codepage == 1234, "codepage = %d\n", codepage); + CHECK_CALLED(Stat); + CHECK_CALLED(ReadMultipleCodePage); + CHECK_CALLED(WriteMultiple); + + SET_EXPECT(Stat); + SET_EXPECT(ReadMultipleCodePage); + SET_EXPECT(WriteMultiple); + hres = pSHPropStgWriteMultiple(property_storage, NULL, 20, (void*)0xdeadbeef, (void*)0xdeadbeef, PID_FIRST_USABLE); + ok(hres == S_OK, "hres = %x\n", hres); + CHECK_CALLED(Stat); + CHECK_CALLED(ReadMultipleCodePage); + CHECK_CALLED(WriteMultiple); + + SET_EXPECT(Stat); + SET_EXPECT(WriteMultiple); + codepage = 1000; + hres = pSHPropStgWriteMultiple(property_storage, &codepage, 20, (void*)0xdeadbeef, (void*)0xdeadbeef, PID_FIRST_USABLE); + ok(hres == S_OK, "hres = %x\n", hres); + ok(codepage == 1000, "codepage = %d\n", codepage); + CHECK_CALLED(Stat); + CHECK_CALLED(WriteMultiple); + + read[0].vt = VT_BSTR; + read[0].u.bstrVal = (void*)0xdeadbeef; + SET_EXPECT(ReadMultiple); + SET_EXPECT(ReadMultipleCodePage); + SET_EXPECT(Stat); + hres = pSHPropStgReadMultiple(property_storage, 0, 10, (void*)0xdeadbeef, read); + ok(hres == S_OK, "hres = %x\n", hres); + CHECK_CALLED(ReadMultiple); + CHECK_CALLED(ReadMultipleCodePage); + CHECK_CALLED(Stat); + + SET_EXPECT(ReadMultiple); + SET_EXPECT(Stat); + hres = pSHPropStgReadMultiple(property_storage, 1251, 10, (void*)0xdeadbeef, read); + ok(hres == S_OK, "hres = %x\n", hres); + CHECK_CALLED(ReadMultiple); + CHECK_CALLED(Stat); +} + +START_TEST(shellole) +{ + init(); + + test_SHPropStg_functions(); +} diff --git a/rostests/winetests/shell32/shellpath.c b/rostests/winetests/shell32/shellpath.c index b874b5a1ee4..0db72dfc15e 100644 --- a/rostests/winetests/shell32/shellpath.c +++ b/rostests/winetests/shell32/shellpath.c @@ -31,6 +31,7 @@ #include "shlobj.h" #include "shlwapi.h" #include "initguid.h" +#include "knownfolders.h" #include "wine/test.h" /* CSIDL_MYDOCUMENTS is now the same as CSIDL_PERSONAL, but what we want @@ -38,6 +39,8 @@ */ #define OLD_CSIDL_MYDOCUMENTS 0x000c +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) ) #endif @@ -93,6 +96,10 @@ static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST); static int (WINAPI *pSHFileOperationA)(LPSHFILEOPSTRUCTA); static HRESULT (WINAPI *pSHGetMalloc)(LPMALLOC *); static UINT (WINAPI *pGetSystemWow64DirectoryA)(LPSTR,UINT); +static HRESULT (WINAPI *pSHGetKnownFolderPath)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *); +static HRESULT (WINAPI *pSHSetKnownFolderPath)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR); +static HRESULT (WINAPI *pSHGetFolderPathEx)(REFKNOWNFOLDERID, DWORD, HANDLE, LPWSTR, DWORD); + static DLLVERSIONINFO shellVersion = { 0 }; static LPMALLOC pMalloc; static const BYTE guidType[] = { PT_GUID }; @@ -187,7 +194,10 @@ static void loadShell32(void) GET_PROC(DllGetVersion) GET_PROC(SHGetFolderPathA) + GET_PROC(SHGetFolderPathEx) GET_PROC(SHGetFolderLocation) + GET_PROC(SHGetKnownFolderPath) + GET_PROC(SHSetKnownFolderPath) GET_PROC(SHGetSpecialFolderPathA) GET_PROC(SHGetSpecialFolderLocation) GET_PROC(ILFindLastID) @@ -352,7 +362,7 @@ static void test_parameters(void) BOOL ret; if (0) - ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE); + pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE); /* odd but true: calling with a NULL path still succeeds if it's a real * dir (on some windows platform). on winME it generates exception. @@ -833,6 +843,1651 @@ static void test_NonExistentPath(void) else skip("RegOpenKeyExA(HKEY_CURRENT_USER, %s, ...) failed\n", userShellFolders); } +static void test_SHGetFolderPathEx(void) +{ + HRESULT hr; + WCHAR buffer[MAX_PATH], *path; + DWORD len; + + if (!pSHGetKnownFolderPath || !pSHGetFolderPathEx) + { + win_skip("SHGetKnownFolderPath or SHGetFolderPathEx not available\n"); + return; + } + +if (0) { /* crashes */ + hr = pSHGetKnownFolderPath(&FOLDERID_Desktop, 0, NULL, NULL); + ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr); +} + path = NULL; + hr = pSHGetKnownFolderPath(&FOLDERID_Desktop, 0, NULL, &path); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(path != NULL, "expected path != NULL\n"); + + hr = pSHGetFolderPathEx(&FOLDERID_Desktop, 0, NULL, buffer, MAX_PATH); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(!lstrcmpiW(path, buffer), "expected equal paths\n"); + len = lstrlenW(buffer); + CoTaskMemFree(path); + + hr = pSHGetFolderPathEx(&FOLDERID_Desktop, 0, NULL, buffer, 0); + ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr); + +if (0) { /* crashes */ + hr = pSHGetFolderPathEx(&FOLDERID_Desktop, 0, NULL, NULL, len + 1); + ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr); + + hr = pSHGetFolderPathEx(NULL, 0, NULL, buffer, MAX_PATH); + ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr); +} + hr = pSHGetFolderPathEx(&FOLDERID_Desktop, 0, NULL, buffer, len); + ok(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), "expected 0x8007007a, got 0x%08x\n", hr); + + hr = pSHGetFolderPathEx(&FOLDERID_Desktop, 0, NULL, buffer, len + 1); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); +} + +/* Standard CSIDL values (and their flags) uses only two less-significant bytes */ +#define NO_CSIDL 0x10000 +#define CSIDL_TODO_WINE 0x20000 +#define KNOWN_FOLDER(id, csidl, name, category, parent, relative_path, parsing_name, attributes, definitionFlags) \ + { &id, # id, csidl, # csidl, name, category, &parent, # parent, relative_path, parsing_name, attributes, definitionFlags, __LINE__ } + +/* non-published known folders test */ +static const GUID _FOLDERID_CryptoKeys = {0xB88F4DAA, 0xE7BD, 0x49A9, {0xB7, 0x4D, 0x02, 0x88, 0x5A, 0x5D, 0xC7, 0x65} }; +static const GUID _FOLDERID_DpapiKeys = {0x10C07CD0, 0xEF91, 0x4567, {0xB8, 0x50, 0x44, 0x8B, 0x77, 0xCB, 0x37, 0xF9} }; +static const GUID _FOLDERID_SystemCertificates = {0x54EED2E0, 0xE7CA, 0x4FDB, {0x91, 0x48, 0x0F, 0x42, 0x47, 0x29, 0x1C, 0xFA} }; +static const GUID _FOLDERID_CredentialManager = {0x915221FB, 0x9EFE, 0x4BDA, {0x8F, 0xD7, 0xF7, 0x8D, 0xCA, 0x77, 0x4F, 0x87} }; + +struct knownFolderDef { + const KNOWNFOLDERID *folderId; + const char *sFolderId; + const int csidl; + const char *sCsidl; + const char *sName; + const KF_CATEGORY category; + const KNOWNFOLDERID *fidParent; + const char *sParent; + const char *sRelativePath; + const char *sParsingName; + const DWORD attributes; + const KF_DEFINITION_FLAGS definitionFlags; + const int line; +}; + +/* Note: content of parsing name may vary between Windows versions. + * As a base, values from 6.0 (Vista) were used. Some entries may contain + * alternative values. In that case, Windows version where the value was + * found is noted. + * + * The list of values for parsing name was encoded as a number of null- + * terminated strings placed one by one (separated by null byte only). + * End of list is marked by two consecutive null bytes. + */ +static const struct knownFolderDef known_folders[] = { + KNOWN_FOLDER(FOLDERID_AddNewPrograms, + NO_CSIDL, + "AddNewProgramsFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{15eae92e-f17a-4431-9f28-805e482dafd4}\0" + "shell:::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{15eae92e-f17a-4431-9f28-805e482dafd4}\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_AdminTools, + CSIDL_ADMINTOOLS, + "Administrative Tools", + KF_CATEGORY_PERUSER, + FOLDERID_Programs, + "Administrative Tools", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_AppUpdates, + NO_CSIDL, + "AppUpdatesFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}\\::{d450a8a1-9568-45c7-9c0e-b4f9fb4537bd}\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}\\::{d450a8a1-9568-45c7-9c0e-b4f9fb4537bd}\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_CDBurning, + CSIDL_CDBURN_AREA, + "CD Burning", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Microsoft\\Windows\\Burn\\Burn", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_LOCAL_REDIRECT_ONLY), + KNOWN_FOLDER(FOLDERID_ChangeRemovePrograms, + NO_CSIDL, + "ChangeRemoveProgramsFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{7b81be6a-ce2b-4676-a29e-eb907a5126c5}\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_CommonAdminTools, + CSIDL_COMMON_ADMINTOOLS, + "Common Administrative Tools", + KF_CATEGORY_COMMON, + FOLDERID_CommonPrograms, + "Administrative Tools", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_CommonOEMLinks, + CSIDL_COMMON_OEM_LINKS,"OEM Links", + KF_CATEGORY_COMMON, + FOLDERID_ProgramData, + "OEM Links", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_CommonPrograms, + CSIDL_COMMON_PROGRAMS, + "Common Programs", + KF_CATEGORY_COMMON, + FOLDERID_CommonStartMenu, + "Programs", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_CommonStartMenu, + CSIDL_COMMON_STARTMENU, + "Common Start Menu", + KF_CATEGORY_COMMON, + FOLDERID_ProgramData, + "Microsoft\\Windows\\Start Menu", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_CommonStartup, + CSIDL_COMMON_STARTUP, + "Common Startup", + KF_CATEGORY_COMMON, + FOLDERID_CommonPrograms, + "StartUp", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_CommonTemplates, + CSIDL_COMMON_TEMPLATES, + "Common Templates", + KF_CATEGORY_COMMON, + FOLDERID_ProgramData, + "Microsoft\\Windows\\Templates", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ComputerFolder, + CSIDL_DRIVES, + "MyComputerFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_ConflictFolder, + NO_CSIDL, + "ConflictFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{E413D040-6788-4C22-957E-175D1C513A34},\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{E413D040-6788-4C22-957E-175D1C513A34},\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ConnectionsFolder, + CSIDL_CONNECTIONS, + "ConnectionsFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Contacts, + NO_CSIDL, + "Contacts", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Contacts", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{56784854-C6CB-462B-8169-88E350ACB882}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_ControlPanelFolder, + CSIDL_CONTROLS, + "ControlPanelFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Cookies, + CSIDL_COOKIES, + "Cookies", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\Cookies", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Desktop, + CSIDL_DESKTOP, + "Desktop", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Desktop", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_DeviceMetadataStore, + NO_CSIDL, + "Device Metadata Store", + KF_CATEGORY_COMMON, + FOLDERID_ProgramData, + "Microsoft\\Windows\\DeviceMetadataStore", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Documents, + CSIDL_MYDOCUMENTS, + "Personal", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Documents", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{FDD39AD0-238F-46AF-ADB4-6C85480369C7}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_DocumentsLibrary, + NO_CSIDL, + "DocumentsLibrary", + KF_CATEGORY_PERUSER, + FOLDERID_Libraries, + "Documents.library-ms", + "::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{7b0db17d-9cd2-4a93-9733-46cc89022e7c}\0\0", + 0, + KFDF_PRECREATE | KFDF_STREAM), + KNOWN_FOLDER(FOLDERID_Downloads, + NO_CSIDL, + "Downloads", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Downloads", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_Favorites, + CSIDL_FAVORITES, + "Favorites", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Favorites", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_Fonts, + CSIDL_FONTS, + "Fonts", + KF_CATEGORY_FIXED, + FOLDERID_Windows, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Games, + NO_CSIDL, + "Games", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{ED228FDF-9EA8-4870-83b1-96b02CFE0D52}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_GameTasks, + NO_CSIDL, + "GameTasks", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Microsoft\\Windows\\GameExplorer", + NULL, + 0, + KFDF_LOCAL_REDIRECT_ONLY), + KNOWN_FOLDER(FOLDERID_History, + CSIDL_HISTORY, + "History", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Microsoft\\Windows\\History", + NULL, + 0, + KFDF_LOCAL_REDIRECT_ONLY), + KNOWN_FOLDER(FOLDERID_HomeGroup, + NO_CSIDL, + "HomeGroupFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{B4FB3F98-C1EA-428d-A78A-D1F5659CBA93}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_ImplicitAppShortcuts, + NO_CSIDL, + "ImplicitAppShortcuts", + KF_CATEGORY_PERUSER, + FOLDERID_UserPinned, + "ImplicitAppShortcuts", + NULL, + 0, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_InternetCache, + CSIDL_INTERNET_CACHE, + "Cache", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Microsoft\\Windows\\Temporary Internet Files", + NULL, + 0, + KFDF_LOCAL_REDIRECT_ONLY), + KNOWN_FOLDER(FOLDERID_InternetFolder, + CSIDL_INTERNET, + "InternetFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{871C5380-42A0-1069-A2EA-08002B30309D}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_Libraries, + NO_CSIDL, + "Libraries", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\Libraries", + NULL, + 0, + KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_Links, + NO_CSIDL, + "Links", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Links", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{bfb9d5e0-c6a9-404c-b2b2-ae6db6af4968}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_LocalAppData, + CSIDL_LOCAL_APPDATA, + "Local AppData", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "AppData\\Local", + NULL, + 0, + KFDF_LOCAL_REDIRECT_ONLY | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_LocalAppDataLow, + NO_CSIDL, + "LocalAppDataLow", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "AppData\\LocalLow", + NULL, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + KFDF_LOCAL_REDIRECT_ONLY | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_LocalizedResourcesDir, + CSIDL_RESOURCES_LOCALIZED, + "LocalizedResourcesDir", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Music, + CSIDL_MYMUSIC, + "My Music", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Music", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4BD8D571-6D19-48D3-BE97-422220080E43}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_MusicLibrary, + NO_CSIDL, + "MusicLibrary", + KF_CATEGORY_PERUSER, + FOLDERID_Libraries, + "Music.library-ms", + "::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{2112AB0A-C86A-4ffe-A368-0DE96E47012E}\0\0", + 0, + KFDF_PRECREATE | KFDF_STREAM), + KNOWN_FOLDER(FOLDERID_NetHood, + CSIDL_NETHOOD, + "NetHood", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\Network Shortcuts", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_NetworkFolder, + CSIDL_NETWORK, + "NetworkPlacesFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_OriginalImages, + NO_CSIDL, + "Original Images", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Microsoft\\Windows Photo Gallery\\Original Images", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_PhotoAlbums, + NO_CSIDL, + "PhotoAlbums", + KF_CATEGORY_PERUSER, + FOLDERID_Pictures, + "Slide Shows", + NULL, + FILE_ATTRIBUTE_READONLY, + 0), + KNOWN_FOLDER(FOLDERID_Pictures, + CSIDL_MYPICTURES, + "My Pictures", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Pictures", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{33E28130-4E1E-4676-835A-98395C3BC3BB}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PicturesLibrary, + NO_CSIDL, + "PicturesLibrary", + KF_CATEGORY_PERUSER, + FOLDERID_Libraries, + "Pictures.library-ms", + "::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{A990AE9F-A03B-4e80-94BC-9912D7504104}\0\0", + 0, + KFDF_PRECREATE | KFDF_STREAM), + KNOWN_FOLDER(FOLDERID_Playlists, + NO_CSIDL, + "Playlists", + KF_CATEGORY_PERUSER, + FOLDERID_Music, + "Playlists", + NULL, + FILE_ATTRIBUTE_READONLY, + 0), + KNOWN_FOLDER(FOLDERID_PrintersFolder, + CSIDL_PRINTERS, + "PrintersFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_PrintHood, + CSIDL_PRINTHOOD, + "PrintHood", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\Printer Shortcuts", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Profile, + CSIDL_PROFILE, + "Profile", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ProgramData, + CSIDL_COMMON_APPDATA, + "Common AppData", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ProgramFiles, + CSIDL_PROGRAM_FILES, + "ProgramFiles", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE + ), + KNOWN_FOLDER(FOLDERID_ProgramFilesCommon, + CSIDL_PROGRAM_FILES_COMMON, + "ProgramFilesCommon", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ProgramFilesCommonX64, + NO_CSIDL, + "ProgramFilesCommonX64", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ProgramFilesCommonX86, + NO_CSIDL, + "ProgramFilesCommonX86", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ProgramFilesX64, + NO_CSIDL, + "ProgramFilesX64", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_ProgramFilesX86, + CSIDL_PROGRAM_FILESX86, + "ProgramFilesX86", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_Programs, + CSIDL_PROGRAMS, + "Programs", + KF_CATEGORY_PERUSER, + FOLDERID_StartMenu, + "Programs", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_Public, + NO_CSIDL, + "Public", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + "::{4336a54d-038b-4685-ab02-99bb52d3fb8b}\0" + "(null)\0\0" /* 6.1 */, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicDesktop, + CSIDL_COMMON_DESKTOPDIRECTORY, + "Common Desktop", + KF_CATEGORY_COMMON, + FOLDERID_Public, + "Desktop", + NULL, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicDocuments, + CSIDL_COMMON_DOCUMENTS, + "Common Documents", + KF_CATEGORY_COMMON, + FOLDERID_Public, + "Documents", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicDownloads, + NO_CSIDL, + "CommonDownloads", + KF_CATEGORY_COMMON, + FOLDERID_Public, + "Downloads", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicGameTasks, + NO_CSIDL, + "PublicGameTasks", + KF_CATEGORY_COMMON, + FOLDERID_ProgramData, + "Microsoft\\Windows\\GameExplorer", + NULL, + 0, + KFDF_LOCAL_REDIRECT_ONLY), + KNOWN_FOLDER(FOLDERID_PublicLibraries, + NO_CSIDL, + "PublicLibraries", + KF_CATEGORY_COMMON, + FOLDERID_Public, + "Libraries", + NULL, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicMusic, + CSIDL_COMMON_MUSIC, + "CommonMusic", + KF_CATEGORY_COMMON, + FOLDERID_Public, + "Music", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicPictures, + CSIDL_COMMON_PICTURES, + "CommonPictures", + KF_CATEGORY_COMMON, + FOLDERID_Public, + "Pictures", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicRingtones, + NO_CSIDL, + "CommonRingtones", + KF_CATEGORY_COMMON, + FOLDERID_ProgramData, + "Microsoft\\Windows\\Ringtones", + NULL, + 0, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_PublicVideos, + CSIDL_COMMON_VIDEO, + "CommonVideo", + KF_CATEGORY_COMMON, + FOLDERID_Public, + "Videos", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_QuickLaunch, + NO_CSIDL, + "Quick Launch", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Internet Explorer\\Quick Launch", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Recent, + CSIDL_RECENT, + "Recent", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\Recent", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_RecordedTVLibrary, + NO_CSIDL, + "RecordedTVLibrary", + KF_CATEGORY_COMMON, + FOLDERID_PublicLibraries, + "RecordedTV.library-ms", + NULL, + 0, + KFDF_PRECREATE | KFDF_STREAM), + KNOWN_FOLDER(FOLDERID_RecycleBinFolder, + CSIDL_BITBUCKET, + "RecycleBinFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{645FF040-5081-101B-9F08-00AA002F954E}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_ResourceDir, + CSIDL_RESOURCES, + "ResourceDir", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Ringtones, + NO_CSIDL, + "Ringtones", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Microsoft\\Windows\\Ringtones", + NULL, + 0, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_RoamingAppData, + CSIDL_APPDATA, + "AppData", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "AppData\\Roaming", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_SampleMusic, + NO_CSIDL, + "SampleMusic", + KF_CATEGORY_COMMON, + FOLDERID_PublicMusic, + "Sample Music", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_SamplePictures, + NO_CSIDL, + "SamplePictures", + KF_CATEGORY_COMMON, + FOLDERID_PublicPictures, + "Sample Pictures", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_SamplePlaylists, + NO_CSIDL, + "SamplePlaylists", + KF_CATEGORY_COMMON, + FOLDERID_PublicMusic, + "Sample Playlists", + NULL, + FILE_ATTRIBUTE_READONLY, + 0), + KNOWN_FOLDER(FOLDERID_SampleVideos, + NO_CSIDL, + "SampleVideos", + KF_CATEGORY_COMMON, + FOLDERID_PublicVideos, + "Sample Videos", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_SavedGames, + NO_CSIDL, + "SavedGames", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Saved Games", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_SavedSearches, + NO_CSIDL, + "Searches", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Searches", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{7d1d3a04-debb-4115-95cf-2f29da2920da}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE | KFDF_PUBLISHEXPANDEDPATH), + KNOWN_FOLDER(FOLDERID_SEARCH_CSC, + NO_CSIDL, + "CSCFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "shell:::{BD7A2E7B-21CB-41b2-A086-B309680C6B7E}\\*\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_SearchHome, + NO_CSIDL, + "SearchHomeFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{9343812e-1c37-4a49-a12e-4b2d810d956b}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_SEARCH_MAPI, + NO_CSIDL, + "MAPIFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "shell:::{89D83576-6BD1-4C86-9454-BEB04E94C819}\\*\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_SendTo, + CSIDL_SENDTO, + "SendTo", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\SendTo", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_SidebarDefaultParts, + NO_CSIDL, + "Default Gadgets", + KF_CATEGORY_COMMON, + FOLDERID_ProgramFiles, + "Windows Sidebar\\Gadgets", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_SidebarParts, + NO_CSIDL, + "Gadgets", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Microsoft\\Windows Sidebar\\Gadgets", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_StartMenu, + CSIDL_STARTMENU, + "Start Menu", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\Start Menu", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_Startup, + CSIDL_STARTUP, + "Startup", + KF_CATEGORY_PERUSER, + FOLDERID_Programs, + "StartUp", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_SyncManagerFolder, + NO_CSIDL, + "SyncCenterFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_SyncResultsFolder, + NO_CSIDL, + "SyncResultsFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{BC48B32F-5910-47F5-8570-5074A8A5636A},\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{BC48B32F-5910-47F5-8570-5074A8A5636A},\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_SyncSetupFolder, + NO_CSIDL, + "SyncSetupFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{F1390A9A-A3F4-4E5D-9C5F-98F3BD8D935C},\0" + "::{26EE0668-A00A-44D7-9371-BEB064C98683}\\0\\::{9C73F5E5-7AE7-4E32-A8E8-8D23B85255BF}\\::{F1390A9A-A3F4-4E5D-9C5F-98F3BD8D935C},\0\0" /* 6.1 */, + 0, + 0), + KNOWN_FOLDER(FOLDERID_System, + CSIDL_SYSTEM, + "System", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_SystemX86, + CSIDL_SYSTEMX86, + "SystemX86", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_Templates, + CSIDL_TEMPLATES, + "Templates", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, + "Microsoft\\Windows\\Templates", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_UserPinned, + NO_CSIDL, + "User Pinned", + KF_CATEGORY_PERUSER, + FOLDERID_QuickLaunch, + "User Pinned", + NULL, + FILE_ATTRIBUTE_HIDDEN, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_UserProfiles, + NO_CSIDL, + "UserProfiles", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_UserProgramFiles, + NO_CSIDL, + "UserProgramFiles", + KF_CATEGORY_PERUSER, + FOLDERID_LocalAppData, + "Programs", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_UserProgramFilesCommon, + NO_CSIDL, + "UserProgramFilesCommon", + KF_CATEGORY_PERUSER, + FOLDERID_UserProgramFiles, + "Common", + NULL, + 0, + 0), + KNOWN_FOLDER(FOLDERID_UsersFiles, + NO_CSIDL, + "UsersFilesFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_UsersLibraries, + NO_CSIDL, + "UsersLibrariesFolder", + KF_CATEGORY_VIRTUAL, + GUID_NULL, + NULL, + "::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\0\0", + 0, + 0), + KNOWN_FOLDER(FOLDERID_Videos, + CSIDL_MYVIDEO, + "My Video", + KF_CATEGORY_PERUSER, + FOLDERID_Profile, + "Videos", + "::{59031a47-3f72-44a7-89c5-5595fe6b30ee}\\{18989B1D-99B5-455B-841C-AB7C74E4DDFC}\0\0", + FILE_ATTRIBUTE_READONLY, + KFDF_ROAMABLE | KFDF_PRECREATE), + KNOWN_FOLDER(FOLDERID_VideosLibrary, + NO_CSIDL, + "VideosLibrary", + KF_CATEGORY_PERUSER, + FOLDERID_Libraries, + "Videos.library-ms", + "::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{491E922F-5643-4af4-A7EB-4E7A138D8174}\0\0", + 0, + KFDF_PRECREATE | KFDF_STREAM), + KNOWN_FOLDER(FOLDERID_Windows, + CSIDL_WINDOWS, + "Windows", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(_FOLDERID_CredentialManager, + NO_CSIDL, + "CredentialManager", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(_FOLDERID_CryptoKeys, + NO_CSIDL, + "CryptoKeys", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(_FOLDERID_DpapiKeys, + NO_CSIDL, + "DpapiKeys", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + KNOWN_FOLDER(_FOLDERID_SystemCertificates, + NO_CSIDL, + "SystemCertificates", + KF_CATEGORY_FIXED, + GUID_NULL, + NULL, + NULL, + 0, + 0), + { NULL, NULL, 0, NULL, NULL, 0, 0 } +}; +#undef KNOWN_FOLDER +BOOL known_folder_found[sizeof(known_folders)/sizeof(known_folders[0])-1]; + +static void check_known_folder(IKnownFolderManager *mgr, KNOWNFOLDERID *folderId) +{ + HRESULT hr; + const struct knownFolderDef *known_folder = &known_folders[0]; + int csidl, expectedCsidl, ret; + KNOWNFOLDER_DEFINITION kfd; + IKnownFolder *folder; + WCHAR sName[1024], sRelativePath[MAX_PATH], sParsingName[MAX_PATH]; + BOOL validPath; + char sParentGuid[39]; + BOOL *current_known_folder_found = &known_folder_found[0]; + BOOL found = FALSE; + const char *srcParsingName; + + while(known_folder->folderId != NULL) + { + if(IsEqualGUID(known_folder->folderId, folderId)) + { + *current_known_folder_found = TRUE; + found = TRUE; + /* verify CSIDL */ + if(known_folder->csidl != NO_CSIDL) + { + expectedCsidl = known_folder->csidl & (~CSIDL_TODO_WINE); + + hr = IKnownFolderManager_FolderIdToCsidl(mgr, folderId, &csidl); + ok_(__FILE__, known_folder->line)(hr == S_OK, "cannot retrieve CSIDL for folder %s\n", known_folder->sFolderId); + + if(known_folder->csidl & CSIDL_TODO_WINE) + todo_wine ok_(__FILE__, known_folder->line)(csidl == expectedCsidl, "invalid CSIDL retrieved for folder %s. %d (%s) expected, but %d found\n", known_folder->sFolderId, expectedCsidl, known_folder->sCsidl, csidl); + else + ok_(__FILE__, known_folder->line)(csidl == expectedCsidl, "invalid CSIDL retrieved for folder %s. %d (%s) expected, but %d found\n", known_folder->sFolderId, expectedCsidl, known_folder->sCsidl, csidl); + } + + hr = IKnownFolderManager_GetFolder(mgr, folderId, &folder); + ok_(__FILE__, known_folder->line)(hr == S_OK, "cannot get known folder for %s\n", known_folder->sFolderId); + if(SUCCEEDED(hr)) + { + hr = IKnownFolder_GetFolderDefinition(folder, &kfd); + todo_wine + ok_(__FILE__, known_folder->line)(hr == S_OK, "cannot get known folder definition for %s\n", known_folder->sFolderId); + if(SUCCEEDED(hr)) + { + ret = MultiByteToWideChar(CP_ACP, 0, known_folder->sName, -1, sName, sizeof(sName)/sizeof(sName[0])); + ok_(__FILE__, known_folder->line)(ret != 0, "cannot convert known folder name \"%s\" to wide characters\n", known_folder->sName); + + todo_wine + ok_(__FILE__, known_folder->line)(lstrcmpW(kfd.pszName, sName)==0, "invalid known folder name returned for %s: %s expected, but %s retrieved\n", known_folder->sFolderId, wine_dbgstr_w(sName), wine_dbgstr_w(kfd.pszName)); + + ok_(__FILE__, known_folder->line)(kfd.category == known_folder->category, "invalid known folder category for %s: %d expected, but %d retrieved\n", known_folder->sFolderId, known_folder->category, kfd.category); + + printGUID(&kfd.fidParent, sParentGuid); + ok_(__FILE__, known_folder->line)(IsEqualGUID(known_folder->fidParent, &kfd.fidParent), "invalid known folder parent for %s: %s expected, but %s retrieved\n", known_folder->sFolderId, known_folder->sParent, sParentGuid); + + if(!known_folder->sRelativePath) + validPath = (kfd.pszRelativePath==NULL); + else + { + ret = MultiByteToWideChar(CP_ACP, 0, known_folder->sRelativePath, -1, sRelativePath, sizeof(sRelativePath)/sizeof(sRelativePath[0])); + ok_(__FILE__, known_folder->line)(ret != 0, "cannot convert known folder path \"%s\" to wide characters\n", known_folder->sRelativePath); + + validPath = (lstrcmpW(kfd.pszRelativePath, sRelativePath)==0); + } + + ok_(__FILE__, known_folder->line)(validPath, "invalid known folder relative path returned for %s: %s expected, but %s retrieved\n", known_folder->sFolderId, known_folder->sRelativePath, wine_dbgstr_w(kfd.pszRelativePath)); + + /* to check parsing name, we need to iterate list */ + srcParsingName = known_folder->sParsingName; + + /* if we expect NULL, then we don't even check the list */ + validPath = (srcParsingName==NULL) && (kfd.pszParsingName==NULL); + + if(srcParsingName) + while(*srcParsingName && !validPath) + { + /* when NULL is only one of possible value, we mark path as valid */ + validPath = (strcmp(srcParsingName, "(null)")==0) && (kfd.pszParsingName==NULL); + + /* in the other case, we compare string from list with retrieved value */ + if(!validPath) + { + ret = MultiByteToWideChar(CP_ACP, 0, srcParsingName, -1, sParsingName, sizeof(sParsingName)/sizeof(sParsingName[0])); + ok_(__FILE__, known_folder->line)(ret != 0, "cannot convert known folder path \"%s\" to wide characters\n", srcParsingName); + + validPath = (lstrcmpW(kfd.pszParsingName, sParsingName)==0); + } + + srcParsingName += strlen(srcParsingName)+1; + } + + ok_(__FILE__, known_folder->line)(validPath, "invalid known folder parsing name returned for %s: %s retrieved\n", known_folder->sFolderId, wine_dbgstr_w(kfd.pszParsingName)); + + ok_(__FILE__, known_folder->line)(known_folder->attributes == kfd.dwAttributes, "invalid known folder attributes for %s: 0x%08x expected, but 0x%08x retrieved\n", known_folder->sFolderId, known_folder->attributes, kfd.dwAttributes); + + ok_(__FILE__, known_folder->line)(!(kfd.kfdFlags & (~known_folder->definitionFlags)), "invalid known folder flags for %s: 0x%08x expected, but 0x%08x retrieved\n", known_folder->sFolderId, known_folder->definitionFlags, kfd.kfdFlags); + + FreeKnownFolderDefinitionFields(&kfd); + } + + IKnownFolder_Release(folder); + } + + break; + } + known_folder++; + current_known_folder_found++; + } + + if(!found) + { + printGUID(folderId, sParentGuid); + trace("unknown known folder found: %s\n", sParentGuid); + + hr = IKnownFolderManager_GetFolder(mgr, folderId, &folder); + ok(hr == S_OK, "cannot get known folder for %s\n", sParentGuid); + if(SUCCEEDED(hr)) + { + hr = IKnownFolder_GetFolderDefinition(folder, &kfd); + todo_wine + ok(hr == S_OK, "cannot get known folder definition for %s\n", sParentGuid); + if(SUCCEEDED(hr)) + { + trace(" category: %d\n", kfd.category); + trace(" name: %s\n", wine_dbgstr_w(kfd.pszName)); + trace(" description: %s\n", wine_dbgstr_w(kfd.pszDescription)); + printGUID(&kfd.fidParent, sParentGuid); + trace(" parent: %s\n", sParentGuid); + trace(" relative path: %s\n", wine_dbgstr_w(kfd.pszRelativePath)); + trace(" parsing name: %s\n", wine_dbgstr_w(kfd.pszParsingName)); + trace(" tooltip: %s\n", wine_dbgstr_w(kfd.pszTooltip)); + trace(" localized name: %s\n", wine_dbgstr_w(kfd.pszLocalizedName)); + trace(" icon: %s\n", wine_dbgstr_w(kfd.pszIcon)); + trace(" security: %s\n", wine_dbgstr_w(kfd.pszSecurity)); + trace(" attributes: 0x%08x\n", kfd.dwAttributes); + trace(" flags: 0x%08x\n", kfd.kfdFlags); + printGUID(&kfd.ftidType, sParentGuid); + trace(" type: %s\n", sParentGuid); + FreeKnownFolderDefinitionFields(&kfd); + } + + IKnownFolder_Release(folder); + } + } +} +#undef NO_CSIDL +#undef CSIDL_TODO_WINE + +static void test_knownFolders(void) +{ + static const WCHAR sWindows[] = {'W','i','n','d','o','w','s',0}; + static const WCHAR sExample[] = {'E','x','a','m','p','l','e',0}; + static const WCHAR sExample2[] = {'E','x','a','m','p','l','e','2',0}; + static const WCHAR sSubFolder[] = {'S','u','b','F','o','l','d','e','r',0}; + static const WCHAR sBackslash[] = {'\\',0}; + static const KNOWNFOLDERID newFolderId = {0x01234567, 0x89AB, 0xCDEF, {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x01} }; + static const KNOWNFOLDERID subFolderId = {0xFEDCBA98, 0x7654, 0x3210, {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF} }; + HRESULT hr; + IKnownFolderManager *mgr = NULL; + IKnownFolder *folder = NULL, *subFolder = NULL; + KNOWNFOLDERID folderId, *folders; + KF_CATEGORY cat = 0; + KNOWNFOLDER_DEFINITION kfDefinition, kfSubDefinition; + int csidl, i; + UINT nCount = 0; + LPWSTR folderPath, errorMsg; + KF_REDIRECTION_CAPABILITIES redirectionCapabilities = 1; + WCHAR sWinDir[MAX_PATH], sExamplePath[MAX_PATH], sExample2Path[MAX_PATH], sSubFolderPath[MAX_PATH], sSubFolder2Path[MAX_PATH]; + BOOL bRes; + DWORD dwAttributes; + + GetWindowsDirectoryW( sWinDir, MAX_PATH ); + + GetTempPathW(sizeof(sExamplePath)/sizeof(sExamplePath[0]), sExamplePath); + lstrcatW(sExamplePath, sExample); + + GetTempPathW(sizeof(sExample2Path)/sizeof(sExample2Path[0]), sExample2Path); + lstrcatW(sExample2Path, sExample2); + + lstrcpyW(sSubFolderPath, sExamplePath); + lstrcatW(sSubFolderPath, sBackslash); + lstrcatW(sSubFolderPath, sSubFolder); + + lstrcpyW(sSubFolder2Path, sExample2Path); + lstrcatW(sSubFolder2Path, sBackslash); + lstrcatW(sSubFolder2Path, sSubFolder); + + CoInitialize(NULL); + + hr = CoCreateInstance(&CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, + &IID_IKnownFolderManager, (LPVOID*)&mgr); + if(hr == REGDB_E_CLASSNOTREG) + win_skip("IKnownFolderManager unavailable\n"); + else + { + ok(hr == S_OK, "failed to create KnownFolderManager instance: 0x%08x\n", hr); + + hr = IKnownFolderManager_FolderIdFromCsidl(mgr, CSIDL_WINDOWS, &folderId); + ok(hr == S_OK, "failed to convert CSIDL to KNOWNFOLDERID: 0x%08x\n", hr); + ok(IsEqualGUID(&folderId, &FOLDERID_Windows)==TRUE, "invalid KNOWNFOLDERID returned\n"); + + hr = IKnownFolderManager_FolderIdToCsidl(mgr, &FOLDERID_Windows, &csidl); + ok(hr == S_OK, "failed to convert CSIDL to KNOWNFOLDERID: 0x%08x\n", hr); + ok(csidl == CSIDL_WINDOWS, "invalid CSIDL returned\n"); + + hr = IKnownFolderManager_GetFolder(mgr, &FOLDERID_Windows, &folder); + ok(hr == S_OK, "failed to get known folder: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IKnownFolder_GetCategory(folder, &cat); + todo_wine + ok(hr == S_OK, "failed to get folder category: 0x%08x\n", hr); + todo_wine + ok(cat==KF_CATEGORY_FIXED, "invalid folder category: %d\n", cat); + + hr = IKnownFolder_GetId(folder, &folderId); + ok(hr == S_OK, "failed to get folder id: 0x%08x\n", hr); + ok(IsEqualGUID(&folderId, &FOLDERID_Windows)==TRUE, "invalid KNOWNFOLDERID returned\n"); + + hr = IKnownFolder_GetPath(folder, 0, &folderPath); + ok(lstrcmpiW(sWinDir, folderPath)==0, "invalid path returned: \"%s\", expected: \"%s\"\n", wine_dbgstr_w(folderPath), wine_dbgstr_w(sWinDir)); + CoTaskMemFree(folderPath); + + hr = IKnownFolder_GetRedirectionCapabilities(folder, &redirectionCapabilities); + todo_wine + ok(hr == S_OK, "failed to get redirection capabilities: 0x%08x\n", hr); + todo_wine + ok(redirectionCapabilities==0, "invalid redirection capabilities returned: %d\n", redirectionCapabilities); + + hr = IKnownFolder_SetPath(folder, 0, sWinDir); + todo_wine + ok(hr == E_INVALIDARG, "unexpected value from SetPath: 0x%08x\n", hr); + + hr = IKnownFolder_GetFolderDefinition(folder, &kfDefinition); + todo_wine + ok(hr == S_OK, "failed to get folder definition: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + todo_wine + ok(kfDefinition.category==KF_CATEGORY_FIXED, "invalid folder category: 0x%08x\n", kfDefinition.category); + todo_wine + ok(lstrcmpW(kfDefinition.pszName, sWindows)==0, "invalid folder name: %s\n", wine_dbgstr_w(kfDefinition.pszName)); + todo_wine + ok(kfDefinition.dwAttributes==0, "invalid folder attributes: %d\n", kfDefinition.dwAttributes); + FreeKnownFolderDefinitionFields(&kfDefinition); + } + + hr = IKnownFolder_Release(folder); + ok(hr == S_OK, "failed to release KnownFolder instance: 0x%08x\n", hr); + } + + hr = IKnownFolderManager_GetFolderByName(mgr, sWindows, &folder); + todo_wine + ok(hr == S_OK, "failed to get known folder: 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IKnownFolder_GetId(folder, &folderId); + ok(hr == S_OK, "failed to get folder id: 0x%08x\n", hr); + ok(IsEqualGUID(&folderId, &FOLDERID_Windows)==TRUE, "invalid KNOWNFOLDERID returned\n"); + + hr = IKnownFolder_Release(folder); + ok(hr == S_OK, "failed to release KnownFolder instance: 0x%08x\n", hr); + } + + for(i=0; icommand, test->ddeexec, @@ -1732,7 +1759,9 @@ static void test_dde(void) } ddeExec[0] = 0; + dde_ready_event = CreateEventA(NULL, FALSE, FALSE, "winetest_shlexec_dde_ready"); rc = shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, filename, NULL, NULL); + CloseHandle(dde_ready_event); if ((test->todo & 0x1)==0) { ok(32 < rc, "%s failed: rc=%d err=%d\n", shell_call, @@ -2118,8 +2147,9 @@ static void test_commandline(void) static const WCHAR chkfmt3[] = {'\\','\"','%','s','\"',0}; static const WCHAR chkfmt4[] = {'%','s','=','%','s','\"',' ','%','s','\"',0}; WCHAR cmdline[255]; - LPWSTR *args = (LPWSTR*)0xdeadcafe; + LPWSTR *args = (LPWSTR*)0xdeadcafe, pbuf; INT numargs = -1; + size_t buflen; wsprintfW(cmdline,fmt1,one,two,three,four); args=CommandLineToArgvW(cmdline,&numargs); @@ -2170,6 +2200,46 @@ static void test_commandline(void) wsprintfW(cmdline,fmt6); args=CommandLineToArgvW(cmdline,&numargs); ok(numargs == 1, "expected 1 args, got %i\n",numargs); + if (numargs == 1) { + buflen = max(lstrlenW(args[0])+1,256); + pbuf = HeapAlloc(GetProcessHeap(), 0, buflen*sizeof(pbuf[0])); + GetModuleFileNameW(NULL, pbuf, buflen); + pbuf[buflen-1] = 0; + /* check args[0] is module file name */ + ok(lstrcmpW(args[0],pbuf)==0, "wrong path to the current executable\n"); + HeapFree(GetProcessHeap(), 0, pbuf); + } +} + +static void test_directory(void) +{ + char path[MAX_PATH], newdir[MAX_PATH]; + char params[1024]; + int rc; + + /* copy this executable to a new folder and cd to it */ + sprintf(newdir, "%s\\newfolder", tmpdir); + rc = CreateDirectoryA( newdir, NULL ); + ok( rc, "failed to create %s err %u\n", newdir, GetLastError() ); + sprintf(path, "%s\\%s", newdir, path_find_file_name(argv0)); + CopyFileA(argv0, path, FALSE); + SetCurrentDirectory(tmpdir); + + sprintf(params, "shlexec \"%s\" Exec", child_file); + + rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, + NULL, path_find_file_name(argv0), params, NULL); + todo_wine ok(rc == SE_ERR_FNF, "%s returned %d\n", shell_call, rc); + + rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI, + NULL, path_find_file_name(argv0), params, newdir); + ok(rc > 32, "%s returned %d\n", shell_call, rc); + okChildInt("argcA", 4); + okChildString("argvA3", "Exec"); + todo_wine okChildPath("longPath", path); + + DeleteFile(path); + RemoveDirectoryA(newdir); } START_TEST(shlexec) @@ -2194,6 +2264,7 @@ START_TEST(shlexec) test_dde(); test_dde_default_app(); test_commandline(); + test_directory(); cleanup_test(); } diff --git a/rostests/winetests/shell32/shlfileop.c b/rostests/winetests/shell32/shlfileop.c index 3e9a2e49e14..f1e2a3ed18e 100644 --- a/rostests/winetests/shell32/shlfileop.c +++ b/rostests/winetests/shell32/shlfileop.c @@ -820,6 +820,7 @@ static void test_copy(void) DWORD retval; LPSTR ptr; BOOL on_nt4 = FALSE; + BOOL ret; if (old_shell32) { @@ -973,16 +974,16 @@ static void test_copy(void) shfo.pTo = "test2.txt\0"; /* suppress the error-dialog in win9x here */ shfo.fFlags = FOF_NOERRORUI | FOF_NOCONFIRMATION | FOF_SILENT; - ok(SetFileAttributesA(shfo.pTo, FILE_ATTRIBUTE_READONLY), - "Failure to set file attributes (error %x)\n", GetLastError()); + ret = SetFileAttributesA(shfo.pTo, FILE_ATTRIBUTE_READONLY); + ok(ret, "Failure to set file attributes (error %x)\n", GetLastError()); retval = CopyFileA(shfo.pFrom, shfo.pTo, FALSE); ok(!retval && GetLastError() == ERROR_ACCESS_DENIED, "CopyFileA should have fail with ERROR_ACCESS_DENIED\n"); retval = SHFileOperationA(&shfo); /* Does not work on Win95, Win95B, NT4WS and NT4SRV */ ok(!retval || broken(retval == DE_OPCANCELLED), "SHFileOperationA failed to copy (error %x)\n", retval); /* Set back normal attributes to make the file deletion succeed */ - ok(SetFileAttributesA(shfo.pTo, FILE_ATTRIBUTE_NORMAL), - "Failure to set file attributes (error %x)\n", GetLastError()); + ret = SetFileAttributesA(shfo.pTo, FILE_ATTRIBUTE_NORMAL); + ok(ret, "Failure to set file attributes (error %x)\n", GetLastError()); shfo.fFlags = tmp_flags; /* try to copy files to a file */ @@ -1715,11 +1716,11 @@ static void test_copy(void) { ok(DeleteFileA("threedir\\two.txt"), "Expected file to exist\n"); ok(!DeleteFileA("fourdir"), "Expected file to not exist\n"); - ok(!RemoveDirectoryA("fourdir"), "Expected dit to not exist\n"); + ok(!RemoveDirectoryA("fourdir"), "Expected dir to not exist\n"); } ok(RemoveDirectoryA("threedir"), "Expected dir to exist\n"); ok(!DeleteFileA("five"), "Expected file to not exist\n"); - ok(!RemoveDirectoryA("five"), "Expected dit to not exist\n"); + ok(!RemoveDirectoryA("five"), "Expected dir to not exist\n"); createTestFile("aa.txt"); createTestFile("ab.txt"); @@ -2057,6 +2058,7 @@ static void test_sh_create_dir(void) ok(ERROR_ALREADY_EXISTS == ret, "SHCreateDirectoryEx should fail to create existing directory, ret = %d\n", ret); ret = pSHCreateDirectoryExA(NULL, "c:\\testdir3", NULL); + ok(ERROR_SUCCESS == ret, "SHCreateDirectoryEx failed to create directory, ret = %d\n", ret); ok(file_exists("c:\\testdir3"), "The directory is not created\n"); } @@ -2065,6 +2067,7 @@ static void test_sh_path_prepare(void) HRESULT res; CHAR path[MAX_PATH]; CHAR UNICODE_PATH_A[MAX_PATH]; + BOOL UsedDefaultChar; if(!pSHPathPrepareForWriteA) { @@ -2156,7 +2159,19 @@ static void test_sh_path_prepare(void) win_skip("Skipping SHPathPrepareForWriteW tests\n"); return; } - WideCharToMultiByte(CP_ACP, 0, UNICODE_PATH, -1, UNICODE_PATH_A, sizeof(UNICODE_PATH_A), NULL, NULL); + + SetLastError(0xdeadbeef); + UsedDefaultChar = FALSE; + if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, UNICODE_PATH, -1, UNICODE_PATH_A, sizeof(UNICODE_PATH_A), NULL, &UsedDefaultChar) == 0) + { + win_skip("Could not convert Unicode path name to multibyte (%d)\n", GetLastError()); + return; + } + if (UsedDefaultChar) + { + win_skip("Could not find unique multibyte representation for directory name using default codepage\n"); + return; + } /* unicode directory doesn't exist, SHPPFW_NONE */ RemoveDirectoryA(UNICODE_PATH_A); diff --git a/rostests/winetests/shell32/shlfolder.c b/rostests/winetests/shell32/shlfolder.c index 085c91e064e..fb634b45348 100644 --- a/rostests/winetests/shell32/shlfolder.c +++ b/rostests/winetests/shell32/shlfolder.c @@ -39,6 +39,9 @@ #include "wine/test.h" +#include +DEFINE_GUID(IID_IParentAndItem, 0xB3A4B685, 0xB685, 0x4805, 0x99,0xD9, 0x5D,0xEA,0xD2,0x87,0x32,0x36); +DEFINE_GUID(CLSID_ShellDocObjView, 0xe7e4bc40, 0xe76a, 0x11ce, 0xa9,0xbb, 0x00,0xaa,0x00,0x4a,0xe8,0x37); static IMalloc *ppM; @@ -46,15 +49,56 @@ static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEM static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR); static HRESULT (WINAPI *pSHGetFolderPathAndSubDirA)(HWND, int, HANDLE, DWORD, LPCSTR, LPSTR); static BOOL (WINAPI *pSHGetPathFromIDListW)(LPCITEMIDLIST,LPWSTR); +static HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *); static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL); static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL); static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT); static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST); static void (WINAPI *pILFree)(LPITEMIDLIST); static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST); +static HRESULT (WINAPI *pSHCreateItemFromIDList)(PCIDLIST_ABSOLUTE pidl, REFIID riid, void **ppv); +static HRESULT (WINAPI *pSHCreateItemFromParsingName)(PCWSTR,IBindCtx*,REFIID,void**); static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**); +static HRESULT (WINAPI *pSHCreateShellItemArray)(LPCITEMIDLIST,IShellFolder*,UINT,LPCITEMIDLIST*,IShellItemArray**); +static HRESULT (WINAPI *pSHCreateShellItemArrayFromDataObject)(IDataObject*, REFIID, void **); +static HRESULT (WINAPI *pSHCreateShellItemArrayFromShellItem)(IShellItem*, REFIID, void **); static LPITEMIDLIST (WINAPI *pILCombine)(LPCITEMIDLIST,LPCITEMIDLIST); static HRESULT (WINAPI *pSHParseDisplayName)(LPCWSTR,IBindCtx*,LPITEMIDLIST*,SFGAOF,SFGAOF*); +static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID); +static HRESULT (WINAPI *pSHGetNameFromIDList)(PCIDLIST_ABSOLUTE,SIGDN,PWSTR*); +static HRESULT (WINAPI *pSHGetItemFromDataObject)(IDataObject*,DATAOBJ_GET_ITEM_FLAGS,REFIID,void**); +static HRESULT (WINAPI *pSHGetIDListFromObject)(IUnknown*, PIDLIST_ABSOLUTE*); +static HRESULT (WINAPI *pSHGetItemFromObject)(IUnknown*,REFIID,void**); +static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); +static UINT (WINAPI *pGetSystemWow64DirectoryW)(LPWSTR, UINT); +static HRESULT (WINAPI *pSHCreateDefaultContextMenu)(const DEFCONTEXTMENU*,REFIID,void**); + +static WCHAR *make_wstr(const char *str) +{ + WCHAR *ret; + int len; + + if(!str || strlen(str) == 0) + return NULL; + + len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + if(!len || len < 0) + return NULL; + + ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if(!ret) + return NULL; + + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + return ret; +} + +static int strcmp_wa(LPCWSTR strw, const char *stra) +{ + CHAR buf[512]; + WideCharToMultiByte(CP_ACP, 0, strw, -1, buf, sizeof(buf), NULL, NULL); + return lstrcmpA(stra, buf); +} static void init_function_pointers(void) { @@ -66,13 +110,24 @@ static void init_function_pointers(void) #define MAKEFUNC(f) (p##f = (void*)GetProcAddress(hmod, #f)) MAKEFUNC(SHBindToParent); + MAKEFUNC(SHCreateItemFromIDList); + MAKEFUNC(SHCreateItemFromParsingName); MAKEFUNC(SHCreateShellItem); + MAKEFUNC(SHCreateShellItemArray); + MAKEFUNC(SHCreateShellItemArrayFromDataObject); + MAKEFUNC(SHCreateShellItemArrayFromShellItem); MAKEFUNC(SHGetFolderPathA); MAKEFUNC(SHGetFolderPathAndSubDirA); MAKEFUNC(SHGetPathFromIDListW); MAKEFUNC(SHGetSpecialFolderPathA); MAKEFUNC(SHGetSpecialFolderPathW); + MAKEFUNC(SHGetSpecialFolderLocation); MAKEFUNC(SHParseDisplayName); + MAKEFUNC(SHGetNameFromIDList); + MAKEFUNC(SHGetItemFromDataObject); + MAKEFUNC(SHGetIDListFromObject); + MAKEFUNC(SHGetItemFromObject); + MAKEFUNC(SHCreateDefaultContextMenu); #undef MAKEFUNC #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord))) @@ -80,6 +135,7 @@ static void init_function_pointers(void) MAKEFUNC_ORD(ILIsEqual, 21); MAKEFUNC_ORD(ILCombine, 25); MAKEFUNC_ORD(ILFree, 155); + MAKEFUNC_ORD(SHSimpleIDListFromPathAW, 162); #undef MAKEFUNC_ORD /* test named exports */ @@ -112,10 +168,34 @@ static void init_function_pointers(void) hmod = GetModuleHandleA("shlwapi.dll"); pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW"); + hmod = GetModuleHandleA("kernel32.dll"); + pIsWow64Process = (void*)GetProcAddress(hmod, "IsWow64Process"); + pGetSystemWow64DirectoryW = (void*)GetProcAddress(hmod, "GetSystemWow64DirectoryW"); + hr = SHGetMalloc(&ppM); ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr); } +/* Based on PathAddBackslashW from dlls/shlwapi/path.c */ +static LPWSTR myPathAddBackslashW( LPWSTR lpszPath ) +{ + size_t iLen; + + if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH) + return NULL; + + if (iLen) + { + lpszPath += iLen; + if (lpszPath[-1] != '\\') + { + *lpszPath++ = '\\'; + *lpszPath = '\0'; + } + } + return lpszPath; +} + static void test_ParseDisplayName(void) { HRESULT hr; @@ -361,12 +441,19 @@ static void test_BindToObject(void) UINT cChars; IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir; SHITEMID emptyitem = { 0, { 0 } }; - LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem; + LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidl, pidlEmpty = (LPITEMIDLIST)&emptyitem; WCHAR wszSystemDir[MAX_PATH]; char szSystemDir[MAX_PATH]; + char buf[MAX_PATH]; + WCHAR path[MAX_PATH]; + CHAR pathA[MAX_PATH]; + HANDLE hfile; WCHAR wszMyComputer[] = { ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-', 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 }; + static const CHAR filename_html[] = "winetest.html"; + static const CHAR filename_txt[] = "winetest.txt"; + static const CHAR filename_foo[] = "winetest.foo"; /* The following tests shows that BindToObject should fail with E_INVALIDARG if called * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder @@ -438,26 +525,168 @@ if (0) } IShellFolder_Release(psfSystemDir); -} -/* Based on PathAddBackslashW from dlls/shlwapi/path.c */ -static LPWSTR myPathAddBackslashW( LPWSTR lpszPath ) -{ - size_t iLen; - - if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH) - return NULL; - - if (iLen) - { - lpszPath += iLen; - if (lpszPath[-1] != '\\') + GetCurrentDirectoryA(MAX_PATH, buf); + if(!lstrlenA(buf)) { - *lpszPath++ = '\\'; - *lpszPath = '\0'; + skip("Failed to get current directory, skipping tests.\n"); + return; } - } - return lpszPath; + + SHGetDesktopFolder(&psfDesktop); + + /* Attempt BindToObject on files. */ + + /* .html */ + lstrcpyA(pathA, buf); + lstrcatA(pathA, "\\"); + lstrcatA(pathA, filename_html); + hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if(hfile != INVALID_HANDLE_VALUE) + { + CloseHandle(hfile); + MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH); + hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild); + ok(hr == S_OK || + hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP, W2K3 */ + "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + IPersist *pp; + hr = IShellFolder_QueryInterface(psfChild, &IID_IPersist, (void**)&pp); + ok(hr == S_OK || + broken(hr == E_NOINTERFACE), /* Win9x, NT4, W2K */ + "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + CLSID id; + hr = IPersist_GetClassID(pp, &id); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(IsEqualIID(&id, &CLSID_ShellDocObjView), "Unexpected classid\n"); + IPersist_Release(pp); + } + + IShellFolder_Release(psfChild); + } + pILFree(pidl); + } + DeleteFileA(pathA); + } + else + win_skip("Failed to create .html testfile.\n"); + + /* .txt */ + lstrcpyA(pathA, buf); + lstrcatA(pathA, "\\"); + lstrcatA(pathA, filename_txt); + hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if(hfile != INVALID_HANDLE_VALUE) + { + CloseHandle(hfile); + MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH); + hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild); + ok(hr == E_FAIL || /* Vista+ */ + hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || /* XP, W2K3 */ + broken(hr == S_OK), /* Win9x, NT4, W2K */ + "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IShellFolder_Release(psfChild); + pILFree(pidl); + } + DeleteFileA(pathA); + } + else + win_skip("Failed to create .txt testfile.\n"); + + /* .foo */ + lstrcpyA(pathA, buf); + lstrcatA(pathA, "\\"); + lstrcatA(pathA, filename_foo); + hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if(hfile != INVALID_HANDLE_VALUE) + { + CloseHandle(hfile); + MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH); + hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild); + ok(hr == E_FAIL || /* Vista+ */ + hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || /* XP, W2K3 */ + broken(hr == S_OK), /* Win9x, NT4, W2K */ + "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IShellFolder_Release(psfChild); + pILFree(pidl); + } + DeleteFileA(pathA); + } + else + win_skip("Failed to create .foo testfile.\n"); + + /* And on the desktop */ + if(pSHGetSpecialFolderPathA) + { + pSHGetSpecialFolderPathA(NULL, pathA, CSIDL_DESKTOP, FALSE); + lstrcatA(pathA, "\\"); + lstrcatA(pathA, filename_html); + hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if(hfile != INVALID_HANDLE_VALUE) + { + CloseHandle(hfile); + MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH); + hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild); + ok(hr == S_OK || + hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), /* XP, W2K3 */ + "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IShellFolder_Release(psfChild); + pILFree(pidl); + } + if(!DeleteFileA(pathA)) + trace("Failed to delete: %d\n", GetLastError()); + + } + else + win_skip("Failed to create .html testfile.\n"); + + pSHGetSpecialFolderPathA(NULL, pathA, CSIDL_DESKTOP, FALSE); + lstrcatA(pathA, "\\"); + lstrcatA(pathA, filename_foo); + hfile = CreateFileA(pathA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if(hfile != INVALID_HANDLE_VALUE) + { + CloseHandle(hfile); + MultiByteToWideChar(CP_ACP, 0, pathA, -1, path, MAX_PATH); + hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, path, NULL, &pidl, NULL); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellFolder_BindToObject(psfDesktop, pidl, NULL, &IID_IShellFolder, (void**)&psfChild); + ok(hr == E_FAIL || /* Vista+ */ + hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || /* XP, W2K3 */ + broken(hr == S_OK), /* Win9x, NT4, W2K */ + "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IShellFolder_Release(psfChild); + pILFree(pidl); + } + DeleteFileA(pathA); + } + else + win_skip("Failed to create .foo testfile.\n"); + } + + IShellFolder_Release(psfDesktop); } static void test_GetDisplayName(void) @@ -548,7 +777,6 @@ static void test_GetDisplayName(void) /* It seems as if we cannot bind to regular files on windows, but only directories. */ hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile); - todo_wine ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || hr == E_NOTIMPL || /* Vista */ broken(hr == S_OK), /* Win9x, W2K */ @@ -985,9 +1213,7 @@ static void test_SHGetPathFromIDList(void) STRRET strret; static WCHAR wszTestFile[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 }; - HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *); - HMODULE hShell32; - LPITEMIDLIST pidlPrograms; + LPITEMIDLIST pidlPrograms; if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW) { @@ -1099,10 +1325,7 @@ static void test_SHGetPathFromIDList(void) ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n"); - /* Test if we can get the path from the start menu "program files" PIDL. */ - hShell32 = GetModuleHandleA("shell32"); - pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation"); - + /* Test if we can get the path from the start menu "program files" PIDL. */ hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms); ok(hr == S_OK, "SHGetFolderLocation failed: 0x%08x\n", hr); @@ -1555,7 +1778,7 @@ static void test_ITEMIDLIST_format(void) { /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably * can't implement this correctly, since unix filesystems don't support * this nasty short/long filename stuff. So we'll probably stay with our - * current habbit of storing the long filename here, which seems to work + * current habit of storing the long filename here, which seems to work * just fine. */ todo_wine ok(pidlFile->mkid.abID[18] == '~' || @@ -1632,6 +1855,93 @@ static void test_ITEMIDLIST_format(void) { IShellFolder_Release(psfPersonal); } +static void test_SHGetFolderPathA(void) +{ + static const BOOL is_win64 = sizeof(void *) > sizeof(int); + BOOL is_wow64; + char path[MAX_PATH]; + char path_x86[MAX_PATH]; + char path_key[MAX_PATH]; + HRESULT hr; + HKEY key; + + if (!pSHGetFolderPathA) + { + win_skip("SHGetFolderPathA not present\n"); + return; + } + if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; + + hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILES, 0, SHGFP_TYPE_CURRENT, path ); + ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILESX86, 0, SHGFP_TYPE_CURRENT, path_x86 ); + if (hr == E_FAIL) + { + win_skip( "Program Files (x86) not supported\n" ); + return; + } + ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + if (is_win64) + { + ok( lstrcmpiA( path, path_x86 ), "paths are identical '%s'\n", path ); + ok( strstr( path, "x86" ) == NULL, "64-bit path '%s' contains x86\n", path ); + ok( strstr( path_x86, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path_x86 ); + } + else + { + ok( !lstrcmpiA( path, path_x86 ), "paths differ '%s' != '%s'\n", path, path_x86 ); + if (is_wow64) + ok( strstr( path, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path ); + else + ok( strstr( path, "x86" ) == NULL, "32-bit path '%s' contains x86\n", path ); + } + if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &key )) + { + DWORD type, count = sizeof(path_x86); + if (!RegQueryValueExA( key, "ProgramFilesDir (x86)", NULL, &type, (BYTE *)path_key, &count )) + { + ok( is_win64 || is_wow64, "ProgramFilesDir (x86) exists on 32-bit setup\n" ); + ok( !lstrcmpiA( path_key, path_x86 ), "paths differ '%s' != '%s'\n", path_key, path_x86 ); + } + else ok( !is_win64 && !is_wow64, "ProgramFilesDir (x86) should exist on 64-bit setup\n" ); + RegCloseKey( key ); + } + + hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILES_COMMON, 0, SHGFP_TYPE_CURRENT, path ); + ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + hr = pSHGetFolderPathA( 0, CSIDL_PROGRAM_FILES_COMMONX86, 0, SHGFP_TYPE_CURRENT, path_x86 ); + if (hr == E_FAIL) + { + win_skip( "Common Files (x86) not supported\n" ); + return; + } + ok( !hr, "SHGetFolderPathA failed %x\n", hr ); + if (is_win64) + { + ok( lstrcmpiA( path, path_x86 ), "paths are identical '%s'\n", path ); + ok( strstr( path, "x86" ) == NULL, "64-bit path '%s' contains x86\n", path ); + ok( strstr( path_x86, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path_x86 ); + } + else + { + ok( !lstrcmpiA( path, path_x86 ), "paths differ '%s' != '%s'\n", path, path_x86 ); + if (is_wow64) + ok( strstr( path, "x86" ) != NULL, "32-bit path '%s' doesn't contain x86\n", path ); + else + ok( strstr( path, "x86" ) == NULL, "32-bit path '%s' contains x86\n", path ); + } + if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &key )) + { + DWORD type, count = sizeof(path_x86); + if (!RegQueryValueExA( key, "CommonFilesDir (x86)", NULL, &type, (BYTE *)path_key, &count )) + { + ok( is_win64 || is_wow64, "CommonFilesDir (x86) exists on 32-bit setup\n" ); + ok( !lstrcmpiA( path_key, path_x86 ), "paths differ '%s' != '%s'\n", path_key, path_x86 ); + } + else ok( !is_win64 && !is_wow64, "CommonFilesDir (x86) should exist on 64-bit setup\n" ); + } +} + static void test_SHGetFolderPathAndSubDirA(void) { HRESULT ret; @@ -1756,6 +2066,7 @@ static void test_LocalizedNames(void) DWORD res; HANDLE file; STRRET strret; + BOOL ret; static const char desktopini_contents1[] = "[.ShellClassInfo]\r\n" @@ -1775,10 +2086,10 @@ static void test_LocalizedNames(void) file = CreateFileA(".\\testfolder\\desktop.ini", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed %i\n", GetLastError()); - ok(WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) && - WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) && - WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL), - "WriteFile failed %i\n", GetLastError()); + ret = WriteFile(file, desktopini_contents1, strlen(desktopini_contents1), &res, NULL) && + WriteFile(file, resourcefile, strlen(resourcefile), &res, NULL) && + WriteFile(file, desktopini_contents2, strlen(desktopini_contents2), &res, NULL); + ok(ret, "WriteFile failed %i\n", GetLastError()); CloseHandle(file); /* get IShellFolder for parent */ @@ -1862,10 +2173,11 @@ static void test_SHCreateShellItem(void) { IShellItem *shellitem, *shellitem2; IPersistIDList *persistidl; - LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test; + LPITEMIDLIST pidl_cwd=NULL, pidl_testfile, pidl_abstestfile, pidl_test, pidl_desktop; HRESULT ret; char curdirA[MAX_PATH]; WCHAR curdirW[MAX_PATH]; + WCHAR fnbufW[MAX_PATH]; IShellFolder *desktopfolder=NULL, *currentfolder=NULL; static WCHAR testfileW[] = {'t','e','s','t','f','i','l','e',0}; @@ -1883,6 +2195,17 @@ static void test_SHCreateShellItem(void) return; } + if(pSHGetSpecialFolderLocation) + { + ret = pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl_desktop); + ok(ret == S_OK, "Got 0x%08x\n", ret); + } + else + { + win_skip("pSHGetSpecialFolderLocation missing.\n"); + pidl_desktop = NULL; + } + MultiByteToWideChar(CP_ACP, 0, curdirA, -1, curdirW, MAX_PATH); ret = SHGetDesktopFolder(&desktopfolder); @@ -2014,14 +2337,1394 @@ static void test_SHCreateShellItem(void) IShellItem_Release(shellitem); } + ret = pSHCreateShellItem(NULL, desktopfolder, pidl_testfile, &shellitem); + ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl); + ok(SUCCEEDED(ret), "QueryInterface returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ret = IPersistIDList_GetIDList(persistidl, &pidl_test); + ok(SUCCEEDED(ret), "GetIDList returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n"); + pILFree(pidl_test); + } + IPersistIDList_Release(persistidl); + } + + IShellItem_Release(shellitem); + } + + ret = pSHCreateShellItem(NULL, NULL, pidl_desktop, &shellitem); + ok(SUCCEEDED(ret), "SHCreateShellItem returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ret = IShellItem_GetParent(shellitem, &shellitem2); + ok(FAILED(ret), "Got 0x%08x\n", ret); + if(SUCCEEDED(ret)) IShellItem_Release(shellitem2); + IShellItem_Release(shellitem); + } + + /* SHCreateItemFromParsingName */ + if(pSHCreateItemFromParsingName) + { + if(0) + { + /* Crashes under windows 7 */ + pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, NULL); + } + + shellitem = (void*)0xdeadbeef; + ret = pSHCreateItemFromParsingName(NULL, NULL, &IID_IShellItem, (void**)&shellitem); + ok(ret == E_INVALIDARG, "SHCreateItemFromParsingName returned %x\n", ret); + ok(shellitem == NULL, "shellitem was %p.\n", shellitem); + + ret = pSHCreateItemFromParsingName(testfileW, NULL, &IID_IShellItem, (void**)&shellitem); + ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "SHCreateItemFromParsingName returned %x\n", ret); + if(SUCCEEDED(ret)) IShellItem_Release(shellitem); + + lstrcpyW(fnbufW, curdirW); + myPathAddBackslashW(fnbufW); + lstrcatW(fnbufW, testfileW); + + ret = pSHCreateItemFromParsingName(fnbufW, NULL, &IID_IShellItem, (void**)&shellitem); + ok(ret == S_OK, "SHCreateItemFromParsingName returned %x\n", ret); + if(SUCCEEDED(ret)) + { + LPWSTR tmp_fname; + ret = IShellItem_GetDisplayName(shellitem, SIGDN_FILESYSPATH, &tmp_fname); + ok(ret == S_OK, "GetDisplayName returned %x\n", ret); + if(SUCCEEDED(ret)) + { + ok(!lstrcmpW(fnbufW, tmp_fname), "strings not equal\n"); + CoTaskMemFree(tmp_fname); + } + IShellItem_Release(shellitem); + } + } + else + win_skip("No SHCreateItemFromParsingName\n"); + + + /* SHCreateItemFromIDList */ + if(pSHCreateItemFromIDList) + { + if(0) + { + /* Crashes under win7 */ + pSHCreateItemFromIDList(NULL, &IID_IShellItem, NULL); + } + + ret = pSHCreateItemFromIDList(NULL, &IID_IShellItem, (void**)&shellitem); + ok(ret == E_INVALIDARG, "SHCreateItemFromIDList returned %x\n", ret); + + ret = pSHCreateItemFromIDList(pidl_cwd, &IID_IShellItem, (void**)&shellitem); + ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl); + ok(ret == S_OK, "QueryInterface returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ret = IPersistIDList_GetIDList(persistidl, &pidl_test); + ok(ret == S_OK, "GetIDList returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ok(ILIsEqual(pidl_cwd, pidl_test), "id lists are not equal\n"); + pILFree(pidl_test); + } + IPersistIDList_Release(persistidl); + } + IShellItem_Release(shellitem); + } + + ret = pSHCreateItemFromIDList(pidl_testfile, &IID_IShellItem, (void**)&shellitem); + ok(ret == S_OK, "SHCreateItemFromIDList returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ret = IShellItem_QueryInterface(shellitem, &IID_IPersistIDList, (void**)&persistidl); + ok(ret == S_OK, "QueryInterface returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ret = IPersistIDList_GetIDList(persistidl, &pidl_test); + ok(ret == S_OK, "GetIDList returned %x\n", ret); + if (SUCCEEDED(ret)) + { + ok(ILIsEqual(pidl_testfile, pidl_test), "id lists are not equal\n"); + pILFree(pidl_test); + } + IPersistIDList_Release(persistidl); + } + IShellItem_Release(shellitem); + } + } + else + win_skip("No SHCreateItemFromIDList\n"); + DeleteFileA(".\\testfile"); pILFree(pidl_abstestfile); pILFree(pidl_testfile); + pILFree(pidl_desktop); pILFree(pidl_cwd); IShellFolder_Release(currentfolder); IShellFolder_Release(desktopfolder); } +static void test_SHGetNameFromIDList(void) +{ + IShellItem *shellitem; + LPITEMIDLIST pidl; + LPWSTR name_string; + HRESULT hres; + UINT i; + static const DWORD flags[] = { + SIGDN_NORMALDISPLAY, SIGDN_PARENTRELATIVEPARSING, + SIGDN_DESKTOPABSOLUTEPARSING,SIGDN_PARENTRELATIVEEDITING, + SIGDN_DESKTOPABSOLUTEEDITING, /*SIGDN_FILESYSPATH, SIGDN_URL, */ + SIGDN_PARENTRELATIVEFORADDRESSBAR,SIGDN_PARENTRELATIVE, -1234}; + + if(!pSHGetNameFromIDList) + { + win_skip("SHGetNameFromIDList missing.\n"); + return; + } + + /* These should be available on any platform that passed the above test. */ + ok(pSHCreateShellItem != NULL, "SHCreateShellItem missing.\n"); + ok(pSHBindToParent != NULL, "SHBindToParent missing.\n"); + ok(pSHGetSpecialFolderLocation != NULL, "SHGetSpecialFolderLocation missing.\n"); + ok(pStrRetToBufW != NULL, "StrRetToBufW missing.\n"); + + if(0) + { + /* Crashes under win7 */ + pSHGetNameFromIDList(NULL, 0, NULL); + } + + hres = pSHGetNameFromIDList(NULL, 0, &name_string); + ok(hres == E_INVALIDARG, "Got 0x%08x\n", hres); + + /* Test the desktop */ + hres = pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl); + ok(hres == S_OK, "Got 0x%08x\n", hres); + hres = pSHCreateShellItem(NULL, NULL, pidl, &shellitem); + ok(hres == S_OK, "Got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + WCHAR *nameSI, *nameSH; + WCHAR buf[MAX_PATH]; + HRESULT hrSI, hrSH, hrSF; + STRRET strret; + IShellFolder *psf; + BOOL res; + + SHGetDesktopFolder(&psf); + for(i = 0; flags[i] != -1234; i++) + { + hrSI = IShellItem_GetDisplayName(shellitem, flags[i], &nameSI); + ok(hrSI == S_OK, "Got 0x%08x\n", hrSI); + hrSH = pSHGetNameFromIDList(pidl, flags[i], &nameSH); + ok(hrSH == S_OK, "Got 0x%08x\n", hrSH); + hrSF = IShellFolder_GetDisplayNameOf(psf, pidl, flags[i] & 0xffff, &strret); + ok(hrSF == S_OK, "Got 0x%08x\n", hrSF); + + if(SUCCEEDED(hrSI) && SUCCEEDED(hrSH)) + ok(!lstrcmpW(nameSI, nameSH), "Strings differ.\n"); + + if(SUCCEEDED(hrSF)) + { + pStrRetToBufW(&strret, NULL, buf, MAX_PATH); + if(SUCCEEDED(hrSI)) + ok(!lstrcmpW(nameSI, buf), "Strings differ.\n"); + if(SUCCEEDED(hrSF)) + ok(!lstrcmpW(nameSI, buf), "Strings differ.\n"); + } + if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI); + if(SUCCEEDED(hrSH)) CoTaskMemFree(nameSH); + } + IShellFolder_Release(psf); + + if(pSHGetPathFromIDListW){ + hrSI = pSHGetNameFromIDList(pidl, SIGDN_FILESYSPATH, &nameSI); + ok(hrSI == S_OK, "Got 0x%08x\n", hrSI); + res = pSHGetPathFromIDListW(pidl, buf); + ok(res == TRUE, "Got %d\n", res); + if(SUCCEEDED(hrSI) && res) + ok(!lstrcmpW(nameSI, buf), "Strings differ.\n"); + if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI); + }else + win_skip("pSHGetPathFromIDListW not available\n"); + + hres = pSHGetNameFromIDList(pidl, SIGDN_URL, &name_string); + todo_wine ok(hres == S_OK, "Got 0x%08x\n", hres); + if(SUCCEEDED(hres)) CoTaskMemFree(name_string); + + IShellItem_Release(shellitem); + } + pILFree(pidl); + + /* Test the control panel */ + hres = pSHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidl); + ok(hres == S_OK, "Got 0x%08x\n", hres); + hres = pSHCreateShellItem(NULL, NULL, pidl, &shellitem); + ok(hres == S_OK, "Got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + WCHAR *nameSI, *nameSH; + WCHAR buf[MAX_PATH]; + HRESULT hrSI, hrSH, hrSF; + STRRET strret; + IShellFolder *psf; + BOOL res; + + SHGetDesktopFolder(&psf); + for(i = 0; flags[i] != -1234; i++) + { + hrSI = IShellItem_GetDisplayName(shellitem, flags[i], &nameSI); + ok(hrSI == S_OK, "Got 0x%08x\n", hrSI); + hrSH = pSHGetNameFromIDList(pidl, flags[i], &nameSH); + ok(hrSH == S_OK, "Got 0x%08x\n", hrSH); + hrSF = IShellFolder_GetDisplayNameOf(psf, pidl, flags[i] & 0xffff, &strret); + ok(hrSF == S_OK, "Got 0x%08x\n", hrSF); + + if(SUCCEEDED(hrSI) && SUCCEEDED(hrSH)) + ok(!lstrcmpW(nameSI, nameSH), "Strings differ.\n"); + + if(SUCCEEDED(hrSF)) + { + pStrRetToBufW(&strret, NULL, buf, MAX_PATH); + if(SUCCEEDED(hrSI)) + ok(!lstrcmpW(nameSI, buf), "Strings differ.\n"); + if(SUCCEEDED(hrSF)) + ok(!lstrcmpW(nameSI, buf), "Strings differ.\n"); + } + if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI); + if(SUCCEEDED(hrSH)) CoTaskMemFree(nameSH); + } + IShellFolder_Release(psf); + + if(pSHGetPathFromIDListW){ + hrSI = pSHGetNameFromIDList(pidl, SIGDN_FILESYSPATH, &nameSI); + ok(hrSI == E_INVALIDARG, "Got 0x%08x\n", hrSI); + res = pSHGetPathFromIDListW(pidl, buf); + ok(res == FALSE, "Got %d\n", res); + if(SUCCEEDED(hrSI) && res) + ok(!lstrcmpW(nameSI, buf), "Strings differ.\n"); + if(SUCCEEDED(hrSI)) CoTaskMemFree(nameSI); + }else + win_skip("pSHGetPathFromIDListW not available\n"); + + hres = pSHGetNameFromIDList(pidl, SIGDN_URL, &name_string); + todo_wine ok(hres == E_NOTIMPL /* Win7 */ || hres == S_OK /* Vista */, + "Got 0x%08x\n", hres); + if(SUCCEEDED(hres)) CoTaskMemFree(name_string); + + IShellItem_Release(shellitem); + } + pILFree(pidl); +} + +static void test_SHGetItemFromDataObject(void) +{ + IShellFolder *psfdesktop; + IShellItem *psi; + IShellView *psv; + HRESULT hres; + + if(!pSHGetItemFromDataObject) + { + win_skip("No SHGetItemFromDataObject.\n"); + return; + } + + if(0) + { + /* Crashes under win7 */ + pSHGetItemFromDataObject(NULL, 0, &IID_IShellItem, NULL); + } + + hres = pSHGetItemFromDataObject(NULL, 0, &IID_IShellItem, (void**)&psv); + ok(hres == E_INVALIDARG, "got 0x%08x\n", hres); + + SHGetDesktopFolder(&psfdesktop); + + hres = IShellFolder_CreateViewObject(psfdesktop, NULL, &IID_IShellView, (void**)&psv); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + IEnumIDList *peidl; + IDataObject *pdo; + SHCONTF enum_flags; + + enum_flags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN; + hres = IShellFolder_EnumObjects(psfdesktop, NULL, enum_flags, &peidl); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + LPITEMIDLIST apidl[5]; + UINT count = 0, i; + + for(count = 0; count < 5; count++) + if(IEnumIDList_Next(peidl, 1, &apidl[count], NULL) != S_OK) + break; + + if(count) + { + hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, 1, (LPCITEMIDLIST*)apidl, + &IID_IDataObject, NULL, (void**)&pdo); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + hres = pSHGetItemFromDataObject(pdo, DOGIF_DEFAULT, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_TRAVERSE_LINK, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_HDROP, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_URL, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_ONLY_IF_ONE, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + + IDataObject_Release(pdo); + } + } + else + skip("No file(s) found - skipping single-file test.\n"); + + if(count > 1) + { + hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, count, (LPCITEMIDLIST*)apidl, + &IID_IDataObject, NULL, (void**)&pdo); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + hres = pSHGetItemFromDataObject(pdo, DOGIF_DEFAULT, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_TRAVERSE_LINK, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_HDROP, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_NO_URL, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + hres = pSHGetItemFromDataObject(pdo, DOGIF_ONLY_IF_ONE, &IID_IShellItem, (void**)&psi); + ok(hres == E_FAIL, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) IShellItem_Release(psi); + IDataObject_Release(pdo); + } + } + else + skip("zero or one file found - skipping multi-file test.\n"); + + for(i = 0; i < count; i++) + pILFree(apidl[i]); + + IEnumIDList_Release(peidl); + } + + IShellView_Release(psv); + } + + IShellFolder_Release(psfdesktop); +} + +static void test_ShellItemCompare(void) +{ + IShellItem *psi[9]; /* a\a, a\b, a\c, b\a, .. */ + IShellItem *psi_a = NULL, *psi_b = NULL, *psi_c = NULL; + IShellFolder *psf_desktop, *psf_current; + LPITEMIDLIST pidl_cwd; + WCHAR curdirW[MAX_PATH]; + BOOL failed; + HRESULT hr; + static const WCHAR filesW[][9] = { + {'a','\\','a',0}, {'a','\\','b',0}, {'a','\\','c',0}, + {'b','\\','a',0}, {'b','\\','b',0}, {'b','\\','c',0}, + {'c','\\','a',0}, {'c','\\','b',0}, {'c','\\','c',0} }; + int order; + UINT i; + + if(!pSHCreateShellItem) + { + win_skip("SHCreateShellItem missing.\n"); + return; + } + + GetCurrentDirectoryW(MAX_PATH, curdirW); + if(!lstrlenW(curdirW)) + { + skip("Failed to get current directory, skipping.\n"); + return; + } + + CreateDirectoryA(".\\a", NULL); + CreateDirectoryA(".\\b", NULL); + CreateDirectoryA(".\\c", NULL); + CreateTestFile(".\\a\\a"); + CreateTestFile(".\\a\\b"); + CreateTestFile(".\\a\\c"); + CreateTestFile(".\\b\\a"); + CreateTestFile(".\\b\\b"); + CreateTestFile(".\\b\\c"); + CreateTestFile(".\\c\\a"); + CreateTestFile(".\\c\\b"); + CreateTestFile(".\\c\\c"); + + SHGetDesktopFolder(&psf_desktop); + hr = IShellFolder_ParseDisplayName(psf_desktop, NULL, NULL, curdirW, NULL, &pidl_cwd, NULL); + ok(SUCCEEDED(hr), "ParseDisplayName returned %x\n", hr); + hr = IShellFolder_BindToObject(psf_desktop, pidl_cwd, NULL, &IID_IShellFolder, (void**)&psf_current); + ok(SUCCEEDED(hr), "BindToObject returned %x\n", hr); + IShellFolder_Release(psf_desktop); + ILFree(pidl_cwd); + + /* Generate ShellItems for the files */ + memset(&psi, 0, sizeof(psi)); + failed = FALSE; + for(i = 0; i < 9; i++) + { + LPITEMIDLIST pidl_testfile = NULL; + + hr = IShellFolder_ParseDisplayName(psf_current, NULL, NULL, (LPWSTR)filesW[i], + NULL, &pidl_testfile, NULL); + ok(SUCCEEDED(hr), "ParseDisplayName returned %x\n", hr); + if(SUCCEEDED(hr)) + { + hr = pSHCreateShellItem(NULL, NULL, pidl_testfile, &psi[i]); + ok(hr == S_OK, "Got 0x%08x\n", hr); + pILFree(pidl_testfile); + } + if(FAILED(hr)) failed = TRUE; + } + if(failed) + { + skip("Failed to create all shellitems.\n"); + goto cleanup; + } + + /* Generate ShellItems for the folders */ + hr = IShellItem_GetParent(psi[0], &psi_a); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(FAILED(hr)) failed = TRUE; + hr = IShellItem_GetParent(psi[3], &psi_b); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(FAILED(hr)) failed = TRUE; + hr = IShellItem_GetParent(psi[6], &psi_c); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(FAILED(hr)) failed = TRUE; + + if(failed) + { + skip("Failed to create shellitems.\n"); + goto cleanup; + } + + if(0) + { + /* Crashes on native (win7, winxp) */ + IShellItem_Compare(psi_a, NULL, 0, NULL); + IShellItem_Compare(psi_a, psi_b, 0, NULL); + IShellItem_Compare(psi_a, NULL, 0, &order); + } + + /* Basics */ + for(i = 0; i < 9; i++) + { + hr = IShellItem_Compare(psi[i], psi[i], SICHINT_DISPLAY, &order); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(order == 0, "Got order %d\n", order); + hr = IShellItem_Compare(psi[i], psi[i], SICHINT_CANONICAL, &order); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(order == 0, "Got order %d\n", order); + hr = IShellItem_Compare(psi[i], psi[i], SICHINT_ALLFIELDS, &order); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(order == 0, "Got order %d\n", order); + } + + /* Order */ + /* a\b:a\a , a\b:a\c, a\b:a\b */ + hr = IShellItem_Compare(psi[1], psi[0], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[1], psi[2], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[1], psi[1], SICHINT_DISPLAY, &order); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(order == 0, "Got order %d\n", order); + + /* b\b:a\b, b\b:c\b, b\b:c\b */ + hr = IShellItem_Compare(psi[4], psi[1], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[4], psi[7], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[4], psi[4], SICHINT_DISPLAY, &order); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(order == 0, "Got order %d\n", order); + + /* b:a\a, b:a\c, b:a\b */ + hr = IShellItem_Compare(psi_b, psi[0], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + todo_wine ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[2], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + todo_wine ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[1], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + todo_wine ok(order == 1, "Got order %d\n", order); + + /* b:c\a, b:c\c, b:c\b */ + hr = IShellItem_Compare(psi_b, psi[6], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[8], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[7], SICHINT_DISPLAY, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + + /* a\b:a\a , a\b:a\c, a\b:a\b */ + hr = IShellItem_Compare(psi[1], psi[0], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[1], psi[2], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[1], psi[1], SICHINT_CANONICAL, &order); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(order == 0, "Got order %d\n", order); + + /* b\b:a\b, b\b:c\b, b\b:c\b */ + hr = IShellItem_Compare(psi[4], psi[1], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[4], psi[7], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi[4], psi[4], SICHINT_CANONICAL, &order); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(order == 0, "Got order %d\n", order); + + /* b:a\a, b:a\c, b:a\b */ + hr = IShellItem_Compare(psi_b, psi[0], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + todo_wine ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[2], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + todo_wine ok(order == 1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[1], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + todo_wine ok(order == 1, "Got order %d\n", order); + + /* b:c\a, b:c\c, b:c\b */ + hr = IShellItem_Compare(psi_b, psi[6], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[8], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + hr = IShellItem_Compare(psi_b, psi[7], SICHINT_CANONICAL, &order); + ok(hr == S_FALSE, "Got 0x%08x\n", hr); + ok(order == -1, "Got order %d\n", order); + +cleanup: + IShellFolder_Release(psf_current); + + DeleteFileA(".\\a\\a"); + DeleteFileA(".\\a\\b"); + DeleteFileA(".\\a\\c"); + DeleteFileA(".\\b\\a"); + DeleteFileA(".\\b\\b"); + DeleteFileA(".\\b\\c"); + DeleteFileA(".\\c\\a"); + DeleteFileA(".\\c\\b"); + DeleteFileA(".\\c\\c"); + RemoveDirectoryA(".\\a"); + RemoveDirectoryA(".\\b"); + RemoveDirectoryA(".\\c"); + + if(psi_a) IShellItem_Release(psi_a); + if(psi_b) IShellItem_Release(psi_b); + if(psi_c) IShellItem_Release(psi_c); + + for(i = 0; i < 9; i++) + if(psi[i]) IShellItem_Release(psi[i]); +} + +/**************************************************************/ +/* IUnknown implementation for counting QueryInterface calls. */ +typedef struct { + IUnknown IUnknown_iface; + struct if_count { + REFIID id; + LONG count; + } *ifaces; + LONG unknown; +} IUnknownImpl; + +static inline IUnknownImpl *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, IUnknownImpl, IUnknown_iface); +} + +static HRESULT WINAPI unk_fnQueryInterface(IUnknown *iunk, REFIID riid, void** punk) +{ + IUnknownImpl *This = impl_from_IUnknown(iunk); + UINT i, found; + for(i = found = 0; This->ifaces[i].id != NULL; i++) + { + if(IsEqualIID(This->ifaces[i].id, riid)) + { + This->ifaces[i].count++; + found = 1; + break; + } + } + if(!found) + This->unknown++; + return E_NOINTERFACE; +} + +static ULONG WINAPI unk_fnAddRef(IUnknown *iunk) +{ + return 2; +} + +static ULONG WINAPI unk_fnRelease(IUnknown *iunk) +{ + return 1; +} + +static const IUnknownVtbl vt_IUnknown = { + unk_fnQueryInterface, + unk_fnAddRef, + unk_fnRelease +}; + +static void test_SHGetIDListFromObject(void) +{ + IUnknownImpl *punkimpl; + IShellFolder *psfdesktop; + IShellView *psv; + LPITEMIDLIST pidl, pidl_desktop; + HRESULT hres; + UINT i; + struct if_count ifaces[] = + { {&IID_IPersistIDList, 0}, + {&IID_IPersistFolder2, 0}, + {&IID_IDataObject, 0}, + {&IID_IParentAndItem, 0}, + {&IID_IFolderView, 0}, + {NULL, 0} }; + + if(!pSHGetIDListFromObject) + { + win_skip("SHGetIDListFromObject missing.\n"); + return; + } + + ok(pSHGetSpecialFolderLocation != NULL, "SHGetSpecialFolderLocation missing.\n"); + + if(0) + { + /* Crashes native */ + pSHGetIDListFromObject(NULL, NULL); + pSHGetIDListFromObject((void*)0xDEADBEEF, NULL); + } + + hres = pSHGetIDListFromObject(NULL, &pidl); + ok(hres == E_NOINTERFACE, "Got %x\n", hres); + + punkimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(IUnknownImpl)); + punkimpl->IUnknown_iface.lpVtbl = &vt_IUnknown; + punkimpl->ifaces = ifaces; + punkimpl->unknown = 0; + + hres = pSHGetIDListFromObject((IUnknown*)punkimpl, &pidl); + ok(hres == E_NOINTERFACE, "Got %x\n", hres); + ok(ifaces[0].count, "interface not requested.\n"); + ok(ifaces[1].count, "interface not requested.\n"); + ok(ifaces[2].count, "interface not requested.\n"); + todo_wine + ok(ifaces[3].count || broken(!ifaces[3].count /*vista*/), + "interface not requested.\n"); + ok(ifaces[4].count || broken(!ifaces[4].count /*vista*/), + "interface not requested.\n"); + + ok(!punkimpl->unknown, "Got %d unknown.\n", punkimpl->unknown); + HeapFree(GetProcessHeap(), 0, punkimpl); + + pidl_desktop = NULL; + pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl_desktop); + ok(pidl_desktop != NULL, "Failed to get desktop pidl.\n"); + + SHGetDesktopFolder(&psfdesktop); + + /* Test IShellItem */ + if(pSHCreateShellItem) + { + IShellItem *shellitem; + hres = pSHCreateShellItem(NULL, NULL, pidl_desktop, &shellitem); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + hres = pSHGetIDListFromObject((IUnknown*)shellitem, &pidl); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + ok(ILIsEqual(pidl_desktop, pidl), "pidl not equal.\n"); + pILFree(pidl); + } + IShellItem_Release(shellitem); + } + } + else + skip("no SHCreateShellItem.\n"); + + /* Test IShellFolder */ + hres = pSHGetIDListFromObject((IUnknown*)psfdesktop, &pidl); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + ok(ILIsEqual(pidl_desktop, pidl), "pidl not equal.\n"); + pILFree(pidl); + } + + hres = IShellFolder_CreateViewObject(psfdesktop, NULL, &IID_IShellView, (void**)&psv); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + IEnumIDList *peidl; + IDataObject *pdo; + SHCONTF enum_flags; + + /* Test IFolderView */ + hres = pSHGetIDListFromObject((IUnknown*)psv, &pidl); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + ok(ILIsEqual(pidl_desktop, pidl), "pidl not equal.\n"); + pILFree(pidl); + } + + /* Test IDataObject */ + enum_flags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN; + hres = IShellFolder_EnumObjects(psfdesktop, NULL, enum_flags, &peidl); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + LPITEMIDLIST apidl[5]; + UINT count = 0; + for(count = 0; count < 5; count++) + if(IEnumIDList_Next(peidl, 1, &apidl[count], NULL) != S_OK) + break; + + if(count) + { + hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, 1, (LPCITEMIDLIST*)apidl, + &IID_IDataObject, NULL, (void**)&pdo); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + pidl = (void*)0xDEADBEEF; + hres = pSHGetIDListFromObject((IUnknown*)pdo, &pidl); + ok(hres == S_OK, "got 0x%08x\n", hres); + ok(pidl != NULL, "pidl is NULL.\n"); + ok(ILIsEqual(pidl, apidl[0]), "pidl not equal.\n"); + pILFree(pidl); + + IDataObject_Release(pdo); + } + } + else + skip("No files found - skipping single-file test.\n"); + + if(count > 1) + { + hres = IShellFolder_GetUIObjectOf(psfdesktop, NULL, count, (LPCITEMIDLIST*)apidl, + &IID_IDataObject, NULL, (void**)&pdo); + ok(hres == S_OK, "got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + pidl = (void*)0xDEADBEEF; + hres = pSHGetIDListFromObject((IUnknown*)pdo, &pidl); + ok(hres == E_NOINTERFACE || hres == E_FAIL /*Vista*/, + "got 0x%08x\n", hres); + ok(pidl == NULL, "pidl is not NULL.\n"); + + IDataObject_Release(pdo); + } + } + else + skip("zero or one file found - skipping multi-file test.\n"); + + for(i = 0; i < count; i++) + pILFree(apidl[i]); + + IEnumIDList_Release(peidl); + } + + IShellView_Release(psv); + } + + IShellFolder_Release(psfdesktop); + pILFree(pidl_desktop); +} + +static void test_SHGetItemFromObject(void) +{ + IUnknownImpl *punkimpl; + IShellFolder *psfdesktop; + LPITEMIDLIST pidl; + IShellItem *psi; + IUnknown *punk; + HRESULT hres; + struct if_count ifaces[] = + { {&IID_IPersistIDList, 0}, + {&IID_IPersistFolder2, 0}, + {&IID_IDataObject, 0}, + {&IID_IParentAndItem, 0}, + {&IID_IFolderView, 0}, + {NULL, 0} }; + + if(!pSHGetItemFromObject) + { + skip("No SHGetItemFromObject.\n"); + return; + } + + SHGetDesktopFolder(&psfdesktop); + + if(0) + { + /* Crashes with Windows 7 */ + pSHGetItemFromObject((IUnknown*)psfdesktop, &IID_IUnknown, NULL); + pSHGetItemFromObject(NULL, &IID_IUnknown, NULL); + pSHGetItemFromObject((IUnknown*)psfdesktop, NULL, (void**)&punk); + } + + hres = pSHGetItemFromObject(NULL, &IID_IUnknown, (void**)&punk); + ok(hres == E_NOINTERFACE, "Got 0x%08x\n", hres); + + punkimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(IUnknownImpl)); + punkimpl->IUnknown_iface.lpVtbl = &vt_IUnknown; + punkimpl->ifaces = ifaces; + punkimpl->unknown = 0; + + /* The same as SHGetIDListFromObject */ + hres = pSHGetIDListFromObject((IUnknown*)punkimpl, &pidl); + ok(hres == E_NOINTERFACE, "Got %x\n", hres); + ok(ifaces[0].count, "interface not requested.\n"); + ok(ifaces[1].count, "interface not requested.\n"); + ok(ifaces[2].count, "interface not requested.\n"); + todo_wine + ok(ifaces[3].count || broken(!ifaces[3].count /*vista*/), + "interface not requested.\n"); + ok(ifaces[4].count || broken(!ifaces[4].count /*vista*/), + "interface not requested.\n"); + + ok(!punkimpl->unknown, "Got %d unknown.\n", punkimpl->unknown); + HeapFree(GetProcessHeap(), 0, punkimpl); + + /* Test IShellItem */ + hres = pSHGetItemFromObject((IUnknown*)psfdesktop, &IID_IShellItem, (void**)&psi); + ok(hres == S_OK, "Got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + IShellItem *psi2; + hres = pSHGetItemFromObject((IUnknown*)psi, &IID_IShellItem, (void**)&psi2); + ok(hres == S_OK, "Got 0x%08x\n", hres); + if(SUCCEEDED(hres)) + { + todo_wine + ok(psi == psi2, "Different instances (%p != %p).\n", psi, psi2); + IShellItem_Release(psi2); + } + IShellItem_Release(psi); + } + + IShellFolder_Release(psfdesktop); +} + +static void test_SHCreateShellItemArray(void) +{ + IShellFolder *pdesktopsf, *psf; + IShellItemArray *psia; + IEnumIDList *peidl; + HRESULT hr; + WCHAR cTestDirW[MAX_PATH]; + LPITEMIDLIST pidl_testdir, pidl; + static const WCHAR testdirW[] = {'t','e','s','t','d','i','r',0}; + + if(!pSHCreateShellItemArray) { + skip("No pSHCreateShellItemArray!\n"); + return; + } + + ok(pSHGetSpecialFolderLocation != NULL, "SHGetSpecialFolderLocation missing.\n"); + + if(0) + { + /* Crashes under native */ + pSHCreateShellItemArray(NULL, NULL, 0, NULL, NULL); + pSHCreateShellItemArray(NULL, NULL, 1, NULL, NULL); + pSHCreateShellItemArray(NULL, pdesktopsf, 0, NULL, NULL); + pSHCreateShellItemArray(pidl, NULL, 0, NULL, NULL); + } + + hr = pSHCreateShellItemArray(NULL, NULL, 0, NULL, &psia); + ok(hr == E_POINTER, "got 0x%08x\n", hr); + + SHGetDesktopFolder(&pdesktopsf); + hr = pSHCreateShellItemArray(NULL, pdesktopsf, 0, NULL, &psia); + ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); + + hr = pSHCreateShellItemArray(NULL, pdesktopsf, 1, NULL, &psia); + ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); + + pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl); + hr = pSHCreateShellItemArray(pidl, NULL, 0, NULL, &psia); + ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); + pILFree(pidl); + + GetCurrentDirectoryW(MAX_PATH, cTestDirW); + myPathAddBackslashW(cTestDirW); + lstrcatW(cTestDirW, testdirW); + + CreateFilesFolders(); + + hr = IShellFolder_ParseDisplayName(pdesktopsf, NULL, NULL, cTestDirW, NULL, &pidl_testdir, 0); + ok(hr == S_OK, "got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellFolder_BindToObject(pdesktopsf, pidl_testdir, NULL, (REFIID)&IID_IShellFolder, + (void**)&psf); + ok(hr == S_OK, "Got 0x%08x\n", hr); + } + IShellFolder_Release(pdesktopsf); + + if(FAILED(hr)) + { + skip("Failed to set up environment for SHCreateShellItemArray tests.\n"); + pILFree(pidl_testdir); + Cleanup(); + return; + } + + hr = IShellFolder_EnumObjects(psf, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &peidl); + ok(hr == S_OK, "Got %08x\n", hr); + if(SUCCEEDED(hr)) + { + LPITEMIDLIST apidl[5]; + UINT done, numitems, i; + + for(done = 0; done < 5; done++) + if(IEnumIDList_Next(peidl, 1, &apidl[done], NULL) != S_OK) + break; + ok(done == 5, "Got %d pidls\n", done); + IEnumIDList_Release(peidl); + + /* Create a ShellItemArray */ + hr = pSHCreateShellItemArray(NULL, psf, done, (LPCITEMIDLIST*)apidl, &psia); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + IShellItem *psi; + + if(0) + { + /* Crashes in Windows 7 */ + IShellItemArray_GetCount(psia, NULL); + } + + IShellItemArray_GetCount(psia, &numitems); + ok(numitems == done, "Got %d, expected %d\n", numitems, done); + + hr = IShellItemArray_GetItemAt(psia, numitems, &psi); + ok(hr == E_FAIL, "Got 0x%08x\n", hr); + + /* Compare all the items */ + for(i = 0; i < numitems; i++) + { + LPITEMIDLIST pidl_abs; + pidl_abs = ILCombine(pidl_testdir, apidl[i]); + + hr = IShellItemArray_GetItemAt(psia, i, &psi); + ok(hr == S_OK, "(%d) Failed with 0x%08x\n", i, hr); + if(SUCCEEDED(hr)) + { + hr = pSHGetIDListFromObject((IUnknown*)psi, &pidl); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + ok(ILIsEqual(pidl_abs, pidl), "Pidl not equal.\n"); + pILFree(pidl); + } + IShellItem_Release(psi); + } + pILFree(pidl_abs); + } + for(i = 0; i < done; i++) + pILFree(apidl[i]); + IShellItemArray_Release(psia); + } + } + + /* SHCreateShellItemArrayFromShellItem */ + if(pSHCreateShellItemArrayFromShellItem) + { + IShellItem *psi; + + if(0) + { + /* Crashes under Windows 7 */ + pSHCreateShellItemArrayFromShellItem(NULL, &IID_IShellItemArray, NULL); + pSHCreateShellItemArrayFromShellItem(NULL, &IID_IShellItemArray, (void**)&psia); + pSHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, NULL); + } + + hr = pSHCreateItemFromIDList(pidl_testdir, &IID_IShellItem, (void**)&psi); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = pSHCreateShellItemArrayFromShellItem(psi, &IID_IShellItemArray, (void**)&psia); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + IShellItem *psi2; + UINT count; + hr = IShellItemArray_GetCount(psia, &count); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(count == 1, "Got count %d\n", count); + hr = IShellItemArray_GetItemAt(psia, 0, &psi2); + ok(hr == S_OK, "Got 0x%08x\n", hr); + todo_wine + ok(psi != psi2, "ShellItems are of the same instance.\n"); + if(SUCCEEDED(hr)) + { + LPITEMIDLIST pidl1, pidl2; + hr = pSHGetIDListFromObject((IUnknown*)psi, &pidl1); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(pidl1 != NULL, "pidl1 was null.\n"); + hr = pSHGetIDListFromObject((IUnknown*)psi2, &pidl2); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(pidl2 != NULL, "pidl2 was null.\n"); + ok(ILIsEqual(pidl1, pidl2), "pidls not equal.\n"); + pILFree(pidl1); + pILFree(pidl2); + IShellItem_Release(psi2); + } + hr = IShellItemArray_GetItemAt(psia, 1, &psi2); + ok(hr == E_FAIL, "Got 0x%08x\n", hr); + IShellItemArray_Release(psia); + } + IShellItem_Release(psi); + } + } + else + skip("No SHCreateShellItemArrayFromShellItem.\n"); + + if(pSHCreateShellItemArrayFromDataObject) + { + IShellView *psv; + + if(0) + { + /* Crashes under Windows 7 */ + pSHCreateShellItemArrayFromDataObject(NULL, &IID_IShellItemArray, NULL); + } + hr = pSHCreateShellItemArrayFromDataObject(NULL, &IID_IShellItemArray, (void**)&psia); + ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr); + + hr = IShellFolder_CreateViewObject(psf, NULL, &IID_IShellView, (void**)&psv); + ok(hr == S_OK, "got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + IEnumIDList *peidl; + IDataObject *pdo; + SHCONTF enum_flags; + + enum_flags = SHCONTF_NONFOLDERS | SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN; + hr = IShellFolder_EnumObjects(psf, NULL, enum_flags, &peidl); + ok(hr == S_OK, "got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + LPITEMIDLIST apidl[5]; + UINT count, i; + + for(count = 0; count < 5; count++) + if(IEnumIDList_Next(peidl, 1, &apidl[count], NULL) != S_OK) + break; + ok(count == 5, "Got %d\n", count); + + if(count) + { + hr = IShellFolder_GetUIObjectOf(psf, NULL, count, (LPCITEMIDLIST*)apidl, + &IID_IDataObject, NULL, (void**)&pdo); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = pSHCreateShellItemArrayFromDataObject(pdo, &IID_IShellItemArray, + (void**)&psia); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + UINT count_sia, i; + hr = IShellItemArray_GetCount(psia, &count_sia); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(count_sia == count, "Counts differ (%d, %d)\n", count, count_sia); + for(i = 0; i < count_sia; i++) + { + LPITEMIDLIST pidl_abs = ILCombine(pidl_testdir, apidl[i]); + IShellItem *psi; + hr = IShellItemArray_GetItemAt(psia, i, &psi); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + LPITEMIDLIST pidl; + hr = pSHGetIDListFromObject((IUnknown*)psi, &pidl); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(pidl != NULL, "pidl as NULL.\n"); + ok(ILIsEqual(pidl, pidl_abs), "pidls differ.\n"); + pILFree(pidl); + IShellItem_Release(psi); + } + pILFree(pidl_abs); + } + + IShellItemArray_Release(psia); + } + + IDataObject_Release(pdo); + } + for(i = 0; i < count; i++) + pILFree(apidl[i]); + } + else + skip("No files found - skipping test.\n"); + + IEnumIDList_Release(peidl); + } + IShellView_Release(psv); + } + } + else + skip("No SHCreateShellItemArrayFromDataObject.\n"); + + IShellFolder_Release(psf); + pILFree(pidl_testdir); + Cleanup(); +} + +static void test_ShellItemBindToHandler(void) +{ + IShellItem *psi; + LPITEMIDLIST pidl_desktop; + HRESULT hr; + + if(!pSHCreateShellItem) + { + skip("SHCreateShellItem missing.\n"); + return; + } + + hr = pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl_desktop); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psi); + ok(hr == S_OK, "Got 0x%08x\n", hr); + } + if(SUCCEEDED(hr)) + { + IPersistFolder2 *ppf2; + IUnknown *punk; + + if(0) + { + /* Crashes under Windows 7 */ + IShellItem_BindToHandler(psi, NULL, NULL, NULL, NULL); + IShellItem_BindToHandler(psi, NULL, &IID_IUnknown, &IID_IUnknown, NULL); + } + hr = IShellItem_BindToHandler(psi, NULL, &IID_IUnknown, &IID_IUnknown, (void**)&punk); + ok(hr == MK_E_NOOBJECT, "Got 0x%08x\n", hr); + + /* BHID_SFObject */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFObject, &IID_IShellFolder, (void**)&punk); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFObject, &IID_IPersistFolder2, (void**)&ppf2); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + LPITEMIDLIST pidl_tmp; + hr = IPersistFolder2_GetCurFolder(ppf2, &pidl_tmp); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + ok(ILIsEqual(pidl_desktop, pidl_tmp), "Pidl not equal (%p, %p)\n", pidl_desktop, pidl_tmp); + pILFree(pidl_tmp); + } + IPersistFolder2_Release(ppf2); + } + + /* BHID_SFUIObject */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFUIObject, &IID_IDataObject, (void**)&punk); + ok(hr == S_OK || broken(hr == E_NOINTERFACE /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFUIObject, &IID_IContextMenu, (void**)&punk); + ok(hr == S_OK || broken(hr == E_NOINTERFACE /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_DataObject */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_DataObject, &IID_IDataObject, (void**)&punk); + ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + todo_wine + { + /* BHID_SFViewObject */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFViewObject, &IID_IShellView, (void**)&punk); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFViewObject, &IID_IShellFolderView, (void**)&punk); + ok(hr == E_NOINTERFACE, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_Storage */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_Storage, &IID_IStream, (void**)&punk); + ok(hr == E_NOINTERFACE, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + hr = IShellItem_BindToHandler(psi, NULL, &BHID_Storage, &IID_IUnknown, (void**)&punk); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_Stream */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_Stream, &IID_IStream, (void**)&punk); + ok(hr == E_NOINTERFACE, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + hr = IShellItem_BindToHandler(psi, NULL, &BHID_Stream, &IID_IUnknown, (void**)&punk); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_StorageEnum */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_StorageEnum, &IID_IEnumShellItems, (void**)&punk); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_Transfer */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_Transfer, &IID_IUnknown, (void**)&punk); + ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_EnumItems */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_EnumItems, &IID_IEnumShellItems, (void**)&punk); + ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_Filter */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_Filter, &IID_IUnknown, (void**)&punk); + ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_LinkTargetItem */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_LinkTargetItem, &IID_IShellItem, (void**)&punk); + ok(hr == E_NOINTERFACE || broken(hr == E_INVALIDARG /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + hr = IShellItem_BindToHandler(psi, NULL, &BHID_LinkTargetItem, &IID_IUnknown, (void**)&punk); + ok(hr == E_NOINTERFACE || broken(hr == E_INVALIDARG /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_PropertyStore */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_PropertyStore, &IID_IPropertyStore, (void**)&punk); + ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + hr = IShellItem_BindToHandler(psi, NULL, &BHID_PropertyStore, &IID_IPropertyStoreFactory, (void**)&punk); + ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_ThumbnailHandler */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_ThumbnailHandler, &IID_IUnknown, (void**)&punk); + ok(hr == E_INVALIDARG || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_AssociationArray */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_AssociationArray, &IID_IQueryAssociations, (void**)&punk); + ok(hr == S_OK || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + + /* BHID_EnumAssocHandlers */ + hr = IShellItem_BindToHandler(psi, NULL, &BHID_EnumAssocHandlers, &IID_IUnknown, (void**)&punk); + ok(hr == E_NOINTERFACE || broken(hr == MK_E_NOOBJECT /* XP */), "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) IUnknown_Release(punk); + } + + IShellItem_Release(psi); + } + else + skip("Failed to create ShellItem.\n"); + + pILFree(pidl_desktop); +} + +static void test_ShellItemGetAttributes(void) +{ + IShellItem *psi; + LPITEMIDLIST pidl_desktop; + SFGAOF sfgao; + HRESULT hr; + + if(!pSHCreateShellItem) + { + skip("SHCreateShellItem missing.\n"); + return; + } + + hr = pSHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl_desktop); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psi); + ok(hr == S_OK, "Got 0x%08x\n", hr); + pILFree(pidl_desktop); + } + if(FAILED(hr)) + { + skip("Skipping tests.\n"); + return; + } + + if(0) + { + /* Crashes on native (Win 7) */ + IShellItem_GetAttributes(psi, 0, NULL); + } + + /* Test GetAttributes on the desktop folder. */ + sfgao = 0xdeadbeef; + hr = IShellItem_GetAttributes(psi, SFGAO_FOLDER, &sfgao); + ok(hr == S_OK || broken(hr == E_FAIL) /* 0, "GetSystemDirectoryW failed: %u\n", GetLastError()); + hr = pSHParseDisplayName(dirW, NULL, &pidl1, 0, NULL); + ok(hr == S_OK, "failed %08x\n", hr); + *dirW = 0; + ok(pGetSystemWow64DirectoryW(dirW, MAX_PATH) > 0, "GetSystemWow64DirectoryW failed: %u\n", GetLastError()); + hr = pSHParseDisplayName(dirW, NULL, &pidl2, 0, NULL); + ok(hr == S_OK, "failed %08x\n", hr); + ret = pILIsEqual(pidl1, pidl2); + ok(ret == FALSE, "expected different idls\n"); + pILFree(pidl1); + pILFree(pidl2); + } + IShellFolder_Release(desktop); } @@ -2084,6 +3805,7 @@ static void test_desktop_IPersist(void) { IShellFolder *desktop; IPersist *persist; + IPersistFolder2 *ppf2; CLSID clsid; HRESULT hr; @@ -2098,7 +3820,7 @@ static void test_desktop_IPersist(void) if (0) { /* crashes on native */ - hr = IPersist_GetClassID(persist, NULL); + IPersist_GetClassID(persist, NULL); } memset(&clsid, 0, sizeof(clsid)); hr = IPersist_GetClassID(persist, &clsid); @@ -2107,9 +3829,755 @@ static void test_desktop_IPersist(void) IPersist_Release(persist); } + hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder2, (void**)&ppf2); + ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* pre-Vista */, "failed %08x\n", hr); + if(SUCCEEDED(hr)) + { + IPersistFolder *ppf; + LPITEMIDLIST pidl; + hr = IShellFolder_QueryInterface(desktop, &IID_IPersistFolder, (void**)&ppf); + ok(hr == S_OK, "IID_IPersistFolder2 without IID_IPersistFolder.\n"); + if(SUCCEEDED(hr)) + IPersistFolder_Release(ppf); + + todo_wine { + hr = IPersistFolder2_Initialize(ppf2, NULL); + ok(hr == S_OK, "got %08x\n", hr); + } + + pidl = NULL; + hr = IPersistFolder2_GetCurFolder(ppf2, &pidl); + ok(hr == S_OK, "got %08x\n", hr); + ok(pidl != NULL, "pidl was NULL.\n"); + if(SUCCEEDED(hr)) pILFree(pidl); + + IPersistFolder2_Release(ppf2); + } + IShellFolder_Release(desktop); } +static void test_GetUIObject(void) +{ + IShellFolder *psf_desktop; + IContextMenu *pcm; + LPITEMIDLIST pidl; + HRESULT hr; + WCHAR path[MAX_PATH]; + const WCHAR filename[] = + {'\\','t','e','s','t','d','i','r','\\','t','e','s','t','1','.','t','x','t',0}; + + if(!pSHBindToParent) + { + win_skip("SHBindToParent missing.\n"); + return; + } + + GetCurrentDirectoryW(MAX_PATH, path); + if(!lstrlenW(path)) + { + skip("GetCurrentDirectoryW returned an empty string.\n"); + return; + } + lstrcatW(path, filename); + SHGetDesktopFolder(&psf_desktop); + + CreateFilesFolders(); + + hr = IShellFolder_ParseDisplayName(psf_desktop, NULL, NULL, path, NULL, &pidl, 0); + ok(hr == S_OK || broken(hr == E_FAIL) /* WinME */, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + IShellFolder *psf; + LPCITEMIDLIST pidl_child; + hr = pSHBindToParent(pidl, &IID_IShellFolder, (void**)&psf, &pidl_child); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellFolder_GetUIObjectOf(psf, NULL, 1, &pidl_child, &IID_IContextMenu, NULL, + (void**)&pcm); + ok(hr == S_OK, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + HMENU hmenu = CreatePopupMenu(); + INT max_id, max_id_check; + UINT count, i; + const int id_upper_limit = 32767; + hr = IContextMenu_QueryContextMenu(pcm, hmenu, 0, 0, id_upper_limit, CMF_NORMAL); + ok(SUCCEEDED(hr), "Got 0x%08x\n", hr); + max_id = HRESULT_CODE(hr) - 1; /* returns max_id + 1 */ + ok(max_id <= id_upper_limit, "Got %d\n", max_id); + count = GetMenuItemCount(hmenu); + ok(count, "Got %d\n", count); + + max_id_check = 0; + for(i = 0; i < count; i++) + { + MENUITEMINFOA mii; + INT res; + ZeroMemory(&mii, sizeof(MENUITEMINFOA)); + mii.cbSize = sizeof(MENUITEMINFOA); + mii.fMask = MIIM_ID | MIIM_FTYPE; + + SetLastError(0); + res = GetMenuItemInfoA(hmenu, i, TRUE, &mii); + ok(res, "Failed (last error: %d).\n", GetLastError()); + + ok( (mii.wID <= id_upper_limit) || (mii.fType & MFT_SEPARATOR), + "Got non-separator ID out of range: %d (type: %x)\n", mii.wID, mii.fType); + if(!(mii.fType & MFT_SEPARATOR)) + max_id_check = (mii.wID>max_id_check)?mii.wID:max_id_check; + } + ok((max_id_check == max_id) || + (max_id_check == max_id-1 /* Win 7 */), + "Not equal (or near equal), got %d and %d\n", max_id_check, max_id); + +#define is_win2k() (pSHGetFolderPathA && !pSHGetFolderPathAndSubDirA) + + if(count && !is_win2k()) /* Test is interactive on w2k, so skip */ + { + CMINVOKECOMMANDINFO cmi; + ZeroMemory(&cmi, sizeof(CMINVOKECOMMANDINFO)); + cmi.cbSize = sizeof(CMINVOKECOMMANDINFO); + + /* Attempt to execute a nonexistent command */ + cmi.lpVerb = MAKEINTRESOURCEA(9999); + hr = IContextMenu_InvokeCommand(pcm, &cmi); + ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr); + + cmi.lpVerb = "foobar_wine_test"; + hr = IContextMenu_InvokeCommand(pcm, &cmi); + ok( (hr == E_INVALIDARG) || (hr == E_FAIL /* Win7 */) || + (hr == HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION) /* Vista */), + "Got 0x%08x\n", hr); + } +#undef is_win2k + + DestroyMenu(hmenu); + IContextMenu_Release(pcm); + } + IShellFolder_Release(psf); + } + if(pILFree) pILFree(pidl); + } + + IShellFolder_Release(psf_desktop); + Cleanup(); +} + +#define verify_pidl(i,p) r_verify_pidl(__LINE__, i, p) +static void r_verify_pidl(unsigned l, LPCITEMIDLIST pidl, const WCHAR *path) +{ + LPCITEMIDLIST child; + IShellFolder *parent; + STRRET filename; + HRESULT hr; + + if(!pSHBindToParent){ + win_skip("SHBindToParent is not available, not performing full PIDL verification\n"); + if(path) + ok_(__FILE__,l)(pidl != NULL, "Expected PIDL to be non-NULL\n"); + else + ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n"); + return; + } + + if(path){ + if(!pidl){ + ok_(__FILE__,l)(0, "didn't get expected path (%s), instead: NULL\n", wine_dbgstr_w(path)); + return; + } + + hr = pSHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&parent, &child); + ok_(__FILE__,l)(hr == S_OK, "SHBindToParent failed: 0x%08x\n", hr); + if(FAILED(hr)) + return; + + hr = IShellFolder_GetDisplayNameOf(parent, child, SHGDN_FORPARSING, &filename); + ok_(__FILE__,l)(hr == S_OK, "GetDisplayNameOf failed: 0x%08x\n", hr); + if(FAILED(hr)){ + IShellFolder_Release(parent); + return; + } + + ok_(__FILE__,l)(filename.uType == STRRET_WSTR || filename.uType == STRRET_CSTR, + "Got unexpected string type: %d\n", filename.uType); + if(filename.uType == STRRET_WSTR){ + ok_(__FILE__,l)(lstrcmpW(path, U(filename).pOleStr) == 0, + "didn't get expected path (%s), instead: %s\n", + wine_dbgstr_w(path), wine_dbgstr_w(U(filename).pOleStr)); + SHFree(U(filename).pOleStr); + }else if(filename.uType == STRRET_CSTR){ + ok_(__FILE__,l)(strcmp_wa(path, U(filename).cStr) == 0, + "didn't get expected path (%s), instead: %s\n", + wine_dbgstr_w(path), U(filename).cStr); + } + + IShellFolder_Release(parent); + }else + ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n"); +} + +static void test_SHSimpleIDListFromPath(void) +{ + const WCHAR adirW[] = {'C',':','\\','s','i','d','l','f','p','d','i','r',0}; + const CHAR adirA[] = "C:\\sidlfpdir"; + BOOL br, is_unicode = !(GetVersion() & 0x80000000); + + LPITEMIDLIST pidl = NULL; + + if(!pSHSimpleIDListFromPathAW){ + win_skip("SHSimpleIDListFromPathAW not available\n"); + return; + } + + br = CreateDirectoryA(adirA, NULL); + ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError()); + + if(is_unicode) + pidl = pSHSimpleIDListFromPathAW(adirW); + else + pidl = pSHSimpleIDListFromPathAW(adirA); + verify_pidl(pidl, adirW); + pILFree(pidl); + + br = RemoveDirectoryA(adirA); + ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError()); + + if(is_unicode) + pidl = pSHSimpleIDListFromPathAW(adirW); + else + pidl = pSHSimpleIDListFromPathAW(adirA); + verify_pidl(pidl, adirW); + pILFree(pidl); +} + +/* IFileSystemBindData impl */ +static HRESULT WINAPI fsbd_QueryInterface(IFileSystemBindData *fsbd, + REFIID riid, void **ppv) +{ + if(IsEqualIID(riid, &IID_IFileSystemBindData) || + IsEqualIID(riid, &IID_IUnknown)){ + *ppv = fsbd; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI fsbd_AddRef(IFileSystemBindData *fsbd) +{ + return 2; +} + +static ULONG WINAPI fsbd_Release(IFileSystemBindData *fsbd) +{ + return 1; +} + +static HRESULT WINAPI fsbd_SetFindData(IFileSystemBindData *fsbd, + const WIN32_FIND_DATAW *pfd) +{ + ok(0, "SetFindData called\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI fsbd_GetFindData_nul(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + memset(pfd, 0, sizeof(WIN32_FIND_DATAW)); + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_junk(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + memset(pfd, 0xef, sizeof(WIN32_FIND_DATAW)); + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_invalid(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + memset(pfd, 0, sizeof(WIN32_FIND_DATAW)); + *pfd->cFileName = 'a'; + *pfd->cAlternateFileName = 'a'; + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_valid(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + static const WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0}; + HANDLE handle = FindFirstFileW(adirW, pfd); + FindClose(handle); + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_fail(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + return E_FAIL; +} + +static IFileSystemBindDataVtbl fsbdVtbl = { + fsbd_QueryInterface, + fsbd_AddRef, + fsbd_Release, + fsbd_SetFindData, + NULL +}; + +static IFileSystemBindData fsbd = { &fsbdVtbl }; + +static void test_ParseDisplayNamePBC(void) +{ + WCHAR wFileSystemBindData[] = + {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0}; + WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0}; + WCHAR afileW[] = {'C',':','\\','f','s','b','d','d','i','r','\\','f','i','l','e','.','t','x','t',0}; + WCHAR afile2W[] = {'C',':','\\','f','s','b','d','d','i','r','\\','s','\\','f','i','l','e','.','t','x','t',0}; + const HRESULT exp_err = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + IShellFolder *psf; + IBindCtx *pbc; + HRESULT hres; + ITEMIDLIST *pidl; + + /* Check if we support WCHAR functions */ + SetLastError(0xdeadbeef); + lstrcmpiW(adirW, adirW); + if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){ + win_skip("Most W-calls are not implemented\n"); + return; + } + + hres = SHGetDesktopFolder(&psf); + ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres); + if(FAILED(hres)){ + win_skip("Failed to get IShellFolder, can't run tests\n"); + return; + } + + /* fails on unknown dir with no IBindCtx */ + hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, adirW, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, afileW, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, afile2W, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + + /* fails on unknown dir with IBindCtx with no IFileSystemBindData */ + hres = CreateBindCtx(0, &pbc); + ok(hres == S_OK, "CreateBindCtx failed: 0x%08x\n", hres); + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afileW, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afile2W, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + + /* unknown dir with IBindCtx with IFileSystemBindData */ + hres = IBindCtx_RegisterObjectParam(pbc, wFileSystemBindData, (IUnknown*)&fsbd); + ok(hres == S_OK, "RegisterObjectParam failed: 0x%08x\n", hres); + + /* return E_FAIL from GetFindData */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_fail; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afileW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afileW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afile2W, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afile2W); + ILFree(pidl); + } + + /* set FIND_DATA struct to NULLs */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_nul; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afileW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afileW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afile2W, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afile2W); + ILFree(pidl); + } + + /* set FIND_DATA struct to junk */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_junk; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afileW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afileW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afile2W, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afile2W); + ILFree(pidl); + } + + /* set FIND_DATA struct to invalid data */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_invalid; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afileW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afileW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afile2W, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afile2W); + ILFree(pidl); + } + + /* set FIND_DATA struct to valid data */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_valid; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afileW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afileW); + ILFree(pidl); + } + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, afile2W, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, afile2W); + ILFree(pidl); + } + + IBindCtx_Release(pbc); + IShellFolder_Release(psf); +} + +static const CHAR testwindow_class[] = "testwindow"; +#define WM_USER_NOTIFY (WM_APP+1) + +struct ChNotifyTest { + const char id[256]; + const UINT notify_count; + UINT missing_events; + UINT signal; + const char path_1[256]; + const char path_2[256]; +} chnotify_tests[] = { + {"MKDIR", 1, 0, SHCNE_MKDIR, "C:\\shell32_cn_test\\test", ""}, + {"CREATE", 1, 0, SHCNE_CREATE, "C:\\shell32_cn_test\\test\\file.txt", ""}, + {"RMDIR", 1, 0, SHCNE_RMDIR, "C:\\shell32_cn_test\\test", ""}, +}; + +struct ChNotifyTest *exp_data; +BOOL test_new_delivery_flag; + +static LRESULT CALLBACK testwindow_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + LONG signal = (LONG)lparam; + + switch(msg){ + case WM_USER_NOTIFY: + if(exp_data->missing_events > 0) { + WCHAR *path1, *path2; + LPITEMIDLIST *pidls = (LPITEMIDLIST*)wparam; + HANDLE hLock = NULL; + + if(test_new_delivery_flag) { + hLock = SHChangeNotification_Lock((HANDLE)wparam, lparam, &pidls, &signal); + ok(hLock != NULL, "SHChangeNotification_Lock returned NULL\n"); + } + + ok(exp_data->signal == signal, + "%s: expected notification type %x, got: %x\n", + exp_data->id, exp_data->signal, signal); + + trace("verifying pidls for: %s\n", exp_data->id); + path1 = make_wstr(exp_data->path_1); + path2 = make_wstr(exp_data->path_2); + verify_pidl(pidls[0], path1); + verify_pidl(pidls[1], path2); + HeapFree(GetProcessHeap(), 0, path1); + HeapFree(GetProcessHeap(), 0, path2); + + exp_data->missing_events--; + + if(test_new_delivery_flag) + SHChangeNotification_Unlock(hLock); + }else + ok(0, "Didn't expect a WM_USER_NOTIFY message (event: %x)\n", signal); + return 0; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +static void register_testwindow_class(void) +{ + WNDCLASSEXA cls; + ATOM ret; + + ZeroMemory(&cls, sizeof(cls)); + cls.cbSize = sizeof(cls); + cls.style = 0; + cls.lpfnWndProc = testwindow_wndproc; + cls.hInstance = GetModuleHandleA(NULL); + cls.lpszClassName = testwindow_class; + + SetLastError(0); + ret = RegisterClassExA(&cls); + ok(ret != 0, "RegisterClassExA failed: %d\n", GetLastError()); +} + +/* SHCNF_FLUSH doesn't seem to work as advertised for SHCNF_PATHA, so we + * have to poll repeatedly for the message to appear */ +static void do_events(void) +{ + int c = 0; + while (exp_data->missing_events && (c++ < 10)){ + MSG msg; + while(PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)){ + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + if(exp_data->missing_events) + Sleep(500); + } + trace("%s: took %d tries\n", exp_data->id, c); +} + +static void test_SHChangeNotify(BOOL test_new_delivery) +{ + HWND wnd; + ULONG notifyID, i; + HRESULT hr; + BOOL br, has_unicode; + SHChangeNotifyEntry entries[1]; + const CHAR root_dirA[] = "C:\\shell32_cn_test"; + const WCHAR root_dirW[] = {'C',':','\\','s','h','e','l','l','3','2','_','c','n','_','t','e','s','t',0}; + + trace("SHChangeNotify tests (%x)\n", test_new_delivery); + + CreateDirectoryW(NULL, NULL); + has_unicode = !(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED); + + test_new_delivery_flag = test_new_delivery; + if(!test_new_delivery) + register_testwindow_class(); + + wnd = CreateWindowExA(0, testwindow_class, testwindow_class, 0, + CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, + NULL, NULL, GetModuleHandleA(NULL), 0); + ok(wnd != NULL, "Failed to make a window\n"); + + br = CreateDirectoryA(root_dirA, NULL); + ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError()); + + entries[0].pidl = NULL; + if(has_unicode) + hr = SHILCreateFromPath(root_dirW, (LPITEMIDLIST*)&entries[0].pidl, 0); + else + hr = SHILCreateFromPath((LPCVOID)root_dirA, (LPITEMIDLIST*)&entries[0].pidl, 0); + ok(hr == S_OK, "SHILCreateFromPath failed: 0x%08x\n", hr); + entries[0].fRecursive = TRUE; + + notifyID = SHChangeNotifyRegister(wnd, !test_new_delivery ? SHCNRF_ShellLevel : SHCNRF_ShellLevel|SHCNRF_NewDelivery, + SHCNE_ALLEVENTS, WM_USER_NOTIFY, 1, entries); + ok(notifyID != 0, "Failed to register a window for change notifications\n"); + + for(i = 0; i < sizeof(chnotify_tests) / sizeof(*chnotify_tests); ++i){ + exp_data = chnotify_tests + i; + + exp_data->missing_events = exp_data->notify_count; + SHChangeNotify(exp_data->signal, SHCNF_PATHA | SHCNF_FLUSH, + strlen(exp_data->path_1) > 0 ? exp_data->path_1 : NULL, + strlen(exp_data->path_2) > 0 ? exp_data->path_2 : NULL); + do_events(); + ok(exp_data->missing_events == 0, "%s: Expected wndproc to be called\n", exp_data->id); + + if(has_unicode){ + WCHAR *path1, *path2; + + path1 = make_wstr(exp_data->path_1); + path2 = make_wstr(exp_data->path_2); + + exp_data->missing_events = exp_data->notify_count; + SHChangeNotify(exp_data->signal, SHCNF_PATHW | SHCNF_FLUSH, path1, path2); + do_events(); + ok(exp_data->missing_events == 0, "%s: Expected wndproc to be called\n", exp_data->id); + + HeapFree(GetProcessHeap(), 0, path1); + HeapFree(GetProcessHeap(), 0, path2); + } + } + + SHChangeNotifyDeregister(notifyID); + DestroyWindow(wnd); + + ILFree((LPITEMIDLIST)entries[0].pidl); + br = RemoveDirectoryA(root_dirA); + ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError()); +} + +static void test_SHCreateDefaultContextMenu(void) +{ + HKEY keys[16]; + WCHAR path[MAX_PATH]; + IShellFolder *desktop,*folder; + IPersistFolder2 *persist; + IContextMenu *cmenu; + LONG status; + LPITEMIDLIST pidlFolder, pidl_child, pidl; + DEFCONTEXTMENU cminfo; + HRESULT hr; + UINT i; + const WCHAR filename[] = + {'\\','t','e','s','t','d','i','r','\\','t','e','s','t','1','.','t','x','t',0}; + if(!pSHCreateDefaultContextMenu) + { + win_skip("SHCreateDefaultContextMenu missing.\n"); + return; + } + + if(!pSHBindToParent) + { + skip("SHBindToParent missing.\n"); + return; + } + + GetCurrentDirectoryW(MAX_PATH, path); + if(!lstrlenW(path)) + { + skip("GetCurrentDirectoryW returned an empty string.\n"); + return; + } + lstrcatW(path, filename); + SHGetDesktopFolder(&desktop); + + CreateFilesFolders(); + + hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, path, NULL, &pidl, 0); + ok(hr == S_OK || broken(hr == E_FAIL) /* WinME */, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + + hr = pSHBindToParent(pidl, &IID_IShellFolder, (void**)&folder, (LPCITEMIDLIST*)&pidl_child); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + IShellFolder_QueryInterface(folder,&IID_IPersistFolder2,(void**)&persist); + IPersistFolder2_GetCurFolder(persist,&pidlFolder); + IPersistFolder2_Release(persist); + if(SUCCEEDED(hr)) + { + + cminfo.hwnd=NULL; + cminfo.pcmcb=NULL; + cminfo.psf=folder; + cminfo.pidlFolder=NULL; + cminfo.apidl=(LPCITEMIDLIST*)&pidl_child; + cminfo.cidl=1; + cminfo.aKeys=NULL; + cminfo.cKeys=0; + cminfo.punkAssociationInfo=NULL; + hr = pSHCreateDefaultContextMenu(&cminfo,&IID_IContextMenu,(void**)&cmenu); + ok(hr==S_OK,"Got 0x%08x\n", hr); + IContextMenu_Release(cmenu); + cminfo.pidlFolder=pidlFolder; + hr = pSHCreateDefaultContextMenu(&cminfo,&IID_IContextMenu,(void**)&cmenu); + ok(hr==S_OK,"Got 0x%08x\n", hr); + IContextMenu_Release(cmenu); + status = RegOpenKeyExA(HKEY_CLASSES_ROOT,"*",0,KEY_READ,keys); + if(status==ERROR_SUCCESS){ + for(i=1;i<16;i++) + keys[i]=keys[0]; + cminfo.aKeys=keys; + cminfo.cKeys=16; + hr = pSHCreateDefaultContextMenu(&cminfo,&IID_IContextMenu,(void**)&cmenu); + RegCloseKey(keys[0]); + ok(hr==S_OK,"Got 0x%08x\n", hr); + IContextMenu_Release(cmenu); + } + } + ILFree(pidlFolder); + IShellFolder_Release(folder); + } + IShellFolder_Release(desktop); + ILFree(pidl); + Cleanup(); +} + START_TEST(shlfolder) { init_function_pointers(); @@ -2127,10 +4595,25 @@ START_TEST(shlfolder) test_CallForAttributes(); test_FolderShortcut(); test_ITEMIDLIST_format(); + test_SHGetFolderPathA(); test_SHGetFolderPathAndSubDirA(); test_LocalizedNames(); test_SHCreateShellItem(); + test_SHCreateShellItemArray(); test_desktop_IPersist(); + test_GetUIObject(); + test_SHSimpleIDListFromPath(); + test_ParseDisplayNamePBC(); + test_SHGetNameFromIDList(); + test_SHGetItemFromDataObject(); + test_SHGetIDListFromObject(); + test_SHGetItemFromObject(); + test_ShellItemCompare(); + test_SHChangeNotify(FALSE); + test_SHChangeNotify(TRUE); + test_ShellItemBindToHandler(); + test_ShellItemGetAttributes(); + test_SHCreateDefaultContextMenu(); OleUninitialize(); } diff --git a/rostests/winetests/shell32/shlview.c b/rostests/winetests/shell32/shlview.c index e3e0ef24ed5..6e974992159 100644 --- a/rostests/winetests/shell32/shlview.c +++ b/rostests/winetests/shell32/shlview.c @@ -78,6 +78,16 @@ static HWND subclass_listview(HWND hwnd) /* listview is a first child */ listview = FindWindowExA(hwnd, NULL, WC_LISTVIEWA, NULL); + if(!listview) + { + /* .. except for some versions of Windows XP, where things + are slightly more complicated. */ + HWND hwnd_tmp; + hwnd_tmp = FindWindowExA(hwnd, NULL, "DUIViewWndClassName", NULL); + hwnd_tmp = FindWindowExA(hwnd_tmp, NULL, "DirectUIHWND", NULL); + hwnd_tmp = FindWindowExA(hwnd_tmp, NULL, "CtrlNotifySink", NULL); + listview = FindWindowExA(hwnd_tmp, NULL, WC_LISTVIEWA, NULL); + } oldproc = (WNDPROC)SetWindowLongPtrA(listview, GWLP_WNDPROC, (LONG_PTR)listview_subclass_proc); @@ -86,28 +96,71 @@ static HWND subclass_listview(HWND hwnd) return listview; } +static UINT get_msg_count(struct msg_sequence **seq, int sequence_index, UINT message) +{ + struct msg_sequence *msg_seq = seq[sequence_index]; + UINT i, count = 0; + + for(i = 0; i < msg_seq->count ; i++) + if(msg_seq->sequence[i].message == message) + count++; + + return count; +} + +/* Checks that every message in the sequence seq is also present in + * the UINT array msgs */ +static void verify_msgs_in_(struct msg_sequence *seq, const UINT *msgs, + const char *file, int line) +{ + UINT i, j, msg, failcount = 0; + for(i = 0; i < seq->count; i++) + { + BOOL found = FALSE; + msg = seq->sequence[i].message; + for(j = 0; msgs[j] != 0; j++) + if(msgs[j] == msg) found = TRUE; + + if(!found) + { + failcount++; + trace("Unexpected message %d\n", msg); + } + } + ok_(file, line) (!failcount, "%d failures.\n", failcount); + flush_sequences(sequences, NUM_MSG_SEQUENCES); +} + +#define verify_msgs_in(seq, msgs) \ + verify_msgs_in_(seq, msgs, __FILE__, __LINE__) + /* dummy IDataObject implementation */ typedef struct { - const IDataObjectVtbl *lpVtbl; + IDataObject IDataObject_iface; LONG ref; } IDataObjectImpl; static const IDataObjectVtbl IDataObjectImpl_Vtbl; +static inline IDataObjectImpl *impl_from_IDataObject(IDataObject *iface) +{ + return CONTAINING_RECORD(iface, IDataObjectImpl, IDataObject_iface); +} + static IDataObject* IDataObjectImpl_Construct(void) { IDataObjectImpl *obj; obj = HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); - obj->lpVtbl = &IDataObjectImpl_Vtbl; + obj->IDataObject_iface.lpVtbl = &IDataObjectImpl_Vtbl; obj->ref = 1; - return (IDataObject*)obj; + return &obj->IDataObject_iface; } static HRESULT WINAPI IDataObjectImpl_QueryInterface(IDataObject *iface, REFIID riid, void **ppvObj) { - IDataObjectImpl *This = (IDataObjectImpl *)iface; + IDataObjectImpl *This = impl_from_IDataObject(iface); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDataObject)) @@ -126,13 +179,13 @@ static HRESULT WINAPI IDataObjectImpl_QueryInterface(IDataObject *iface, REFIID static ULONG WINAPI IDataObjectImpl_AddRef(IDataObject * iface) { - IDataObjectImpl *This = (IDataObjectImpl *)iface; + IDataObjectImpl *This = impl_from_IDataObject(iface); return InterlockedIncrement(&This->ref); } static ULONG WINAPI IDataObjectImpl_Release(IDataObject * iface) { - IDataObjectImpl *This = (IDataObjectImpl *)iface; + IDataObjectImpl *This = impl_from_IDataObject(iface); ULONG ref = InterlockedDecrement(&This->ref); if (!ref) @@ -210,28 +263,33 @@ static const IDataObjectVtbl IDataObjectImpl_Vtbl = /* dummy IShellBrowser implementation */ typedef struct { - const IShellBrowserVtbl *lpVtbl; + IShellBrowser IShellBrowser_iface; LONG ref; } IShellBrowserImpl; static const IShellBrowserVtbl IShellBrowserImpl_Vtbl; +static inline IShellBrowserImpl *impl_from_IShellBrowser(IShellBrowser *iface) +{ + return CONTAINING_RECORD(iface, IShellBrowserImpl, IShellBrowser_iface); +} + static IShellBrowser* IShellBrowserImpl_Construct(void) { IShellBrowserImpl *browser; browser = HeapAlloc(GetProcessHeap(), 0, sizeof(*browser)); - browser->lpVtbl = &IShellBrowserImpl_Vtbl; + browser->IShellBrowser_iface.lpVtbl = &IShellBrowserImpl_Vtbl; browser->ref = 1; - return (IShellBrowser*)browser; + return &browser->IShellBrowser_iface; } static HRESULT WINAPI IShellBrowserImpl_QueryInterface(IShellBrowser *iface, REFIID riid, LPVOID *ppvObj) { - IShellBrowserImpl *This = (IShellBrowserImpl *)iface; + IShellBrowserImpl *This = impl_from_IShellBrowser(iface); *ppvObj = NULL; @@ -253,13 +311,13 @@ static HRESULT WINAPI IShellBrowserImpl_QueryInterface(IShellBrowser *iface, static ULONG WINAPI IShellBrowserImpl_AddRef(IShellBrowser * iface) { - IShellBrowserImpl *This = (IShellBrowserImpl *)iface; + IShellBrowserImpl *This = impl_from_IShellBrowser(iface); return InterlockedIncrement(&This->ref); } static ULONG WINAPI IShellBrowserImpl_Release(IShellBrowser * iface) { - IShellBrowserImpl *This = (IShellBrowserImpl *)iface; + IShellBrowserImpl *This = impl_from_IShellBrowser(iface); ULONG ref = InterlockedDecrement(&This->ref); if (!ref) @@ -423,7 +481,7 @@ static const struct message folderview_getselectionmarked_seq[] = { }; static const struct message folderview_getfocused_seq[] = { - { LVM_GETNEXTITEM, sent|wparam|lparam, -1, LVNI_FOCUSED }, + { LVM_GETNEXTITEM, sent|wparam|lparam|optional, -1, LVNI_FOCUSED }, { 0 } }; @@ -451,7 +509,7 @@ static void test_IShellView_CreateViewWindow(void) if (0) { /* crashes on native */ - hr = IShellView_CreateViewWindow(view, NULL, &settings, NULL, NULL, NULL); + IShellView_CreateViewWindow(view, NULL, &settings, NULL, NULL, NULL); } settings.ViewMode = FVM_ICON; @@ -487,7 +545,7 @@ static void test_IFolderView(void) HWND hwnd_view, hwnd_list; PITEMID_CHILD pidl; HRESULT hr; - INT ret; + INT ret, count; POINT pt; LONG ref1, ref2; RECT r; @@ -519,14 +577,14 @@ static void test_IFolderView(void) if (0) { /* crashes on Vista and Win2k8 - List not created yet case */ - hr = IFolderView_GetSpacing(fv, &pt); + IFolderView_GetSpacing(fv, &pt); /* crashes on XP */ - hr = IFolderView_GetSelectionMarkedItem(fv, NULL); - hr = IFolderView_GetFocusedItem(fv, NULL); + IFolderView_GetSelectionMarkedItem(fv, NULL); + IFolderView_GetFocusedItem(fv, NULL); /* crashes on Vista+ */ - hr = IFolderView_Item(fv, 0, NULL); + IFolderView_Item(fv, 0, NULL); } browser = IShellBrowserImpl_Construct(); @@ -570,32 +628,48 @@ if (0) ok(pt.x == LOWORD(ret) && pt.y == HIWORD(ret), "got (%d, %d)\n", LOWORD(ret), HIWORD(ret)); } + /* IFolderView::ItemCount */ +if (0) +{ + /* crashes on XP */ + IFolderView_ItemCount(fv, SVGIO_ALLVIEW, NULL); +} + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + IFolderView_ItemCount(fv, SVGIO_ALLVIEW, &count); + /* IFolderView::GetSelectionMarkedItem */ if (0) { /* crashes on XP */ - hr = IFolderView_GetSelectionMarkedItem(fv, NULL); + IFolderView_GetSelectionMarkedItem(fv, NULL); } flush_sequences(sequences, NUM_MSG_SEQUENCES); hr = IFolderView_GetSelectionMarkedItem(fv, &ret); - ok(hr == S_OK, "got (0x%08x)\n", hr); + if (count) + ok(hr == S_OK, "got (0x%08x)\n", hr); + else + ok(hr == S_FALSE, "got (0x%08x)\n", hr); ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_getselectionmarked_seq, - "IFolderView::GetSelectionMarkedItem", FALSE); + "IFolderView::GetSelectionMarkedItem", FALSE); /* IFolderView::GetFocusedItem */ flush_sequences(sequences, NUM_MSG_SEQUENCES); hr = IFolderView_GetFocusedItem(fv, &ret); - ok(hr == S_OK, "got (0x%08x)\n", hr); + if (count) + ok(hr == S_OK, "got (0x%08x)\n", hr); + else + ok(hr == S_FALSE, "got (0x%08x)\n", hr); ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_getfocused_seq, - "IFolderView::GetFocusedItem", FALSE); + "IFolderView::GetFocusedItem", FALSE); /* IFolderView::GetFolder, just return pointer */ if (0) { /* crashes on XP */ - hr = IFolderView_GetFolder(fv, NULL, (void**)&folder); - hr = IFolderView_GetFolder(fv, NULL, NULL); + IFolderView_GetFolder(fv, NULL, (void**)&folder); + IFolderView_GetFolder(fv, NULL, NULL); } hr = IFolderView_GetFolder(fv, &IID_IShellFolder, NULL); @@ -607,22 +681,10 @@ if (0) ok(hr == S_OK, "got (0x%08x)\n", hr); ref2 = IShellFolder_AddRef(desktop); IShellFolder_Release(desktop); - ok(ref1 == ref2, "expected same refcount, got %d\n", ref2); + ok(ref1 == ref2 || ref1 + 1 == ref2, /* >= vista */ + "expected same refcount, got %d\n", ref2); ok(desktop == folder, "\n"); - /* IFolderView::ItemCount */ -if (0) -{ - /* crashes on XP */ - hr = IFolderView_ItemCount(fv, SVGIO_ALLVIEW, NULL); -} - - flush_sequences(sequences, NUM_MSG_SEQUENCES); - hr = IFolderView_ItemCount(fv, SVGIO_ALLVIEW, &ret); - ok(hr == S_OK, "got (0x%08x)\n", hr); - ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_itemcount_seq, - "IFolderView::ItemCount", FALSE); - IShellBrowser_Release(browser); IFolderView_Release(fv); IShellView_Release(view); @@ -711,9 +773,9 @@ static void test_IShellFolderView(void) /* ::RemoveObject */ i = 0xdeadbeef; hr = IShellFolderView_RemoveObject(folderview, NULL, &i); - ok(hr == S_OK, "got (0x%08x)\n", hr); - ok(i == 0 || i == -1 /* Win7 */ || broken(i == 0xdeadbeef) /* Vista, 2k8 */, - "got %d\n", i); + ok(hr == S_OK || hr == E_FAIL, "got (0x%08x)\n", hr); + if (hr == S_OK) ok(i == 0 || broken(i == 0xdeadbeef) /* Vista, 2k8 */, + "got %d\n", i); IShellFolderView_Release(folderview); @@ -743,6 +805,352 @@ static void test_IOleWindow(void) IShellFolder_Release(desktop); } +static const struct message folderview_setcurrentviewmode1_2_prevista[] = { + { LVM_SETVIEW, sent|wparam, LV_VIEW_ICON}, + { LVM_SETIMAGELIST, sent|wparam, 0}, + { LVM_SETIMAGELIST, sent|wparam, 1}, + { 0x105a, sent}, + { LVM_SETBKIMAGEW, sent|optional}, /* w2k3 */ + { LVM_GETBKCOLOR, sent|optional}, /* w2k3 */ + { LVM_GETTEXTBKCOLOR, sent|optional}, /* w2k3 */ + { LVM_GETTEXTCOLOR, sent|optional}, /* w2k3 */ + { LVM_SETEXTENDEDLISTVIEWSTYLE, sent|optional|wparam, 0xc8}, /* w2k3 */ + { LVM_ARRANGE, sent }, + { LVM_ARRANGE, sent|optional }, /* WinXP */ + { 0 } +}; + +static const struct message folderview_setcurrentviewmode3_prevista[] = { + { LVM_SETVIEW, sent|wparam, LV_VIEW_LIST}, + { LVM_SETIMAGELIST, sent|wparam, 0}, + { LVM_SETIMAGELIST, sent|wparam, 1}, + { 0x105a, sent}, + { LVM_SETBKIMAGEW, sent|optional}, /* w2k3 */ + { LVM_GETBKCOLOR, sent|optional}, /* w2k3 */ + { LVM_GETTEXTBKCOLOR, sent|optional}, /* w2k3 */ + { LVM_GETTEXTCOLOR, sent|optional}, /* w2k3 */ + { LVM_SETEXTENDEDLISTVIEWSTYLE, sent|optional|wparam, 0xc8}, /* w2k3 */ + { 0 } +}; + +static const struct message folderview_setcurrentviewmode4_prevista[] = { + { LVM_GETHEADER, sent}, + { LVM_GETITEMCOUNT, sent|optional }, + { LVM_SETSELECTEDCOLUMN, sent}, + { WM_NOTIFY, sent }, + { WM_NOTIFY, sent }, + { WM_NOTIFY, sent }, + { WM_NOTIFY, sent }, + { LVM_SETVIEW, sent|wparam, LV_VIEW_DETAILS}, + { LVM_SETIMAGELIST, sent|wparam, 0}, + { LVM_SETIMAGELIST, sent|wparam, 1}, + { 0x105a, sent}, + { LVM_SETBKIMAGEW, sent|optional}, /* w2k3 */ + { LVM_GETBKCOLOR, sent|optional}, /* w2k3 */ + { LVM_GETTEXTBKCOLOR, sent|optional}, /* w2k3 */ + { LVM_GETTEXTCOLOR, sent|optional}, /* w2k3 */ + { LVM_SETEXTENDEDLISTVIEWSTYLE, sent|optional|wparam, 0xc8}, /* w2k3 */ + { 0 } +}; + +/* XP, SetCurrentViewMode(5) + 108e - LVM_SETVIEW (LV_VIEW_ICON); + 1036 - LVM_SETEXTEDEDLISTVIEWSTYLE (0x8000, 0) + 100c/104c repeated X times + 1003 - LVM_SETIMAGELIST + 1035 - LVM_SETICONSPACING + 1004 - LVM_GETITEMCOUNT + 105a - ? + 1016 - LVM_ARRANGE + 1016 - LVM_ARRANGE +*/ + +/* XP, SetCurrentViewMode(6) + 1036 - LVM_SETEXTENDEDLISTVIEWSTYLE (0x8000, 0) + 1035 - LVM_SETICONSPACING + 1003 - LVM_SETIMAGELIST + 1003 - LVM_SETIMAGELIST + 100c/104c repeated X times + 10a2 - LVM_SETTILEVIEWINFO + 108e - LVM_SETVIEW (LV_VIEW_TILE) + 1003 - LVM_SETIMAGELIST + 105a - ? + 1016 - LVM_ARRANGE + 1016 - LVM_ARRANGE +*/ + +/* XP, SetCurrentViewMode (7) + 10a2 - LVM_SETTILEVIEWINFO + 108e - LVM_SETVIEW (LV_VIEW_ICON) + 1004/10a4 (LVM_GETITEMCOUNT/LVM_SETTILEINFO) X times + 1016 - LVM_ARRANGE + 1016 - LVM_ARRANGE + ... + LVM_SETEXTENDEDLISTVIEWSTYLE (0x40000, 0x40000) + ... + LVM_SETEXTENDEDLISTVIEWSTYLE (0x8000, 0x8000) +*/ + +static void test_GetSetCurrentViewMode(void) +{ + IShellFolder *desktop; + IShellView *sview; + IFolderView *fview; + IShellBrowser *browser; + FOLDERSETTINGS fs; + UINT viewmode; + HWND hwnd; + RECT rc = {0, 0, 10, 10}; + HRESULT hr; + UINT i; + static const int winxp_res[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + static const int win2k3_res[11] = {0, 1, 2, 3, 4, 5, 6, 5, 8, 0, 0}; + static const int vista_res[11] = {0, 1, 5, 3, 4, 5, 6, 7, 7, 0, 0}; + static const int win7_res[11] = {1, 1, 1, 3, 4, 1, 6, 1, 8, 8, 8}; + + hr = SHGetDesktopFolder(&desktop); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IShellFolder_CreateViewObject(desktop, NULL, &IID_IShellView, (void**)&sview); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + fs.ViewMode = 1; + fs.fFlags = 0; + browser = IShellBrowserImpl_Construct(); + hr = IShellView_CreateViewWindow(sview, NULL, &fs, browser, &rc, &hwnd); + ok(hr == S_OK || broken(hr == S_FALSE /*Win2k*/ ), "got (0x%08x)\n", hr); + + hr = IShellView_QueryInterface(sview, &IID_IFolderView, (void**)&fview); + ok(hr == S_OK || broken(hr == E_NOINTERFACE), "got (0x%08x)\n", hr); + if(SUCCEEDED(hr)) + { + HWND hwnd_lv; + UINT count; + + if (0) + { + /* Crashes under Win7/WinXP */ + IFolderView_GetCurrentViewMode(fview, NULL); + } + + hr = IFolderView_GetCurrentViewMode(fview, &viewmode); + ok(hr == S_OK, "got (0x%08x)\n", hr); + ok(viewmode == 1, "ViewMode was %d\n", viewmode); + + hr = IFolderView_SetCurrentViewMode(fview, FVM_AUTO); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IFolderView_SetCurrentViewMode(fview, 0); + ok(hr == E_INVALIDARG || broken(hr == S_OK), + "got (0x%08x)\n", hr); + + hr = IFolderView_GetCurrentViewMode(fview, &viewmode); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + for(i = 1; i < 9; i++) + { + hr = IFolderView_SetCurrentViewMode(fview, i); + ok(hr == S_OK || (i == 8 && hr == E_INVALIDARG /*Vista*/), + "(%d) got (0x%08x)\n", i, hr); + + hr = IFolderView_GetCurrentViewMode(fview, &viewmode); + ok(hr == S_OK, "(%d) got (0x%08x)\n", i, hr); + + /* Wine currently behaves like winxp here. */ + ok((viewmode == win7_res[i]) || (viewmode == vista_res[i]) || + (viewmode == win2k3_res[i]) || (viewmode == winxp_res[i]), + "(%d) got %d\n",i , viewmode); + } + + hr = IFolderView_SetCurrentViewMode(fview, 9); + ok(hr == E_INVALIDARG || broken(hr == S_OK), + "got (0x%08x)\n", hr); + + /* Test messages */ + hwnd_lv = subclass_listview(hwnd); + ok(hwnd_lv != NULL, "Failed to subclass listview\n"); + if(hwnd_lv) + { + /* Vista seems to set the viewmode by other means than + sending messages. At least no related messages are + captured by subclassing. + */ + BOOL vista_plus = FALSE; + static const UINT vista_plus_msgs[] = { + WM_SETREDRAW, WM_NOTIFY, WM_NOTIFYFORMAT, WM_QUERYUISTATE, + WM_MENUCHAR, WM_WINDOWPOSCHANGING, WM_NCCALCSIZE, WM_WINDOWPOSCHANGED, + WM_PARENTNOTIFY, LVM_GETHEADER, 0 }; + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + hr = IFolderView_SetCurrentViewMode(fview, 1); + ok(hr == S_OK, "got 0x%08x\n", hr); + + /* WM_SETREDRAW is not sent in versions before Vista. */ + vista_plus = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, WM_SETREDRAW); + if(vista_plus) + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + else + ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_setcurrentviewmode1_2_prevista, + "IFolderView::SetCurrentViewMode(1)", TRUE); + + hr = IFolderView_SetCurrentViewMode(fview, 2); + ok(hr == S_OK, "got 0x%08x\n", hr); + if(vista_plus) + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + else + ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_setcurrentviewmode1_2_prevista, + "IFolderView::SetCurrentViewMode(2)", TRUE); + + hr = IFolderView_SetCurrentViewMode(fview, 3); + ok(hr == S_OK, "got 0x%08x\n", hr); + if(vista_plus) + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + else + ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_setcurrentviewmode3_prevista, + "IFolderView::SetCurrentViewMode(3)", TRUE); + + hr = IFolderView_SetCurrentViewMode(fview, 4); + ok(hr == S_OK, "got 0x%08x\n", hr); + if(vista_plus) + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + else + ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_setcurrentviewmode4_prevista, + "IFolderView::SetCurrentViewMode(4)", TRUE); + + hr = IFolderView_SetCurrentViewMode(fview, 5); + ok(hr == S_OK, "got 0x%08x\n", hr); + todo_wine + { + if(vista_plus) + { + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + } + else + { + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETVIEW); + ok(count == 1, "LVM_SETVIEW sent %d times.\n", count); + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETEXTENDEDLISTVIEWSTYLE); + ok(count == 1 || count == 2, "LVM_SETEXTENDEDLISTVIEWSTYLE sent %d times.\n", count); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + } + } + + hr = IFolderView_SetCurrentViewMode(fview, 6); + ok(hr == S_OK, "got 0x%08x\n", hr); + todo_wine + { + if(vista_plus) + { + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + } + else + { + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETVIEW); + ok(count == 1, "LVM_SETVIEW sent %d times.\n", count); + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETEXTENDEDLISTVIEWSTYLE); + ok(count == 1 || count == 2, "LVM_SETEXTENDEDLISTVIEWSTYLE sent %d times.\n", count); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + } + } + + hr = IFolderView_SetCurrentViewMode(fview, 7); + ok(hr == S_OK, "got 0x%08x\n", hr); + todo_wine + { + if(vista_plus) + { + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + } + else + { + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETVIEW); + ok(count == 1, "LVM_SETVIEW sent %d times.\n", count); + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETEXTENDEDLISTVIEWSTYLE); + ok(count == 2, "LVM_SETEXTENDEDLISTVIEWSTYLE sent %d times.\n", count); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + } + } + + hr = IFolderView_SetCurrentViewMode(fview, 8); + ok(hr == S_OK || broken(hr == E_INVALIDARG /* Vista */), "got 0x%08x\n", hr); + todo_wine + { + if(vista_plus) + { + verify_msgs_in(sequences[LISTVIEW_SEQ_INDEX], vista_plus_msgs); + } + else + { + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETVIEW); + ok(count == 1, "LVM_SETVIEW sent %d times.\n", count); + count = get_msg_count(sequences, LISTVIEW_SEQ_INDEX, LVM_SETEXTENDEDLISTVIEWSTYLE); + ok(count == 2, "LVM_SETEXTENDEDLISTVIEWSTYLE sent %d times.\n", count); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + } + } + + hr = IFolderView_GetCurrentViewMode(fview, &viewmode); + ok(hr == S_OK, "Failed to get current viewmode.\n"); + ok_sequence(sequences, LISTVIEW_SEQ_INDEX, empty_seq, + "IFolderView::GetCurrentViewMode", FALSE); + } + + IFolderView_Release(fview); + } + else + { + skip("No IFolderView for the desktop folder.\n"); + } + + IShellBrowser_Release(browser); + IShellView_DestroyViewWindow(sview); + IShellView_Release(sview); + IShellFolder_Release(desktop); +} + +static void test_IOleCommandTarget(void) +{ + IShellFolder *psf_desktop; + IShellView *psv; + IOleCommandTarget *poct; + HRESULT hr; + + hr = SHGetDesktopFolder(&psf_desktop); + ok(hr == S_OK, "got (0x%08x)\n", hr); + + hr = IShellFolder_CreateViewObject(psf_desktop, NULL, &IID_IShellView, (void**)&psv); + ok(hr == S_OK, "got (0x%08x)\n", hr); + if(SUCCEEDED(hr)) + { + hr = IShellView_QueryInterface(psv, &IID_IOleCommandTarget, (void**)&poct); + ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Win95/NT4 */, "Got 0x%08x\n", hr); + if(SUCCEEDED(hr)) + { + OLECMD oc; + + hr = IOleCommandTarget_QueryStatus(poct, NULL, 0, NULL, NULL); + ok(hr == E_INVALIDARG, "Got 0x%08x\n", hr); + + oc.cmdID = 1; + hr = IOleCommandTarget_QueryStatus(poct, NULL, 0, &oc, NULL); + ok(hr == OLECMDERR_E_UNKNOWNGROUP, "Got 0x%08x\n", hr); + + oc.cmdID = 1; + hr = IOleCommandTarget_QueryStatus(poct, NULL, 1, &oc, NULL); + ok(hr == OLECMDERR_E_UNKNOWNGROUP, "Got 0x%08x\n", hr); + + hr = IOleCommandTarget_Exec(poct, NULL, 0, 0, NULL, NULL); + ok(hr == OLECMDERR_E_UNKNOWNGROUP, "Got 0x%08x\n", hr); + + IOleCommandTarget_Release(poct); + } + + IShellView_Release(psv); + } + + IShellFolder_Release(psf_desktop); +} + START_TEST(shlview) { OleInitialize(NULL); @@ -754,6 +1162,8 @@ START_TEST(shlview) test_GetItemObject(); test_IShellFolderView(); test_IOleWindow(); + test_GetSetCurrentViewMode(); + test_IOleCommandTarget(); OleUninitialize(); } diff --git a/rostests/winetests/shell32/systray.c b/rostests/winetests/shell32/systray.c index 1f505ecfca3..23d30341dad 100644 --- a/rostests/winetests/shell32/systray.c +++ b/rostests/winetests/shell32/systray.c @@ -45,19 +45,15 @@ static void test_cbsize(void) nidW.hIcon = LoadIcon(NULL, IDI_APPLICATION); nidW.uCallbackMessage = WM_USER+17; ret = pShell_NotifyIconW(NIM_ADD, &nidW); - if (ret) - { - /* using an invalid cbSize does work */ - nidW.cbSize = 3; - nidW.hWnd = hMainWnd; - nidW.uID = 1; - ret = pShell_NotifyIconW(NIM_DELETE, &nidW); - ok( ret || broken(!ret), /* nt4 */ "NIM_DELETE failed!\n"); - /* as icon doesn't exist anymore - now there will be an error */ - nidW.cbSize = sizeof(nidW); - ok(!pShell_NotifyIconW(NIM_DELETE, &nidW) != !ret, "The icon was not deleted\n"); - } - else win_skip( "Shell_NotifyIconW not working\n" ); /* win9x */ + /* using an invalid cbSize does work */ + nidW.cbSize = 3; + nidW.hWnd = hMainWnd; + nidW.uID = 1; + ret = pShell_NotifyIconW(NIM_DELETE, &nidW); + ok( ret || broken(!ret), /* nt4 */ "NIM_DELETE failed!\n"); + /* as icon doesn't exist anymore - now there will be an error */ + nidW.cbSize = sizeof(nidW); + ok(!pShell_NotifyIconW(NIM_DELETE, &nidW) != !ret, "The icon was not deleted\n"); } /* same for Shell_NotifyIconA */ @@ -75,7 +71,7 @@ static void test_cbsize(void) nidA.hWnd = hMainWnd; nidA.uID = 1; ret = Shell_NotifyIconA(NIM_DELETE, &nidA); - ok( ret || broken(!ret), /* win9x */ "NIM_DELETE failed!\n"); + ok(ret, "NIM_DELETE failed!\n"); /* as icon doesn't exist anymore - now there will be an error */ nidA.cbSize = sizeof(nidA); ok(!Shell_NotifyIconA(NIM_DELETE, &nidA) != !ret, "The icon was not deleted\n"); diff --git a/rostests/winetests/shell32/testlist.c b/rostests/winetests/shell32/testlist.c index 8f9119596c9..67bafbcda31 100644 --- a/rostests/winetests/shell32/testlist.c +++ b/rostests/winetests/shell32/testlist.c @@ -8,9 +8,14 @@ extern void func_appbar(void); extern void func_autocomplete(void); +extern void func_brsfolder(void); +extern void func_ebrowser(void); extern void func_generated(void); extern void func_progman_dde(void); +extern void func_recyclebin(void); +extern void func_shelldispatch(void); extern void func_shelllink(void); +extern void func_shellole(void); extern void func_shellpath(void); extern void func_shfldr_special(void); extern void func_shlexec(void); @@ -22,17 +27,22 @@ extern void func_systray(void); const struct test winetest_testlist[] = { - { "appbar", func_appbar }, - { "autocomplete", func_autocomplete }, - { "generated", func_generated }, - { "progman_dde", func_progman_dde }, + { "appbar", func_appbar }, + { "autocomplete", func_autocomplete }, + { "brsfolder", func_brsfolder }, + { "ebrowser", func_ebrowser }, + { "generated", func_generated }, + { "progman_dde", func_progman_dde }, + { "recyclebin", func_recyclebin }, + { "shelldispatch", func_shelldispatch }, { "shelllink", func_shelllink }, + { "shellole", func_shellole }, { "shellpath", func_shellpath }, - { "shfldr_special", func_shfldr_special }, + { "shfldr_special", func_shfldr_special }, { "shlexec", func_shlexec }, { "shlfileop", func_shlfileop }, { "shlfolder", func_shlfolder }, - { "shlview", func_shlview }, + { "shlview", func_shlview }, { "string", func_string }, { "systray", func_systray }, { 0, 0 }