Skip to content

Commit 2869418

Browse files
Refactor + Update MSStoreApps implementation (#43436)
### What does this PR do? - Renamed MSStoreApps VS project to DatadogInterop - Renamed DLL output file to `libdatadog-interop.dll` - Updated C++ implementation to leverage `hstring` pointers instead of allocating `char *`s ### Motivation https://datadoghq.atlassian.net/browse/WINA-1543 ### Describe how you validated your changes Compiled DLL using VS and tested in standalone Go project. Tested with a console app for memory leaks using the flags: ``` void EnableMemoryLeakChecking() { int dbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); dbgFlags |= _CRTDBG_ALLOC_MEM_DF; // track allocations dbgFlags |= _CRTDBG_LEAK_CHECK_DF; // dump leaks at process exit _CrtSetDbgFlag(dbgFlags); } ``` Must compile DLL and run console app in debug mode. Co-authored-by: julien-lebot <julien.lebot@datadoghq.com> Co-authored-by: brian.tu <brian.tu@datadoghq.com>
1 parent bf611ba commit 2869418

11 files changed

Lines changed: 262 additions & 268 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed
2+
// under the Apache License Version 2.0.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
// Copyright 2025-present Datadog, Inc.
5+
6+
//go:build windows
7+
8+
// Package msstoreapps provides the API for MS Store apps from the Datadog Interop DLL, libdatadog-interop.dll.
9+
package msstoreapps
10+
11+
import (
12+
"fmt"
13+
"syscall"
14+
"unsafe"
15+
)
16+
17+
// Must match the MSStoreEntry struct in msstoreapps.h
18+
type cStoreEntry struct {
19+
DisplayName *uint16
20+
VersionMajor uint16
21+
VersionMinor uint16
22+
VersionBuild uint16
23+
VersionRevision uint16
24+
InstallDate int64
25+
Is64Bit uint64
26+
Publisher *uint16
27+
ProductCode *uint16
28+
}
29+
30+
// Must match the MSStore struct in msstoreapps.h
31+
type CStore struct {
32+
Count int64
33+
Entries *cStoreEntry
34+
}
35+
36+
var (
37+
mod = syscall.NewLazyDLL("libdatadog-interop.dll")
38+
procGetStore = mod.NewProc("GetStore")
39+
procFreeStore = mod.NewProc("FreeStore")
40+
)
41+
42+
// GetStore returns a pointer to a CStore struct containing the list of MS Store apps.
43+
// The caller is responsible for freeing the memory allocated for the CStore struct using FreeStore.
44+
func GetStore() (*CStore, error) {
45+
var out *CStore
46+
r1, _, lastErr := procGetStore.Call(uintptr(unsafe.Pointer(&out)))
47+
if r1 == 0 {
48+
return nil, fmt.Errorf("GetStore failed: %w", lastErr)
49+
}
50+
return out, nil
51+
}
52+
53+
// FreeStore frees the memory allocated for the CStore struct.
54+
// Returns an error if the free operation fails.
55+
func FreeStore(store *CStore) error {
56+
r1, _, lastErr := procFreeStore.Call(uintptr(unsafe.Pointer(store)))
57+
if r1 == 0 {
58+
return fmt.Errorf("FreeStore failed: %w", lastErr)
59+
}
60+
return nil
61+
}

tasks/msi.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -283,16 +283,16 @@ def _build_msi(ctx, env, outdir, name, allowlist):
283283
sign_file(ctx, out_file)
284284

285285

286-
def _build_msstoreapps(ctx, env, configuration, arch, vstudio_root):
287-
msstoreapps_sln = os.path.join(os.getcwd(), "tools", "windows", "MSStoreApps", "MSStoreApps.sln")
286+
def _build_datadog_interop(ctx, env, configuration, arch, vstudio_root):
287+
datadog_interop_sln = os.path.join(os.getcwd(), "tools", "windows", "DatadogInterop", "DatadogInterop.sln")
288288
cmd = _get_vs_build_command(
289-
f'msbuild "{msstoreapps_sln}" /p:Configuration={configuration} /p:Platform="{arch}" /verbosity:minimal',
289+
f'msbuild "{datadog_interop_sln}" /p:Configuration={configuration} /p:Platform="{arch}" /verbosity:minimal',
290290
vstudio_root,
291291
)
292-
print(f"Building MSStoreApps: {cmd}")
292+
print(f"Building DatadogInterop: {cmd}")
293293
succeeded = ctx.run(cmd, warn=True, env=env, err_stream=sys.stdout)
294294
if not succeeded:
295-
raise Exit("Failed to build MSStoreApps.", code=1)
295+
raise Exit("Failed to build DatadogInterop.", code=1)
296296

297297

298298
def _msi_output_name(env):
@@ -327,13 +327,13 @@ def build(
327327
vstudio_root=vstudio_root,
328328
)
329329

330-
# Build MSStoreApps.dll
331-
_build_msstoreapps(ctx, env, configuration, arch, vstudio_root)
332-
msstoreapps_output = os.path.join(
333-
os.getcwd(), "tools", "windows", "MSStoreApps", arch, configuration, "MSStoreApps.dll"
330+
# Build libdatadog-interop.dll
331+
_build_datadog_interop(ctx, env, configuration, arch, vstudio_root)
332+
datadog_interop_output = os.path.join(
333+
os.getcwd(), "tools", "windows", "DatadogInterop", arch, configuration, "libdatadog-interop.dll"
334334
)
335-
shutil.copy2(msstoreapps_output, AGENT_BIN_SOURCE_DIR)
336-
sign_file(ctx, os.path.join(AGENT_BIN_SOURCE_DIR, 'MSStoreApps.dll'))
335+
shutil.copy2(datadog_interop_output, AGENT_BIN_SOURCE_DIR)
336+
sign_file(ctx, os.path.join(AGENT_BIN_SOURCE_DIR, 'libdatadog-interop.dll'))
337337

338338
# sign build output that will be included in the installer MSI
339339
sign_file(ctx, os.path.join(build_outdir, 'CustomActions.dll'))

tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentBinaries.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class AgentBinaries
1616
// if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WINDOWS_DDPROCMON_DRIVER")))
1717
public string SecurityAgent => $@"{_binSource}\security-agent.exe";
1818
public string LibDatadogAgentThree => $@"{_binSource}\libdatadog-agent-three.dll";
19-
public string MSStoreApps => $@"{_binSource}\MSStoreApps.dll";
19+
public string DatadogInterop => $@"{_binSource}\libdatadog-interop.dll";
2020

2121
public AgentBinaries(string binSource, string installerSource)
2222
{

tools/windows/DatadogAgentInstaller/WixSetup/Datadog Agent/AgentInstaller.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ private Dir CreateBinFolder()
630630
},
631631
agentBinDir,
632632
new WixSharp.File(_agentBinaries.LibDatadogAgentThree),
633-
new WixSharp.File(_agentBinaries.MSStoreApps),
633+
new WixSharp.File(_agentBinaries.DatadogInterop),
634634
new WixSharp.File(@"C:\opt\datadog-installer\datadog-installer.exe",
635635
new ServiceInstaller
636636
{

tools/windows/MSStoreApps/MSStoreApps.sln renamed to tools/windows/DatadogInterop/DatadogInterop.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.14.36623.8
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MSStoreApps", "MSStoreApps\MSStoreApps.vcxproj", "{A8D4744D-505B-42F3-B56F-27186A32A6CF}"
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DatadogInterop", "DatadogInterop\DatadogInterop.vcxproj", "{A8D4744D-505B-42F3-B56F-27186A32A6CF}"
77
EndProject
88
Global
99
GlobalSection(SolutionConfigurationPlatforms) = preSolution

tools/windows/MSStoreApps/MSStoreApps/MSStoreApps.vcxproj renamed to tools/windows/DatadogInterop/DatadogInterop/DatadogInterop.vcxproj

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
<VCProjectVersion>17.0</VCProjectVersion>
1515
<Keyword>Win32Proj</Keyword>
1616
<ProjectGuid>{a8d4744d-505b-42f3-b56f-27186a32a6cf}</ProjectGuid>
17-
<RootNamespace>MSStoreApps</RootNamespace>
17+
<RootNamespace>DatadogInterop</RootNamespace>
1818
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
19+
<ProjectName>DatadogInterop</ProjectName>
1920
</PropertyGroup>
2021
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
2122
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
@@ -30,6 +31,7 @@
3031
<PlatformToolset>v143</PlatformToolset>
3132
<WholeProgramOptimization>true</WholeProgramOptimization>
3233
<CharacterSet>Unicode</CharacterSet>
34+
<SpectreMitigation>Spectre</SpectreMitigation>
3335
</PropertyGroup>
3436
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
3537
<ImportGroup Label="ExtensionSettings">
@@ -43,41 +45,55 @@
4345
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
4446
</ImportGroup>
4547
<PropertyGroup Label="UserMacros" />
48+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
49+
<LinkIncremental>false</LinkIncremental>
50+
<TargetName>libdatadog-interop</TargetName>
51+
</PropertyGroup>
52+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
53+
<TargetName>libdatadog-interop</TargetName>
54+
</PropertyGroup>
4655
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
4756
<ClCompile>
48-
<WarningLevel>Level3</WarningLevel>
57+
<WarningLevel>Level4</WarningLevel>
4958
<SDLCheck>true</SDLCheck>
50-
<PreprocessorDefinitions>_DEBUG;MSSTOREAPPS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
59+
<PreprocessorDefinitions>_DEBUG;DATADOGINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
5160
<ConformanceMode>true</ConformanceMode>
52-
<PrecompiledHeader>Use</PrecompiledHeader>
53-
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
61+
<PrecompiledHeader>NotUsing</PrecompiledHeader>
62+
<PrecompiledHeaderFile>
63+
</PrecompiledHeaderFile>
64+
<LanguageStandard>stdcpp20</LanguageStandard>
5465
</ClCompile>
5566
<Link>
5667
<SubSystem>Windows</SubSystem>
5768
<GenerateDebugInformation>true</GenerateDebugInformation>
5869
<EnableUAC>false</EnableUAC>
70+
<AdditionalDependencies>$(CoreLibraryDependencies);runtimeobject.lib; ole32.lib; %(AdditionalDependencies)</AdditionalDependencies>
5971
</Link>
6072
</ItemDefinitionGroup>
6173
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
6274
<ClCompile>
63-
<WarningLevel>Level3</WarningLevel>
75+
<WarningLevel>Level4</WarningLevel>
6476
<FunctionLevelLinking>true</FunctionLevelLinking>
6577
<IntrinsicFunctions>true</IntrinsicFunctions>
6678
<SDLCheck>true</SDLCheck>
67-
<PreprocessorDefinitions>NDEBUG;MSSTOREAPPS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN</PreprocessorDefinitions>
79+
<PreprocessorDefinitions>NDEBUG;DATADOGINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN</PreprocessorDefinitions>
6880
<ConformanceMode>true</ConformanceMode>
69-
<PrecompiledHeader>Use</PrecompiledHeader>
70-
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
81+
<PrecompiledHeader>NotUsing</PrecompiledHeader>
82+
<PrecompiledHeaderFile>
83+
</PrecompiledHeaderFile>
7184
<LanguageStandard>stdcpp20</LanguageStandard>
7285
<ExceptionHandling>Async</ExceptionHandling>
7386
<EnableVectorLength>VectorLength256</EnableVectorLength>
7487
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
88+
<TreatWarningAsError>true</TreatWarningAsError>
89+
<ControlFlowGuard>Guard</ControlFlowGuard>
7590
</ClCompile>
7691
<Link>
7792
<SubSystem>Windows</SubSystem>
7893
<GenerateDebugInformation>true</GenerateDebugInformation>
7994
<EnableUAC>false</EnableUAC>
8095
<AdditionalDependencies>$(CoreLibraryDependencies);runtimeobject.lib; ole32.lib; %(AdditionalDependencies)</AdditionalDependencies>
96+
<AdditionalOptions>/HIGHENTROPYVA %(AdditionalOptions)</AdditionalOptions>
8197
</Link>
8298
</ItemDefinitionGroup>
8399
<ItemGroup>
@@ -88,7 +104,10 @@
88104
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
89105
</PrecompiledHeaderFile>
90106
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
91-
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;MSSTOREAPPS_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
107+
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NDEBUG;DATADOGINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
108+
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
109+
</PrecompiledHeaderFile>
110+
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
92111
</ClCompile>
93112
</ItemGroup>
94113
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

tools/windows/MSStoreApps/MSStoreApps/MSStoreApps.vcxproj.filters renamed to tools/windows/DatadogInterop/DatadogInterop/DatadogInterop.vcxproj.filters

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@
2424
<Filter>Source Files</Filter>
2525
</ClCompile>
2626
</ItemGroup>
27-
</Project>
27+
</Project>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include <windows.h>
2+
#include <winrt/Windows.Foundation.Collections.h>
3+
#include <winrt/Windows.ApplicationModel.Core.h>
4+
#include <winrt/Windows.Management.Deployment.h>
5+
6+
#include "msstoreapps.h"
7+
8+
using namespace winrt::Windows::Foundation;
9+
using namespace winrt::Windows::ApplicationModel;
10+
using namespace winrt::Windows::Management::Deployment;
11+
using namespace winrt::Windows::System;
12+
13+
static uint64_t is64(ProcessorArchitecture a) {
14+
if (a == ProcessorArchitecture::X64 || a == ProcessorArchitecture::Arm64) {
15+
return 1;
16+
}
17+
return 0;
18+
}
19+
20+
static int64_t dtToUnixTimestamp(const DateTime &dt) {
21+
// Convert to Unix time_t (seconds since 1970-01-01)
22+
return winrt::clock::to_time_t(dt);
23+
}
24+
25+
static const wchar_t *copyHStr(MSStoreInternal *msStore, winrt::hstring hstr) {
26+
// Add to msStore's string vector to keep hstring alive
27+
msStore->strings.push_back(hstr);
28+
return hstr.c_str();
29+
}
30+
31+
// Safe accessor template for fields that may throw exceptions
32+
template <typename Func>
33+
static auto safeAccess(Func fn, auto defaultValue) -> decltype(defaultValue) {
34+
try {
35+
return fn();
36+
} catch (...) {
37+
return defaultValue;
38+
}
39+
}
40+
41+
static void addEntryToStore(MSStoreInternal *msStore, const Package &pkg, winrt::hstring displayName) {
42+
auto id = pkg.Id();
43+
PackageVersion version = safeAccess([&]() { return id.Version(); }, PackageVersion{});
44+
45+
MSStoreEntry e{};
46+
e.display_name = copyHStr(msStore, displayName);
47+
e.version_major = version.Major;
48+
e.version_minor = version.Minor;
49+
e.version_build = version.Build;
50+
e.version_revision = version.Revision;
51+
e.install_date = safeAccess([&]() { return dtToUnixTimestamp(pkg.InstalledDate()); }, 0LL);
52+
e.is_64bit = safeAccess([&]() { return is64(id.Architecture()); }, 0ULL);
53+
e.publisher = copyHStr(msStore, safeAccess([&]() { return id.Publisher(); }, winrt::hstring{}));
54+
e.product_code = copyHStr(msStore, safeAccess([&]() { return id.FamilyName(); }, winrt::hstring{}));
55+
56+
msStore->entriesVec.push_back(e);
57+
}
58+
59+
extern "C" __declspec(dllexport) BOOL GetStore(MSStore **out) {
60+
if (!out) {
61+
SetLastError(ERROR_INVALID_PARAMETER);
62+
return FALSE;
63+
}
64+
65+
try {
66+
auto msStore = std::make_unique<MSStoreInternal>();
67+
msStore->count = 0;
68+
msStore->entries = nullptr;
69+
70+
winrt::init_apartment();
71+
72+
PackageManager pm;
73+
74+
auto packages = pm.FindPackagesWithPackageTypes(PackageTypes::Main);
75+
76+
for (auto const &pkg : packages) {
77+
auto id = pkg.Id();
78+
auto displayName = safeAccess([&]() { return id.Name(); }, winrt::hstring{});
79+
auto appListEntries = pkg.GetAppListEntries();
80+
81+
if (appListEntries.Size() == 0) {
82+
addEntryToStore(msStore.get(), pkg, displayName);
83+
} else {
84+
for (auto const &appListEntry : appListEntries) {
85+
auto displayInfo = appListEntry.DisplayInfo();
86+
if (displayInfo) {
87+
auto dn = displayInfo.DisplayName();
88+
if (!dn.empty()) {
89+
displayName = dn;
90+
}
91+
}
92+
93+
addEntryToStore(msStore.get(), pkg, displayName);
94+
}
95+
}
96+
}
97+
98+
msStore->count = static_cast<int64_t>(msStore->entriesVec.size());
99+
if (!msStore->entriesVec.empty()) {
100+
msStore->entries = msStore->entriesVec.data();
101+
}
102+
103+
*out = msStore.release();
104+
SetLastError(ERROR_SUCCESS);
105+
return TRUE;
106+
} catch (...) {
107+
SetLastError(ERROR_UNHANDLED_EXCEPTION);
108+
return FALSE;
109+
}
110+
}
111+
112+
extern "C" __declspec(dllexport) BOOL FreeStore(MSStore *msStore) {
113+
if (!msStore) {
114+
SetLastError(ERROR_INVALID_PARAMETER);
115+
return FALSE;
116+
}
117+
delete static_cast<MSStoreInternal *>(msStore);
118+
SetLastError(ERROR_SUCCESS);
119+
return TRUE;
120+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
#include <stdint.h>
3+
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
7+
typedef struct MSStoreEntry {
8+
const wchar_t *display_name;
9+
10+
uint16_t version_major;
11+
uint16_t version_minor;
12+
uint16_t version_build;
13+
uint16_t version_revision;
14+
15+
int64_t install_date; // unix timestamp (seconds since epoch)
16+
uint64_t is_64bit; // uint64 to avoid padding
17+
18+
const wchar_t *publisher;
19+
const wchar_t *product_code;
20+
} MSStoreEntry;
21+
22+
typedef struct MSStore {
23+
int64_t count; // int64 to avoid padding
24+
MSStoreEntry *entries;
25+
} MSStore;
26+
27+
__declspec(dllexport) BOOL GetStore(MSStore **out);
28+
29+
__declspec(dllexport) BOOL FreeStore(MSStore *store);
30+
31+
#ifdef __cplusplus
32+
}
33+
struct MSStoreInternal : MSStore {
34+
std::vector<MSStoreEntry> entriesVec;
35+
std::vector<winrt::hstring> strings;
36+
};
37+
#endif

0 commit comments

Comments
 (0)