C# Запуск Active Automation Object - Как источник событий?

У меня есть (долго работающее) консольное приложение, написанное на C#, которым я хочу иметь возможность манипулировать через COM (поэтому нет InProc DLL и regasm.exe). IDispatch это все, что мне нужно - это классический объект OLE Automation.

Здесь я представлю минимальную версию того, что я пытаюсь сделать. Я определил класс COM следующим образом:

[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("9009311a-c0b2-42a4-8e7c-f42091d71594")]
public interface ITestEvents {
    void OnEvent();
}

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComSourceInterfaces(typeof(ITestEvents))]
public class ComClass {
    public event Action OnEvent;

    public int Test() {
        return 100;
    }
}

В Main() Я просто регистрирую объект в таблице запущенных объектов (ROT) с помощью "comTestApp" Моникер и сон приложение. Вы можете увидеть полный источник здесь.

Это прекрасно работает, когда я пытаюсь вызвать методы объекта. Например, этот VBScript работает нормально:

Set obj = GetObject("comTestApp")
WScript.Echo obj.Test() Rem prints 100

Но когда я пытаюсь связать события:

Set obj = GetObject("comTestApp")
WScript.Echo obj.Test() Rem Works

WScript.ConnectObject obj, "obj_" Rem Fails

Sub obj_OnEvent
    WScript.Echo "Wish it worked"
End Sub

Я получаю ошибку при звонке ConnectObject (ошибка 0x80020009 "Не удалось подключить объект").

Тот же код (нужно добавить только класс GUID/ProgID) работает, если я регистрирую сборку как объект InProc в regasm.exe, но мне это не нужно. Мне нужен доступ к запущенному приложению, и поэтому я использую ROT.

Я создал простой тест на C++, чтобы узнать, смогу ли я узнать больше о проблеме. Источник здесь. Я написал минимальный COM-объект, реализующий IDispatch интерфейс, который должен действовать как приемник событий. Сначала я получаю объект из ROT, запрашиваю IConnectionPointContainer, тогда получите IConnectionPoint за ITestEventsIID и наконец позвони Advise() метод. Как и в случае с VBScript, происходит сбой (хотя я получаю еще одну ошибку - 0x80040202). Я установил точку останова на QueryInterface Метод приемника событий, чтобы увидеть, что происходит, когда Advise() называется. я могу увидеть это QueryInterface вызывается для различных интерфейсов и, наконец, он запрашивает мой ITestEvents, который я возвращаю и устанавливаю статус S_OK, Но все же, Advise() Метод возвращает вышеуказанную ошибку.

Я также попробовал еще одну вещь: я установил GUID ITestEvents в {00020400-0000-0000-C000-000000000046} который является IID IDispatch, И сейчас Advise() возвращается S_OK! Я даже смоделировал событие и Invoke() вызывается метод приемника событий! Увы, это не решает проблему в целом. Если вы запрашиваете это непосредственно от IConnectionPointContainer по IID - вы получаете это, но, кажется, это не правильно перечислено ITypeInfo и VBScript все еще не работает.

У меня почти нет опыта работы с COM, поэтому я не уверен, куда идти дальше. Тот факт, что если я использую IDispatch IID заставляет это работать, заставляет меня задаться вопросом, нужен ли какой-то специальный маршалинг для ITestEvents интерфейс, хотя это чисто IDispatch поэтому я думаю, что это должно быть хорошо обработано во время выполнения.

Спасибо!

1 ответ

Решение

Мой вывод заключается в том, что невозможно предоставить приемник подключений (для объекта Out of Process), чей IID интерфейса не зарегистрирован в реестре.

Кажется, что информация о маршалинге обязательна для отправки интерфейса приемника на сервер. Я надеялся, что так как классы C# поставляют полный ITypeInfoсреда выполнения COM выяснит, что интерфейс IDispatch введите и используйте прокси по умолчанию. Увы, похоже, что это не так, поэтому остается единственный вариант - реестр.

Как минимум я обнаружил, что это необходимо:

[HKEY_CLASSES_ROOT\Interface\{9009311a-c0b2-42a4-8e7c-f42091d71594}\ProxyStubClsid32]
@="{00020424-0000-0000-C000-000000000046}"

Это определяет наш интерфейс событий и говорит, что мы используем для прокси стандартный прокси IDispatch.

Я проверил это на Windows 7 и 10, и это работает! - VBScript может ConnectObject и все работает отлично.

Случайно я нашел установку Win7, где это не сработало (win ver 6.1 build 7601 sp1). Работало только после регистрации библиотеки типов (regasm.exe /tlb). В рассматриваемой установке отключены обновления Windows (мой другой Win7 полностью обновлен), поэтому я думаю, что в какой-то момент что-то изменилось, что позволяет маршалировать интерфейс даже без библиотеки типов, основываясь на том факте, что мы указываем прокси-сервер IDispatch по умолчанию,

Наконец, поскольку я хотел сохранить свое приложение переносимым и независимым от реестра, я прибегнул к ручной реализации событий. Нечто подобное можно увидеть здесь.

Другие вопросы по тегам