Подтвердить что ты не робот

Проблемы с доступом к таблице текущих объектов

В моей программе я использую таблицу Running Object (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)).

Во-вторых, ответы на вопросы "следователя" Кена Бриттайна: - SharePoint не находится в миксе - Я пытаюсь запросить правильный объект из ROT - Еще одна подсказка может быть, что одна из 3 проблем (кроме 39 000, работающих правильно) запускает приложения на WTS (сервер терминалов Windows)

Изменить 3:

Вот стоп-тракт одного из этих исключений: (я перевел stacktrace, потому что он был на немецкой машине)

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);
    }
}
4b9b3361

Ответ 1

По моему опыту вероятность столкновения с GUID, по возможности, представляется маловероятной, поэтому она не исследовалась. Первый трек, который я взял, смотрел, что может вызвать AccessDeniedException. Отправляясь оттуда, вы можете видеть, что GetDisplayName явно не выбрасывает это исключение (или возвращает что-либо подобное).

Так что же? Ваш код, похоже, находится на С#. Если я не ошибаюсь, используя COM из С#, вы пройдете первичный interop. Есть только два (2) интерполя, которые отображают интерфейс IMoniker, который я мог найти:

  • System.Runtime.InteropServices.ComTypes содержит IMoniker
  • Microsoft.VisualStudio.OLE.Interop также содержит IMoniker

Вы говорите о приложении, так что моя кишка говорит, что вы используете версию времени исполнения. Глядя на вызовы, я не смог найти вызов, возвращающий любую форму Access Denied HRESULT или аналогичный. В разделе VisualStudio interop упоминается следующее о доступе и доверии: Использование библиотек из частично доверенного кода. Это звучало как путь следования и применялся, если вы используете переходы Visual Studio.

Если вы используете пространство имен служб времени исполнения, которое содержится в сборке mscorlib.dll (которое согласно этой странице . СЕТИ NET Framework Абоненты, вызываемые частично защищенным кодом обозначается как частично доверенный код), пояснение не применяется.

Так что теперь? Я выполнил поиск AccessDeniedException и не нашел поддерживаемой реализации, кроме класса Microsoft.Office.Server.ApplicationRegistry.Infrastructure.AccessDeniedException, который помечен как устаревший в MSDN. Класс помещается в библиотеку классов SharePoint 2010.

Итак, вот мои вопросы: Какой из них вы используете? Является ли SharePoint в миксе вообще? Я сказал, что ранее GUID-столкновение не подозревалось, но теперь я задаюсь этим предположением. Вы запрашиваете соответствующий объект из ROT? Этот объект работает под другим процессом (что означает не ваше)?

Ответ 2

Из этого сайта, похоже, это может быть связано с параметром реестра или из-за настроек безопасности для объекта, зарегистрированного в таблице:

           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                             
           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.

Ответ 3

Возможно, это не тот ответ, который вы ищете, но, унаследовав этот код, вы перестали задаваться вопросом, была ли это даже правильная техника для вашего случая использования? Это первый раз, когда я видел приложение С#, использующее Com Interop для чего-то вроде предотвращения нескольких экземпляров приложения. У меня никогда не было хорошего опыта с Com и были найдены подобные необъяснимые или недокументированные исключения.

Почему бы не взглянуть на альтернативный метод предотвращения нескольких экземпляров приложения? Я использовал Mutex в моих прошлых решениях и никогда не имел проблемы. Хотя у меня нет моего прошлого кода, эта проблема была рассмотрена несколько раз раньше, чем в stackoverflow с некоторыми довольно хорошими ответами, которые были просмотрены экспертами и отредактированы сообществом.

Например, Что такое хороший шаблон для использования Глобального Mutex в С#?, есть хороший отредактированный ответ сообщества, который, как представляется, учитывает всевозможные нечетные условий шаровой гонки и завершения потоков/процессов, а также потенциальных проблем безопасности.

Поэтому мои рекомендации состоят в том, чтобы отступить от Com Interop и вместо этого реализовать реализацию Mutex.