[PATCH 1/5] kernel32: Implement PackageIdFromFullName().

Paul Gofman pgofman at codeweavers.com
Fri Mar 26 04:37:43 CDT 2021


Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
    Forza Horizon 4 depends on successfully getting the path for Microsoft.VCLibs.140.00 x64 package
    by using GetPackagesByPackageFamily(), PackageIdFromFullName(), GetPackagePath().
    Since we provide builtin VC runtime 140 in the default prefix it seems reasonable to add the corresponding
    package info to registry on prefix creation / update.

 .../api-ms-win-appmodel-runtime-l1-1-1.spec   |   2 +-
 .../ext-ms-win-kernel32-package-l1-1-1.spec   |   2 +-
 dlls/kernel32/kernel32.spec                   |   1 +
 dlls/kernel32/tests/version.c                 | 173 ++++++++++++++++++
 dlls/kernelbase/kernelbase.spec               |   2 +-
 dlls/kernelbase/version.c                     | 116 ++++++++++++
 include/appmodel.h                            |  31 ++++
 7 files changed, 324 insertions(+), 3 deletions(-)

diff --git a/dlls/api-ms-win-appmodel-runtime-l1-1-1/api-ms-win-appmodel-runtime-l1-1-1.spec b/dlls/api-ms-win-appmodel-runtime-l1-1-1/api-ms-win-appmodel-runtime-l1-1-1.spec
index 4c00082a151..99d1d9f3835 100644
--- a/dlls/api-ms-win-appmodel-runtime-l1-1-1/api-ms-win-appmodel-runtime-l1-1-1.spec
+++ b/dlls/api-ms-win-appmodel-runtime-l1-1-1/api-ms-win-appmodel-runtime-l1-1-1.spec
@@ -22,6 +22,6 @@
 @ stub PackageFamilyNameFromFullName
 @ stub PackageFamilyNameFromId
 @ stub PackageFullNameFromId
-@ stub PackageIdFromFullName
+@ stdcall PackageIdFromFullName(wstr long ptr ptr) kernel32.PackageIdFromFullName
 @ stub PackageNameAndPublisherIdFromFamilyName
 @ stub ParseApplicationUserModelId
diff --git a/dlls/ext-ms-win-kernel32-package-l1-1-1/ext-ms-win-kernel32-package-l1-1-1.spec b/dlls/ext-ms-win-kernel32-package-l1-1-1/ext-ms-win-kernel32-package-l1-1-1.spec
index 214cd344716..61fd115cca7 100644
--- a/dlls/ext-ms-win-kernel32-package-l1-1-1/ext-ms-win-kernel32-package-l1-1-1.spec
+++ b/dlls/ext-ms-win-kernel32-package-l1-1-1/ext-ms-win-kernel32-package-l1-1-1.spec
@@ -18,4 +18,4 @@
 @ stub PackageFamilyNameFromFullName
 @ stub PackageFamilyNameFromId
 @ stub PackageFullNameFromId
-@ stub PackageIdFromFullName
+@ stdcall PackageIdFromFullName(wstr long ptr ptr) kernel32.PackageIdFromFullName
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 0312e044a86..230a2a09ea2 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -1152,6 +1152,7 @@
 @ stdcall -import PeekConsoleInputW(ptr ptr long ptr)
 @ stdcall -import PeekNamedPipe(long ptr long ptr ptr ptr)
 @ stdcall -import PostQueuedCompletionStatus(long long ptr ptr)
+@ stdcall -import PackageIdFromFullName(wstr long ptr ptr)
 @ stdcall PowerClearRequest(long long)
 @ stdcall PowerCreateRequest(ptr)
 @ stdcall PowerSetRequest(long long)
diff --git a/dlls/kernel32/tests/version.c b/dlls/kernel32/tests/version.c
index 861b870b569..835c5398685 100644
--- a/dlls/kernel32/tests/version.c
+++ b/dlls/kernel32/tests/version.c
@@ -21,9 +21,11 @@
 #include "wine/test.h"
 #include "winbase.h"
 #include "winternl.h"
+#include "appmodel.h"
 
 static BOOL (WINAPI * pGetProductInfo)(DWORD, DWORD, DWORD, DWORD, DWORD *);
 static UINT (WINAPI * pGetSystemFirmwareTable)(DWORD, DWORD, void *, DWORD);
+static LONG (WINAPI * pPackageIdFromFullName)(const WCHAR *, UINT32, UINT32 *, BYTE *);
 static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, void *, ULONG, ULONG *);
 static NTSTATUS (WINAPI * pRtlGetVersion)(RTL_OSVERSIONINFOEXW *);
 
@@ -43,6 +45,7 @@ static void init_function_pointers(void)
 
     GET_PROC(GetProductInfo);
     GET_PROC(GetSystemFirmwareTable);
+    GET_PROC(PackageIdFromFullName);
 
     hmod = GetModuleHandleA("ntdll.dll");
 
@@ -746,6 +749,175 @@ static void test_GetSystemFirmwareTable(void)
     HeapFree(GetProcessHeap(), 0, smbios_table);
 }
 
+static const struct
+{
+    UINT32 code;
+    const WCHAR *name;
+    BOOL broken;
+}
+arch_data[] =
+{
+    {PROCESSOR_ARCHITECTURE_INTEL,   L"X86"},
+    {PROCESSOR_ARCHITECTURE_ARM,     L"Arm"},
+    {PROCESSOR_ARCHITECTURE_AMD64,   L"X64"},
+    {PROCESSOR_ARCHITECTURE_NEUTRAL, L"Neutral"},
+    {PROCESSOR_ARCHITECTURE_ARM64,   L"Arm64",   TRUE /* Before Win10. */},
+    {PROCESSOR_ARCHITECTURE_UNKNOWN, L"Unknown", TRUE /* Before Win10 1709. */},
+};
+
+static const WCHAR *arch_string_from_code(UINT32 arch)
+{
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(arch_data); ++i)
+        if (arch_data[i].code == arch)
+            return arch_data[i].name;
+
+    return NULL;
+}
+
+static unsigned int get_package_str_size(const WCHAR *str)
+{
+    return str ? (lstrlenW(str) + 1) * sizeof(*str) : 0;
+}
+
+static unsigned int get_package_id_size(const PACKAGE_ID *id)
+{
+    return sizeof(*id) + get_package_str_size(id->name)
+            + get_package_str_size(id->resourceId) + 14 * sizeof(WCHAR);
+}
+
+static void packagefullname_from_packageid(WCHAR *buffer, size_t count, const PACKAGE_ID *id)
+{
+    swprintf(buffer, count, L"%s_%u.%u.%u.%u_%s_%s_%s", id->name, id->version.Major,
+            id->version.Minor, id->version.Build, id->version.Revision,
+            arch_string_from_code(id->processorArchitecture), id->resourceId,
+            id->publisherId);
+}
+
+static void test_PackageIdFromFullName(void)
+{
+    static const PACKAGE_ID test_package_id =
+    {
+        0, PROCESSOR_ARCHITECTURE_INTEL,
+                {{.Major = 1, .Minor = 2, .Build = 3, .Revision = 4}},
+                (WCHAR *)L"TestPackage", NULL,
+                (WCHAR *)L"TestResourceId", (WCHAR *)L"0abcdefghjkme"
+    };
+    UINT32 size, expected_size;
+    PACKAGE_ID test_id;
+    WCHAR fullname[512];
+    BYTE id_buffer[512];
+    unsigned int i;
+    PACKAGE_ID *id;
+    LONG ret;
+
+    if (!pPackageIdFromFullName)
+    {
+        win_skip("PackageIdFromFullName not available.\n");
+        return;
+    }
+
+    packagefullname_from_packageid(fullname, ARRAY_SIZE(fullname), &test_package_id);
+
+    id = (PACKAGE_ID *)id_buffer;
+
+    memset(id_buffer, 0xcc, sizeof(id_buffer));
+    expected_size = get_package_id_size(&test_package_id);
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(fullname, 0, &size, id_buffer);
+    ok(ret == ERROR_SUCCESS, "Got unexpected ret %u.\n", ret);
+    ok(size == expected_size, "Got unexpected length %u, expected %u.\n", size, expected_size);
+    ok(!lstrcmpW(id->name, test_package_id.name), "Got unexpected name %s.\n", debugstr_w(id->name));
+    ok(!lstrcmpW(id->resourceId, test_package_id.resourceId), "Got unexpected resourceId %s.\n",
+            debugstr_w(id->resourceId));
+    ok(!lstrcmpW(id->publisherId, test_package_id.publisherId), "Got unexpected publisherId %s.\n",
+            debugstr_w(id->publisherId));
+    ok(!id->publisher, "Got unexpected publisher %s.\n", debugstr_w(id->publisher));
+    ok(id->processorArchitecture == PROCESSOR_ARCHITECTURE_INTEL, "Got unexpected processorArchitecture %u.\n",
+            id->processorArchitecture);
+    ok(id->version.Version == 0x0001000200030004, "Got unexpected Version %s.\n",
+            wine_dbgstr_longlong(id->version.Version));
+    ok((BYTE *)id->name == id_buffer + sizeof(*id), "Got unexpected name %p, buffer %p.\n", id->name, id_buffer);
+    ok((BYTE *)id->resourceId == (BYTE *)id->name + (lstrlenW(id->name) + 1) * 2,
+            "Got unexpected resourceId %p, buffer %p.\n", id->resourceId, id_buffer);
+    ok((BYTE *)id->publisherId == (BYTE *)id->resourceId + (lstrlenW(id->resourceId) + 1) * 2,
+            "Got unexpected publisherId %p, buffer %p.\n", id->resourceId, id_buffer);
+
+    ret = pPackageIdFromFullName(fullname, 0, NULL, id_buffer);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %d.\n", ret);
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(NULL, 0, &size, id_buffer);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %d.\n", ret);
+    ok(size == sizeof(id_buffer), "Got unexpected size %u.\n", size);
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(fullname, 0, &size, NULL);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %d.\n", ret);
+    ok(size == sizeof(id_buffer), "Got unexpected size %u.\n", size);
+
+    size = expected_size - 1;
+    ret = pPackageIdFromFullName(fullname, 0, &size, NULL);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %d.\n", ret);
+    ok(size == expected_size - 1, "Got unexpected size %u.\n", size);
+
+    size = expected_size - 1;
+    ret = pPackageIdFromFullName(fullname, 0, &size, id_buffer);
+    ok(ret == ERROR_INSUFFICIENT_BUFFER, "Got unexpected ret %d.\n", ret);
+    ok(size == expected_size, "Got unexpected size %u.\n", size);
+
+    size = 0;
+    ret = pPackageIdFromFullName(fullname, 0, &size, NULL);
+    ok(ret == ERROR_INSUFFICIENT_BUFFER, "Got unexpected ret %d.\n", ret);
+    ok(size == expected_size, "Got unexpected size %u.\n", size);
+
+    for (i = 0; i < ARRAY_SIZE(arch_data); ++i)
+    {
+        test_id = test_package_id;
+        test_id.processorArchitecture = arch_data[i].code;
+        packagefullname_from_packageid(fullname, ARRAY_SIZE(fullname), &test_id);
+        size = expected_size;
+        ret = pPackageIdFromFullName(fullname, 0, &size, id_buffer);
+        ok(ret == ERROR_SUCCESS || broken(arch_data[i].broken && ret == ERROR_INVALID_PARAMETER),
+                "Got unexpected ret %u.\n", ret);
+        if (ret != ERROR_SUCCESS)
+            continue;
+        ok(size == expected_size, "Got unexpected length %u, expected %u.\n", size, expected_size);
+        ok(id->processorArchitecture == arch_data[i].code, "Got unexpected processorArchitecture %u, arch %S.\n",
+                id->processorArchitecture, arch_data[i].name);
+    }
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(L"TestPackage_1.2.3.4_X86_TestResourceId_0abcdefghjkme", 0, &size, id_buffer);
+    ok(ret == ERROR_SUCCESS, "Got unexpected ret %u.\n", ret);
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(L"TestPackage_1.2.3.4_X86_TestResourceId_abcdefghjkme", 0, &size, id_buffer);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %u.\n", ret);
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(L"TestPackage_1.2.3.4_X86_TestResourceId_0abcdefghjkmee", 0, &size, id_buffer);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %u.\n", ret);
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(L"TestPackage_1.2.3_X86_TestResourceId_0abcdefghjkme", 0, &size, id_buffer);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %u.\n", ret);
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(L"TestPackage_1.2.3.4_X86_TestResourceId_0abcdefghjkme_", 0, &size, id_buffer);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %u.\n", ret);
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(L"TestPackage_1.2.3.4_X86__0abcdefghjkme", 0, &size, id_buffer);
+    ok(ret == ERROR_SUCCESS, "Got unexpected ret %u.\n", ret);
+    ok(!lstrcmpW(id->resourceId, L""), "Got unexpected resourceId %s.\n", debugstr_w(id->resourceId));
+
+    size = sizeof(id_buffer);
+    ret = pPackageIdFromFullName(L"TestPackage_1.2.3.4_X86_0abcdefghjkme", 0, &size, id_buffer);
+    ok(ret == ERROR_INVALID_PARAMETER, "Got unexpected ret %u.\n", ret);
+}
+
 START_TEST(version)
 {
     init_function_pointers();
@@ -754,4 +926,5 @@ START_TEST(version)
     test_GetVersionEx();
     test_VerifyVersionInfo();
     test_GetSystemFirmwareTable();
+    test_PackageIdFromFullName();
 }
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 8cb15fff084..615b928bd22 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -1014,7 +1014,7 @@
 # @ stub PackageFamilyNameFromProductId
 # @ stub PackageFullNameFromId
 # @ stub PackageFullNameFromProductId
-# @ stub PackageIdFromFullName
+@ stdcall PackageIdFromFullName(wstr long ptr ptr)
 # @ stub PackageIdFromProductId
 # @ stub PackageNameAndPublisherIdFromFamilyName
 # @ stub PackageRelativeApplicationIdFromProductId
diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c
index d5bfa081939..1ee82ce019a 100644
--- a/dlls/kernelbase/version.c
+++ b/dlls/kernelbase/version.c
@@ -39,6 +39,7 @@
 #include "winnls.h"
 #include "winternl.h"
 #include "winerror.h"
+#include "appmodel.h"
 
 #include "kernelbase.h"
 #include "wine/debug.h"
@@ -1565,3 +1566,118 @@ LONG WINAPI /* DECLSPEC_HOTPATCH */ GetPackageFamilyName( HANDLE process, UINT32
     FIXME( "(%p %p %p): stub\n", process, length, name );
     return APPMODEL_ERROR_NO_PACKAGE;
 }
+
+
+static const struct
+{
+    UINT32 code;
+    const WCHAR *name;
+}
+arch_names[] =
+{
+    {PROCESSOR_ARCHITECTURE_INTEL,         L"x86"},
+    {PROCESSOR_ARCHITECTURE_ARM,           L"arm"},
+    {PROCESSOR_ARCHITECTURE_AMD64,         L"x64"},
+    {PROCESSOR_ARCHITECTURE_NEUTRAL,       L"neutral"},
+    {PROCESSOR_ARCHITECTURE_ARM64,         L"arm64"},
+    {PROCESSOR_ARCHITECTURE_UNKNOWN,       L"unknown"},
+};
+
+UINT32 processor_arch_from_string(const WCHAR *str, unsigned int len)
+{
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(arch_names); ++i)
+        if (lstrlenW(arch_names[i].name) == len && !wcsnicmp(str, arch_names[i].name, len))
+            return arch_names[i].code;
+    return ~0u;
+}
+
+/***********************************************************************
+ *         PackageIdFromFullName   (kernelbase.@)
+ */
+LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer)
+{
+    const WCHAR *name, *version_str, *arch_str, *resource_id, *publisher_id, *s;
+    PACKAGE_ID *id = (PACKAGE_ID *)buffer;
+    UINT32 size, buffer_size, len;
+
+    TRACE("full_name %s, flags %#x, buffer_length %p, buffer %p.\n",
+            debugstr_w(full_name), flags, buffer_length, buffer);
+
+    if (flags)
+        FIXME("Flags %#x are not supported.\n", flags);
+
+    if (!full_name || !buffer_length)
+        return ERROR_INVALID_PARAMETER;
+
+    if (!buffer && *buffer_length)
+        return ERROR_INVALID_PARAMETER;
+
+    name = full_name;
+    if (!(version_str = wcschr(name, L'_')))
+        return ERROR_INVALID_PARAMETER;
+    ++version_str;
+
+    if (!(arch_str = wcschr(version_str, L'_')))
+        return ERROR_INVALID_PARAMETER;
+    ++arch_str;
+
+    if (!(resource_id = wcschr(arch_str, L'_')))
+        return ERROR_INVALID_PARAMETER;
+    ++resource_id;
+
+    if (!(publisher_id = wcschr(resource_id, L'_')))
+        return ERROR_INVALID_PARAMETER;
+    ++publisher_id;
+
+    /* Publisher id length should be 13. */
+    size = sizeof(*id) + sizeof(WCHAR) * ((version_str - name) + (publisher_id - resource_id) + 13 + 1);
+    buffer_size = *buffer_length;
+    *buffer_length = size;
+    if (buffer_size < size)
+        return ERROR_INSUFFICIENT_BUFFER;
+
+    memset(id, 0, sizeof(*id));
+    if ((id->processorArchitecture = processor_arch_from_string(arch_str, resource_id - arch_str - 1)) == ~0u)
+    {
+        FIXME("Unrecognized arch %s.\n", debugstr_w(arch_str));
+        return ERROR_INVALID_PARAMETER;
+    }
+    buffer += sizeof(*id);
+
+    id->version.u.s.Major = wcstol(version_str, NULL, 10);
+    if (!(s = wcschr(version_str, L'.')))
+        return ERROR_INVALID_PARAMETER;
+    ++s;
+    id->version.u.s.Minor = wcstol(s, NULL, 10);
+    if (!(s = wcschr(s, L'.')))
+        return ERROR_INVALID_PARAMETER;
+    ++s;
+    id->version.u.s.Build = wcstol(s, NULL, 10);
+    if (!(s = wcschr(s, L'.')))
+        return ERROR_INVALID_PARAMETER;
+    ++s;
+    id->version.u.s.Revision = wcstol(s, NULL, 10);
+
+    id->name = (WCHAR *)buffer;
+    len = version_str - name - 1;
+    memcpy(id->name, name, sizeof(*id->name) * len);
+    id->name[len] = 0;
+    buffer += sizeof(*id->name) * (len + 1);
+
+    id->resourceId = (WCHAR *)buffer;
+    len = publisher_id - resource_id - 1;
+    memcpy(id->resourceId, resource_id, sizeof(*id->resourceId) * len);
+    id->resourceId[len] = 0;
+    buffer += sizeof(*id->resourceId) * (len + 1);
+
+    id->publisherId = (WCHAR *)buffer;
+    len = lstrlenW(publisher_id);
+    if (len != 13)
+        return ERROR_INVALID_PARAMETER;
+    memcpy(id->publisherId, publisher_id, sizeof(*id->publisherId) * len);
+    id->publisherId[len] = 0;
+
+    return ERROR_SUCCESS;
+}
diff --git a/include/appmodel.h b/include/appmodel.h
index 34da979782d..e4288bbfbb0 100644
--- a/include/appmodel.h
+++ b/include/appmodel.h
@@ -48,10 +48,41 @@ typedef enum AppPolicyWindowingModel
     AppPolicyWindowingModel_ClassicPhone   = 3
 } AppPolicyWindowingModel;
 
+typedef struct PACKAGE_VERSION
+{
+    union
+    {
+        UINT64 Version;
+        struct
+        {
+            USHORT Revision;
+            USHORT Build;
+            USHORT Minor;
+            USHORT Major;
+        }
+        DUMMYSTRUCTNAME;
+    }
+    DUMMYUNIONNAME;
+}
+PACKAGE_VERSION;
+
+typedef struct PACKAGE_ID
+{
+    UINT32 reserved;
+    UINT32 processorArchitecture;
+    PACKAGE_VERSION version;
+    WCHAR *name;
+    WCHAR *publisher;
+    WCHAR *resourceId;
+    WCHAR *publisherId;
+}
+PACKAGE_ID;
+
 LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessTerminationMethod *policy);
 LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy);
 LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy);
 LONG WINAPI AppPolicyGetWindowingModel(HANDLE processToken, AppPolicyWindowingModel *policy);
+LONG WINAPI PackageIdFromFullName(const WCHAR *full_name, UINT32 flags, UINT32 *buffer_length, BYTE *buffer);
 
 #if defined(__cplusplus)
 }
-- 
2.30.2




More information about the wine-devel mailing list