VS2017启动调用CoInitializeSecurity抛RPC_E_TOO_LATE异常排查
我最近遇到了完全一样的问题,分享下我的分析和解决办法:
问题重现
你开发的WPF应用在启动时调用CoInitializeSecurity,在VS2013中运行正常,但在VS2017调试时会抛出RPC_E_TOO_LATE(0x80010119)的COM异常,提示"已调用CoInitialize"。对比程序集加载日志发现,VS2017比VS2013多加载了System.Core.dll和Microsoft.VisualStudio.Debugger.Runtime.dll。捕获异常后,调试时会弹出提示框,但直接运行可执行文件时无异常。
VS2013加载的程序集:
'WPFTestVS2017.exe' (CLR v4.0.30319: DefaultDomain): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: DefaultDomain): Loaded 'C:\WPFTestVS2017\bin\Debug\WPFTestVS2017.exe'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_64\PresentationCore\v4.0_4.0.0.0__31bf3856ad364e35\PresentationCore.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Xaml\v4.0_4.0.0.0__b77a5c561934e089\System.Xaml.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll'.
VS2017加载的程序集:
'WPFTestVS2017.exe' (CLR v4.0.30319: DefaultDomain): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: DefaultDomain): Loaded 'C:\WPFTestVS2017\bin\Debug\WPFTestVS2017.exe'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\WindowsBase\v4.0_4.0.0.0__31bf3856ad364e35\WindowsBase.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_64\PresentationCore\v4.0_4.0.0.0__31bf3856ad364e35\PresentationCore.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Xaml\v4.0_4.0.0.0__b77a5c561934e089\System.Xaml.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Remote Debugger\x64\Runtime\Microsoft.VisualStudio.Debugger.Runtime.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll'. 'WPFTestVS2017.exe' (CLR v4.0.30319: WPFTestVS2017.exe): Loaded 'C:\windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll'.
原因分析
CoInitializeSecurity的核心规则是必须在当前线程的任何COM初始化操作之前调用,一旦有COM对象被创建或者CoInitialize/CoInitializeEx被调用,再调用它就会抛出RPC_E_TOO_LATE异常。
在VS2017的调试环境中,额外加载的Microsoft.VisualStudio.Debugger.Runtime.dll是VS调试器的运行时组件,它很可能在你的App构造函数执行之前就触发了COM初始化(比如为了实现调试功能进行的内部COM调用),导致你后续调用CoInitializeSecurity时已经错过了时机。而直接运行可执行文件时,这个调试器组件不会被加载,所以没有提前初始化COM,你的调用就能正常执行。
至于System.Core.dll,它本身不会触发COM初始化,不是导致问题的原因。
解决方案
1. 提前调用CoInitializeSecurity到应用入口点
最彻底的解决办法是把CoInitializeSecurity的调用移到所有WPF组件和调试器组件加载之前,也就是自定义应用的Main方法:
修改你的App.xaml.cs,添加静态Main方法,并设置为应用的入口点:
using System; using System.Runtime.InteropServices; using System.Windows; using System.Diagnostics; namespace WPFTestVS2017 { internal static class NativeMethods { private enum RpcAuthnLevel { Default = 0, None = 1, Connect = 2, Call = 3, Pkt = 4, PktIntegrity = 5, PktPrivacy = 6 } private enum RpcImpLevel { Default = 0, Anonymous = 1, Identify = 2, Impersonate = 3, Delegate = 4 } private enum EoAuthnCap { None = 0x0000, MutualAuth = 0x0001, StaticCloaking = 0x0020, DynamicCloaking = 0x0040, AnyAuthority = 0x0080, MakeFullSIC = 0x0100, Default = 0x0800, SecureRefs = 0x0002, AccessControl = 0x0004, AppID = 0x0008, Dynamic = 0x0010, RequireFullSIC = 0x0200, AutoImpersonate = 0x0400, NoCustomMarshal = 0x2000, DisableAAA = 0x1000 } [DllImport("Ole32.dll", ExactSpelling = true, EntryPoint = "CoInitializeSecurity", CallingConvention = CallingConvention.StdCall, SetLastError = false, PreserveSig = false)] private static extern void CoInitializeSecurity( IntPtr pVoid, int cAuthSvc, IntPtr asAuthSvc, IntPtr pReserved1, uint dwAuthnLevel, uint dwImpLevel, IntPtr pAuthList, uint dwCapabilities, IntPtr pReserved3); public static void Initialize() { CoInitializeSecurity(IntPtr.Zero, -1, IntPtr.Zero, IntPtr.Zero, (uint)RpcAuthnLevel.PktPrivacy, (uint)RpcImpLevel.Impersonate, IntPtr.Zero, (uint)EoAuthnCap.DynamicCloaking, IntPtr.Zero); } } public partial class App : Application { [STAThread] public static void Main() { try { // 在这里调用,确保在任何COM初始化之前执行 NativeMethods.Initialize(); } catch (COMException ex) when (ex.ErrorCode == unchecked((int)0x80010119)) { Debug.WriteLine("CoInitializeSecurity called too late, likely due to external COM initialization."); } catch (Exception ex) { MessageBox.Show($"Unexpected error initializing COM security: {ex.ToString()}"); throw; } App app = new App(); app.Run(); } public App() { // 移除原来构造函数中的初始化代码 } } }
然后设置项目的启动对象:
- 右键项目 -> 属性 -> 应用程序 -> 启动对象,选择
WPFTestVS2017.App(如果没显示,先编译一次项目)。
这样就能保证CoInitializeSecurity在所有可能触发COM初始化的组件加载前执行,无论调试还是直接运行都不会有问题。
2. 忽略调试时的RPC_E_TOO_LATE异常提示
如果不想修改代码结构,可以在VS2017中配置忽略特定异常:
- 打开调试菜单 -> Windows -> 异常设置
- 展开
Common Language Runtime Exceptions,找到System.Runtime.InteropServices.COMException - 右键点击它,选择“编辑条件”
- 在弹出的窗口中,勾选“异常代码”,输入
0x80010119,点击确定 - 这样调试时VS就不会再弹出这个特定异常的提示框了
3. 优化异常捕获逻辑
如果你保留原有的构造函数调用方式,可以优化异常捕获,只忽略RPC_E_TOO_LATE,避免隐藏其他潜在问题:
public App() { try { NativeMethods.Initialize(); } catch (COMException ex) when (ex.ErrorCode == unchecked((int)0x80010119)) { // 仅忽略调试环境下的"太晚调用"异常 Debug.WriteLine("CoInitializeSecurity skipped: already initialized by debugger runtime."); } catch (Exception ex) { MessageBox.Show($"Failed to initialize COM security: {ex.ToString()}"); throw; } }
附带你的完整代码参考
App.xaml
<Application x:Class="WPFTestVS2017.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> </Application>
MainWindow.xaml
<Window x:Class="WPFTestVS2017.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"/>
MainWindow.xaml.cs
using System.Windows; namespace WPFTestVS2017 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
内容的提问来源于stack exchange,提问作者sweerpotato

