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..
}
Другие вопросы по тегам