Проблемы с доступом к таблице текущих объектов
В моей программе я использую таблицу 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);
}
}
Ответы
Ответ 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.