在 Windows Service 中创建 VpnApi COM 对象的实例

本文关键字:COM 对象 实例 VpnApi 创建 Windows Service | 更新日期: 2023-09-27 18:30:16

我正在使用Cisco AnyConnect VpnApi,我创建了一个COM 'Manifest?' dll,我在我的服务中引用了它。我也尝试过这样做,只是添加对 COM 服务的引用并让 VS 嵌入互操作程序集。该服务在后台旋转并执行一些操作,等待自定义推荐,并在ServiceBase.OnCustomCommand函数中启动该过程。该服务在系统帐户下运行。

用:

using VpnApiLib;

代码非常简单:

 IVpnApi vpn = null;
 try
 {
     vpn = new VpnApi();
     return true;
 }
 catch (Exception ex)
 {
     EventManager.PublishExceptionLogMessage(ex);
 }
 finally {
     if (vpn!= null)
         Marshal.ReleaseComObject(vpn);
 }
 return false;

但是,当从服务调用它时,我得到一个访问违规异常:

"试图读取或写入受保护的内存。这通常表明其他内存已损坏。

我的堆栈跟踪:

at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at ...ConnectivityAction.IsVpnInstalled() in D:'...'ConnectivityAction.cs:line 208

有一次,我得到了一个不同的例外:"检索具有 CLSID {C15C0F4F-DDFB-4591-AD53-C9A71C9C15C0} 的组件的 COM 类工厂失败,出现以下错误:800701e7。

所以我尝试将此代码放在控制台应用程序中并引用 COM 对象,它解析得很好,没有例外。

我已经尝试了我能想到的所有可能的事情,包括使用我拥有的一些经过测试和磨损的 pinvoke 代码进行模拟。似乎没有任何效果,每当从Windows服务调用它时,我都会得到这些异常之一。

在 Windows Service 中创建 VpnApi COM 对象的实例

由于链接问题,有时会发生此类错误,此论坛帖子似乎表明这里可能是这种情况:

https://supportforums.cisco.com/discussion/12188291/c-new-vpnapiclass-get-com-exception-800701e7-till-reboot

所以我在上面旋转了 3 天后终于想通了。

问题在于,COM 对象实际上是在应用程序的另一个类(我继承的)的"引导程序"中引用的,并且通过程序集搜索模式通过 Unity 间接加载,这在一定程度上掩盖了问题。

问题的根源源于内存中加载的 COM 对象的行为。显然,当通过代理项(由 tlbimp.exe 生成)引用时,COM 对象本身被加载到内存中的固定位置,然后在实例化时存储在代理项中。它在引导程序中被引用并通过 Unity 加载的简单事实在主线程上的内存中创建实例。

当触发 OnCustomCommand 事件时,当显然无法访问主线程加载 COM 对象的内存时,它会返回到不同的线程上。因此,"受保护的内存"错误。

也就是说,引用它的类没有被使用,所以我只是把它注释掉了,一切都开始工作了。但是,我认为最终的解决方案将是某种代理类,用于对 VpnApi COM 对象的所有访问,该代理类要么具有指向单个线程的调度程序,要么可能是某种单例类。

还不确定,我还没有考虑太多。一旦我想通了,我就放下麦克风回家了!哈哈,如果你们中的任何人有任何建议,你肯定会得到我的赞成票。