C#: получить объект COM из таблицы запущенных объектов
Я работаю над проектом, который использует API стороннего COM-сервера. COM-сервер является локальным сервером (вне процесса exe), на котором у меня нет контроля.
Я пытаюсь получить доступ к COM-объектам из таблицы объектов runnin, чтобы выбрать между несколькими экземплярами COM-объектов, запущенных с каждым экземпляром приложения:
private static List<object> GetRunningInstances(string progId) {
// get Running Object Table ...
IRunningObjectTable Rot = null;
GetRunningObjectTable(0, out Rot);
if (Rot == null)
return null;
// get enumerator for ROT entries
IEnumMoniker monikerEnumerator = null;
Rot.EnumRunning(out monikerEnumerator);
if (monikerEnumerator == null) return null;
monikerEnumerator.Reset();
List<object> instances = new List<object>();
IntPtr pNumFetched = new IntPtr();
IMoniker[] monikers = new IMoniker[1];
while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0) {
IBindCtx bindCtx;
CreateBindCtx(0, out bindCtx);
if (bindCtx == null) continue;
Guid clsid = Type.GetTypeFromProgID(progId).GUID;
string displayName;
Guid monikerClsid;
Guid riid = Marshal.GenerateGuidForType(typeof(IApplication));
object obj;
monikers[0].GetDisplayName(bindCtx, null, out displayName);
monikers[0].GetClassID(out monikerClsid);
if (displayName.IndexOf(clsid.ToString(), StringComparison.OrdinalIgnoreCase) > 0) {
//monikers[0].BindToObject(bindCtx, null, ref unkid, out obj);
Rot.GetObject(monikers[0], out obj);
instances.Add((IApplication)obj);
}
}
}
Если я запускаю два экземпляра целевого приложения, дамп ROT показывает два экземпляра соответствующего COM-объекта (здесь он называется IApplication), поскольку GetDisplayName показывает правильный clsid для интерфейса IApplication, зарегистрированного в реестре.
Проблема в том, что объекты, которые я получаю из Rot.GetObject, описаны как System.__ComObject и не могут быть преобразованы в IApplication (InvalidCastException, потому что QueryInterface не удалось с E_NOINTERFACE), даже если их моникер описывает правильный clsid...
Я попытался привести его прогамматически ко всем доступным типам в моем проекте, просто чтобы посмотреть, но единственный успех - это приведение его к System.__ComObject...
Я также попытался использовать IMoniker.BindToObject вместо Rot.GetObject, но на этот раз я получаю исключение FileNotFound, когда я предоставляю соответствующий GUID интерфейса. BindToObject работает, когда я предоставляю рид для IUnknown, но он дает мне Систему.__ ComObject, который я не могу разыграть (обратно на круги своя!).
Для информации, в дампе ROT я также могу показать моникер файла, соответствующий открытому проекту в целевом приложении, но я также не могу создать COM-объект из этого.
У кого-нибудь есть идеи о том, как правильно извлечь объекты из ROT?
С уважением.
РЕДАКТИРОВАТЬ:
Спустя несколько дней и по-новому взглянув на эту проблему, я обнаружил, что CLSID в отображаемом имени моникера не совсем то, что я хочу, но имеет две цифры (тест indexof оказывается неправильным).
После тщательного прочтения clsid выясняется, что clsid в отображаемом имени моникера является clsid кокласса IApplication, а не clsid IApplication.
Я попытался привести объект к "ApplicationClass" (вышеупомянутый coclass), но это дает мне исключение. Дополнительная информация, которую я получаю (на французском), может быть переведена следующим образом: Невозможно привести экземпляры-обертки __ComObject к другому классу, но возможно привести эти экземпляры к интерфейсам, если базовый компонент com поддерживает вызовы QueryInterface для интерфейса IID.
Есть идеи, как действовать дальше?
0 ответов
Я не хочу сейчас, если у вас все еще есть проблема, но, поскольку ответа еще нет, я хочу поделиться тем, что я сделал после нескольких недель исследований по работе с несколькими COM-интерфейсами на ROT.
Я получил все объекты из ROT и сохранил их в хэш-таблице, чтобы обеспечить уникальность с помощью ключа / значения, но вы можете настроить его на список по своему усмотрению. Вам просто нужно получить значение текущего объекта, чтобы добавить свой список.
public static Hashtable GetRunningObjectTable()
{
Hashtable result = new Hashtable();
IntPtr numFetched = new IntPtr();
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
result[runningObjectName] = runningObjectVal;
}
return result;
}
После этого вы можете преобразовать объект, хранящийся в вашем списке, в свой COM-интерфейс. Например, я также поделился своим способом, который работает для интерфейса COM Rhapsody, ниже;
Hashtable runningObjects = GetRunningObjectTable();
IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
while (rotEnumerator.MoveNext())
{
string candidateName = (string)rotEnumerator.Key;
if (!candidateName.StartsWith("Rhapsody"))
continue;
rhapsody.RPApplication app = rotEnumerator.Value as rhapsody.RPApplication;
if (app == null)
continue;
// Do your stuff app (com object) in here..
}