Передать функцию обратного вызова C++ во внепроцессный метод COM

Уже есть ответ на вопрос о передаче обратного вызова C++ в метод COM внутри процесса. Но тот же метод не работает для использования вне процесса.

У меня есть интерфейс С#, и у него есть метод, который регистрирует обратный вызов/делегат с двумя параметрами.

void ProcessNotification(ConsoleNotificationType type, IntPtr parameter);

Этот интерфейс C# регистрируется с помощью внепроцессной COM-конфигурации (exe-файл). Теперь я хочу передать в этот метод функцию обратного вызова C++, но Visual Studio выдает ошибку:

InvalidFunctionPointerInDelegate

Итак, как я могу передать свою функцию обратного вызова C++ в регистрирующую функцию обратного вызова интерфейса?


person Dr. MefistO    schedule 03.11.2020    source источник


Ответы (1)


В COM нет ничего, что позволило бы вам сделать это так просто. Вы действительно не можете сделать это даже в любой программной парадигме, о которой я могу думать. Указатель в одном процессе не имеет смысла в другом процессе.

Официальный ответ состоит в том, чтобы определить интерфейс обратного вызова и передать ссылку на этот интерфейс в реализующем объекте вместо передачи простого указателя на функцию. И, конечно же, вы должны убедиться, что интерфейс можно проксировать. (В качестве альтернативы вы можете реализовать IConnectionPointContainer на своем сервере, что по сути является той же идеей.)

person Michael Gunter    schedule 03.11.2020
comment
Я не понимаю, как этот интерфейс обратного вызова из моей программы будет вызываться в серверной программе? - person Dr. MefistO; 04.11.2020
comment
@Dr.MefistO В терминологии IDL: представьте, что ваш интерфейс называется ICallback и определяет метод HRESULT Invoke(). Вы бы реализовали этот интерфейс на стороне клиента, чтобы делать все, что нужно сделать в обратном вызове. На стороне сервера у вас будет такой метод, как HRESULT Request([in] ICallback* pCallback). Ваш сервер вызовет обратный вызов как pCallback->Invoke() (или pCallback.Invoke() в C#). Предполагая, что интерфейс правильно настроен для проксирования, вызов Invoke() будет передан клиентскому процессу. - person Michael Gunter; 04.11.2020
comment
@Dr.MefistO Чтобы уточнить: обычно интерфейсы обратного вызова определяются на стороне сервера границы COM, но реализуются на стороне клиента. В вашем случае ваш сервер C#, поэтому сторона C# определит интерфейс обратного вызова как что-то вроде public interface ICallback { public void Invoke(); } и предоставит его COM с использованием необходимых атрибутов. На стороне клиента C++ у вас, вероятно, уже есть какая-то ссылка на типы, определенные в C# (возможно, с помощью директивы #import). Таким образом, вы можете просто начать реализацию интерфейса после его определения (и, возможно, перестроения). - person Michael Gunter; 04.11.2020
comment
Не могли бы вы рассказать немного больше о проксировании? Как серверный вызов функции будет направлен в мое приложение? Одна и та же COM-dll должна быть загружена (и зарегистрирована для этого ранее) для обеих сторон? - person Dr. MefistO; 04.11.2020
comment
Кажется, я понимаю. Мой клиент будет экспортировать интерфейс обратного вызова через реестр, и когда сервер захочет вызвать обратный вызов, он перечислит точки подключения, а затем вызовет каждую функцию Invoke(), верно? - person Dr. MefistO; 04.11.2020
comment
@Dr.MefistO Это глубокая и сложная тема - слишком глубокая для раздела комментариев. Дополнительную информацию можно найти по адресу docs.microsoft.com/ en-us/windows/win32/com/ . - person Michael Gunter; 04.11.2020
comment
@Dr.MefistO См. выше. Как правило, сервер определяет интерфейсы обратного вызова и при необходимости должен поместить их в реестр (вероятно, в вашем случае). Поскольку ваш COM-сервер находится вне процесса, серверная COM-dll/exe не будет загружена непосредственно в память клиента во время выполнения. Все коммуникации от клиента к серверу или наоборот будут осуществляться через прокси и объекты-заглушки, которые существуют на соответствующей стороне. Вы уже используете эти функции, даже не подозревая об этом. Любой внепроцессный COM-сервер требует, чтобы клиент вызывал прокси-интерфейсы. - person Michael Gunter; 04.11.2020
comment
Я успешно зарегистрировался в реестре и создал экземпляр такого класса обратного вызова на С++, но когда я передаю этот объект класса в запрос (обратный вызов), он сообщает мне 0x80040155: интерфейс не зарегистрирован. Что это значит? - person Dr. MefistO; 05.11.2020