Проблемы с доступом к таблице запущенных объектов
В моей программе я использую таблицу запущенных объектов (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 - Еще один намек на то, что 1 из 3 проблем (помимо 39 000 работающих правильно) работает приложения на WTS (Windows Terminal Server)
Изменить 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 ответа
По моему опыту вероятность коллизии GUID пока возможна, представляется маловероятной, поэтому она не исследовалась. Первый трек, который я выбрал, искал то, что могло вызвать AccessDeniedException
, Работая в обратном направлении, вы можете увидеть, что GetDisplayName явно не выдает это исключение (или не возвращает ничего подобного).
Так что же? Ваш код, кажется, находится в C#. Если я не ошибаюсь, использование COM из C# пройдет через первичное взаимодействие. Есть только два (2) взаимодействия, которые IMoniker
интерфейс, который я мог найти:
System.Runtime.InteropServices.ComTypes
содержит IMonikerMicrosoft.VisualStudio.OLE.Interop
также содержит один IMoniker
Вы говорите о приложении, поэтому моя интуиция говорит мне, что вы используете версию во время выполнения. Глядя на звонки, я не смог найти звонок, возвращающий какую-либо форму отказа в доступе HRESULT
или что-то подобное. VisualStudio
В interop упоминается следующее о доступе и доверии: Использование библиотек из частично доверенного кода. Это звучало как путь для подражания и будет применяться, если вы используете взаимодействия Visual Studio.
Если вы используете пространство имен сервисов времени выполнения, которое содержится в mscorlib.dll
сборка (которая в соответствии с этой страницей .NET Framework Сборки, вызываемые частично доверенным кодом помечается как вызываемый частично доверенный код), объяснение, по-видимому, не применимо.
Что теперь? Я сделал поиск AccessDeniedException
и не нашел поддерживаемой реализации, кроме класса http://msdn.microsoft.com/en-us/library/microsoft.office.server.applicationregistry.infrastructure.accessdeniedexception.aspx, который помечен как устаревший в MSDN. Класс хранится в библиотеке классов 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 (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.
Возможно, это не тот ответ, который вы ищете, но, унаследовав этот код, вы перестали задавать вопрос, является ли этот метод даже подходящим для вашего варианта использования? Это первый случай, когда приложение C# использует Com Interop для чего-то вроде предотвращения нескольких экземпляров приложения. У меня никогда не было хорошего опыта работы с Com, и я нашел похожие необъяснимые или недокументированные исключения.
Почему бы не взглянуть на альтернативный метод предотвращения нескольких экземпляров приложения? Я использовал Mutex в моих прошлых решениях, и у меня никогда не было проблем. Хотя мой предыдущий код не был под рукой, эта проблема уже была рассмотрена несколько раз в stackru с некоторыми довольно хорошими ответами, которые были рассмотрены коллегами и отредактированы сообществом.
Например, " Что такое хороший шаблон для использования Global Mutex в C#?'имеет хороший отредактированный ответ сообщества, который, кажется, учитывает все виды нечетных условий гонки мяча и завершения потока / процесса, а также потенциальные проблемы безопасности.
Поэтому я рекомендую отойти от Com Interop и вместо этого воспользоваться реализацией Mutex.