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

时间:2011-11-21 16:38:12

标签: c# running-object-table

在我的程序中,我使用运行对象表(ROT)来确保只运行一个程序实例。由于我从一位不幸离开公司的开发人员那里“继承”了这段代码,我是解决问题的穷人。代码工作正常,但我们有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的构造函数中引发异常。以下是该构造函数的代码:

private IRunningObjectTable rot;
private IEnumMoniker enumMoniker;

public RunningObjectTableClientManager()
{
    int retVal = GetRunningObjectTable(0, out this.rot);

    if (retVal == 0)
    {
        rot.EnumRunning(out this.enumMoniker);
    }
}

3 个答案:

答案 0 :(得分:5)

根据我的经验,GUID碰撞的概率虽然可能不太可能,但是没有进行调查。我拍摄的第一首曲目是查看导致AccessDeniedException的原因。从那里向后工作,你可以看到GetDisplayName没有明确地抛出这个异常(或返回任何类似的东西)。

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

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

您正在谈论一个应用程序,所以我的直觉告诉我您正在使用运行时版本。看到通话,我找不到任何形式的拒绝访问 HRESULT或类似的simething。 VisualStudio互操作确实提到了有关访问和信任的以下内容:Using Libraries from Partially Trusted Code。这听起来像是一条要遵循的路径,如果你正在使用Visual Studio interops,那么它将适用。

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

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

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

答案 1 :(得分:1)

this site出现它可能是由于注册表设置或由于表中注册的对象的安全设置所致:

           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        
           (i.e., 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.

答案 2 :(得分:1)

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

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

例如,“What is a good pattern for using a Global Mutex in C#?”有一个很好的社区编辑答案,似乎考虑到各种奇怪的竞争条件和线程/进程终止以及潜在的安全问题。

因此,我的建议是远离Com Interop并转而使用Mutex实现。