[PATCH v7 0/3] MR10565: shcore: Implement Set/GetCurrentProcessExplicitAppUserModelID.
Wine Bug: https://bugs.winehq.org/show_bug.cgi?id=59625 Set/GetCurrentProcessExplicitAppUserModelID were stubs. Set returned S_OK but discarded the value, Get always returned NULL with E_NOTIMPL. This broke .NET WPF applications that set an AppUserModelID during startup and later retrieve it, causing "String argument cannot be null or empty" crashes. Implement both using a process-wide variable protected by a critical section. Callers receive CoTaskMemAlloc'd copies per the API contract. -- v7: shcore/tests: Add tests for Set/GetCurrentProcessExplicitAppUserModelID. shcore: Implement Set/GetCurrentProcessExplicitAppUserModelID. include: Add identity string length constants to appmodel.h. https://gitlab.winehq.org/wine/wine/-/merge_requests/10565
From: Robert Gerigk <Robert-Gerigk@online.de> Add APPLICATION_USER_MODEL_ID, PACKAGE_RELATIVE_APPLICATION_ID, and all PACKAGE_* length constants from the Windows SDK. APPLICATION_USER_MODEL_ID and PACKAGE_RELATIVE_APPLICATION_ID lengths include space for the NULL terminator. PACKAGE_* lengths do not. Signed-off-by: Jan Robert Gerigk <Robert-Gerigk@online.de> --- include/appmodel.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/include/appmodel.h b/include/appmodel.h index 8c219e8080a..fba40d3f2ed 100644 --- a/include/appmodel.h +++ b/include/appmodel.h @@ -18,6 +18,46 @@ #ifndef _APPMODEL_H_ #define _APPMODEL_H_ +/* Application User Model ID (space included for NULL terminator) */ +#define APPLICATION_USER_MODEL_ID_MIN_LENGTH 21 +#define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130 + +/* Package architecture (no space for NULL terminator) */ +#define PACKAGE_ARCHITECTURE_MIN_LENGTH 3 +#define PACKAGE_ARCHITECTURE_MAX_LENGTH 7 + +/* Package family name (no space for NULL terminator) */ +#define PACKAGE_FAMILY_NAME_MIN_LENGTH 17 +#define PACKAGE_FAMILY_NAME_MAX_LENGTH 64 + +/* Package full name (no space for NULL terminator) */ +#define PACKAGE_FULL_NAME_MIN_LENGTH 30 +#define PACKAGE_FULL_NAME_MAX_LENGTH 127 + +/* Package name (no space for NULL terminator) */ +#define PACKAGE_NAME_MIN_LENGTH 3 +#define PACKAGE_NAME_MAX_LENGTH 50 + +/* Package publisher ID (no space for NULL terminator) */ +#define PACKAGE_PUBLISHERID_MIN_LENGTH 13 +#define PACKAGE_PUBLISHERID_MAX_LENGTH 13 + +/* Package publisher (no space for NULL terminator) */ +#define PACKAGE_PUBLISHER_MIN_LENGTH 4 +#define PACKAGE_PUBLISHER_MAX_LENGTH 8192 + +/* Package relative application ID (space included for NULL terminator) */ +#define PACKAGE_RELATIVE_APPLICATION_ID_MIN_LENGTH 2 +#define PACKAGE_RELATIVE_APPLICATION_ID_MAX_LENGTH 65 + +/* Package resource ID (no space for NULL terminator) */ +#define PACKAGE_RESOURCEID_MIN_LENGTH 0 +#define PACKAGE_RESOURCEID_MAX_LENGTH 30 + +/* Package version (no space for NULL terminator) */ +#define PACKAGE_VERSION_MIN_LENGTH 7 +#define PACKAGE_VERSION_MAX_LENGTH 23 + #if defined(__cplusplus) extern "C" { #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10565
From: Robert Gerigk <Robert-Gerigk@online.de> Wine's shcore.dll had Set/GetCurrentProcessExplicitAppUserModelID as stubs. SetCurrentProcessExplicitAppUserModelID returned S_OK but discarded the value, and GetCurrentProcessExplicitAppUserModelID always returned NULL with E_NOTIMPL. This broke applications that set an AppUserModelID and later retrieve it, causing "String argument cannot be null or empty" crashes in .NET WPF applications. Implement both functions using a process-wide static variable protected by a critical section. SetCurrentProcessExplicitAppUserModelID validates the input (rejects NULL and strings longer than 128 characters) and stores a CoTaskMemAlloc'd copy. GetCurrentProcessExplicitAppUserModelID returns a CoTaskMemAlloc'd copy to the caller. Signed-off-by: Jan Robert Gerigk <Robert-Gerigk@online.de> --- dlls/shcore/main.c | 54 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/dlls/shcore/main.c b/dlls/shcore/main.c index f04853cb9f5..7a14113eacb 100644 --- a/dlls/shcore/main.c +++ b/dlls/shcore/main.c @@ -30,6 +30,7 @@ #include "featurestagingapi.h" #include "shellscalingapi.h" #include "shcore.h" +#include "appmodel.h" #define WINSHLWAPI #include "shlwapi.h" @@ -249,17 +250,64 @@ HRESULT WINAPI IUnknown_SetSite(IUnknown *obj, IUnknown *site) return hr; } +static WCHAR *explicit_app_user_model_id; +static CRITICAL_SECTION appid_cs; +static CRITICAL_SECTION_DEBUG appid_cs_debug = +{ + 0, 0, &appid_cs, + { &appid_cs_debug.ProcessLocksList, &appid_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": appid_cs") } +}; +static CRITICAL_SECTION appid_cs = { &appid_cs_debug, -1, 0, 0, 0, 0 }; + HRESULT WINAPI SetCurrentProcessExplicitAppUserModelID(const WCHAR *appid) { - FIXME("%s: stub\n", debugstr_w(appid)); + WCHAR *new_id = NULL; + DWORD len; + + TRACE("%s\n", debugstr_w(appid)); + + if (!appid) + return E_INVALIDARG; + + len = lstrlenW(appid); + if (len > APPLICATION_USER_MODEL_ID_MAX_LENGTH - 2) + return E_INVALIDARG; + + new_id = CoTaskMemAlloc((len + 1) * sizeof(WCHAR)); + if (!new_id) return E_OUTOFMEMORY; + memcpy(new_id, appid, (len + 1) * sizeof(WCHAR)); + + EnterCriticalSection(&appid_cs); + CoTaskMemFree(explicit_app_user_model_id); + explicit_app_user_model_id = new_id; + LeaveCriticalSection(&appid_cs); + return S_OK; } HRESULT WINAPI GetCurrentProcessExplicitAppUserModelID(const WCHAR **appid) { - FIXME("%p: stub\n", appid); + TRACE("%p\n", appid); + + if (!appid) return E_INVALIDARG; + *appid = NULL; - return E_NOTIMPL; + + EnterCriticalSection(&appid_cs); + if (explicit_app_user_model_id) + { + DWORD len = (lstrlenW(explicit_app_user_model_id) + 1) * sizeof(WCHAR); + WCHAR *copy = CoTaskMemAlloc(len); + if (copy) + { + memcpy(copy, explicit_app_user_model_id, len); + *appid = copy; + } + } + LeaveCriticalSection(&appid_cs); + + return *appid ? S_OK : E_FAIL; } /************************************************************************* -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10565
From: Robert Gerigk <Robert-Gerigk@online.de> Test max length validation (128 chars = S_OK, 129 chars = E_INVALIDARG), set/get round-trip, and ID update behavior. Signed-off-by: Jan Robert Gerigk <Robert-Gerigk@online.de> --- dlls/shcore/tests/Makefile.in | 2 +- dlls/shcore/tests/shcore.c | 63 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/dlls/shcore/tests/Makefile.in b/dlls/shcore/tests/Makefile.in index 2b7eaa65686..d40498fda15 100644 --- a/dlls/shcore/tests/Makefile.in +++ b/dlls/shcore/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = shcore.dll -IMPORTS = advapi32 +IMPORTS = advapi32 ole32 SOURCES = \ shcore.c diff --git a/dlls/shcore/tests/shcore.c b/dlls/shcore/tests/shcore.c index 539c2e5c1bc..af14e660006 100644 --- a/dlls/shcore/tests/shcore.c +++ b/dlls/shcore/tests/shcore.c @@ -23,10 +23,14 @@ #include <windows.h> #include "initguid.h" #include "objidl.h" +#include "objbase.h" #include "shlwapi.h" +#include "appmodel.h" #include "wine/test.h" +static HRESULT (WINAPI *pSetCurrentProcessExplicitAppUserModelID)(const WCHAR *); +static HRESULT (WINAPI *pGetCurrentProcessExplicitAppUserModelID)(const WCHAR **); static HRESULT (WINAPI *pGetProcessReference)(IUnknown **); static void (WINAPI *pSetProcessReference)(IUnknown *); static HRESULT (WINAPI *pSHGetInstanceExplorer)(IUnknown **); @@ -62,6 +66,8 @@ static const char * initial_buffer ="0123456789"; static void init(HMODULE hshcore) { #define X(f) p##f = (void*)GetProcAddress(hshcore, #f) + X(SetCurrentProcessExplicitAppUserModelID); + X(GetCurrentProcessExplicitAppUserModelID); X(GetProcessReference); X(SetProcessReference); X(SHUnicodeToAnsi); @@ -777,6 +783,62 @@ static void test_stream_size(void) DeleteFileA(filename); } +static void test_AppUserModelID(void) +{ + const WCHAR *appid = NULL; + HRESULT hr; + + if (!pSetCurrentProcessExplicitAppUserModelID || !pGetCurrentProcessExplicitAppUserModelID) + { + win_skip("AppUserModelID functions not available.\n"); + return; + } + + /* String length validation. Native rejects strings longer than 128 characters. */ + { + WCHAR long_id[APPLICATION_USER_MODEL_ID_MAX_LENGTH + 10]; + memset(long_id, 'A', sizeof(long_id)); + + /* 129 chars — should fail */ + long_id[129] = 0; + hr = pSetCurrentProcessExplicitAppUserModelID(long_id); + ok(hr == E_INVALIDARG, "Got hr %#lx for 129-char string.\n", hr); + + /* 128 chars — should succeed */ + long_id[128] = 0; + hr = pSetCurrentProcessExplicitAppUserModelID(long_id); + ok(hr == S_OK, "Got hr %#lx for 128-char string.\n", hr); + } + + /* Set a valid ID */ + hr = pSetCurrentProcessExplicitAppUserModelID(L"Wine.Test.AppId"); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + /* Get should return the ID we set */ + appid = NULL; + hr = pGetCurrentProcessExplicitAppUserModelID(&appid); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(appid != NULL, "Expected non-NULL appid.\n"); + if (appid) + { + ok(!lstrcmpW(appid, L"Wine.Test.AppId"), "Got %s.\n", wine_dbgstr_w(appid)); + CoTaskMemFree((void *)appid); + } + + /* Set a different ID */ + hr = pSetCurrentProcessExplicitAppUserModelID(L"Wine.Test.AppId2"); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + appid = NULL; + hr = pGetCurrentProcessExplicitAppUserModelID(&appid); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + if (appid) + { + ok(!lstrcmpW(appid, L"Wine.Test.AppId2"), "Got %s.\n", wine_dbgstr_w(appid)); + CoTaskMemFree((void *)appid); + } +} + START_TEST(shcore) { HMODULE hshcore = LoadLibraryA("shcore.dll"); @@ -789,6 +851,7 @@ START_TEST(shcore) init(hshcore); + test_AppUserModelID(); test_process_reference(); test_SHUnicodeToAnsi(); test_SHAnsiToUnicode(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10565
participants (2)
-
Jan Robert Gerigk (@RgSg86) -
Robert Gerigk