WebView2初始化浏览器触发路径非法格式ArgumentException异常排查
WinForms应用集成Microsoft.Web.WebView2控件,预期实现以下功能:
- 浏览器控件初始化
- 自动导航至穆迪官网
- 检测用户登录状态
- 自动触发指定文件下载、等待下载流程完成
程序执行初始化代码时抛出路径格式非法异常。
System.ArgumentException: The path is not of a legal form.
完整调用堆栈:
at System.IO.Path.LegacyNormalizePath(String path, Boolean fullCheck, Int32 maxPathLength, Boolean expandShortPaths)
at System.IO.Path.InternalGetDirectoryName(String path)
at Microsoft.Web.WebView2.Core.CoreWebView2Environment.LoadWebView2LoaderDll()
at Microsoft.Web.WebView2.Core.CoreWebView2Environment.d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Web.WebView2.WinForms.WebView2.d__23.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ECLautomation.Form1.d__150.MoveNext() in xxx\Form1.cs:line xxx
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ECLautomation.Form1.<Form1_Load>d__149.MoveNext() in xxx\Form1.cs:line xxx
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
异常在执行以下初始化逻辑时抛出:
CoreWebView2Environment webView2Environment = null; await webView21.EnsureCoreWebView2Async(webView2Environment);
完整业务实现代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; namespace WinFormsApp2 { public partial class Form1 : Form { private static readonly string indexUrl = "http://www.moodys.com"; public Form1() { InitializeComponent(); } private async void Form1_Load(object sender, EventArgs e) { await InitializeAsync(webView21); } public async Task InitializeAsync(WebView2 webView21) { CoreWebView2Environment webView2Environment = null; await webView21.EnsureCoreWebView2Async(webView2Environment); } private void webView21_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e) { webView21.CoreWebView2.Navigate(indexUrl); } private void webView21_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e) { string url = webView21.CoreWebView2.Source.ToString(); Console.WriteLine(url); } private void button1_Click(object sender, EventArgs e) { bool res = IsLogin(); if (res == true) { MessageBox.Show("Please login Moody's"); } else { MessageBox.Show("Successful"); webView21.CoreWebView2.Navigate("https://www.moodys.com/search?keyword=annual%20default%20study%20excel&searchfrom=GS"); WaitForSeconds(10); SendKeys.SendWait("{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}"); SendKeys.SendWait("{ENTER}"); MessageBox.Show("Annual Default Study file downloaded, please close the popup edge browser."); WaitForSeconds(10); webView21.CoreWebView2.Navigate("https://www.moodys.com/search?keyword=Sovereign%20Default%20And%20Recovery%20Rates%20excel%20data&searchfrom=GS"); WaitForSeconds(10); SendKeys.SendWait("{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}{TAB}"); SendKeys.SendWait("{ENTER}"); MessageBox.Show("Sovereign file downloaded, please close the popup edge browser."); } } public static void WaitForSeconds(int seconds) { DateTime startTime; DateTime endTime; startTime = DateTime.Now; for (int i = 0; ; i++) { endTime = DateTime.Now; TimeSpan difference = endTime - startTime; if (difference.TotalSeconds < seconds) { Application.DoEvents(); } else { break; } } } private bool IsLogin() { var js = @"document.documentElement.innerText.indexOf('Sign In or Register');"; int res = Convert.ToInt32(ExecuteScript(js)); return res >= 1; } private string ExecuteScript(string javaScript) { var taskAwaiter = webView21.CoreWebView2.ExecuteScriptAsync(javaScript).GetAwaiter(); WaitAsyncTask(5, taskAwaiter); return taskAwaiter.GetResult(); } public void WaitAsyncTask(int waitSecond, System.Runtime.CompilerServices.TaskAwaiter<string> activeTask) { DateTime WaitStartTime = DateTime.Now; double secondDiffer = (DateTime.Now - WaitStartTime).TotalSeconds; while (secondDiffer < waitSecond) { secondDiffer = (DateTime.Now - WaitStartTime).TotalSeconds; Application.DoEvents(); if (activeTask.IsCompleted) { break; } } } } }
初始化代码存在明确错误,直接触发异常的原因如下:
- 向
EnsureCoreWebView2Async传入null作为环境参数时,WebView2 SDK会自动尝试推导运行时路径、用户数据目录,旧版本SDK存在路径解析bug:当程序从含特殊字符的路径、网络共享路径启动时,内部调用Path.InternalGetDirectoryName处理空值或非法格式路径,直接抛出路径格式非法异常。 - 代码存在其他稳定性隐患:
- 未显式指定用户数据目录,不同运行环境下自动生成的路径可能出现权限不足、格式非法问题
- 用固定延时+
SendKeys模拟页面操作,完全依赖页面加载耗时,极易失效 - 异步任务用阻塞循环+
Application.DoEvents()等待,存在UI死锁风险 - 登录状态判断逻辑颠倒:检测到"Sign In or Register"文本说明用户未登录,现有判断分支的提示逻辑完全相反
- 下载流程依赖人工关闭弹窗,没有对接WebView2原生下载事件,无法准确判断下载完成状态。
- 修正初始化逻辑,显式创建WebView2环境,指定合法的用户数据目录,禁止传入null:
using System.IO; public async Task InitializeAsync(WebView2 webView21) { // 显式指定用户数据存储目录,使用程序启动路径下的独立文件夹,避免路径解析问题 string userDataPath = Path.Combine(Application.StartupPath, "WebView2_UserData"); // 创建WebView2运行环境,第一个参数为null时自动查找系统已安装的WebView2 Runtime CoreWebView2Environment env = await CoreWebView2Environment.CreateAsync(null, userDataPath); await webView21.EnsureCoreWebView2Async(env); }
- 提前确认运行环境:目标机器安装与项目NuGet包版本匹配的WebView2 Runtime,或把固定版本运行时嵌入程序输出目录。
- 重构交互逻辑:移除所有
SendKeys、阻塞等待写法,页面操作全部通过ExecuteScriptAsync直接操作DOM元素触发,异步方法统一用await等待结果。 - 对接WebView2原生下载事件:订阅
CoreWebView2.DownloadStarting事件,监听每个下载任务的StateChanged事件,在下载完成/失败后再执行后续流程,无需人工介入。 - 修正登录状态判断逻辑,调整提示分支的对应关系。
内容的提问来源于stack exchange,提问作者Jason Wong

