[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