VSTO Excel插件Azure AD认证:MSAL.NET弹窗尺寸优化问询
自定义VSTO Excel插件AAD认证小型弹窗实现方案
核心思路
MSAL.NET支持通过自定义Web UI替代默认的外部浏览器弹窗,我们可以用WPF创建一个小型窗口,嵌入WebView2控件加载AAD授权页面,完全控制窗口尺寸和样式,同时避免直接关联Excel主窗口导致的崩溃问题。
具体实现步骤
1. 添加WebView2控件依赖
在VSTO项目中安装Microsoft.Web.WebView2 NuGet包,用于嵌入浏览器承载授权页面。
2. 创建自定义WPF认证窗口
新建一个WPF Window(命名为AuthPopupWindow),设置固定尺寸并嵌入WebView2:
<Window x:Class="YourAddIn.AuthPopupWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf" Title="账户登录" Width="600" Height="700" ResizeMode="CanMinimize" WindowStartupLocation="CenterOwner"> <Grid> <wv2:WebView2 x:Name="AuthWebView" /> </Grid> </Window>
3. 实现MSAL自定义Web UI逻辑
创建实现IWebUi接口的类,处理授权页面加载和回调URL捕获:
using Microsoft.Identity.Client; using Microsoft.Web.WebView2.Wpf; using System; using System.Threading; using System.Threading.Tasks; using System.Windows; public class CustomAuthWebUi : IWebUi { public async Task<Uri> AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirectUri, CancellationToken cancellationToken) { var tcs = new TaskCompletionSource<Uri>(); var authWindow = new AuthPopupWindow(); // 初始化WebView2环境 await authWindow.AuthWebView.EnsureCoreWebView2Async(); // 监听导航事件,捕获AAD授权回调 authWindow.AuthWebView.NavigationStarting += (sender, e) => { if (e.Uri.AbsoluteUri.StartsWith(redirectUri.AbsoluteUri)) { e.Cancel = true; tcs.SetResult(e.Uri); authWindow.Close(); } }; // 加载AAD授权页面 authWindow.AuthWebView.Source = authorizationUri; // 在VSTO的STA线程中显示窗口 Application.Current.Dispatcher.Invoke(() => authWindow.ShowDialog()); return await tcs.Task; } }
4. 修改MSAL认证调用代码
替换原有的AcquireTokenInteractive调用,使用自定义Web UI:
Log.Verbose("calling AcquireTokenInteractive with custom web UI..."); authResult = await app.AcquireTokenInteractive(scopes) .WithAccount(firstAccount) .WithCustomWebUi(new CustomAuthWebUi()) // 启用自定义弹窗 .WithPrompt(Prompt.SelectAccount) .ExecuteAsync();
5. 处理VSTO线程兼容问题
VSTO插件运行在STA线程,若出现线程异常,可通过单独线程启动窗口:
if (!Thread.CurrentThread.GetApartmentState().Equals(ApartmentState.STA)) { var thread = new Thread(() => { var authWindow = new AuthPopupWindow(); // 此处复用前面的WebView初始化和导航逻辑 authWindow.ShowDialog(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); }
额外优化点
- 可在自定义窗口中添加加载动画、关闭按钮等元素,优化用户体验;
- 若需将Excel主窗口设为弹窗父窗口,正确获取句柄的方式:
using System.Diagnostics; IntPtr excelMainHandle = Process.GetCurrentProcess().MainWindowHandle; new WindowInteropHelper(authWindow).Owner = excelMainHandle;
内容的提问来源于stack exchange,提问作者Leon
相关产品推荐
相关产品推荐

