|
| 1 | +ProcessId for ProcessFailedEventArgs |
| 2 | +=== |
| 3 | + |
| 4 | +# Background |
| 5 | + |
| 6 | +WebView2 provides applications with the |
| 7 | +[ProcessFailed](https://learn.microsoft.com/microsoft-edge/webview2/reference/win32/icorewebview2?view=webview2-1.0.705.50#add_processfailed) |
| 8 | +event so they can react accordingly when a process failure occurs. However, |
| 9 | +this event does not currently provide the process ID of the failed process. |
| 10 | +This is particularly problematic when running multiple renderers. It becomes |
| 11 | +difficult for the application to determine which process to address. |
| 12 | + |
| 13 | +In this document we describe an extended version of the |
| 14 | +[ProcessFailedEventArgs](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2processfailedeventargs?view=webview2-1.0.2151.40), |
| 15 | +which provides access to an `ICoreWebView2ProcessInfo` object for the failed process. This object includes the process ID, process kind, and other relevant information. This enables the host application to collect |
| 16 | +additional information about the process failure, whether it is a renderer, GPU, or |
| 17 | +even the browser process. |
| 18 | + |
| 19 | +The updated API is detailed below. We'd appreciate your feedback. |
| 20 | + |
| 21 | +# Description |
| 22 | + |
| 23 | +The `ICoreWebView2ProcessFailedEventArgs4` interface extends the existing |
| 24 | +`ProcessFailedEventArgs` to include, when available, the `ICoreWebView2ProcessInfo` of the failed process. Note that `ProcessInfo` may be null or unavailable in certain scenarios. This |
| 25 | +enables applications to: |
| 26 | +- Correlate process failures with running process data from the ProcessInfo API |
| 27 | +- Collect process-specific diagnostic information for logging and telemetry |
| 28 | +- Analyze crash dumps for specific processes |
| 29 | +- Better track and respond to failures in multi-renderer scenarios |
| 30 | + |
| 31 | +# Examples |
| 32 | + |
| 33 | +The following code snippets demonstrate how the updated |
| 34 | +`ProcessFailedEventArgs` can be used by the host application: |
| 35 | + |
| 36 | +## Win32 C++ |
| 37 | + |
| 38 | +```cpp |
| 39 | +//! [ProcessFailed] |
| 40 | +// Register a handler for the ProcessFailed event. |
| 41 | + // This handler collects extended diagnostics so the host can: |
| 42 | + // * Inspect the failure kind together with reason, description, and exit code. |
| 43 | + // * Retrieve the CoreWebView2ProcessInfo for the failed process (ID and kind). |
| 44 | + // * Log the gathered information for telemetry or later correlation and decide |
| 45 | + // how to react (reload/recreate) based on app policy outside of this sample. |
| 46 | +CHECK_FAILURE(m_webView->add_ProcessFailed( |
| 47 | + Callback<ICoreWebView2ProcessFailedEventHandler>( |
| 48 | + [this](ICoreWebView2* sender, |
| 49 | + ICoreWebView2ProcessFailedEventArgs* argsRaw) |
| 50 | + -> HRESULT { |
| 51 | + wil::com_ptr<ICoreWebView2ProcessFailedEventArgs> args = argsRaw; |
| 52 | + COREWEBVIEW2_PROCESS_FAILED_KIND kind; |
| 53 | + CHECK_FAILURE(args->get_ProcessFailedKind(&kind)); |
| 54 | + |
| 55 | + // Try to get the newer interface with additional failure details |
| 56 | + auto args2 = |
| 57 | + args.try_query<ICoreWebView2ProcessFailedEventArgs2>(); |
| 58 | + if (args2) |
| 59 | + { |
| 60 | + COREWEBVIEW2_PROCESS_FAILED_REASON reason; |
| 61 | + wil::unique_cotaskmem_string processDescription; |
| 62 | + INT32 exitCode; |
| 63 | + CHECK_FAILURE(args2->get_Reason(&reason)); |
| 64 | + CHECK_FAILURE( |
| 65 | + args2->get_ProcessDescription(&processDescription)); |
| 66 | + CHECK_FAILURE(args2->get_ExitCode(&exitCode)); |
| 67 | + |
| 68 | + // Get the process ID of the failed process |
| 69 | + wil::com_ptr<ICoreWebView2ProcessInfo> processInfo; |
| 70 | + auto argProcessInfo = args.try_query<ICoreWebView2ProcessFailedEventArgs4>(); |
| 71 | + if (argProcessInfo) |
| 72 | + { |
| 73 | + CHECK_FAILURE(argProcessInfo->get_ProcessInfo(&processInfo)); |
| 74 | + } |
| 75 | + INT32 processId = 0; |
| 76 | + COREWEBVIEW2_PROCESS_KIND processKind = COREWEBVIEW2_PROCESS_KIND_UNKNOWN; |
| 77 | + if (processInfo) |
| 78 | + { |
| 79 | + CHECK_FAILURE(processInfo->get_ProcessId(&processId)); |
| 80 | + CHECK_FAILURE(processInfo->get_Kind(&processKind)); |
| 81 | + } |
| 82 | + |
| 83 | + // Log the failure details including the process ID |
| 84 | + std::wstringstream message; |
| 85 | + message << L"Kind: " << ProcessFailedKindToString(kind) |
| 86 | + << L"\n" |
| 87 | + << L"Reason: " << ProcessFailedReasonToString(reason) |
| 88 | + << L"\n" |
| 89 | + << L"Exit code: " << exitCode << L"\n" |
| 90 | + << L"Process ID: " << processId << L"\n" |
| 91 | + << L"Process Kind: " << ProcessKindToString(processKind) << L"\n" |
| 92 | + << L"Process description: " |
| 93 | + << processDescription.get(); |
| 94 | + |
| 95 | + OutputDebugString(message.str().c_str()); |
| 96 | + // Collect the process ID for telemetry or further |
| 97 | + // analysis |
| 98 | + } |
| 99 | + return S_OK; |
| 100 | + }) |
| 101 | + .Get(), |
| 102 | + &m_processFailedToken)); |
| 103 | +//! [ProcessFailed] |
| 104 | +``` |
| 105 | +
|
| 106 | +## .NET C# |
| 107 | +
|
| 108 | +```c# |
| 109 | +void WebView_CoreWebView2InitializationCompleted(object sender, |
| 110 | + CoreWebView2InitializationCompletedEventArgs e) |
| 111 | +{ |
| 112 | + if (e.IsSuccess) |
| 113 | + { |
| 114 | + webView.CoreWebView2.ProcessFailed += WebView_ProcessFailed; |
| 115 | + } |
| 116 | +} |
| 117 | +
|
| 118 | +void WebView_ProcessFailed(object sender, |
| 119 | + CoreWebView2ProcessFailedEventArgs e) |
| 120 | +{ |
| 121 | + // Collect failure details including the process ID |
| 122 | + StringBuilder messageBuilder = new StringBuilder(); |
| 123 | + messageBuilder.AppendLine($"Process kind: {e.ProcessFailedKind}"); |
| 124 | + messageBuilder.AppendLine($"Reason: {e.Reason}"); |
| 125 | + messageBuilder.AppendLine($"Exit code: {e.ExitCode}"); |
| 126 | + messageBuilder.AppendLine( |
| 127 | + $"Process description: {e.ProcessDescription}"); |
| 128 | +
|
| 129 | + // Get the process ID of the failed process |
| 130 | + if (e.ProcessInfo != null) |
| 131 | + { |
| 132 | + messageBuilder.AppendLine($"Process ID: {e.ProcessInfo.ProcessId}"); |
| 133 | + messageBuilder.AppendLine($"Process Kind: {e.ProcessInfo.Kind}"); |
| 134 | + } |
| 135 | + else |
| 136 | + { |
| 137 | + messageBuilder.AppendLine("Process Info: unavailable (process may have been terminated externally, e.g., via Task Manager)"); |
| 138 | + } |
| 139 | +
|
| 140 | + // Log the failure or send to telemetry |
| 141 | + System.Diagnostics.Debug.WriteLine(messageBuilder.ToString()); |
| 142 | +
|
| 143 | + // You can also correlate with process info collected earlier |
| 144 | + var failedProcessInfo = _processInfoList.FirstOrDefault( |
| 145 | + p => p.ProcessId == e.ProcessInfo.ProcessId); |
| 146 | + if (failedProcessInfo != null) |
| 147 | + { |
| 148 | + System.Diagnostics.Debug.WriteLine( |
| 149 | + $"Failed process was of kind: {failedProcessInfo.Kind}"); |
| 150 | + } |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +# Remarks |
| 155 | + |
| 156 | +The `ProcessInfo` property returns an `ICoreWebView2ProcessInfo` object that contains the process ID of the failed process |
| 157 | +and the process kind (GPU, Renderer, Browser, Utility, etc.). When the failing |
| 158 | +process starts successfully (for example, GPU process hangs, browser process |
| 159 | +exits, utility process exits, renderer process hangs), the process ID is |
| 160 | +available so apps can correlate diagnostics. If the process never starts or if |
| 161 | +the main frame renderer process is terminated externally (for example, by Task |
| 162 | +Manager or taskkill) the associated process information is unavailable and the |
| 163 | +reported process ID is 0. |
| 164 | + |
| 165 | +# API Details |
| 166 | + |
| 167 | +## COM |
| 168 | + |
| 169 | +```cpp |
| 170 | + |
| 171 | +/// A continuation of the ICoreWebView2ProcessFailedEventArgs3 interface |
| 172 | +/// for getting the process ID of the failed process. |
| 173 | +/// |
| 174 | +[uuid(f71c6e90-b2dc-4f81-bb56-bb3ef56dd8c7), object, |
| 175 | + pointer_default(unique)] |
| 176 | +interface ICoreWebView2ProcessFailedEventArgs4 : |
| 177 | + ICoreWebView2ProcessFailedEventArgs3 { |
| 178 | + /// The process info of the failed process, which can be used to |
| 179 | + /// correlate the failing process with the running process data or to |
| 180 | + /// analyze crash dumps for that process. The process ID is available when the |
| 181 | + /// process starts successfully (GPU process hangs, browser process exits, |
| 182 | + /// utility process exits, renderer process hangs). If the process never |
| 183 | + /// started or when the main frame renderer process is terminated externally |
| 184 | + /// (for example by Task Manager or taskkill), the process ID will be set to 0. |
| 185 | + // MSOWNERS: core ( [email protected]) |
| 186 | + [propget] HRESULT ProcessInfo([out, retval] ICoreWebView2ProcessInfo** value); |
| 187 | +} |
| 188 | + |
| 189 | +``` |
| 190 | +
|
| 191 | +## .NET / WinRT |
| 192 | +
|
| 193 | +```c# |
| 194 | +namespace Microsoft.Web.WebView2.Core |
| 195 | +{ |
| 196 | + runtimeclass CoreWebView2ProcessFailedEventArgs |
| 197 | + { |
| 198 | + /// The process info of the failed process, which can be used to |
| 199 | + /// correlate the failing process with the running process data or to |
| 200 | + /// analyze crash dumps for that process. |
| 201 | + /// |
| 202 | + /// This property may be <c>null</c> if the process never started or when the main frame renderer process |
| 203 | + /// is terminated externally (for example, by Task Manager or taskkill). In these cases, process information |
| 204 | + /// is not available. When available, the process ID is set to 0 if the process could not be identified. |
| 205 | +
|
| 206 | + [interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2ProcessFailedEventArgs4")] |
| 207 | + { |
| 208 | + CoreWebView2ProcessInfo ProcessInfo { get; }; |
| 209 | + } |
| 210 | + } |
| 211 | +} |
| 212 | +``` |
0 commit comments