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