Commit 73100ce6 authored by David Hedberg's avatar David Hedberg Committed by Alexandre Julliard

shell32: Implement IExplorerBrowser::BrowseToIDList and IShellBrowser::BrowseToObject.

parent 1f73f3a5
......@@ -33,6 +33,7 @@
#include "debughlp.h"
#include "shell32_main.h"
#include "pidl.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
......@@ -59,6 +60,7 @@ typedef struct _ExplorerBrowserImpl {
IShellView *psv;
RECT sv_rc;
LPITEMIDLIST current_pidl;
} ExplorerBrowserImpl;
/**************************************************************************
......@@ -78,6 +80,65 @@ static void events_unadvise_all(ExplorerBrowserImpl *This)
}
}
static HRESULT events_NavigationPending(ExplorerBrowserImpl *This, PCIDLIST_ABSOLUTE pidl)
{
event_client *cursor;
HRESULT hres = S_OK;
TRACE("%p\n", This);
LIST_FOR_EACH_ENTRY(cursor, &This->event_clients, event_client, entry)
{
TRACE("Notifying %p\n", cursor);
hres = IExplorerBrowserEvents_OnNavigationPending(cursor->pebe, pidl);
/* If this failed for any reason, the browsing is supposed to be aborted. */
if(FAILED(hres))
break;
}
return hres;
}
static void events_NavigationComplete(ExplorerBrowserImpl *This, PCIDLIST_ABSOLUTE pidl)
{
event_client *cursor;
TRACE("%p\n", This);
LIST_FOR_EACH_ENTRY(cursor, &This->event_clients, event_client, entry)
{
TRACE("Notifying %p\n", cursor);
IExplorerBrowserEvents_OnNavigationComplete(cursor->pebe, pidl);
}
}
static void events_NavigationFailed(ExplorerBrowserImpl *This, PCIDLIST_ABSOLUTE pidl)
{
event_client *cursor;
TRACE("%p\n", This);
LIST_FOR_EACH_ENTRY(cursor, &This->event_clients, event_client, entry)
{
TRACE("Notifying %p\n", cursor);
IExplorerBrowserEvents_OnNavigationFailed(cursor->pebe, pidl);
}
}
static void events_ViewCreated(ExplorerBrowserImpl *This, IShellView *psv)
{
event_client *cursor;
TRACE("%p\n", This);
LIST_FOR_EACH_ENTRY(cursor, &This->event_clients, event_client, entry)
{
TRACE("Notifying %p\n", cursor);
IExplorerBrowserEvents_OnViewCreated(cursor->pebe, psv);
}
}
/**************************************************************************
* Helper functions
*/
......@@ -116,6 +177,55 @@ static HRESULT change_viewmode(ExplorerBrowserImpl *This, UINT viewmode)
return hr;
}
static HRESULT create_new_shellview(ExplorerBrowserImpl *This, IShellItem *psi)
{
IShellBrowser *psb = (IShellBrowser*)&This->lpsbVtbl;
IShellFolder *psf;
IShellView *psv;
HWND hwnd_new;
HRESULT hr;
TRACE("%p, %p\n", This, psi);
hr = IShellItem_BindToHandler(psi, NULL, &BHID_SFObject, &IID_IShellFolder, (void**)&psf);
if(SUCCEEDED(hr))
{
hr = IShellFolder_CreateViewObject(psf, This->hwnd_main, &IID_IShellView, (void**)&psv);
if(SUCCEEDED(hr))
{
if(This->hwnd_sv)
{
IShellView_DestroyViewWindow(This->psv);
This->hwnd_sv = NULL;
}
hr = IShellView_CreateViewWindow(psv, This->psv, &This->fs, psb, &This->sv_rc, &hwnd_new);
if(SUCCEEDED(hr))
{
/* Replace the old shellview */
if(This->psv) IShellView_Release(This->psv);
This->psv = psv;
This->hwnd_sv = hwnd_new;
events_ViewCreated(This, psv);
}
else
{
ERR("CreateViewWindow failed (0x%x)\n", hr);
IShellView_Release(psv);
}
}
else
ERR("CreateViewObject failed (0x%x)\n", hr);
IShellFolder_Release(psf);
}
else
ERR("SI::BindToHandler failed (0x%x)\n", hr);
return hr;
}
/**************************************************************************
* Main window related functions.
*/
......@@ -277,6 +387,9 @@ static HRESULT WINAPI IExplorerBrowser_fnDestroy(IExplorerBrowser *iface)
events_unadvise_all(This);
ILFree(This->current_pidl);
This->current_pidl = NULL;
DestroyWindow(This->hwnd_main);
This->destroyed = TRUE;
......@@ -407,14 +520,113 @@ static HRESULT WINAPI IExplorerBrowser_fnGetOptions(IExplorerBrowser *iface,
return S_OK;
}
static const UINT unsupported_browse_flags =
SBSP_NAVIGATEBACK | SBSP_NAVIGATEFORWARD | SBSP_NEWBROWSER |
EBF_SELECTFROMDATAOBJECT | EBF_NODROPTARGET;
static HRESULT WINAPI IExplorerBrowser_fnBrowseToIDList(IExplorerBrowser *iface,
PCUIDLIST_RELATIVE pidl,
UINT uFlags)
{
ExplorerBrowserImpl *This = (ExplorerBrowserImpl*)iface;
FIXME("stub, %p (%p, 0x%x)\n", This, pidl, uFlags);
LPITEMIDLIST absolute_pidl = NULL;
HRESULT hr;
TRACE("%p (%p, 0x%x)\n", This, pidl, uFlags);
return E_NOTIMPL;
if(!This->hwnd_main)
return E_FAIL;
if(This->destroyed)
return HRESULT_FROM_WIN32(ERROR_BUSY);
if(This->current_pidl && (This->eb_options & EBO_NAVIGATEONCE))
return E_FAIL;
if(uFlags & SBSP_EXPLOREMODE)
return E_INVALIDARG;
if(uFlags & unsupported_browse_flags)
FIXME("Argument 0x%x contains unsupported flags.\n", uFlags);
if(uFlags & SBSP_PARENT)
{
if(This->current_pidl)
{
if(_ILIsPidlSimple(This->current_pidl))
{
absolute_pidl = _ILCreateDesktop();
}
else
{
absolute_pidl = ILClone(This->current_pidl);
ILRemoveLastID(absolute_pidl);
}
}
if(!absolute_pidl)
{
ERR("Failed to get parent pidl.\n");
return E_FAIL;
}
}
else if(uFlags & SBSP_RELATIVE)
{
/* SBSP_RELATIVE has precedence over SBSP_ABSOLUTE */
TRACE("SBSP_RELATIVE\n");
if(This->current_pidl)
{
absolute_pidl = ILCombine(This->current_pidl, pidl);
}
if(!absolute_pidl)
{
ERR("Failed to get absolute pidl.\n");
return E_FAIL;
}
}
else
{
TRACE("SBSP_ABSOLUTE\n");
absolute_pidl = ILClone(pidl);
if(!absolute_pidl && !This->current_pidl)
return E_INVALIDARG;
else if(!absolute_pidl)
return S_OK;
}
/* TODO: Asynchronous browsing. Return S_OK here and finish in
* another thread. */
hr = events_NavigationPending(This, absolute_pidl);
if(FAILED(hr))
{
TRACE("Browsing aborted.\n");
ILFree(absolute_pidl);
return E_FAIL;
}
/* Only browse if the new pidl differs from the old */
if(!ILIsEqual(This->current_pidl, absolute_pidl))
{
IShellItem *psi;
hr = SHCreateItemFromIDList(absolute_pidl, &IID_IShellItem, (void**)&psi);
if(SUCCEEDED(hr))
{
hr = create_new_shellview(This, psi);
if(FAILED(hr))
{
events_NavigationFailed(This, absolute_pidl);
ILFree(absolute_pidl);
IShellItem_Release(psi);
return E_FAIL;
}
IShellItem_Release(psi);
}
}
events_NavigationComplete(This, absolute_pidl);
ILFree(This->current_pidl);
This->current_pidl = absolute_pidl;
return S_OK;
}
static HRESULT WINAPI IExplorerBrowser_fnBrowseToObject(IExplorerBrowser *iface,
......@@ -594,9 +806,9 @@ static HRESULT WINAPI IShellBrowser_fnBrowseObject(IShellBrowser *iface,
LPCITEMIDLIST pidl, UINT wFlags)
{
ExplorerBrowserImpl *This = impl_from_IShellBrowser(iface);
FIXME("stub, %p\n", This);
TRACE("%p (%p, %x)\n", This, pidl, wFlags);
return E_NOTIMPL;
return IExplorerBrowser_fnBrowseToIDList((IExplorerBrowser*)This, pidl, wFlags);
}
static HRESULT WINAPI IShellBrowser_fnGetViewStateStream(IShellBrowser *iface,
......
......@@ -28,6 +28,18 @@
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
*/
......@@ -44,6 +56,17 @@ static HRESULT ebrowser_initialize(IExplorerBrowser *peb)
return IExplorerBrowser_Initialize(peb, hwnd, &rc, NULL);
}
/* Process some messages */
static void process_msgs(void)
{
MSG msg;
while(PeekMessage( &msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/*********************************************************************
* IExplorerBrowserEvents implementation
*/
......@@ -532,6 +555,247 @@ static void test_Advise(void)
ok(!ref, "Got %d", 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;
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.lpVtbl = &ebevents;
IExplorerBrowser_Advise(peb, (IExplorerBrowserEvents*)&ebev, &cookie);
IExplorerBrowser_Advise(peb2, (IExplorerBrowserEvents*)&ebev, &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);
/* "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);
/* Relative navigation */
test_browse_pidl(peb, &ebev, pidl_current, SBSP_ABSOLUTE, S_OK, 1, 1, 0, 1);
test_browse_pidl_sb(peb2, &ebev, pidl_current, SBSP_ABSOLUTE, S_OK, 1, 1, 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, (IExplorerBrowserEvents*)&ebev, &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);
/* Cleanup */
RemoveDirectoryW(child_path);
ILFree(pidl_current);
ILFree(pidl_child);
}
static BOOL test_instantiate_control(void)
{
IExplorerBrowser *peb;
......@@ -573,12 +837,14 @@ START_TEST(ebrowser)
}
setup_window();
init_function_pointers();
test_QueryInterface();
test_SB_misc();
test_initialization();
test_basics();
test_Advise();
test_navigation();
DestroyWindow(hwnd);
OleUninitialize();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment