[PATCH 1/6] shell32: Implement process-wide explicit AppUserModelID

Jonas Kümmerlin rgcjonas at gmail.com
Tue Jul 14 10:07:41 CDT 2015


We now support [Set|Get]CurrentProcessExplicitAppUserModelID.
This is a basic building block for supporting Win7-like matching
of shortcuts to open windows.
---
 dlls/shell32/shell32.spec         |   1 +
 dlls/shell32/shell32_main.c       | 109 +++++++++++++++++++++++++++++++++++++-
 dlls/shell32/tests/Makefile.in    |   1 +
 dlls/shell32/tests/appusermodel.c | 108 +++++++++++++++++++++++++++++++++++++
 include/shobjidl.idl              |   6 +++
 include/winbase.h                 |   2 +
 6 files changed, 225 insertions(+), 2 deletions(-)
 create mode 100644 dlls/shell32/tests/appusermodel.c

diff --git a/dlls/shell32/shell32.spec b/dlls/shell32/shell32.spec
index ed34919..1aa2c6b 100644
--- a/dlls/shell32/shell32.spec
+++ b/dlls/shell32/shell32.spec
@@ -326,6 +326,7 @@
 @ stub RealShellExecuteW
 @ stdcall RegenerateUserEnvironment(ptr long)
 @ stdcall SetCurrentProcessExplicitAppUserModelID(wstr)
+@ stdcall GetCurrentProcessExplicitAppUserModelID(ptr)
 @ stdcall SHAddToRecentDocs (long ptr)
 @ stdcall SHAppBarMessage(long ptr)
 @ stdcall SHAssocEnumHandlers(wstr long ptr)
diff --git a/dlls/shell32/shell32_main.c b/dlls/shell32/shell32_main.c
index cabf5e4..886cdfb 100644
--- a/dlls/shell32/shell32_main.c
+++ b/dlls/shell32/shell32_main.c
@@ -40,6 +40,7 @@
 #include "rpcproxy.h"
 #include "shlwapi.h"
 #include "propsys.h"
+#include "winternl.h"
 
 #include "undocshell.h"
 #include "pidl.h"
@@ -1370,11 +1371,115 @@ HRESULT WINAPI SHGetLocalizedName(LPCWSTR path, LPWSTR module, UINT size, INT *r
 
 /***********************************************************************
  *              SetCurrentProcessExplicitAppUserModelID (SHELL32.@)
+ *
+ * Sets the AppUserModelID for the current process.
+ *
+ * An AppUserModelID is a unique identifier like
+ *      Company.ProductName.SubProduct.Version
+ * where the SubProduct and Version parts are optional, and no spaces
+ * are allowed (wine does not currently enforce this).
+ * The maximum length is 128 characters (including the null-terminator).
+ *
+ * AppUserModelIDs can be used to reliably match application windows
+ * to shortcuts, similar to the X11 WM_CLASS property.
+ *
+ * Each process can have an associated AppUserModelID which has to be
+ * set using this API call before any windows are created, and each
+ * window can have a custom AppUserModelID which overrides the process-
+ * wide one (see: SHGetPropertyStoreForWindow).
  */
 HRESULT WINAPI SetCurrentProcessExplicitAppUserModelID(PCWSTR appid)
 {
-    FIXME("%s: stub\n", debugstr_w(appid));
-    return E_NOTIMPL;
+    int len;
+    RTL_USER_PROCESS_PARAMETERS *params;
+    static WCHAR *id_buffer = NULL;
+
+    if (!appid)
+    {
+        return E_INVALIDARG;
+    }
+
+    len = lstrlenW(appid);
+
+    if (len > 127)
+    {
+        return E_INVALIDARG;
+    }
+
+    RtlAcquirePebLock();
+
+    params = NtCurrentTeb()->Peb->ProcessParameters;
+
+    /* FIXME: we leak the old buffer, and the new one
+     * if shell32.dll is unloaded and then loaded again */
+    if (!id_buffer)
+    {
+        id_buffer = HeapAlloc(GetProcessHeap(), 0, 128 * sizeof(WCHAR));
+
+        if (id_buffer == NULL)
+        {
+            RtlReleasePebLock();
+
+            return E_OUTOFMEMORY;
+        }
+
+        params->WindowTitle.Buffer = id_buffer;
+        params->WindowTitle.MaximumLength = 128 * sizeof(WCHAR);
+    }
+
+    memcpy(params->WindowTitle.Buffer, appid, (len + 1) * sizeof(WCHAR));
+
+    params->WindowTitle.Length = len * sizeof(WCHAR);
+    params->dwFlags &= ~STARTF_TITLEISLINKNAME;
+    params->dwFlags |= STARTF_TITLEISAPPID;
+
+    RtlReleasePebLock();
+
+    return S_OK;
+}
+
+/***********************************************************************
+ *              GetCurrentProcessExplicitAppUserModelID (SHELL32.@)
+ *
+ * Get the AppUserModelID for the current process if it was already set.
+ */
+HRESULT WINAPI GetCurrentProcessExplicitAppUserModelID(PWSTR *appid)
+{
+    HRESULT ret = S_OK;
+    RTL_USER_PROCESS_PARAMETERS *params;
+
+    if (!appid)
+    {
+        return E_INVALIDARG;
+    }
+
+    *appid = NULL;
+
+    RtlAcquirePebLock();
+
+    params = NtCurrentTeb()->Peb->ProcessParameters;
+
+    if ((params->dwFlags & STARTF_TITLEISAPPID) > 0)
+    {
+        *appid = CoTaskMemAlloc(params->WindowTitle.Length + sizeof(WCHAR));
+        if (*appid)
+        {
+            memcpy(*appid, params->WindowTitle.Buffer, params->WindowTitle.Length);
+            (*appid)[params->WindowTitle.Length / sizeof(WCHAR)] = 0;
+        }
+        else
+        {
+            ret = E_OUTOFMEMORY;
+        }
+    }
+    else
+    {
+        ret = E_FAIL;
+    }
+
+    RtlReleasePebLock();
+
+    return ret;
 }
 
 /***********************************************************************
diff --git a/dlls/shell32/tests/Makefile.in b/dlls/shell32/tests/Makefile.in
index cfe0c50..9a37e2e 100644
--- a/dlls/shell32/tests/Makefile.in
+++ b/dlls/shell32/tests/Makefile.in
@@ -3,6 +3,7 @@ IMPORTS   = shell32 ole32 oleaut32 user32 advapi32
 
 C_SRCS = \
 	appbar.c \
+	appusermodel.c \
 	assoc.c \
 	autocomplete.c \
 	brsfolder.c \
diff --git a/dlls/shell32/tests/appusermodel.c b/dlls/shell32/tests/appusermodel.c
new file mode 100644
index 0000000..09638f3
--- /dev/null
+++ b/dlls/shell32/tests/appusermodel.c
@@ -0,0 +1,108 @@
+/* Unit test suite for the AppUserModelID support
+ *
+ * Copyright 2015 Jonas Kümmerlin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include "windows.h"
+#include "shlguid.h"
+#include "shobjidl.h"
+#include "shlobj.h"
+#include "shellapi.h"
+#include "wine/test.h"
+
+#include "shell32_test.h"
+
+HRESULT (WINAPI *pSetCurrentProcessExplicitAppUserModelID)(const WCHAR *id);
+HRESULT (WINAPI *pGetCurrentProcessExplicitAppUserModelID)(WCHAR **id);
+
+#define RESOLVE(hDll, proc) p##proc = (void*)GetProcAddress(hDll, #proc)
+
+static void test_process_aum_id(void)
+{
+    HMODULE hShell32;
+    HRESULT hr;
+    WCHAR  *received = NULL;
+
+    /* MSDN claims the maximum length to be 128 chars, but in reality,
+       it is 127 chars + terminating NUL byte                          */
+    WCHAR test_id[] = {
+        'W','i','n','e','.','T','e','s','t','.','A','a','a','a','a','a',
+        'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+        'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+        'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+        'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+        'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+        'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+        'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a', 0 };
+
+    hShell32 = GetModuleHandleA("shell32.dll");
+
+    RESOLVE(hShell32, SetCurrentProcessExplicitAppUserModelID);
+    RESOLVE(hShell32, GetCurrentProcessExplicitAppUserModelID);
+
+    if (!pGetCurrentProcessExplicitAppUserModelID
+        || !pSetCurrentProcessExplicitAppUserModelID)
+    {
+        win_skip("SetCurrentProcessExplicitAppUserModelID is not available");
+        return;
+    }
+
+    /* Receiving it without setting will fail with an unspecified error code */
+    hr = pGetCurrentProcessExplicitAppUserModelID(&received);
+    ok(FAILED(hr), "receiving the AppUserModelID succeeded where it shouldn't\n");
+    ok(received == NULL, "AppUserModelID '%s' has been returned even though none was set\n",
+                         wine_dbgstr_w(received));
+
+    if (received)
+    {
+        CoTaskMemFree(received);
+        received = NULL;
+    }
+
+    hr = pSetCurrentProcessExplicitAppUserModelID(test_id);
+    ok(hr == S_OK, "SetCurrentProcessExplicitAppUserModelID failed (0x%08x)\n", hr);
+
+    /* retrieve it again and compare */
+    hr = pGetCurrentProcessExplicitAppUserModelID(&received);
+    ok(hr == S_OK, "GetCurrentProcessExplicitAppUserModelID failed (0x%08x)\n", hr);
+    ok(received != NULL, "GetCurrentProcessExplicitAppUserModelID returned a NULL ID\n");
+
+    if (received)
+    {
+        int length;
+        int cmp;
+        int test_id_length;
+
+        test_id_length = lstrlenW(test_id);
+        length = lstrlenW(received);
+        cmp = lstrcmpW(received, test_id);
+
+        ok(length == test_id_length, "Expected id with length 127, got %d\n", length);
+
+        ok(cmp == 0, "Expected '%s', but got '%s'\n",
+                      wine_dbgstr_w(test_id), wine_dbgstr_w(received));
+
+        CoTaskMemFree(received);
+    }
+}
+
+START_TEST(appusermodel)
+{
+    test_process_aum_id();
+}
diff --git a/include/shobjidl.idl b/include/shobjidl.idl
index 455045d..d1fee7c 100644
--- a/include/shobjidl.idl
+++ b/include/shobjidl.idl
@@ -3638,6 +3638,12 @@ typedef enum ASSOC_FILTER
 cpp_quote("HRESULT WINAPI SHAssocEnumHandlers(PCWSTR extra, ASSOC_FILTER filter, IEnumAssocHandlers **handlersenum);")
 
 /*****************************************************************************
+ * AppUserModelID support
+ */
+cpp_quote("HRESULT WINAPI SetCurrentProcessExplicitAppUserModelID(PCWSTR AppID);")
+cpp_quote("HRESULT WINAPI GetCurrentProcessExplicitAppUserModelID(PWSTR *AppID);")
+
+/*****************************************************************************
  * ShellObjects typelibrary
  */
 [
diff --git a/include/winbase.h b/include/winbase.h
index a1859b0..edbe04f 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -579,6 +579,8 @@ typedef VOID (CALLBACK *LPOVERLAPPED_COMPLETION_ROUTINE)(DWORD,DWORD,LPOVERLAPPE
 #define	STARTF_FORCEOFFFEEDBACK	0x00000080
 #define	STARTF_USESTDHANDLES	0x00000100
 #define	STARTF_USEHOTKEY	0x00000200
+#define	STARTF_TITLEISLINKNAME	0x00000800
+#define	STARTF_TITLEISAPPID	0x00001000
 
 typedef struct _STARTUPINFOA{
         DWORD cb;		/* 00: size of struct */
-- 
2.4.3




More information about the wine-devel mailing list