[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