访问运行对象表时出现问题

在我的程序中,我使用运行对象表(ROT)来确保只运行一个程序实例。 由于我从一位不幸离开公司的开发人员那里“inheritance”了这段代码,我就是解决问题的穷人。 代码工作正常,但我们有3个客户(39,000个)将获得AccessDeniedException 。 每个客户都以用户模式运行软件。

有什么建议可能是错的吗?

 bool retVal = false; IMoniker[] arrMoniker = new IMoniker[1]; IBindCtx bindCtx = null; string displayName; int hResult; int mkSys; Guid clsidRot; bool guidCompare = false; IntPtr number = IntPtr.Zero; moreObjectsListed = false; objectFromRot = null; try { // check the objects in the running object table for fitting the specified class id while ((retVal == false) && (0 == enumMoniker.Next(1, arrMoniker, number))) { hResult = CreateBindCtx(0, out bindCtx); if (hResult == 0) { arrMoniker[0].IsSystemMoniker(out mkSys); if (mkSys == 4) { try { // the display name is the class id of the object in the table // --> AccessDeniedException raises here <-- arrMoniker[0].GetDisplayName(bindCtx, null, out displayName); clsidRot = new Guid(displayName.Substring(1)); guidCompare = clsidRot.Equals(clsid); } catch(Exception) {} // an object with fitting class id was found if (guidCompare == true) { rot.IsRunning(arrMoniker[0]); rot.GetObject(arrMoniker[0], out objectFromRot); retVal = true; } } } } } finally { if (arrMoniker[0] != null) { moreObjectsListed = true; Marshal.ReleaseComObject(arrMoniker[0]); } if (bindCtx != null) { Marshal.ReleaseComObject(bindCtx); } } 

编辑:这是在ROT中注册对象的请求代码:

 internal static extern uint RegisterActiveObject([MarshalAs(UnmanagedType.IUnknown)]object pIUnknown, ref Guid refclsid, uint flags, out uint pdwRegister); internal const uint ActiveObjectStrong = 0; ... NativeMethods.RegisterActiveObject(this, ref guid, NativeMethods.ActiveObjectStrong, out this.runningObjectTableRegisteredId); 

编辑2:

首先是所有调查员的一个大EXCUSE,我们没有得到AccessDeniedException它是一个System.UnauthorizedAccessException(HRESULT:0x80070005(E_ACCESSDENIED))。

其次是“调查员”Ken Brittain问题的答案: – SharePoint 不在混合中 – 我很想从ROT请求正确的对象 – 另一个提示可能是3个问题中的1个(除了39,000个正常工作)正在运行WTS上的应用程序(Windows终端服务器)

编辑3:

这是一个例外的堆栈跟踪:(我已经翻译了堆栈跟踪,因为它是在德国机器上)

 System.UnauthorizedAccessException: Access denied (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) at System.Runtime.InteropServices.ComTypes.IRunningObjectTable.EnumRunning(IEnumMoniker& ppenumMoniker) at Datev.Framework.DirectStart.RunningObjectTableClientManager..ctor() 

堆栈跟踪的其余部分在我们的代码中。 在这种情况下可标记的是在RunningObjectTableClientManager的构造函数中引发exception。 这是构造函数的代码:

 private IRunningObjectTable rot; private IEnumMoniker enumMoniker; public RunningObjectTableClientManager() { int retVal = GetRunningObjectTable(0, out this.rot); if (retVal == 0) { rot.EnumRunning(out this.enumMoniker); } } 

根据我的经验,GUID碰撞的可能性虽然可能不太可能,但是没有进行调查。 我采取的第一个轨道是查看可能导致AccessDeniedException原因。 从那里向后工作,您可以看到GetDisplayName没有显式抛出此exception(或返回任何类似的exception)。

那是什么? 您的代码似乎是在C#中。 除非我错误地使用来自C#的COM将通过主互操作。 只有两(2)个interop暴露了我能找到的IMoniker接口:

  • System.Runtime.InteropServices.ComTypes包含IMoniker
  • Microsoft.VisualStudio.OLE.Interop包含一个以及IMoniker

你在谈论一个应用程序,所以我的直觉告诉我你正在使用运行时版本。 看着这些电话我找不到任何forms的Access Denied HRESULT或类似的东西。 VisualStudio互操作确实提到了有关访问和信任的以下内容: 使用部分可信代码中的库 。 这听起来像是一条路径,如果你正在使用Visual Studio interops,它将适用。

如果您正在使用mscorlib.dll程序集中包含的运行时服务命名空间(根据此页面, .NET Framework程序集可由部分受信任的代码调用标记为可调用的部分受信任的代码),则说明似乎不适用。

那么现在怎么办? 我搜索了AccessDeniedException ,发现除了在MSDN中标记为过时的Microsoft.Office.Server.ApplicationRegistry.Infrastructure.AccessDeniedException类之外,没有任何受支持的实现。 该类在SharePoint 2010类库下提交。

所以这是我的问题:你使用哪个互操作? SharePoint是否在混合中? 我之前说过GUID碰撞没有被怀疑,但现在我在质疑这个假设。 你是从ROT请求正确的对象吗? 此对象是否在另一个进程(不是你的进程)下运行?

从该站点看 ,它可能是由于注册表设置或由于表中注册的对象的安全设置:

  Check "HKLM\Software\Network OLE\Enabled". Fail the request if zero. Check "HKCU\Software\Network OLE\Enabled". Fail the request if zero. Before performing any operation against a ROT entry (ie, IRunningObjectTable::Revoke, IRunningObjectTable::IsRunning, IRunningObjectTable::GetObject, IRunningObjectTable::NoteTimeChange, IRunningObjectTable::GetTimeOfLastChange, or when including an entry in an IEnumMoniker::Next of an IEnumMoniker returned from IRunningObjectTable::EnumRunning), check the call against the SECURITY_DESCRIPTOR available from IRunningObjectTable::Register. This will be either the value returned by the object's IActivationSecurity::GetSecurityDescriptor at the time of IRunningObjectTable::Register or will have been taken from "HKCU\Software\Network OLE\DefaultROTSecurity" or "HKLM\Software\Network OLE\DefaultROTSecurity" at the time of IRunningObjectTable::Register if the object did not support IActivationSecurity. 

也许这不是您正在寻找的答案,但是inheritance了这段代码后,您是否已经停下来质疑这是否适合您的用例? 这是我第一次看到C#应用程序使用Com Interop来防止多个应用程序实例。 我从未与Com有过良好的经历,并且发现了类似的无法解释或无证的例外情况。

为什么不看一下防止多个应用程序实例的替代技术呢? 我在过去的解决方案中使用过Mutex,从来没有遇到过问题。 虽然我没有过去的代码方便,但是在stackoverflow之前已经多次讨论过这个问题,其中有一些非常好的答案已经过同行评审和社区编辑。

例如, 在C#中使用Global Mutex有什么好的模式? ‘有一个很好的社区编辑答案,似乎考虑到各种奇怪的竞争条件和线程/进程终止以及潜在的安全问题。

因此,我的建议是远离Com Interop并取代Mutex实现。