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
за ITestEvents
IID и наконец позвони 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 по умолчанию,
Наконец, поскольку я хотел сохранить свое приложение переносимым и независимым от реестра, я прибегнул к ручной реализации событий. Нечто подобное можно увидеть здесь.