shell32: Add a cache for queried shell folder interfaces

Dmitry Timoshkov dmitry at codeweavers.com
Mon Jul 16 04:46:48 CDT 2007


Hello,

this patch makes the attached test program (and a real one I'm working on)
2 times faster. It's still slower than native shell32, but waiting while an
app starts up for 30 seconds should be better than 60 seconds.

Changelog:
    shell32: Add a cache for queried shell folder interfaces.

---
 dlls/shell32/shlfolder.c |   99 ++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 96 insertions(+), 3 deletions(-)

diff --git a/dlls/shell32/shlfolder.c b/dlls/shell32/shlfolder.c
index b243412..cb2884a 100644
--- a/dlls/shell32/shlfolder.c
+++ b/dlls/shell32/shlfolder.c
@@ -50,6 +50,28 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL (shell);
 
+static CRITICAL_SECTION SHELL32_ClassCacheCS;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+    0, 0, &SHELL32_ClassCacheCS,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_ClassCacheCS") }
+};
+static CRITICAL_SECTION SHELL32_ClassCacheCS = { &critsect_debug, -1, 0, 0, 0, 0 };
+
+struct _cls_cache_entry
+{
+    CLSID clsid;
+    IID iid;
+    LPVOID pv;
+};
+
+static struct _class_cache
+{
+    DWORD allocated, used;
+    struct _cls_cache_entry *cls_cache_entry;
+} cls_cache;
+
 static const WCHAR wszDotShellClassInfo[] = {
     '.','S','h','e','l','l','C','l','a','s','s','I','n','f','o',0};
 
@@ -182,6 +204,62 @@ HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
     return hr;
 }
 
+static BOOL get_iface_from_cache(REFCLSID clsid, REFIID riid, LPVOID *ppvOut)
+{
+    BOOL ret = FALSE;
+    int i;
+
+    EnterCriticalSection(&SHELL32_ClassCacheCS);
+
+    for (i = 0; i < cls_cache.used; i++)
+    {
+        if (IsEqualIID(&cls_cache.cls_cache_entry[i].clsid, clsid) &&
+            IsEqualIID(&cls_cache.cls_cache_entry[i].iid, riid))
+        {
+            *ppvOut = cls_cache.cls_cache_entry[i].pv;
+            /* Pin it */
+            IUnknown_AddRef((IUnknown *)*ppvOut);
+            ret = TRUE;
+            break;
+        }
+    }
+
+    LeaveCriticalSection(&SHELL32_ClassCacheCS);
+    return ret;
+}
+
+static void add_iface_to_cache(REFCLSID clsid, REFIID riid, LPVOID pv)
+{
+    EnterCriticalSection(&SHELL32_ClassCacheCS);
+
+    if (cls_cache.used >= cls_cache.allocated)
+    {
+        if (!cls_cache.allocated)
+        {
+            cls_cache.cls_cache_entry = HeapAlloc(GetProcessHeap(), 0,
+                                                  4 * sizeof(*cls_cache.cls_cache_entry));
+            cls_cache.allocated = 4;
+        }
+        else
+        {
+            cls_cache.cls_cache_entry = HeapReAlloc(GetProcessHeap(), 0, cls_cache.cls_cache_entry,
+                (cls_cache.allocated * 2) * sizeof(*cls_cache.cls_cache_entry));
+            cls_cache.allocated *= 2;
+        }
+        if (!cls_cache.cls_cache_entry) return;
+    }
+
+    /* Pin it */
+    IUnknown_AddRef((IUnknown *)pv);
+
+    cls_cache.cls_cache_entry[cls_cache.used].clsid = *clsid;
+    cls_cache.cls_cache_entry[cls_cache.used].iid = *riid;
+    cls_cache.cls_cache_entry[cls_cache.used].pv = pv;
+    cls_cache.used++;
+
+    LeaveCriticalSection(&SHELL32_ClassCacheCS);
+}
+
 /***********************************************************************
  *	SHELL32_CoCreateInitSF
  *
@@ -195,11 +273,26 @@ HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
 static HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, LPCWSTR pathRoot,
     LPCITEMIDLIST pidlChild, REFCLSID clsid, REFIID riid, LPVOID * ppvOut)
 {
-    HRESULT hr;
+    HRESULT hr = S_OK;
 
     TRACE ("%p %s %p\n", pidlRoot, debugstr_w(pathRoot), pidlChild);
 
-    if (SUCCEEDED ((hr = SHCoCreateInstance (NULL, clsid, NULL, riid, ppvOut)))) {
+    if (!get_iface_from_cache(clsid, riid, ppvOut))
+    {
+        hr = SHCoCreateInstance(NULL, clsid, NULL, riid, ppvOut);
+        if (SUCCEEDED(hr))
+        {
+            TRACE("loaded %p %s/%s\n", *ppvOut,
+                  wine_dbgstr_guid(clsid), wine_dbgstr_guid(riid));
+            add_iface_to_cache(clsid, riid, *ppvOut);
+        }
+    }
+    else
+        TRACE("found in the cache %p %s/%s\n", *ppvOut,
+              wine_dbgstr_guid(clsid), wine_dbgstr_guid(riid));
+
+    if (SUCCEEDED (hr))
+    {
 	LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
 	IPersistFolder *pPF;
 	IPersistFolder3 *ppf;
@@ -294,7 +387,7 @@ HRESULT SHELL32_BindToChild (LPCITEMIDLIST pidlRoot,
             wszDotShellClassInfo, wszCLSID, wszCLSIDValue, CHARS_IN_GUID))
             CLSIDFromString (wszCLSIDValue, &clsidFolder);
 
-		hr = SHELL32_CoCreateInitSF (pidlRoot, pathRoot, pidlChild,
+        hr = SHELL32_CoCreateInitSF (pidlRoot, pathRoot, pidlChild,
             &clsidFolder, &IID_IShellFolder, (LPVOID *)&pSF);
     }
     ILFree (pidlChild);
-- 
1.5.2.3

---
#define COBJMACROS
#define CONST_VTABLE

#include <stdarg.h>
#include <stdio.h>

#include "windef.h"
#include "winbase.h"
#include "shlwapi.h"
#include "shlobj.h"
#ifndef _MSC_VER
#include <sys/time.h>
#endif /* _MSC_VER */

#include "wine/test.h"

#ifdef _MSC_VER
static int gettimeofday(struct timeval *tv, void *tz)
{
    ULARGE_INTEGER ull;

    GetSystemTimeAsFileTime((FILETIME *)&ull.u);

    tv->tv_sec = (long)(ull.QuadPart / (ULONGLONG)10000000);
    tv->tv_usec = (long)((ull.QuadPart % (ULONGLONG)10000000) / (ULONGLONG)10);

    return 0;
}
#endif /* _MSC_VER */

static double diff_time(struct timeval *time2, struct timeval *time1)
{
    double t1, t2;

    t1 = (double)time1->tv_sec + (double)time1->tv_usec / 1000000.0;
    t2 = (double)time2->tv_sec + (double)time2->tv_usec / 1000000.0;

    return t2 - t1;
}

static void test_IShellFolder_BindToObject(void)
{
    HRESULT hr;
    IShellFolder *sf, *sf_bind;
    LPITEMIDLIST pidl_desktop;
    LONG ref;
    STRRET str;
    char buf[MAX_PATH];
#ifdef PERF
    LONG i, n_loop = 200000;
    struct timeval time1, time2;
#endif /* PERF */

    hr = SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, &pidl_desktop);
    ok(!hr, "SHGetSpecialFolderLocation error %lx\n", hr);

    sf = NULL;
    hr = SHGetDesktopFolder(&sf);
    ok(!hr && sf, "SHGetDesktopFolder error %lx\n", hr);

    ref = IShellFolder_Release(sf);
    ok(ref > 0, "ref count after Release should be > 0\n");

    hr = IShellFolder_GetDisplayNameOf(sf, NULL, SHGDN_FORPARSING, &str);
    ok(!hr, "IShellFolder_GetDisplayNameOf error %lx\n", hr);
    hr = StrRetToBuf(&str, NULL, buf, sizeof(buf));
    ok(!hr, "IShellFolder_StrRetToBuf error %lx\n", hr);
    trace("Desktop full dir name: %s\n", buf);

    sf_bind = NULL;
    hr = IShellFolder_BindToObject(sf, pidl_desktop, NULL, &IID_NULL, &sf_bind);
    ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
       "IShellFolder_BindToObject returned %lx, expected ERROR_FILE_NOT_FOUND\n", hr);

    sf_bind = NULL;
    hr = IShellFolder_BindToObject(sf, pidl_desktop, NULL, &IID_IShellFolder, &sf_bind);
    ok(!hr && sf_bind, "IShellFolder_BindToObject error %lx\n", hr);

    hr = IShellFolder_GetDisplayNameOf(sf_bind, NULL, SHGDN_FORPARSING, &str);
    ok(!hr, "IShellFolder_GetDisplayNameOf error %lx\n", hr);
    hr = StrRetToBuf(&str, NULL, buf, sizeof(buf));
    ok(!hr, "IShellFolder_StrRetToBuf error %lx\n", hr);
    trace("Desktop bound full dir name: %s\n", buf);

    ref = IShellFolder_Release(sf_bind);
    ok(!ref, "ref count after Release should be 0\n");

#ifdef PERF
    printf("Calling IShellFolder_BindToObject %lu times\n", n_loop);
    gettimeofday(&time1, NULL);
    for (i = 0; i < n_loop; i++)
    {
//        printf("Call IShellFolder_BindToObject\n");
        sf_bind = NULL;
        hr = IShellFolder_BindToObject(sf, pidl_desktop, NULL, &IID_IShellFolder, &sf_bind);
        if (hr || !sf_bind)
            printf("IShellFolder_BindToObject error %lx\n", hr);

//        printf("got IShellFolder *sf_bind = %p\n", sf_bind);

//        printf("Call IShellFolder_Release\n");
        ref = IShellFolder_Release(sf_bind);
//        printf("ref count after Release %ld\n", ref);
    }

    gettimeofday(&time2, NULL);
    printf("elapsed %f seconds\n", diff_time(&time2, &time1));
#endif /* PERF */
}

START_TEST(shell_folder)
{
    test_IShellFolder_BindToObject();
}





More information about the wine-patches mailing list