Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rich information on Bootstrap initalization failure #2316

Merged
merged 19 commits into from
Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e9fc7df
On error in MddBootstrapIntialize log to EventLog and optionally Debu…
DrusTheAxe Mar 25, 2022
16fff4c
Added ShowUI to C++ auto-initializer.
DrusTheAxe Mar 25, 2022
261c043
Remove hardcoded language tag in URL
DrusTheAxe Mar 25, 2022
aa31e2d
Incorporated feedback
DrusTheAxe Mar 28, 2022
08fddd2
Update C++ AutoInitializer to use MddBootstrapInitialize2 with option…
DrusTheAxe Mar 29, 2022
268aef4
Add MddBootstrapInitializeOptions_OnPackageIdentity_NOP with tests
DrusTheAxe Mar 29, 2022
57ede61
Merge branch 'user/drustheaxe/moar-bootstrap' of https://github.com/m…
DrusTheAxe Mar 29, 2022
38f8471
Moar tests. Fixed NOP/NOOP typo. Added C# support for InitializeOptio…
DrusTheAxe Mar 29, 2022
98eb127
Let's try that. C# AutoInitializer options override
DrusTheAxe Mar 29, 2022
ad1579d
Added build option to control auto-initializer (WindowsAppSDKBootstra…
DrusTheAxe Mar 30, 2022
3dd6707
Minnor reformatting and cleanup
DrusTheAxe Mar 30, 2022
dc1542e
Changed to use TryInitialize and Exit (instead of throwing Initialize…
DrusTheAxe Mar 30, 2022
e6f64ff
Merge branch 'user/drustheaxe/moar-bootstrap' of https://github.com/m…
DrusTheAxe Mar 30, 2022
285e3cd
Incorporated feedbacck
DrusTheAxe Mar 31, 2022
a383b4a
Merge branch 'user/drustheaxe/moar-bootstrap' of https://github.com/m…
DrusTheAxe Mar 31, 2022
53f3eb9
Incorproated feedback. Removed some stale comments
DrusTheAxe Mar 31, 2022
fa7f5f9
Removed unnecessary extra checks for WindowsAppSDKBootstrapInitialize
DrusTheAxe Mar 31, 2022
91804c1
Renamed Microsoft.WindowsAppSDK.MddCommon.targets to Microsoft.Window…
DrusTheAxe Mar 31, 2022
117a462
Updated reference to MddCommon
DrusTheAxe Apr 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 191 additions & 4 deletions dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

#include <filesystem>

HRESULT _MddBootstrapInitialize(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept;
void VerifyInitializationIsCompatible(
UINT32 majorMinorVersion,
PCWSTR versionTag,
Expand Down Expand Up @@ -54,6 +58,16 @@ void FindDDLMViaEnumeration(
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(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion);

static std::mutex g_initializationLock;
static std::atomic<uint32_t> g_initializationCount{};
Expand All @@ -74,11 +88,61 @@ static std::wstring g_test_ddlmPackagePublisherId;
STDAPI MddBootstrapInitialize(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept try
PACKAGE_VERSION minVersion) noexcept
{
return MddBootstrapInitialize2(majorMinorVersion, versionTag, minVersion, MddBootstrapInitializeOptions_None);
}

STDAPI MddBootstrapInitialize2(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
MddBootstrapInitializeOptions options) noexcept try
{
// Dynamic Dependencies Bootstrap API requires a non-packaged process
LOG_HR_IF(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), AppModel::Identity::IsPackagedProcess());

// Are we already initialized?
const auto hr{ _MddBootstrapInitialize(majorMinorVersion, versionTag, minVersion) };
if (FAILED(hr))
{
LOG_IF_FAILED(MddBootstrapInitialize_Log(hr, majorMinorVersion, versionTag, minVersion));

if (WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnError_DebugBreak) ||
(WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnError_DebugBreak_IfDebuggerAttached) && IsDebuggerPresent()) ||
IsOptionEnabled(L"MICROSOFT_WINDOWSAPPRUNTIME_BOOTSTRAP_INITIALIZE_DEBUGBREAK"))
{
DebugBreak();
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
}

if (WI_IsFlagSet(options, MddBootstrapInitializeOptions_OnNoMatch_ShowUI) ||
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
IsOptionEnabled(L"MICROSOFT_WINDOWSAPPRUNTIME_BOOTSTRAP_INITIALIZE_SHOWUI"))
{
LOG_IF_FAILED(MddBootstrapInitialize_ShowUI(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);
}
RETURN_HR(hr);
}

// Success!
++g_initializationCount;
return S_OK;
}
CATCH_RETURN();

HRESULT _MddBootstrapInitialize(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept try
{
// Are we already initialized?
auto lock{ std::lock_guard(g_initializationLock) };
if (g_initializationCount > 0)
Expand All @@ -91,9 +155,6 @@ STDAPI MddBootstrapInitialize(
// First to the key! Do the initialization
FirstTimeInitialization(majorMinorVersion, versionTag, minVersion);
}

// Success!
++g_initializationCount;
return S_OK;
}
CATCH_RETURN();
Expand Down Expand Up @@ -815,3 +876,129 @@ CLSID GetClsid(const winrt::Windows::ApplicationModel::AppExtensions::AppExtensi
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
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
{
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 packages version >= %hu.%hu.%hu.%hu)" };
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
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(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion)
{
//TODO: Show this if ERROR_NO_MATCH vs simpler MessageBox("ERROR 0xN in Bootstrapper initialize", MB_OK) ?
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved

// Get the message caption
PCWSTR caption{};
wil::unique_cotaskmem_string captionString;
WCHAR captionOnError[100]{};
try
{
PCWSTR executable{};
wil::unique_cotaskmem_string module;
auto hr{ LOG_IF_FAILED(wil::GetModuleFileNameW(nullptr, module)) };
if (SUCCEEDED(hr))
{
auto delimiter{ wcsrchr(module.get(), L'\\') };
if (delimiter)
{
executable = delimiter + 1;
}
else
{
executable = module.get();
}
PCWSTR captionSuffix{ L"This application could not be started" };
captionString = wil::str_printf<wil::unique_cotaskmem_string>(L"%s - %s", executable, captionSuffix);
caption = captionString.get();
}
}
catch (...)
{
}
if (!caption)
{
LOG_IF_FAILED(StringCchPrintfW(captionOnError, ARRAYSIZE(captionOnError),
L"<Process %d> - This application could not be started",
GetCurrentProcessId()));
caption = captionOnError;
}

// Get the message body
WCHAR text[1024]{};
PCWSTR textFormat{ L"This application requires the Windows App Runtime\n"
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
L" Version %hu.%hu%s\n"
L" (MSIX packages version >= %hu.%hu.%hu.%hu)\n"
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
L"\n"
L"Do you want to install a compatible Windows App Runtime now?"
};
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(text, ARRAYSIZE(text), textFormat,
majorVersion, minorVersion, formattedVersionTag,
minVersion.Major, minVersion.Minor, minVersion.Build, minVersion.Revision));

// Show the prompt
const auto yesno{ MessageBoxW(nullptr, text, caption, MB_YESNO | MB_ICONERROR) };
if (yesno == IDYES)
{
SHELLEXECUTEINFOW sei{};
sei.cbSize = sizeof(sei);
sei.lpVerb = L"open";
sei.lpFile = L"https://docs.microsoft.com/en-us/windows/apps/windows-app-sdk/downloads";
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
//TODO:Replace with https://aka.ms/windowsappsdk/<major>.<minor>/latest/windowsappruntimeinstall-<architecture>.exe
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
sei.nShow = SW_SHOWNORMAL;
LOG_IF_WIN32_BOOL_FALSE(ShellExecuteExW(&sei));
}
return S_OK;
}
42 changes: 42 additions & 0 deletions dev/WindowsAppRuntime_BootstrapDLL/MddBootstrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,48 @@ STDAPI MddBootstrapInitialize(
PCWSTR versionTag,
PACKAGE_VERSION minVersion) noexcept;

/// Options for MddBootstrapInitialize()
typedef enum MddBootstrapInitializeOptions
DrusTheAxe marked this conversation as resolved.
Show resolved Hide resolved
{
/// Default behavior
MddBootstrapInitializeOptions_None = 0,

/// If not successful call DebugBreak()
MddBootstrapInitializeOptions_OnError_DebugBreak = 0x0001,

/// If not successful call DebugBreak() if a debugger is attached to the process
MddBootstrapInitializeOptions_OnError_DebugBreak_IfDebuggerAttached = 0x0002,

/// If not successful perform a fail-fast
MddBootstrapInitializeOptions_OnError_FailFast = 0x0002,

/// If a compatible Windows App Runtime framework package is not found show UI
MddBootstrapInitializeOptions_OnNoMatch_ShowUI = 0x0004,
} MddBootstrapInitializeOptions;
#if defined(__cplusplus)
DEFINE_ENUM_FLAG_OPERATORS(MddBootstrapInitializeOptions)
#endif // defined(__cplusplus)

/// Initialize the calling process to use Windows App Runtime framework package.
///
/// Find a Windows App Runtime framework package meeting the criteria and make it available
/// for use by the current process. If multiple packages meet the criteria the best
/// candidate is selected.
///
/// If called multiple times the parameters must be compatible with the framework package
/// resolved by the first initialization call (i.e. the framework package currently in use).
/// If the request is not compatible with the framework package currently in use
/// the API fails and an error is returned.
///
/// @param majorMinorVersion the major and minor version to use, e..g 0x00010002 for Major.Minor=1.2
/// @param versionTag the version pre-release identifier, or NULL if none.
/// @param minVersion the minimum version to use
STDAPI MddBootstrapInitialize2(
UINT32 majorMinorVersion,
PCWSTR versionTag,
PACKAGE_VERSION minVersion,
MddBootstrapInitializeOptions options) noexcept;

/// Undo the changes made by MddBoostrapInitialize().
///
/// @warning Packages made available via MddBootstrapInitialize() and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ namespace Microsoft::Windows::ApplicationModel::DynamicDependency::Bootstrap
const UINT32 c_majorMinorVersion{ WINDOWSAPPSDK_RELEASE_MAJORMINOR };
PCWSTR c_versionTag{ WINDOWSAPPSDK_RELEASE_VERSION_TAG_W };
const PACKAGE_VERSION c_minVersion{ WINDOWSAPPSDK_RUNTIME_VERSION_UINT64 };
const HRESULT hr{ ::MddBootstrapInitialize(c_majorMinorVersion, c_versionTag, c_minVersion) };
const auto c_options{ MddBootstrapInitializeOptions_OnNoMatch_ShowUI };
const HRESULT hr{ ::MddBootstrapInitialize2(c_majorMinorVersion, c_versionTag, c_minVersion, c_options) };
if (FAILED(hr))
{
exit(hr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@

EXPORTS
MddBootstrapInitialize
MddBootstrapInitialize2
MddBootstrapShutdown
MddBootstrapTestInitialize