Include AppX event IDs in Windows runtime registration errors (#61224)

## What changed

Augment managed runtime package registration failures with `appx_event_ids` from error-level events in `Microsoft-Windows-AppXDeploymentServer/Operational` that match the deployment activity ID. Report numeric event IDs in sorted, deduplicated order without including free-form event text.

Skip the lookup when the activity ID is unavailable or zero, and leave the registration result unchanged when event diagnostics are unavailable.

GitOrigin-RevId: 50167f3ffed1d07936594ef4b04f5adf2972be8e
This commit is contained in:
zm-oai
2026-11-13 01:31:24 +00:00
committed by copyberry
parent e73744b3f4
commit a46f05e2e3
3 changed files with 70 additions and 2 deletions
@@ -37,6 +37,8 @@ features = [
"Foundation_Collections",
"Management_Deployment",
"Storage",
"Win32_Security",
"Win32_System_EventLog",
"Win32_System_WinRT",
]
@@ -2,6 +2,8 @@
//! Diagnostic getters never replace the completion error or turn success into failure.
//! Free-form Windows ErrorText stays in Windows deployment diagnostics, not this summary.
mod appx_events;
use anyhow::Context;
use anyhow::Result;
use windows::Foundation::AsyncStatus;
@@ -57,7 +59,7 @@ impl RegistrationResult {
let summary = format!("{context} phase=GetResults get_results={:?}", error.code());
anyhow::Error::new(error).context(summary)
})?;
let activity_id = match details.activity_id {
let activity_id = match &details.activity_id {
Ok(id) => format!("{id:?}"),
Err(error) => format!("unavailable({:?})", error.code()),
};
@@ -82,7 +84,14 @@ impl RegistrationResult {
return Ok(());
}
extended_error.ok().with_context(|| {
format!("{context} phase=deployment extended_error={extended_error:?}")
let mut summary =
format!("{context} phase=deployment extended_error={extended_error:?}");
if let Ok(activity) = details.activity_id
&& let Some(ids) = appx_events::failure_event_ids(activity)
{
summary.push_str(&format!(" appx_event_ids={ids}"));
}
summary
})
}
}
@@ -0,0 +1,57 @@
//! Reads numeric error event IDs for one deployment activity.
use std::collections::BTreeSet;
use std::mem::size_of_val;
use windows::Win32::System::EventLog as evt;
use windows::core::GUID;
use windows::core::HSTRING;
use windows::core::Owned;
use windows::core::w;
pub(super) fn failure_event_ids(activity: GUID) -> Option<String> {
if activity == GUID::zeroed() {
return None;
}
let filter = HSTRING::from(format!(
"*[System[Correlation[@ActivityID='{{{activity:?}}}'] and Level=2]]"
));
let channel = w!("Microsoft-Windows-AppXDeploymentServer/Operational");
// Each successful call transfers a handle to us. Owned closes it on every exit.
unsafe {
let query =
Owned::new(evt::EvtQuery(None, channel, &filter, evt::EvtQueryChannelPath.0).ok()?);
let fields = [w!("Event/System/EventID")];
let context = Owned::new(
evt::EvtCreateRenderContext(Some(&fields), evt::EvtRenderContextValues.0).ok()?,
);
let mut ids = BTreeSet::new();
let mut events = [0];
let mut returned = 0;
while evt::EvtNext(*query, &mut events, 0, 0, &mut returned).is_ok() && returned == 1 {
let event = Owned::new(evt::EVT_HANDLE(events[0]));
let mut value = evt::EVT_VARIANT::default();
let mut bytes_used = 0;
let mut properties = 0;
if evt::EvtRender(
*context,
*event,
evt::EvtRenderEventValues.0,
size_of_val(&value) as u32,
Some((&raw mut value).cast()),
&mut bytes_used,
&mut properties,
)
.is_ok()
&& properties == 1
&& value.Type == evt::EvtVarTypeUInt16.0 as u32
{
ids.insert(value.Anonymous.UInt16Val);
}
}
(!ids.is_empty()).then(|| {
ids.into_iter()
.map(|id| id.to_string())
.collect::<Vec<_>>()
.join(",")
})
}
}