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

Creating instances of VpnApi COM object in Windows Service

我正在使用 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;

然而,当从服务中调用它时,我得到一个 AccessViolationException:

"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

我的堆栈跟踪:

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 class 工厂失败,原因如下:800701e7。

所以我尝试将此代码放入控制台应用程序并引用 COM 对象,它解析得很好,没有任何异常。

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

像这样的错误有时是由于 link 问题而发生的,并且这个论坛帖子似乎表明这里可能就是这种情况:

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

所以我在这上面旋转了 3 天后终于弄明白了。

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

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

当 OnCustomCommand 事件被触发时,它在另一个线程上返回,而显然无法访问主线程加载 COM 对象的内存。因此 "protected memory" 错误。

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

还不确定,我还没有考虑太多。一旦我想通了,我就放下麦克风回家了!大声笑如果你们有任何建议,你一定会得到我的up-votes。