-
Notifications
You must be signed in to change notification settings - Fork 334
/
MddBootstrap.cpp
1063 lines (958 loc) · 48.3 KB
/
MddBootstrap.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
#include "pch.h"
#include "MddBootstrap.h"
#include "MddBootstrapTest.h"
#include "MsixDynamicDependency.h"
#include "IDynamicDependencyLifetimeManager.h"
#include <filesystem>
HRESULT _MddBootstrapInitialize(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept;
void VerifyInitializationIsCompatible(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion);
void FirstTimeInitialization(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion);
wil::unique_cotaskmem_ptr<BYTE[]> GetFrameworkPackageInfoForPackage(PCWSTR packageFullName, const PACKAGE_INFO*& frameworkPackageInfo);
DLL_DIRECTORY_COOKIE AddFrameworkToPath(PCWSTR path);
void RemoveFrameworkFromPath(PCWSTR frameworkPath);
bool IsLifetimeManagerViaEnumeration();
void CreateLifetimeManager(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
wil::com_ptr_nothrow<IDynamicDependencyLifetimeManager>& lifetimeManager,
wil::unique_event& endTheLifetimeManagerEvent,
wil::unique_cotaskmem_string& ddlmPackageFullName);
void CreateLifetimeManagerViaAppExtension(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
wil::com_ptr_nothrow<IDynamicDependencyLifetimeManager>& lifetimeManager,
wil::unique_cotaskmem_string& ddlmPackageFullName);
void CreateLifetimeManagerViaEnumeration(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
wil::unique_event& endTheLifetimeManagerEvent,
wil::unique_cotaskmem_string& ddlmPackageFullName);
CLSID FindDDLMViaAppExtension(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion);
void FindDDLMViaEnumeration(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
std::wstring& ddlmPackageFamilyName,
std::wstring& ddlmPackageFullName);
CLSID GetClsid(const winrt::Windows::ApplicationModel::AppExtensions::AppExtension& appExtension);
bool IsOptionEnabled(PCWSTR name);
HRESULT MddBootstrapInitialize_Log(
HRESULT hrInitialize,
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept;
HRESULT MddBootstrapInitialize_ShowUI_OnNoMatch(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion);
static std::mutex g_initializationLock;
static IDynamicDependencyLifetimeManager* g_lifetimeManager{};
static wil::unique_event g_endTheLifetimeManagerEvent;
static wil::unique_hmodule g_windowsAppRuntimeDll;
static wil::unique_process_heap_string g_packageDependencyId;
static MDD_PACKAGEDEPENDENCY_CONTEXT g_packageDependencyContext{};
static UINT32 g_initializationMajorMinorVersion{};
static std::wstring g_initializationVersionTag;
static PACKAGE_VERSION g_initializationFrameworkPackageVersion{};
static std::wstring g_test_ddlmPackageNamePrefix;
static std::wstring g_test_ddlmPackagePublisherId;
STDAPI MddBootstrapInitialize(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept
{
return MddBootstrapInitialize2(majorMinorVersion, versionTag, minVersion, MddBootstrapInitializeOptions_None);
}
STDAPI MddBootstrapInitialize2(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
MddBootstrapInitializeOptions options) noexcept try
{
PWSTR initializationFrameworkPackageFullName{};
auto initializationCount{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializeData(initializationFrameworkPackageFullName) };
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().SetMddBootstrapAPI(WindowsAppRuntime::MddBootstrap::Activity::MddBootstrapAPI::Initialize);
auto threadCallback = wil::ThreadFailureCallback(wilResultLoggingThreadCallback);
auto initializeActivity{
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializeActivity().Start(
majorMinorVersion,
versionTag,
minVersion,
static_cast<UINT32>(options),
initializationCount) };
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().SaveInitializeActivityId(*initializeActivity.Id());
// Dynamic Dependencies Bootstrap API requires an unpackaged process?
HRESULT hr{};
if (AppModel::Identity::IsPackagedProcess())
{
if (WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnPackageIdentity_NOOP))
{
// The process has package identity but that's OK. Do nothing
return S_OK;
}
hr = LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "MddBootstrapInitialize called in a process with package identity");
}
else
{
hr = _MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion);
}
if (FAILED(hr))
{
LOG_IF_FAILED(MddBootstrapInitialize_Log(hr, majorMinorVersion, versionTag, minVersion));
// NOTE: IsDebuggerPresent()=TRUE if running under a debugger context.
// IsDebuggerPresent()=FALSE if not running under a debugger context, even if AEDebug is set.
if (WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnError_DebugBreak) ||
(WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnError_DebugBreak_IfDebuggerAttached) && IsDebuggerPresent()) ||
IsOptionEnabled(L"MICROSOFT_WINDOWSAPPRUNTIME_BOOTSTRAP_INITIALIZE_DEBUGBREAK"))
{
DebugBreak();
}
if (hr == HRESULT_FROM_WIN32(ERROR_NO_MATCH))
{
if (WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnNoMatch_ShowUI) ||
IsOptionEnabled(L"MICROSOFT_WINDOWSAPPRUNTIME_BOOTSTRAP_INITIALIZE_SHOWUI"))
{
LOG_IF_FAILED(MddBootstrapInitialize_ShowUI_OnNoMatch(majorMinorVersion, versionTag, minVersion));
}
}
if (WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnError_FailFast) ||
IsOptionEnabled(L"MICROSOFT_WINDOWSAPPRUNTIME_BOOTSTRAP_INITIALIZE_FAILFAST"))
{
FAIL_FAST_HR_MSG(hr,
"Bootstrap initialize(0x%08X, '%ls', %hu.%hu.%hu.%hu)",
majorMinorVersion, (!versionTag ? L"" : versionTag),
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision);
}
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().StopActivityForWilReturnHR(true);
RETURN_HR(hr);
}
// Success!
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().IncrementInitializationCount();
if (WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializeActivity().IsRunning())
{
initializationCount = WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializeData(initializationFrameworkPackageFullName);
initializeActivity.StopWithResult(
hr,
static_cast<UINT32>(initializationCount),
static_cast<UINT32>(WindowsAppRuntime::MddBootstrap::Activity::Context::GetIntegrityFlags()),
initializationFrameworkPackageFullName,
static_cast <UINT32>(0),
static_cast<PCSTR>(nullptr),
static_cast <unsigned int>(0),
static_cast<PCWSTR>(nullptr),
static_cast<PCSTR>(nullptr));
}
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().SaveInitializeActivityId(GUID_NULL);
return S_OK;
}
CATCH_RETURN();
HRESULT _MddBootstrapInitialize(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept try
{
PWSTR initializationFrameworkPackageFullName{};
auto initializationCount{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializeData(initializationFrameworkPackageFullName) };
// Are we already initialized?
auto lock{ std::lock_guard(g_initializationLock) };
if (initializationCount > 0)
{
// Verify the request is compatible with our already initialized state
VerifyInitializationIsCompatible(majorMinorVersion, versionTag, minVersion);
}
else
{
// First to the key! Do the initialization
FirstTimeInitialization(majorMinorVersion, versionTag, minVersion);
}
return S_OK;
}
CATCH_RETURN();
STDAPI_(void) MddBootstrapShutdown() noexcept
{
PWSTR initializationFrameworkPackageFullName{};
auto initializationCount{ WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializeData(initializationFrameworkPackageFullName) };
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().SetMddBootstrapAPI(WindowsAppRuntime::MddBootstrap::Activity::MddBootstrapAPI::Shutdown);
auto threadCallback = wil::ThreadFailureCallback(wilResultLoggingThreadCallback);
auto shutdownActivity{
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetShutdownActivity().Start(
static_cast<UINT32>(initializationCount),
initializationFrameworkPackageFullName) };
auto lock{ std::lock_guard(g_initializationLock) };
if (!WindowsAppRuntime::MddBootstrap::Activity::Context::Get().DecrementInitializationCount())
{
// Last one out turn out the lights...
if (g_packageDependencyContext && g_windowsAppRuntimeDll)
{
MddRemovePackageDependency(g_packageDependencyContext);
g_packageDependencyContext = nullptr;
}
g_packageDependencyId.reset();
g_windowsAppRuntimeDll.reset();
if (g_endTheLifetimeManagerEvent)
{
g_endTheLifetimeManagerEvent.SetEvent();
g_endTheLifetimeManagerEvent.reset();
}
HRESULT hrLifetimeManagerShutdown = S_OK;
if (g_lifetimeManager)
{
hrLifetimeManagerShutdown = g_lifetimeManager->Shutdown();
(void)LOG_IF_FAILED(hrLifetimeManagerShutdown);
g_lifetimeManager->Release();
g_lifetimeManager = nullptr;
}
}
if (WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetShutdownActivity().IsRunning())
{
shutdownActivity.StopWithResult(
S_OK,
initializationCount - 1,
static_cast <UINT32>(0),
static_cast<PCSTR>(nullptr),
static_cast <unsigned int>(0),
static_cast<PCWSTR>(nullptr),
static_cast<PCSTR>(nullptr));
}
g_initializationMajorMinorVersion = {};
g_initializationVersionTag.clear();
g_initializationFrameworkPackageVersion = {};
WindowsAppRuntime::MddBootstrap::Activity::Context::Get().SaveShutdownActivityId(GUID_NULL);
}
STDAPI MddBootstrapTestInitialize(
_In_ PCWSTR ddlmPackageNamePrefix,
_In_ PCWSTR ddlPackagePublisherId) noexcept try
{
RETURN_HR_IF(E_INVALIDARG, !ddlmPackageNamePrefix);
RETURN_HR_IF(E_INVALIDARG, *ddlmPackageNamePrefix == L'0');
RETURN_HR_IF(E_INVALIDARG, !ddlPackagePublisherId);
RETURN_HR_IF(E_INVALIDARG, *ddlPackagePublisherId == L'0');
g_test_ddlmPackageNamePrefix = ddlmPackageNamePrefix;
g_test_ddlmPackagePublisherId = ddlPackagePublisherId;
return S_OK;
} CATCH_RETURN();
void VerifyInitializationIsCompatible(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion)
{
// Sanity check we're already initialized
// g_lifetimeManager is optional. Don't check it
// g_endTheLifetimeManagerEvent is optional. Don't check it
FAIL_FAST_HR_IF(E_UNEXPECTED, g_windowsAppRuntimeDll == nullptr);
FAIL_FAST_HR_IF(E_UNEXPECTED, g_packageDependencyId == nullptr);
FAIL_FAST_HR_IF(E_UNEXPECTED, g_packageDependencyContext == nullptr);
// Is the initialization request compatible with the current initialization state?
THROW_HR_IF_MSG(MDD_E_BOOTSTRAP_INITIALIZE_INCOMPATIBLE,
majorMinorVersion != g_initializationMajorMinorVersion,
"MddBootstrapInitialize(***0x%08X***, '%ls', %hu.%hu.%hu.%hu) not compatible with current initialization state (0x%X, '%ls', %hu.%hu.%hu.%hu)",
majorMinorVersion, (!versionTag ? L"" : versionTag),
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision,
g_initializationMajorMinorVersion, g_initializationVersionTag.c_str(),
g_initializationFrameworkPackageVersion.Major, g_initializationFrameworkPackageVersion.Minor,
g_initializationFrameworkPackageVersion.Build, g_initializationFrameworkPackageVersion.Revision);
THROW_HR_IF_MSG(MDD_E_BOOTSTRAP_INITIALIZE_INCOMPATIBLE,
CompareStringOrdinal((!versionTag ? L"" : versionTag), -1, g_initializationVersionTag.c_str(), -1, TRUE) != CSTR_EQUAL,
"MddBootstrapInitialize(0x%08X, ***'%ls'***, %hu.%hu.%hu.%hu) not compatible with current initialization state (0x%X, '%ls', %hu.%hu.%hu.%hu)",
majorMinorVersion, (!versionTag ? L"" : versionTag),
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision,
g_initializationMajorMinorVersion, g_initializationVersionTag.c_str(),
g_initializationFrameworkPackageVersion.Major, g_initializationFrameworkPackageVersion.Minor,
g_initializationFrameworkPackageVersion.Build, g_initializationFrameworkPackageVersion.Revision);
THROW_HR_IF_MSG(MDD_E_BOOTSTRAP_INITIALIZE_INCOMPATIBLE,
minVersion.Version > g_initializationFrameworkPackageVersion.Version,
"MddBootstrapInitialize(0x%08X, '%ls', ***%hu.%hu.%hu.%hu***) not compatible with current initialization state (0x%X, '%ls', %hu.%hu.%hu.%hu)",
majorMinorVersion, (!versionTag ? L"" : versionTag),
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision,
g_initializationMajorMinorVersion, g_initializationVersionTag.c_str(),
g_initializationFrameworkPackageVersion.Major, g_initializationFrameworkPackageVersion.Minor,
g_initializationFrameworkPackageVersion.Build, g_initializationFrameworkPackageVersion.Revision);
}
void FirstTimeInitialization(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion)
{
// Sanity check we're not already initialized
// g_lifetimeManager is optional. Don't check it
// g_endTheLifetimeManagerEvent is optional. Don't check it
FAIL_FAST_HR_IF(E_UNEXPECTED, g_windowsAppRuntimeDll != nullptr);
FAIL_FAST_HR_IF(E_UNEXPECTED, g_packageDependencyId != nullptr);
FAIL_FAST_HR_IF(E_UNEXPECTED, g_packageDependencyContext != nullptr);
// Make a copy of the versionTag in preparation of succcess
auto packageVersionTag{ std::wstring(!versionTag ? L"" : versionTag) };
// Create the lifetime manager
wil::com_ptr_nothrow<IDynamicDependencyLifetimeManager> lifetimeManager;
wil::unique_event endTheLifetimeManagerEvent;
CreateLifetimeManager(majorMinorVersion, versionTag, minVersion, lifetimeManager, endTheLifetimeManagerEvent, WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializationPackageFullName());
const PACKAGE_INFO* frameworkPackageInfo{};
auto packageInfoBuffer{ GetFrameworkPackageInfoForPackage(WindowsAppRuntime::MddBootstrap::Activity::Context::Get().GetInitializationPackageFullName().get(), frameworkPackageInfo) };
// Temporarily add the framework's package directory to PATH so LoadLibrary can find it and any colocated imports
wil::unique_dll_directory_cookie dllDirectoryCookie{ AddFrameworkToPath(frameworkPackageInfo->path) };
auto windowsAppRuntimeDllFilename{ std::wstring(frameworkPackageInfo->path) + L"\\Microsoft.WindowsAppRuntime.dll" };
wil::unique_hmodule windowsAppRuntimeDll(LoadLibraryEx(windowsAppRuntimeDllFilename.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH));
if (!windowsAppRuntimeDll)
{
const auto lastError{ GetLastError() };
THROW_WIN32_MSG(lastError, "Error in LoadLibrary: %d (0x%X) loading %ls", lastError, lastError, windowsAppRuntimeDllFilename.c_str());
}
// Add the framework package to the package graph
const MddPackageDependencyProcessorArchitectures architectureFilter{};
const auto lifetimeKind{ MddPackageDependencyLifetimeKind::Process };
const MddCreatePackageDependencyOptions createOptions{};
wil::unique_process_heap_string packageDependencyId;
THROW_IF_FAILED(MddTryCreatePackageDependency(nullptr, frameworkPackageInfo->packageFamilyName, minVersion, architectureFilter, lifetimeKind, nullptr, createOptions, &packageDependencyId));
//
const MddAddPackageDependencyOptions addOptions{};
MDD_PACKAGEDEPENDENCY_CONTEXT packageDependencyContext{};
THROW_IF_FAILED(MddAddPackageDependency(packageDependencyId.get(), MDD_PACKAGE_DEPENDENCY_RANK_DEFAULT, addOptions, &packageDependencyContext, nullptr));
// Remove our temporary path addition
RemoveFrameworkFromPath(frameworkPackageInfo->path);
dllDirectoryCookie.reset();
// Track our initialized state
g_lifetimeManager = lifetimeManager.detach();
g_endTheLifetimeManagerEvent = std::move(endTheLifetimeManagerEvent);
g_windowsAppRuntimeDll = std::move(windowsAppRuntimeDll);
g_packageDependencyId = std::move(packageDependencyId);
g_packageDependencyContext = packageDependencyContext;
//
g_initializationMajorMinorVersion = majorMinorVersion;
g_initializationVersionTag = std::move(packageVersionTag);
g_initializationFrameworkPackageVersion.Version = frameworkPackageInfo->packageId.version.Version;
}
/// Determine the path for the Windows App Runtime Framework package
wil::unique_cotaskmem_ptr<BYTE[]> GetFrameworkPackageInfoForPackage(PCWSTR packageFullName, const PACKAGE_INFO*& frameworkPackageInfo)
{
frameworkPackageInfo = nullptr;
// We need to determine the exact Windows App Runtime Framework package
// in the Dynamic Dependency Lifetime Manager package's dependencies,
// as resolved by Windows. A user can have multiple framework packages
// in a family registered at a time, for multiple reasons:
//
// * Multiple Architectures -- x86/x64 on an x64 machine, x86/arm/arm64/x86ona64 on an arm64 machine, etc
// * Multiple Versions -- v1.0.0.0 in use by processes running as pkg1 and v1.0.0.1 in use by processes running as pkg2
// or v1.0.0.0 in use by running processes and v1.0.0.1 in package graphs for packages w/no running process
//
// Thus FindPackagesByPackageFamily(pkgfamilyname,...) and PackageManager.FindPackages(user="", pkgfamilyname) could be ambiguous.
// We need the actual dependency graph known to Windows for the DDLM package where we got our LifetimeManager.
// That leaves us few options:
//
// * PackageManager.FindPackage(user="", lifetimeManager->GetPackageFullName()).Dependencies
// * GetPackageInfo(OpenPackageInfoByFullName(lifetimeManager->GetPackageFullName())
//
// We'll go with the latter as the simpler (no COM/WinRT) and more performant solution.
// Fetch the package graph for the package (per packageFullName)
wil::unique_package_info_reference packageInfoReference;
THROW_IF_WIN32_ERROR(OpenPackageInfoByFullName(packageFullName, 0, &packageInfoReference));
UINT32 bufferLength{};
UINT32 packageInfoCount{};
const auto hr{ HRESULT_FROM_WIN32(GetPackageInfo(packageInfoReference.get(), PACKAGE_FILTER_DIRECT, &bufferLength, nullptr, &packageInfoCount)) };
THROW_HR_IF(hr, hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
THROW_HR_IF(E_UNEXPECTED, packageInfoCount == 0);
auto buffer{ wil::make_unique_cotaskmem<BYTE[]>(bufferLength) };
THROW_IF_WIN32_ERROR(GetPackageInfo(packageInfoReference.get(), PACKAGE_FILTER_DIRECT, &bufferLength, buffer.get(), &packageInfoCount));
// Find the Windows App Runtime framework package in the package graph to determine its path
//
// NOTE: The Windows App Runtime DDLM package...
// * ...has 1 framework package dependency
// * ...its framework package dependency's name starts with "Microsoft.WindowsAppRuntime"
// * ...its publisher id is "8wekyb3d8bbwe"
// Any failure to find the DDLM's package graph but not find the expected framework dependency
// implies the DDLM is improperly built and cannot be used. Of course ThisShouldNeverHappen
// but a little paranoia isn't a bad thing :-)
//
// Verify the package providing the LifetimeManager declares a <PackageDependency> on the Windows App Runtime framework package.
THROW_HR_IF_MSG(E_UNEXPECTED, packageInfoCount != 1, "PRddlm:%ls PackageGraph.Count:%u", packageFullName, packageInfoCount);
//
const PACKAGE_INFO* packageInfo{ reinterpret_cast<const PACKAGE_INFO*>(buffer.get()) };
const WCHAR c_expectedNamePrefix[]{ L"Microsoft.WindowsAppRuntime" };
const int c_expectedNamePrefixLength{ ARRAYSIZE(c_expectedNamePrefix) - 1 };
THROW_HR_IF_MSG(E_UNEXPECTED, CompareStringOrdinal(packageInfo->packageId.name, c_expectedNamePrefixLength, c_expectedNamePrefix, c_expectedNamePrefixLength, TRUE) != CSTR_EQUAL,
"PRddlm:%ls Expected.Name:%ls PackageGraph[0].PackageFullName:%ls", packageFullName, c_expectedNamePrefix, packageInfo->packageFullName);
//
PCWSTR c_expectedPublisherId{ L"8wekyb3d8bbwe" };
THROW_HR_IF_MSG(E_UNEXPECTED, CompareStringOrdinal(packageInfo->packageId.publisherId, -1, c_expectedPublisherId, -1, TRUE) != CSTR_EQUAL,
"PRddlm:%ls PackageGraph[0].PackageFullName:%ls", packageFullName, packageInfo->packageFullName);
// Gotcha!
frameworkPackageInfo = packageInfo;
return buffer;
}
DLL_DIRECTORY_COOKIE AddFrameworkToPath(PCWSTR frameworkPath)
{
// Add the framework to the Loader's DllDirectory list
wil::unique_dll_directory_cookie dllDirectoryCookie{ AddDllDirectory(frameworkPath) };
THROW_LAST_ERROR_IF_NULL(dllDirectoryCookie);
// Add the framework the the PATH environment variable
wil::unique_cotaskmem_string path;
THROW_IF_FAILED(wil::GetEnvironmentVariableW(L"PATH", path));
if (path)
{
// PATH = frameworkPath + ";" + path
auto newPath{ wil::str_concat<wil::unique_cotaskmem_string>(frameworkPath, L";", path) };
THROW_IF_WIN32_BOOL_FALSE(SetEnvironmentVariableW(L"PATH", newPath.get()));
}
else
{
const auto lastError{ GetLastError() };
THROW_HR_IF(HRESULT_FROM_WIN32(lastError), lastError != ERROR_ENVVAR_NOT_FOUND);
THROW_IF_WIN32_BOOL_FALSE(SetEnvironmentVariableW(L"PATH", frameworkPath));
}
return dllDirectoryCookie.release();
}
void RemoveFrameworkFromPath(PCWSTR frameworkPath)
{
// Remove frameworkPath from PATH (previously added by AddFrameworkToPath())
// PATH should start with frameworkPath since we just prepended it. Remove it
wil::unique_cotaskmem_string path;
const auto hr{ wil::TryGetEnvironmentVariableW(L"PATH", path) };
if (SUCCEEDED(hr) && path)
{
const auto pathLength{ wcslen(path.get()) };
const auto frameworkPathLength{ wcslen(frameworkPath) };
if (pathLength >= frameworkPathLength)
{
if (CompareStringOrdinal(path.get(), static_cast<int>(frameworkPathLength), frameworkPath, static_cast<int>(frameworkPathLength), TRUE) == CSTR_EQUAL)
{
PCWSTR pathWithoutFrameworkPath{ path.get() + frameworkPathLength };
if (*pathWithoutFrameworkPath == L';')
{
++pathWithoutFrameworkPath;
}
(void)LOG_IF_WIN32_BOOL_FALSE(SetEnvironmentVariableW(L"PATH", pathWithoutFrameworkPath));
}
else
{
(void)LOG_HR_MSG(E_UNEXPECTED, "PATH doesn't start with %ls", frameworkPath);
}
}
}
}
bool IsLifetimeManagerViaEnumeration()
{
// AppExtension enumerates appextensions on <=19H1 only if the caller declares a matching AppExtensionHost.
// To workaround on older systems we'll fallback to a more complex but functionally equivalent solution.
if (!WindowsVersion::IsWindows10_20H1OrGreater())
{
// Windows version < 20H1. Enumeration is required
return true;
}
// Select the LifetimeManager implementation if the environment variable
// MICROSOFT_WINDOWSAPPRUNTIME_DDLM_ALGORITHM is defined (for testing scenarios)
// where:
// envvar=0 => Enumeration
// envvar=1 => AppExtension
{
WCHAR value[1 + 1]{};
if (GetEnvironmentVariableW(L"MICROSOFT_WINDOWSAPPRUNTIME_DDLM_ALGORITHM", value, ARRAYSIZE(value)) == 1)
{
if (*value == L'0')
{
return true;
}
else if (*value == L'1')
{
return false;
}
}
}
// Elevated processes MUST use Enumeration.
// MediumIL can go either way so we'll favor it too.
// AppContainer cannot use Enumeration (unless we have the packageQuery capability, which is uncommon).
if (!wil::get_token_is_app_container())
{
return true;
}
// Use the AppExtension-style LifetimeManager
return false;
}
void CreateLifetimeManager(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
wil::com_ptr_nothrow<IDynamicDependencyLifetimeManager>& lifetimeManager,
wil::unique_event& endTheLifetimeManagerEvent,
wil::unique_cotaskmem_string& ddlmPackageFullName)
{
if (IsLifetimeManagerViaEnumeration())
{
CreateLifetimeManagerViaEnumeration(majorMinorVersion, versionTag, minVersion, endTheLifetimeManagerEvent, ddlmPackageFullName);
}
else
{
CreateLifetimeManagerViaAppExtension(majorMinorVersion, versionTag, minVersion, lifetimeManager, ddlmPackageFullName);
}
}
void CreateLifetimeManagerViaAppExtension(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
wil::com_ptr_nothrow<IDynamicDependencyLifetimeManager>& lifetimeManager,
wil::unique_cotaskmem_string& ddlmPackageFullName)
{
const auto appDynamicDependencyLifetimeManagerClsid{ FindDDLMViaAppExtension(majorMinorVersion, versionTag, minVersion) };
wil::com_ptr_nothrow<IDynamicDependencyLifetimeManager> dynamicDependencyLifetimeManager{
wil::CoCreateInstance<IDynamicDependencyLifetimeManager>(appDynamicDependencyLifetimeManagerClsid, CLSCTX_LOCAL_SERVER)
};
THROW_IF_FAILED(dynamicDependencyLifetimeManager->Initialize());
wil::unique_cotaskmem_string packageFullName;
THROW_IF_FAILED(dynamicDependencyLifetimeManager->GetPackageFullName(&packageFullName));
lifetimeManager = std::move(dynamicDependencyLifetimeManager);
ddlmPackageFullName = std::move(packageFullName);
}
void CreateLifetimeManagerViaEnumeration(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
wil::unique_event& endTheLifetimeManagerEvent,
wil::unique_cotaskmem_string& ddlmPackageFullName)
{
std::wstring packageFamilyName;
std::wstring packageFullName;
FindDDLMViaEnumeration(majorMinorVersion, versionTag, minVersion, packageFamilyName, packageFullName);
// Create the named event used later to signal to the lifetime manager it's time to quit
// The named event has the syntax: "<processid>;<packagefullname>;<uniqueid>"
GUID uniqueId{};
THROW_IF_FAILED(CoCreateGuid(&uniqueId));
const auto c_uniqueIdAsString{ winrt::to_hstring(uniqueId) };
auto eventName{ wil::str_printf<wil::unique_cotaskmem_string>(L"%u;%s;%s", GetCurrentProcessId(), packageFullName.c_str(), c_uniqueIdAsString.c_str()) };
wil::unique_event event;
event.create(wil::EventOptions::ManualReset, eventName.get());
WCHAR lifetimeManagerApplicationUserModelId[APPLICATION_USER_MODEL_ID_MAX_LENGTH]{};
uint32_t lifetimeManagerApplicationUserModelIdLength{ ARRAYSIZE(lifetimeManagerApplicationUserModelId) };
PCWSTR c_packageRelativeApplicationId{ L"DDLM" };
THROW_IF_WIN32_ERROR(FormatApplicationUserModelId(packageFamilyName.c_str(), c_packageRelativeApplicationId, &lifetimeManagerApplicationUserModelIdLength, lifetimeManagerApplicationUserModelId));
wil::com_ptr_nothrow<IApplicationActivationManager> aam{
wil::CoCreateInstance<IApplicationActivationManager>(CLSID_ApplicationActivationManager, CLSCTX_INPROC_SERVER)
};
auto arguments{ eventName.get() };
ACTIVATEOPTIONS c_options{ AO_NOERRORUI | AO_NOSPLASHSCREEN };
DWORD processId{};
THROW_IF_FAILED(aam->ActivateApplication(lifetimeManagerApplicationUserModelId, arguments, c_options, &processId));
endTheLifetimeManagerEvent = std::move(event);
ddlmPackageFullName = wil::make_cotaskmem_string(packageFullName.c_str());
}
CLSID FindDDLMViaAppExtension(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion)
{
// Find the best fit
bool foundAny{};
PACKAGE_VERSION bestFitVersion{};
CLSID bestFitClsid{};
// Look for windows.appExtension with name="microsoft.winappruntime.ddlm-<majorversion>.<minorversion>-<shortarchitecture>[-shorttag]"
// NOTE: <majorversion>.<minorversion> MUST have a string length <= 8 characters ("12.34567", "12345.67", etc) to fit within
// the maximum allowed length of a windows.appExtension's Name (39 chars) on Windows versions <= RS5 (10.0.17763.0).
WCHAR appExtensionName[100]{};
const UINT16 majorVersion{ HIWORD(majorMinorVersion) };
const UINT16 minorVersion{ LOWORD(majorMinorVersion) };
const auto versionShortTag{ AppModel::Identity::GetVersionShortTagFromVersionTag(versionTag) };
if (!versionShortTag.empty())
{
wsprintf(appExtensionName, L"microsoft.winappruntime.ddlm-%hu.%hu-%s-%s", majorVersion, minorVersion, AppModel::Identity::GetCurrentArchitectureAsShortString(), versionShortTag.c_str());
}
else
{
wsprintf(appExtensionName, L"microsoft.winappruntime.ddlm-%hu.%hu-%s", majorVersion, minorVersion, AppModel::Identity::GetCurrentArchitectureAsShortString());
}
auto catalog{ winrt::Windows::ApplicationModel::AppExtensions::AppExtensionCatalog::Open(appExtensionName) };
auto appExtensions{ catalog.FindAllAsync().get() };
for (auto appExtension : appExtensions)
{
// Check the package identity against the package identity test qualifiers (if any)
if (!g_test_ddlmPackageNamePrefix.empty())
{
const auto packageId{ appExtension.Package().Id() };
std::wstring name{ packageId.Name().c_str() };
if ((name.rfind(g_test_ddlmPackageNamePrefix.c_str(), 0) != 0) ||
(CompareStringOrdinal(packageId.PublisherId().c_str(), -1, g_test_ddlmPackagePublisherId.c_str(), -1, TRUE) != CSTR_EQUAL))
{
// The package's Name prefix or PublisherId don't match the expected value. Skip it
continue;
}
}
// appExtension.Id == "ddlm-<major.minor.build.revision>-<architecture>"
const auto id{ appExtension.Id() };
PACKAGE_VERSION version{};
WCHAR architectureAsString[9 + 1]{};
const auto maxIdLength{ ARRAYSIZE(L"ddlm-12345.12345.12345.12345-abcdefghi") - 1 }; // -1 for length not counting null-terminator
if ((id.size() >= maxIdLength) ||
(swscanf_s(id.c_str(), L"ddlm-%hu.%hu.%hu.%hu-%9s", &version.Major, &version.Minor, &version.Build, &version.Revision, architectureAsString, static_cast<unsigned>(ARRAYSIZE(architectureAsString))) != 5))
{
(void)LOG_WIN32_MSG(ERROR_INVALID_DATA, "%ls", id.c_str());
continue;
}
// Does the version meet the minVersion criteria?
if (version.Version < minVersion.Version)
{
continue;
}
// Does the architecture match?
const auto architecture{ AppModel::Identity::ParseArchitecture(architectureAsString) };
if (architecture != AppModel::Identity::GetCurrentArchitecture())
{
continue;
}
// Do we have a package under consideration?
if (!foundAny)
{
bestFitVersion = version;
bestFitClsid = GetClsid(appExtension);
foundAny = true;
continue;
}
// Do we already have a higher version under consideration?
if (bestFitVersion.Version < version.Version)
{
bestFitVersion = version;
bestFitClsid = GetClsid(appExtension);
continue;
}
}
THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_NO_MATCH), !foundAny, "AppExtension.Name=%ls, Major=%hu, Minor=%hu, Tag=%ls, MinVersion=%hu.%hu.%hu.%hu",
appExtensionName, majorVersion, minorVersion, (!versionTag ? L"" : versionTag),
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision);
return bestFitClsid;
}
void FindDDLMViaEnumeration(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
std::wstring& ddlmPackageFamilyName,
std::wstring& ddlmPackageFullName)
{
// Find the best fit
bool foundAny{};
PACKAGE_VERSION bestFitVersion{};
winrt::hstring bestFitPackageFamilyName{};
winrt::hstring bestFitPackageFullName{};
// We need to look for DDLM packages in the package family for release <major>.<minor> and <versiontag>
// But we have no single (simple) enumeration to match that so our logic's more involved compared
// to FindDDLMViaAppExtension():
// 1. Look for Framework packages with Name="microsoft.winappruntime.ddlm.<minorversion>*[-shorttag]"
// 1a. Enumerate all Framework packages registered to the user
// 1b. Only consider packages whose Name starts with "microsoft.winappruntime.ddlm.<minorversion>."
// 1c. If versiontag is specified, Only consider packages whose Name ends with [-shorttag]
// 1d. Only consider packages whose PublisherID = "8wekyb3d8bbwe"
// 2. Check if the package is in the <majorversion>.<minorversion> release
// 2a. Check if the package's Description starts with "Microsoft Windows App Runtime DDLM <majorversion>.<minorversion> "
// 3. Check if the architecture matches
// 4. Check if the package meets the specified minVersion
const UINT16 majorVersion{ HIWORD(majorMinorVersion) };
const UINT16 minorVersion{ LOWORD(majorMinorVersion) };
PCWSTR packageNamePrefix{};
if (!g_test_ddlmPackageNamePrefix.empty())
{
packageNamePrefix = g_test_ddlmPackageNamePrefix.c_str();
}
else
{
packageNamePrefix = L"microsoft.winappruntime.ddlm.";
}
const auto packageNamePrefixLength{ wcslen(packageNamePrefix) };
WCHAR packageNameSuffix[10]{};
size_t packageNameSuffixLength{};
const auto versionShortTag{ AppModel::Identity::GetVersionShortTagFromVersionTag(versionTag) };
if (!versionShortTag.empty())
{
packageNameSuffix[0] = L'-';
FAIL_FAST_IF_FAILED(StringCchCopyW(packageNameSuffix + 1, ARRAYSIZE(packageNameSuffix) - 1, versionShortTag.c_str()));
packageNameSuffixLength = wcslen(packageNameSuffix);
}
PCWSTR expectedPublisherId{ L"8wekyb3d8bbwe" };
if (!g_test_ddlmPackagePublisherId.empty())
{
expectedPublisherId = g_test_ddlmPackagePublisherId.c_str();
}
auto criteria{ wil::str_printf<wil::unique_cotaskmem_string>(L"Major.Minor=%hu.%hu, Tag=%ls, MinVersion=%hu.%hu.%hu.%hu",
majorVersion, minorVersion, (!versionTag ? L"" : versionTag),
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision) };
winrt::Windows::Management::Deployment::PackageManager packageManager;
winrt::hstring currentUser;
const auto c_packageTypes{ winrt::Windows::Management::Deployment::PackageTypes::Main };
auto packages{ packageManager.FindPackagesForUserWithPackageTypes(currentUser, c_packageTypes) };
(void)LOG_HR_MSG(MDD_E_BOOTSTRAP_INITIALIZE_SCAN_FOR_DDLM, "Bootstrap.Intitialize: Scanning packages for %ls", criteria.get());
int packagesScanned{};
for (auto package : packages)
{
++packagesScanned;
// Check the package identity against the package identity test qualifiers (if any)
const auto packageId{ package.Id() };
const auto packageName{ packageId.Name() };
const auto packageNameLength{ packageName.size() };
if (packageNameLength < packageNamePrefixLength + packageNameSuffixLength)
{
// The package's Name can't match the expected prefix and/or suffix. Skip it
continue;
}
if (CompareStringOrdinal(packageName.c_str(), static_cast<int>(packageNamePrefixLength), packageNamePrefix, static_cast<int>(packageNamePrefixLength), TRUE) != CSTR_EQUAL)
{
// The package's Name prefix doesn't match the expected value. Skip it
continue;
}
if (packageNameSuffixLength > 0)
{
const auto offsetToSuffix{ packageNameLength - packageNameSuffixLength };
if (CompareStringOrdinal(packageName.c_str() + offsetToSuffix, static_cast<int>(packageNameSuffixLength), packageNameSuffix, static_cast<int>(packageNameSuffixLength), TRUE) != CSTR_EQUAL)
{
// The package's Name suffix doesn't match the expected value. Skip it
continue;
}
}
if (CompareStringOrdinal(packageId.PublisherId().c_str(), -1, expectedPublisherId, -1, TRUE) != CSTR_EQUAL)
{
// The package's PublisherId doesn't match the expected value. Skip it
continue;
}
// Is this DDLM in the major.minor release?
//
// NOTE: Package.InstalledLocation.Path can be expensive as it has to create
// a StorageFolder just to get the path as a string. We'd like to use
// Package.EffectivePath but that didn't exist until 20H1 and we need
// to work down to RS5. So instead we'll use GetPackagePathByFullName()
// as that exists since Win81 (and can be significantly faster than
// Package.InstalledLocation).
const auto packageFullName{ packageId.FullName() };
wil::unique_cotaskmem_string packagePathBufferDynamic;
uint32_t packagePathLength{};
const auto rc{ GetPackagePathByFullName(packageFullName.c_str(), &packagePathLength, nullptr) };
if (rc != ERROR_INSUFFICIENT_BUFFER)
{
THROW_HR_MSG(HRESULT_FROM_WIN32(rc), "Enumeration: %ls", packageFullName.c_str());
}
auto packagePath{ wil::make_cotaskmem_string_nothrow(nullptr, packagePathLength) };
THROW_IF_WIN32_ERROR(GetPackagePathByFullName(packageFullName.c_str(), &packagePathLength, packagePath.get()));
auto fileSpec{ std::filesystem::path(packagePath.get()) };
fileSpec /= L"Microsoft.WindowsAppRuntime.Release!*";
//
WIN32_FIND_DATA findFileData{};
wil::unique_hfind hfind{ FindFirstFile(fileSpec.c_str(), &findFileData) };
if (!hfind)
{
// The package's release version couldn't be determined. Skip it
(void)LOG_LAST_ERROR_MSG("Enumeration: FindFirst(%ls)", fileSpec.c_str());
continue;
}
uint16_t releaseMajorVersion{};
uint16_t releaseMinorVersion{};
if (swscanf_s(findFileData.cFileName, L"Microsoft.WindowsAppRuntime.Release!%hu.%hu", &releaseMajorVersion, &releaseMinorVersion) != 2)
{
// These aren't the droids you're looking for...
(void)LOG_LAST_ERROR_MSG("Enumeration: FindFirst(%ls) found %ls", fileSpec.c_str(), findFileData.cFileName);
continue;
}
if ((releaseMajorVersion != majorVersion) || (releaseMinorVersion != minorVersion))
{
// The package's major or minor release version doesn't match the expected value. Skip it
continue;
}
// Does the version meet the minVersion criteria?
auto packageVersion{ packageId.Version() };
PACKAGE_VERSION version{};
version.Major = packageVersion.Major;
version.Minor = packageVersion.Minor;
version.Build = packageVersion.Build;
version.Revision = packageVersion.Revision;
if (version.Version < minVersion.Version)
{
(void)LOG_HR_MSG(MDD_E_BOOTSTRAP_INITIALIZE_DDLM_SCAN_NO_MATCH,
"Bootstrap.Intitialize: %ls not applicable. Version doesn't match MinVersion criteria (%ls)",
packageFullName.c_str(), criteria.get());
continue;
}
// Does the architecture match?
const auto architecture{ packageId.Architecture() };
const auto currentArchitecture{ AppModel::Identity::GetCurrentArchitecture() };
if (architecture != currentArchitecture)
{
(void)LOG_HR_MSG(MDD_E_BOOTSTRAP_INITIALIZE_DDLM_SCAN_NO_MATCH,
"Bootstrap.Intitialize: %ls not applicable. Architecture doesn't match current architecture %ls (%ls)",
packageFullName.c_str(), ::AppModel::Identity::GetCurrentArchitectureAsString(), criteria.get());
continue;
}
// Do we have a package under consideration?
if (!foundAny)
{
(void)LOG_HR_MSG(MDD_E_BOOTSTRAP_INITIALIZE_DDLM_SCAN_MATCH,
"Bootstrap.Intitialize: %ls is applicable (%ls)",
packageFullName.c_str(), criteria.get());
bestFitVersion = version;
bestFitPackageFamilyName = packageId.FamilyName();
bestFitPackageFullName = packageId.FullName();
foundAny = true;
continue;
}
// Do we already have a higher version under consideration?
if (bestFitVersion.Version < version.Version)
{
(void)LOG_HR_MSG(MDD_E_BOOTSTRAP_INITIALIZE_DDLM_SCAN_MATCH,
"Bootstrap.Intitialize: %ls is more applicable (%ls)",
packageFullName.c_str(), criteria.get());
bestFitVersion = version;
bestFitPackageFamilyName = packageId.FamilyName();
bestFitPackageFullName = packageId.FullName();
continue;
}
}
THROW_HR_IF_MSG(HRESULT_FROM_WIN32(ERROR_NO_MATCH), !foundAny, "Enumeration: %ls", criteria.get());
(void)LOG_HR_MSG(MDD_E_BOOTSTRAP_INITIALIZE_DDLM_FOUND,
"Bootstrap.Intitialize: %ls best matches the criteria (%ls) of %d packages scanned",
bestFitPackageFullName.c_str(), criteria.get(), packagesScanned);
ddlmPackageFamilyName = bestFitPackageFamilyName.c_str();
ddlmPackageFullName = bestFitPackageFullName.c_str();
}
CLSID GetClsid(const winrt::Windows::ApplicationModel::AppExtensions::AppExtension& appExtension)
{
const auto properties{ appExtension.GetExtensionPropertiesAsync().get() };
auto propertiesClsid{ properties.Lookup(L"CLSID").as<winrt::Windows::Foundation::Collections::IPropertySet>() };
auto value{ propertiesClsid.Lookup(L"#text").as<winrt::Windows::Foundation::IPropertyValue>() };
THROW_HR_IF_NULL(E_UNEXPECTED, value);
THROW_HR_IF(E_UNEXPECTED, value.Type() != winrt::Windows::Foundation::PropertyType::String);
const auto text{ value.GetString() };
// Convert the CLSID as a string to a CLSID as a GUID
// Problem: CLSIDFromString() also does a lookup for a registered object by the CLSID.
// We just want the string->GUID conversion, not any additional work.
// Workaround this by using UuidFromString()
// Problem: UuidFromString() takes a RPC_WSTR but that's defined as unsigned short*
// unless RPC_USE_NATIVE_WCHAR is defined.
// Workaround this with casts. Include some asserts to verify we're not misusing memory.
auto textString{ const_cast<PWSTR>(text.c_str()) };
auto textRpcString{ reinterpret_cast<RPC_WSTR>(textString) };
static_assert(sizeof(textString) == sizeof(textRpcString));
static_assert(sizeof(textString[0]) == sizeof(textRpcString[0]));
UUID clsid{};
THROW_IF_WIN32_ERROR(UuidFromStringW(textRpcString, &clsid));
return clsid;
}
bool IsOptionEnabled(PCWSTR name)
{
WCHAR value[1 + 1]{};
if (GetEnvironmentVariableW(name, value, ARRAYSIZE(value)) == 1)
{
if (*value == L'0')
{
return false;
}
else if (*value == L'1')
{
return true;
}
}
return false;
}
HRESULT MddBootstrapInitialize_Log(
HRESULT hrInitialize,
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept try
{
HANDLE hEventLog{ RegisterEventSourceW(nullptr, L"Windows App Runtime") };
RETURN_LAST_ERROR_IF_NULL(hEventLog);
const DWORD c_eventId{ static_cast<DWORD>(hrInitialize) };
PCWSTR message1{ L"Windows App Runtime" };
WCHAR message2[1024]{};
PCWSTR message2Format{ L"ERROR 0x%08X: Bootstrapper initialization failed while looking for version %hu.%hu%s (MSIX package version >= %hu.%hu.%hu.%hu)" };
const UINT16 majorVersion{ HIWORD(majorMinorVersion) };
const UINT16 minorVersion{ LOWORD(majorMinorVersion) };
WCHAR formattedVersionTag[64]{};
if (versionTag && (versionTag[0] != L'\0'))
{
FAIL_FAST_IF_FAILED(StringCchPrintfW(formattedVersionTag, ARRAYSIZE(formattedVersionTag), L"-%s", versionTag));
}
FAIL_FAST_IF_FAILED(StringCchPrintfW(message2, ARRAYSIZE(message2), message2Format,
hrInitialize, majorVersion, minorVersion, formattedVersionTag,
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision));
PCWSTR strings[2]{ message1, message2 };
LOG_IF_WIN32_BOOL_FALSE(ReportEventW(hEventLog, EVENTLOG_ERROR_TYPE, 0, c_eventId, nullptr, ARRAYSIZE(strings), 0, strings, nullptr));
DeregisterEventSource(hEventLog);
return S_OK;
}
CATCH_RETURN()
HRESULT MddBootstrapInitialize_ShowUI_OnNoMatch(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion)
{
// Get the message caption
PCWSTR caption{};
wil::unique_cotaskmem_string captionString;
WCHAR captionOnError[100]{};
try