[PATCH 1/6] explorerframe: Implement InsertRoot and AppendRoot
David Hedberg
david.hedberg at gmail.com
Tue Aug 3 20:57:47 CDT 2010
Regarding events: My assumption is that clients should not release any
objects passed to them through the INameSpaceTreeControlEvents
interface. For example, releasing the IShellItemArray passed through
OnSelectionChanged leads to a crash in Windows. However, clients does
not seem to crash if you free any of the IShellItems passed. In fact,
repeatedly relasing them is not a problem in Windows, although they
will return RPC_E_WRONG_THREAD to any other method call after the
first Release.
The reasoning behind the added reference count checks in the events_*
functions is therefore to be able to easily catch the problem if
applications in fact turns out to be releasing the objects.
---
dlls/explorerframe/Makefile.in | 1 +
dlls/explorerframe/nstc.c | 122 +++++++++++++++++++++++++++++++++++++-
dlls/explorerframe/tests/nstc.c | 97 +++++++++++++++++++++++++++++++
3 files changed, 216 insertions(+), 4 deletions(-)
diff --git a/dlls/explorerframe/Makefile.in b/dlls/explorerframe/Makefile.in
index 8dd4542..2842c48 100644
--- a/dlls/explorerframe/Makefile.in
+++ b/dlls/explorerframe/Makefile.in
@@ -5,6 +5,7 @@ SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = explorerframe.dll
IMPORTS = uuid shell32 user32
+DELAYIMPORTS = ole32
C_SRCS = \
explorerframe_main.c \
diff --git a/dlls/explorerframe/nstc.c b/dlls/explorerframe/nstc.c
index da84ccc..153c4c9 100644
--- a/dlls/explorerframe/nstc.c
+++ b/dlls/explorerframe/nstc.c
@@ -27,13 +27,25 @@
#include "winerror.h"
#include "windef.h"
#include "winbase.h"
+#include "winuser.h"
+#include "shellapi.h"
+#include "wine/list.h"
#include "wine/debug.h"
#include "explorerframe_main.h"
WINE_DEFAULT_DEBUG_CHANNEL(nstc);
+typedef struct nstc_root {
+ IShellItem *psi;
+ HTREEITEM htreeitem;
+ SHCONTF enum_flags;
+ NSTCROOTSTYLE root_style;
+ IShellItemFilter *pif;
+ struct list entry;
+} nstc_root;
+
typedef struct {
const INameSpaceTreeControl2Vtbl *lpVtbl;
const IOleWindowVtbl *lpowVtbl;
@@ -44,6 +56,7 @@ typedef struct {
NSTCSTYLE style;
NSTCSTYLE2 style2;
+ struct list roots;
INameSpaceTreeControlEvents *pnstce;
} NSTC2Impl;
@@ -57,6 +70,22 @@ static const DWORD unsupported_styles2 =
NSTCS2_DISPLAYPINNEDONLY | NTSCS2_NOSINGLETONAUTOEXPAND | NTSCS2_NEVERINSERTNONENUMERATED;
/*************************************************************************
+* NamespaceTree event wrappers
+*/
+static HRESULT events_OnItemAdded(NSTC2Impl *This, IShellItem *psi, BOOL fIsRoot)
+{
+ HRESULT ret;
+ LONG refcount;
+ if(!This->pnstce) return S_OK;
+
+ refcount = IShellItem_AddRef(psi);
+ ret = INameSpaceTreeControlEvents_OnItemAdded(This->pnstce, psi, fIsRoot);
+ if(IShellItem_Release(psi) < refcount - 1)
+ ERR("ShellItem was released by client - please file a bug.\n");
+ return ret;
+}
+
+/*************************************************************************
* NamespaceTree helper functions
*/
static DWORD treeview_style_from_nstcs(NSTC2Impl *This, NSTCSTYLE nstcs,
@@ -106,6 +135,34 @@ static DWORD treeview_style_from_nstcs(NSTC2Impl *This, NSTCSTYLE nstcs,
return old_style^*new_style;
}
+/* Insert a shellitem into the given place in the tree and return the
+ resulting treeitem. */
+static HTREEITEM insert_shellitem(NSTC2Impl *This, IShellItem *psi,
+ HTREEITEM hParent, HTREEITEM hInsertAfter)
+{
+ TVINSERTSTRUCTW tvins;
+ TVITEMEXW *tvi = &tvins.u.itemex;
+ HTREEITEM hinserted;
+ TRACE("%p (%p, %p)\n", psi, hParent, hInsertAfter);
+
+ tvi->mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT;
+ tvi->cChildren = I_CHILDRENCALLBACK;
+ tvi->iImage = tvi->iSelectedImage = I_IMAGECALLBACK;
+ tvi->pszText = LPSTR_TEXTCALLBACKW;
+
+ /* Every treeitem contains a pointer to the corresponding ShellItem. */
+ tvi->lParam = (LPARAM)psi;
+ tvins.hParent = hParent;
+ tvins.hInsertAfter = hInsertAfter;
+
+ hinserted = (HTREEITEM)SendMessageW(This->hwnd_tv, TVM_INSERTITEMW, 0,
+ (LPARAM)(LPTVINSERTSTRUCTW)&tvins);
+ if(hinserted)
+ IShellItem_AddRef(psi);
+
+ return hinserted;
+}
+
/*************************************************************************
* NamespaceTree window functions
*/
@@ -365,8 +422,59 @@ static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface,
IShellItemFilter *pif)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
- FIXME("stub, %p, %p, %x, %x, %p\n", This, psiRoot, grfEnumFlags, grfRootStyle, pif);
- return E_NOTIMPL;
+ nstc_root *new_root;
+ struct list *add_after_entry;
+ HTREEITEM add_after_hitem;
+ UINT i;
+
+ TRACE("%p, %d, %p, %x, %x, %p\n", This, iIndex, psiRoot, grfEnumFlags, grfRootStyle, pif);
+
+ new_root = HeapAlloc(GetProcessHeap(), 0, sizeof(nstc_root));
+ if(!new_root)
+ return E_OUTOFMEMORY;
+
+ new_root->psi = psiRoot;
+ new_root->enum_flags = grfEnumFlags;
+ new_root->root_style = grfRootStyle;
+ new_root->pif = pif;
+
+ /* We want to keep the roots in the internal list and in the
+ * treeview in the same order. */
+ add_after_entry = &This->roots;
+ for(i = 0; i < max(0, iIndex) && list_next(&This->roots, add_after_entry); i++)
+ add_after_entry = list_next(&This->roots, add_after_entry);
+
+ if(add_after_entry == &This->roots)
+ add_after_hitem = TVI_FIRST;
+ else
+ add_after_hitem = LIST_ENTRY(add_after_entry, nstc_root, entry)->htreeitem;
+
+ new_root->htreeitem = insert_shellitem(This, psiRoot, TVI_ROOT, add_after_hitem);
+ if(!new_root->htreeitem)
+ {
+ WARN("Failed to add the root.\n");
+ HeapFree(GetProcessHeap(), 0, new_root);
+ return E_FAIL;
+ }
+
+ list_add_after(add_after_entry, &new_root->entry);
+ events_OnItemAdded(This, psiRoot, TRUE);
+
+ if(grfRootStyle & NSTCRS_HIDDEN)
+ {
+ TVITEMEXW tvi;
+ tvi.mask = TVIF_STATEEX;
+ tvi.uStateEx = TVIS_EX_FLAT;
+ tvi.hItem = new_root->htreeitem;
+
+ SendMessageW(This->hwnd_tv, TVM_SETITEMW, 0, (LPARAM)&tvi);
+ }
+
+ if(grfRootStyle & NSTCRS_EXPANDED)
+ SendMessageW(This->hwnd_tv, TVM_EXPAND, TVE_EXPAND,
+ (LPARAM)new_root->htreeitem);
+
+ return S_OK;
}
static HRESULT WINAPI NSTC2_fnAppendRoot(INameSpaceTreeControl2* iface,
@@ -376,9 +484,13 @@ static HRESULT WINAPI NSTC2_fnAppendRoot(INameSpaceTreeControl2* iface,
IShellItemFilter *pif)
{
NSTC2Impl *This = (NSTC2Impl*)iface;
- FIXME("stub, %p, %p, %x, %x, %p\n",
+ UINT root_count;
+ TRACE("%p, %p, %x, %x, %p\n",
This, psiRoot, grfEnumFlags, grfRootStyle, pif);
- return E_NOTIMPL;
+
+ root_count = list_count(&This->roots);
+
+ return NSTC2_fnInsertRoot(iface, root_count, psiRoot, grfEnumFlags, grfRootStyle, pif);
}
static HRESULT WINAPI NSTC2_fnRemoveRoot(INameSpaceTreeControl2* iface,
@@ -702,6 +814,8 @@ HRESULT NamespaceTreeControl_Constructor(IUnknown *pUnkOuter, REFIID riid, void
nstc->lpVtbl = &vt_INameSpaceTreeControl2;
nstc->lpowVtbl = &vt_IOleWindow;
+ list_init(&nstc->roots);
+
ret = INameSpaceTreeControl_QueryInterface((INameSpaceTreeControl*)nstc, riid, ppv);
INameSpaceTreeControl_Release((INameSpaceTreeControl*)nstc);
diff --git a/dlls/explorerframe/tests/nstc.c b/dlls/explorerframe/tests/nstc.c
index f48418b..5d67105 100644
--- a/dlls/explorerframe/tests/nstc.c
+++ b/dlls/explorerframe/tests/nstc.c
@@ -315,6 +315,35 @@ static INameSpaceTreeControlEventsImpl *create_nstc_events(void)
return This;
}
+/* Process some messages */
+static void process_msgs(void)
+{
+ MSG msg;
+ BOOL got_msg;
+ do {
+ got_msg = FALSE;
+ Sleep(100);
+ while(PeekMessage( &msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ got_msg = TRUE;
+ }
+ } while(got_msg);
+
+ /* There seem to be a timer that sometimes fires after about
+ 500ms, we need to wait for it. Failing to wait can result in
+ seemingly sporadic selection change events. (Timer ID is 87,
+ sending WM_TIMER manually does not seem to help us.) */
+ Sleep(500);
+
+ while(PeekMessage( &msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
/* Returns FALSE if the NamespaceTreeControl failed to be instantiated. */
static BOOL test_initialization(void)
{
@@ -470,15 +499,52 @@ static void test_basics(void)
{
INameSpaceTreeControl *pnstc;
INameSpaceTreeControl2 *pnstc2;
+ IShellFolder *psfdesktop;
+ IShellItem *psidesktop, *psidesktop2;
IOleWindow *pow;
+ LPITEMIDLIST pidl_desktop;
HRESULT hr;
UINT i, res;
RECT rc;
+ /* These should exist on platforms supporting the NSTC */
+ ok(pSHCreateShellItem != NULL, "No SHCreateShellItem.\n");
+ ok(pSHGetIDListFromObject != NULL, "No SHCreateShellItem.\n");
+
+ /* Create ShellItems for testing. */
+ SHGetDesktopFolder(&psfdesktop);
+ hr = pSHGetIDListFromObject((IUnknown*)psfdesktop, &pidl_desktop);
+ ok(hr == S_OK, "Got 0x%08x\n", hr);
+ if(SUCCEEDED(hr))
+ {
+ hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psidesktop);
+ ok(hr == S_OK, "Got 0x%08x\n", hr);
+ if(SUCCEEDED(hr))
+ {
+ hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psidesktop2);
+ ok(hr == S_OK, "Got 0x%08x\n", hr);
+ if(FAILED(hr)) IShellItem_Release(psidesktop);
+ }
+ ILFree(pidl_desktop);
+ }
+ ok(psidesktop != psidesktop2, "psidesktop == psidesktop2\n");
+ IShellFolder_Release(psfdesktop);
+
+ if(FAILED(hr))
+ {
+ win_skip("Test setup failed.\n");
+ return;
+ }
+
hr = CoCreateInstance(&CLSID_NamespaceTreeControl, NULL, CLSCTX_INPROC_SERVER,
&IID_INameSpaceTreeControl, (void**)&pnstc);
ok(hr == S_OK, "Failed to initialize control (0x%08x)\n", hr);
+ /* Some tests on an uninitialized control */
+ hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop, SHCONTF_NONFOLDERS, 0, NULL);
+ ok(hr == E_FAIL, "Got (0x%08x)\n", hr);
+ process_msgs();
+
/* Initialize the control */
rc.top = rc.left = 0; rc.right = rc.bottom = 200;
hr = INameSpaceTreeControl_Initialize(pnstc, hwnd, &rc, 0);
@@ -697,6 +763,37 @@ static void test_basics(void)
skip("INameSpaceTreeControl2 missing.\n");
}
+ /* Append / Insert root */
+ if(0)
+ {
+ /* Crashes under Windows 7 */
+ hr = INameSpaceTreeControl_AppendRoot(pnstc, NULL, SHCONTF_FOLDERS, 0, NULL);
+ hr = INameSpaceTreeControl_InsertRoot(pnstc, 0, NULL, SHCONTF_FOLDERS, 0, NULL);
+ }
+
+ /* Note the usage of psidesktop and psidesktop2 */
+ hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+ hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+ hr = INameSpaceTreeControl_AppendRoot(pnstc, psidesktop2, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+ process_msgs();
+
+ hr = INameSpaceTreeControl_InsertRoot(pnstc, 0, psidesktop, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+ hr = INameSpaceTreeControl_InsertRoot(pnstc, -1, psidesktop, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+ hr = INameSpaceTreeControl_InsertRoot(pnstc, -1, psidesktop, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+ hr = INameSpaceTreeControl_InsertRoot(pnstc, 50, psidesktop, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+ hr = INameSpaceTreeControl_InsertRoot(pnstc, 1, psidesktop, SHCONTF_FOLDERS, 0, NULL);
+ ok(hr == S_OK, "Got (0x%08x)\n", hr);
+
+ IShellItem_Release(psidesktop);
+ IShellItem_Release(psidesktop2);
+
hr = INameSpaceTreeControl_QueryInterface(pnstc, &IID_IOleWindow, (void**)&pow);
ok(hr == S_OK, "Got 0x%08x\n", hr);
if(SUCCEEDED(hr))
--
1.7.2
More information about the wine-patches
mailing list