[PATCH 6/8] explorerframe/nstc: Implement TreeAdvise/TreeUnadvise.

David Hedberg david.hedberg at gmail.com
Thu Jul 29 14:26:05 CDT 2010


---
 dlls/explorerframe/nstc.c       |   36 +++-
 dlls/explorerframe/tests/nstc.c |  436 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 468 insertions(+), 4 deletions(-)

diff --git a/dlls/explorerframe/nstc.c b/dlls/explorerframe/nstc.c
index 297f203..ca8595d 100644
--- a/dlls/explorerframe/nstc.c
+++ b/dlls/explorerframe/nstc.c
@@ -42,6 +42,8 @@ typedef struct {
 
     NSTCSTYLE style;
     NSTCSTYLE2 style2;
+
+    INameSpaceTreeControlEvents *pnstce;
 } NSTC2Impl;
 
 static const DWORD unsupported_styles =
@@ -323,16 +325,42 @@ static HRESULT WINAPI NSTC2_fnTreeAdvise(INameSpaceTreeControl2* iface,
                                          DWORD *pdwCookie)
 {
     NSTC2Impl *This = (NSTC2Impl*)iface;
-    FIXME("stub, %p (%p, %p)\n", This, punk, pdwCookie);
-    return E_NOTIMPL;
+    INameSpaceTreeControlEvents *pnstce;
+    HRESULT hr;
+    TRACE("%p (%p, %p)\n", This, punk, pdwCookie);
+
+    *pdwCookie = 0;
+
+    /* Only one client supported (as in native) */
+    if(This->pnstce)
+        return E_FAIL;
+
+    hr = IUnknown_QueryInterface(punk, &IID_INameSpaceTreeControlEvents,
+                                 (void**)&pnstce);
+    if(SUCCEEDED(hr))
+    {
+        This->pnstce = pnstce;
+        *pdwCookie = 1;
+        return hr;
+    }
+
+    return E_FAIL;
 }
 
 static HRESULT WINAPI NSTC2_fnTreeUnadvise(INameSpaceTreeControl2* iface,
                                            DWORD dwCookie)
 {
     NSTC2Impl *This = (NSTC2Impl*)iface;
-    FIXME("stub, %p (%x)\n", This, dwCookie);
-    return E_NOTIMPL;
+    TRACE("%p (%x)\n", This, dwCookie);
+
+    if(This->pnstce)
+    {
+        /* Tests suggests that the cookie is ignored. */
+        INameSpaceTreeControlEvents_Release(This->pnstce);
+        This->pnstce = NULL;
+    }
+
+    return S_OK;
 }
 
 static HRESULT WINAPI NSTC2_fnInsertRoot(INameSpaceTreeControl2* iface,
diff --git a/dlls/explorerframe/tests/nstc.c b/dlls/explorerframe/tests/nstc.c
index 9406dc0..a3999b1 100644
--- a/dlls/explorerframe/tests/nstc.c
+++ b/dlls/explorerframe/tests/nstc.c
@@ -30,6 +30,296 @@ static HWND hwnd;
 /* "Intended for internal use" */
 #define TVS_EX_NOSINGLECOLLAPSE 0x1
 
+static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**);
+static HRESULT (WINAPI *pSHGetIDListFromObject)(IUnknown*, PIDLIST_ABSOLUTE*);
+
+static void init_function_pointers(void)
+{
+    HMODULE hmod;
+
+    hmod = GetModuleHandleA("shell32.dll");
+    pSHCreateShellItem = (void*)GetProcAddress(hmod, "SHCreateShellItem");
+    pSHGetIDListFromObject = (void*)GetProcAddress(hmod, "SHGetIDListFromObject");
+}
+
+/*******************************************************
+ * INameSpaceTreeControlEvents implementation.
+ */
+enum { OnItemClick = 0, OnPropertyItemCommit, OnItemStateChanging, OnItemStateChanged,
+       OnSelectionChanged, OnKeyboardInput, OnBeforeExpand, OnAfterExpand, OnBeginLabelEdit,
+       OnEndLabelEdit, OnGetToolTip, OnBeforeItemDelete, OnItemAdded, OnItemDeleted,
+       OnBeforeContextMenu, OnAfterContextMenu, OnBeforeStateImageChange, OnGetDefaultIconIndex,
+       LastEvent };
+
+typedef struct {
+    const INameSpaceTreeControlEventsVtbl *lpVtbl;
+    UINT qi_called_count;
+    UINT qi_enable_events;
+    UINT count[LastEvent];
+    LONG ref;
+} INameSpaceTreeControlEventsImpl;
+
+#define NSTCE_IMPL(iface)                       \
+    ((INameSpaceTreeControlEventsImpl*)iface)
+
+static HRESULT WINAPI NSTCEvents_fnQueryInterface(
+    INameSpaceTreeControlEvents* iface,
+    REFIID riid,
+    void **ppvObject)
+{
+    NSTCE_IMPL(iface)->qi_called_count++;
+
+    if(NSTCE_IMPL(iface)->qi_enable_events &&
+       IsEqualIID(riid, &IID_INameSpaceTreeControlEvents))
+    {
+        IUnknown_AddRef(iface);
+        *ppvObject = iface;
+        return S_OK;
+    }
+
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI NSTCEvents_fnAddRef(
+    INameSpaceTreeControlEvents* iface)
+{
+    return InterlockedIncrement(&NSTCE_IMPL(iface)->ref);
+}
+
+static ULONG WINAPI NSTCEvents_fnRelease(
+    INameSpaceTreeControlEvents* iface)
+{
+    return InterlockedDecrement(&NSTCE_IMPL(iface)->ref);
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnItemClick(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    NSTCEHITTEST nstceHitTest,
+    NSTCECLICKTYPE nstceClickType)
+{
+    trace("NSTCEvents_fnOnItemClick\n");
+    NSTCE_IMPL(iface)->count[OnItemClick]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnPropertyItemCommit(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi)
+{
+    trace("NSTCEvents_fnOnPropertyItemCommit\n");
+    NSTCE_IMPL(iface)->count[OnPropertyItemCommit]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnItemStateChanging(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    NSTCITEMSTATE nstcisMask,
+    NSTCITEMSTATE nstcisState)
+{
+    NSTCE_IMPL(iface)->count[OnItemStateChanging]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnItemStateChanged(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    NSTCITEMSTATE nstcisMask,
+    NSTCITEMSTATE nstcisState)
+{
+    NSTCE_IMPL(iface)->count[OnItemStateChanged]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnSelectionChanged(
+    INameSpaceTreeControlEvents* iface,
+    IShellItemArray *psiaSelection)
+{
+    NSTCE_IMPL(iface)->count[OnSelectionChanged]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnKeyboardInput(
+    INameSpaceTreeControlEvents* iface,
+    UINT uMsg,
+    WPARAM wParam,
+    LPARAM lParam)
+{
+    NSTCE_IMPL(iface)->count[OnKeyboardInput]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnBeforeExpand(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi)
+{
+    ok(psi != NULL, "Null shellitem passed.\n");
+    if(psi)
+    {
+        IShellItem *psitmp;
+        HRESULT hr;
+        hr = IShellItem_QueryInterface(psi, &IID_IShellItem, (void**)&psitmp);
+        ok(hr == S_OK, "Got 0x%08x\n", hr);
+        if(SUCCEEDED(hr)) IShellItem_Release(psitmp);
+    }
+    NSTCE_IMPL(iface)->count[OnBeforeExpand]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnAfterExpand(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi)
+{
+    ok(psi != NULL, "Null shellitem passed.\n");
+    if(psi)
+    {
+        IShellItem *psitmp;
+        HRESULT hr;
+        hr = IShellItem_QueryInterface(psi, &IID_IShellItem, (void**)&psitmp);
+        ok(hr == S_OK, "Got 0x%08x\n", hr);
+        if(SUCCEEDED(hr)) IShellItem_Release(psitmp);
+    }
+    NSTCE_IMPL(iface)->count[OnAfterExpand]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnBeginLabelEdit(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi)
+{
+    NSTCE_IMPL(iface)->count[OnBeginLabelEdit]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnEndLabelEdit(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi)
+{
+    NSTCE_IMPL(iface)->count[OnEndLabelEdit]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnGetToolTip(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    LPWSTR pszTip,
+    int cchTip)
+{
+    NSTCE_IMPL(iface)->count[OnGetToolTip]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnBeforeItemDelete(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi)
+{
+    NSTCE_IMPL(iface)->count[OnBeforeItemDelete]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnItemAdded(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    BOOL fIsRoot)
+{
+    ok(psi != NULL, "NULL shellitem passed.\n");
+    if(psi)
+    {
+        IShellItem *psitmp;
+        HRESULT hr;
+        hr = IShellItem_QueryInterface(psi, &IID_IShellItem, (void**)&psitmp);
+        ok(hr == S_OK, "Got 0x%08x\n", hr);
+        if(SUCCEEDED(hr)) IShellItem_Release(psitmp);
+    }
+    NSTCE_IMPL(iface)->count[OnItemAdded]++;
+    return S_OK;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnItemDeleted(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    BOOL fIsRoot)
+{
+    ok(psi != NULL, "Null shellitem passed.\n");
+    if(psi)
+    {
+        IShellItem *psitmp;
+        HRESULT hr;
+        hr = IShellItem_QueryInterface(psi, &IID_IShellItem, (void**)&psitmp);
+        ok(hr == S_OK, "Got 0x%08x\n", hr);
+        if(SUCCEEDED(hr)) IShellItem_Release(psitmp);
+    }
+    NSTCE_IMPL(iface)->count[OnItemDeleted]++;
+    return S_OK;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnBeforeContextMenu(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    REFIID riid,
+    void **ppv)
+{
+    NSTCE_IMPL(iface)->count[OnBeforeContextMenu]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnAfterContextMenu(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    IContextMenu *pcmIn,
+    REFIID riid,
+    void **ppv)
+{
+    NSTCE_IMPL(iface)->count[OnAfterContextMenu]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnBeforeStateImageChange(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    int *piDefaultIcon,
+    int *piOpenIcon)
+{
+    NSTCE_IMPL(iface)->count[OnBeforeStateImageChange]++;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NSTCEvents_fnOnGetDefaultIconIndex(
+    INameSpaceTreeControlEvents* iface,
+    IShellItem *psi,
+    int *piDefaultIcon,
+    int *piOpenIcon)
+{
+    NSTCE_IMPL(iface)->count[OnGetDefaultIconIndex]++;
+    return E_NOTIMPL;
+}
+
+const INameSpaceTreeControlEventsVtbl vt_NSTCEvents = {
+    NSTCEvents_fnQueryInterface,
+    NSTCEvents_fnAddRef,
+    NSTCEvents_fnRelease,
+    NSTCEvents_fnOnItemClick,
+    NSTCEvents_fnOnPropertyItemCommit,
+    NSTCEvents_fnOnItemStateChanging,
+    NSTCEvents_fnOnItemStateChanged,
+    NSTCEvents_fnOnSelectionChanged,
+    NSTCEvents_fnOnKeyboardInput,
+    NSTCEvents_fnOnBeforeExpand,
+    NSTCEvents_fnOnAfterExpand,
+    NSTCEvents_fnOnBeginLabelEdit,
+    NSTCEvents_fnOnEndLabelEdit,
+    NSTCEvents_fnOnGetToolTip,
+    NSTCEvents_fnOnBeforeItemDelete,
+    NSTCEvents_fnOnItemAdded,
+    NSTCEvents_fnOnItemDeleted,
+    NSTCEvents_fnOnBeforeContextMenu,
+    NSTCEvents_fnOnAfterContextMenu,
+    NSTCEvents_fnOnBeforeStateImageChange,
+    NSTCEvents_fnOnGetDefaultIconIndex
+};
+#undef NSTCE_IMPL
+
 static void test_initialization(void)
 {
     INameSpaceTreeControl2 *pnstc;
@@ -421,6 +711,150 @@ static void test_basics(void)
     ok(!res, "res was %d!\n", res);
 }
 
+static void test_events(void)
+{
+    INameSpaceTreeControl *pnstc;
+    INameSpaceTreeControlEventsImpl *pnstceimpl, *pnstceimpl2;
+    INameSpaceTreeControlEvents *pnstce, *pnstce2;
+    IShellFolder *psfdesktop;
+    IShellItem *psidesktop;
+    IOleWindow *pow;
+    LPITEMIDLIST pidl_desktop;
+    DWORD cookie1, cookie2;
+    HRESULT hr;
+    UINT res;
+
+    hr = CoCreateInstance(&CLSID_NamespaceTreeControl, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_INameSpaceTreeControl, (void**)&pnstc);
+    ok(hr == S_OK, "Failed to initialize control (0x%08x)\n", hr);
+
+    ok(pSHCreateShellItem != NULL, "No SHCreateShellItem.\n");
+    ok(pSHGetIDListFromObject != NULL, "No SHCreateShellItem.\n");
+
+    SHGetDesktopFolder(&psfdesktop);
+    hr = pSHGetIDListFromObject((IUnknown*)psfdesktop, &pidl_desktop);
+    IShellFolder_Release(psfdesktop);
+    ok(hr == S_OK, "Got (0x%08x)\n", hr);
+    hr = pSHCreateShellItem(NULL, NULL, pidl_desktop, &psidesktop);
+    ok(hr == S_OK, "Got 0x%08x\n", hr);
+    ILFree(pidl_desktop);
+
+    /* Create two instances of INameSpaceTreeControlEvents */
+    pnstceimpl = HeapAlloc(GetProcessHeap(), 0, sizeof(INameSpaceTreeControlEventsImpl));
+    pnstceimpl->lpVtbl = &vt_NSTCEvents;
+    pnstceimpl->ref = 1;
+    pnstce = (INameSpaceTreeControlEvents*)pnstceimpl;
+    pnstceimpl2 = HeapAlloc(GetProcessHeap(), 0, sizeof(INameSpaceTreeControlEventsImpl));
+    pnstceimpl2->lpVtbl = &vt_NSTCEvents;
+    pnstceimpl2->ref = 1;
+    pnstce2 = (INameSpaceTreeControlEvents*)pnstceimpl2;
+
+    if(0)
+    {
+        /* Crashes native */
+        hr = INameSpaceTreeControl_TreeAdvise(pnstc, NULL, NULL);
+        hr = INameSpaceTreeControl_TreeAdvise(pnstc, NULL, &cookie1);
+        hr = INameSpaceTreeControl_TreeAdvise(pnstc, (IUnknown*)pnstce, NULL);
+    }
+
+    /* TreeAdvise in NameSpaceTreeController seems to support only one
+     * client at the time, as opposed to Advise in ExplorerBrowser
+     * that supports multiple clients.
+     */
+
+    /* First, respond with E_NOINTERFACE to all QI's */
+    pnstceimpl->qi_enable_events = 0;
+    pnstceimpl->qi_called_count = 0;
+    cookie1 = 0xDEADBEEF;
+    hr = INameSpaceTreeControl_TreeAdvise(pnstc, (IUnknown*)pnstce, &cookie1);
+    ok(hr == E_FAIL, "Got (0x%08x)\n", hr);
+    ok(cookie1 == 0, "cookie now (0x%08x)\n", cookie1);
+    todo_wine
+    {
+        ok(pnstceimpl->qi_called_count == 7 || pnstceimpl->qi_called_count == 4 /* Vista */,
+           "QueryInterface called %d times.\n",
+           pnstceimpl->qi_called_count);
+    }
+    ok(pnstceimpl->ref == 1, "refcount was %d\n", pnstceimpl->ref);
+
+    /* Accept query for IID_INameSpaceTreeControlEvents */
+    pnstceimpl->qi_enable_events = 1;
+    pnstceimpl->qi_called_count = 0;
+    cookie1 = 0xDEADBEEF;
+    hr = INameSpaceTreeControl_TreeAdvise(pnstc, (IUnknown*)pnstce, &cookie1);
+    ok(hr == S_OK, "Got (0x%08x)\n", hr);
+    ok(cookie1 == 1, "cookie now (0x%08x)\n", cookie1);
+    todo_wine
+    {
+        ok(pnstceimpl->qi_called_count == 7 || pnstceimpl->qi_called_count == 4 /* Vista */,
+           "QueryInterface called %d times.\n",
+           pnstceimpl->qi_called_count);
+    }
+    ok(pnstceimpl->ref == 2, "refcount was %d\n", pnstceimpl->ref);
+
+    /* A second time, query interface will not be called at all. */
+    pnstceimpl->qi_enable_events = 1;
+    pnstceimpl->qi_called_count = 0;
+    cookie2 = 0xDEADBEEF;
+    hr = INameSpaceTreeControl_TreeAdvise(pnstc, (IUnknown*)pnstce, &cookie2);
+    ok(hr == E_FAIL, "Got (0x%08x)\n", hr);
+    ok(cookie2 == 0, "cookie now (0x%08x)\n", cookie2);
+    ok(!pnstceimpl->qi_called_count, "QueryInterface called %d times.\n",
+       pnstceimpl->qi_called_count);
+    ok(pnstceimpl->ref == 2, "refcount was %d\n", pnstceimpl->ref);
+
+    /* Using another "instance" does not help. */
+    pnstceimpl2->qi_enable_events = 1;
+    pnstceimpl2->qi_called_count = 0;
+    cookie2 = 0xDEADBEEF;
+    hr = INameSpaceTreeControl_TreeAdvise(pnstc, (IUnknown*)pnstce2, &cookie2);
+    ok(hr == E_FAIL, "Got (0x%08x)\n", hr);
+    ok(cookie2 == 0, "cookie now (0x%08x)\n", cookie2);
+    ok(!pnstceimpl2->qi_called_count, "QueryInterface called %d times.\n",
+       pnstceimpl2->qi_called_count);
+    ok(pnstceimpl2->ref == 1, "refcount was %d\n", pnstceimpl->ref);
+
+    /* Unadvise with bogus cookie (will actually unadvise properly) */
+    pnstceimpl->qi_enable_events = 1;
+    pnstceimpl->qi_called_count = 0;
+    hr = INameSpaceTreeControl_TreeUnadvise(pnstc, 1234);
+    ok(hr == S_OK, "Got (0x%08x)\n", hr);
+    ok(!pnstceimpl->qi_called_count, "QueryInterface called %d times.\n",
+       pnstceimpl->qi_called_count);
+    ok(pnstceimpl->ref == 1, "refcount was %d\n", pnstceimpl->ref);
+
+    /* Unadvise "properly" (will have no additional effect) */
+    pnstceimpl->qi_enable_events = 1;
+    pnstceimpl->qi_called_count = 0;
+    hr = INameSpaceTreeControl_TreeUnadvise(pnstc, cookie1);
+    ok(hr == S_OK, "Got (0x%08x)\n", hr);
+    ok(!pnstceimpl->qi_called_count, "QueryInterface called %d times.\n",
+       pnstceimpl->qi_called_count);
+    ok(pnstceimpl->ref == 1, "refcount was %d\n", pnstceimpl->ref);
+
+
+    hr = INameSpaceTreeControl_QueryInterface(pnstc, &IID_IOleWindow, (void**)&pow);
+    ok(hr == S_OK, "Got 0x%08x\n", hr);
+    if(SUCCEEDED(hr))
+    {
+        HWND hwnd_nstc;
+        hr = IOleWindow_GetWindow(pow, &hwnd_nstc);
+        ok(hr == S_OK, "Got 0x%08x\n", hr);
+        DestroyWindow(hwnd_nstc);
+        IOleWindow_Release(pow);
+    }
+
+    res = INameSpaceTreeControl_Release(pnstc);
+    ok(!res, "res was %d!\n", res);
+
+    if(!res)
+    {
+        /* Freeing these prematurely causes a crash. */
+        HeapFree(GetProcessHeap(), 0, pnstceimpl);
+        HeapFree(GetProcessHeap(), 0, pnstceimpl2);
+    }
+}
+
 static BOOL have_INameSpaceTreeControl(void)
 {
     INameSpaceTreeControl2 *pnstc;
@@ -470,10 +904,12 @@ START_TEST(nstc)
         OleUninitialize();
         return;
     }
+    init_function_pointers();
     setup_window();
 
     test_initialization();
     test_basics();
+    test_events();
 
     destroy_window();
     OleUninitialize();
-- 
1.7.2




More information about the wine-patches mailing list