Обратный вызов из C COM DLL в приложение C #

#c# #c #dll #com #ms-media-foundation

#c# #c #dll #com #ms-media-foundation

Вопрос:

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

У меня есть C COM DLL, которая содержит класс VideoPlayer, который использует API Media Foundation для отображения видео.

Класс VideoPlayer определяется с использованием IDL-файла:

 [
    object,
    uuid(74FDBBB1-BFFB-4F7E-ACA3-ADB0C7232790),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IVideoPlayer : IDispatch {

    [id(1)] HRESULT Initialize([in] HWND* video_hwnd, [in] HWND* event_hwnd);
    [id(2)] HRESULT OpenUrl([in] BSTR url_path);
    [id(3)] HRESULT Play();
    [id(4)] HRESULT HandleEvent([in] INT pEventPtr);
    [id(5)] HRESULT Repaint(void);
    [id(6)] HRESULT Resize([in] LONG width, [in] LONG height);
};
  

Этот класс внутренне использует пользовательский презентатор (основанный на проекте WPFMediaKit), который выводит видеокадры внутри объекта IDirect3DSurface9.

Пользовательскому презентатору требуется обратный вызов типа IEVRPresenterCallback, который определяется следующим образом:

 MIDL_INTERFACE("B92D8991-6C42-4e51-B942-E61CB8696FCB")
IEVRPresenterCallback : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE PresentSurfaceCB(IDirect3DSurface9 *pSurface) = 0;
};
  

Как вы можете видеть, он не определен в файле IDL, но объявлен в заголовочном файле.

Мне нужно добавить новую функцию в класс VideoPlayer, которая позволяет вызывающему коду C # передавать экземпляр класса, наследуемого от IEVRPresenterCallback, для которого будет установлен пользовательский presenter.

Я попытался добавить эту строку в IDL-файл видеоплеера:

 [id(7)] HRESULT RegisterCallback2([in] IEVRPresenterCallback * p_PresenterCallback);
  

Но я получаю сообщение об ошибке:

ошибка MIDL2025: синтаксическая ошибка: ожидается спецификация типа рядом с «IEVRPresenterCallback»

Я думаю, это нормально, потому что я ничего не импортировал в IDL. Что нормально, поскольку IEVRPresenterCallback определен в заголовочном файле.

Я попытался импортировать файл заголовка, но макрос MIDL_INTERFACE определения IEVRPresenterCallback выдает ошибку:

ошибка MIDL2025: синтаксическая ошибка: ожидается имя интерфейса, или DispatchInterfaceName, или CoclassName, или ModuleName, или LibraryName, или ContractName, или спецификация типа рядом с «MIDL_INTERFACE»

Затем я попытался перенаправить объявление интерфейса, но я получил эту ошибку:

ошибка MIDL2011: неразрешенное объявление типа: IEVRPresenterCallback [ Параметр ‘p_PresenterCallback’ процедуры ‘RegisterCallback2’ (интерфейс ‘IVideoPlayer’) ]

Моей последней попыткой было изменить определение RegisterCallback, чтобы иметь указатель на IUnknown вместо IEVRPresenterCallback, и в объявлении функции я привел указатель на правильный интерфейс.

Это обеспечивает правильную компиляцию библиотеки DLL C .

В приложении C # я задаю обратный вызов следующим образом:

 [ComVisible(true), ComImport, SuppressUnmanagedCodeSecurity, Guid("B92D8991-6C42-4e51-B942-E61CB8696FCB"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IEVRPresenterCallback
{
    [PreserveSig]
    int PresentSurfaceCB(IntPtr pSurface);
}

internal class EVRPresenterCallback : IEVRPresenterCallback
{
    public int PresentSurfaceCB(IntPtr pSurface)
    {
        return 0;
    }
}

public partial class MainWindow : Window
{
    private EmideeMediaFoundationLib.IVideoPlayer videoPlayer = new EmideeMediaFoundationLib.VideoPlayer();
    private EVRPresenterCallback callback = new EVRPresenterCallback();

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        videoHost.VideoPlayer.RegisterCallback(callback);
        videoHost.VideoPlayer.OpenUrl(@"C:UsersPublicVideosSample Videoswildlife.wmv");
    }
}
  

Проблема, с которой я сталкиваюсь, заключается в том, что, несмотря на то, что пользовательский презентатор вызывает обратный вызов, я никогда не возвращаюсь в функцию C # PresentSurfaceCB.

Я полностью застрял прямо сейчас, и я не знаю, в чем проблема, и как ее решить : (

Есть идеи?

Заранее спасибо

Комментарии:

1. Перестаньте пытаться избежать помещения интерфейса в IDL, в этом нет смысла. Вы не можете приводить указатели COM, вы должны использовать QueryInterface().

2. Я пытался объявить интерфейс в IDL, но тогда у меня возникли проблемы с интерфейсом ID3D9Surface… Если я не изменю объявление IEVRCallback, чтобы использовать длинный указатель вместо ID3D9Surface? Я попробую.

Ответ №1:

Благодаря Хансу я смог заставить это работать.

Я переместил интерфейс в файл IDL, и вместо возврата указателя ID3D9Surface в обратном вызове я возвращаю DOWRD_PTR:

 [
    uuid(B92D8991-6C42-4e51-B942-E61CB8696FCB),
]
interface IEVRPresenterCallback : IUnknown {
    [id(1)] HRESULT PresentSurfaceCB( DWORD_PTR pSurface);
}

[
    object,
    uuid(74FDBBB1-BFFB-4F7E-ACA3-ADB0C7232790),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IVideoPlayer : IDispatch {

    [id(1)] HRESULT Initialize([in] HWND* video_hwnd, [in] HWND* event_hwnd);
    [id(2)] HRESULT OpenUrl([in] BSTR url_path);
    [id(3)] HRESULT Play();
    [id(4)] HRESULT HandleEvent([in] INT pEventPtr);
    [id(5)] HRESULT Repaint(void);
    [id(6)] HRESULT Resize([in] LONG width, [in] LONG height);
    [id(7)] HRESULT RegisterCallback([in] IEVRPresenterCallback * p_PresenterCallback);
};
  

В моем приложении WPF я создаю класс, производный от IEVRCallback:

 internal class EVRPresenterCallback : EmideeMediaFoundationLib.IEVRPresenterCallback
{
    public void PresentSurfaceCB(uint pSurface)
    {
    }
}
  

и я передаю этот экземпляр объекту VideoPlayer.

Комментарии:

1. public void PresentSurfaceCB(uint pSurface) — Не должно ли этого быть public int PresentSurfaceCB(uint pSurface) , поскольку PresentSurfaceCB возвращает HRESULT? Проблема, с которой я сталкиваюсь, заключается в том, что если у меня тип возврата обратного вызова, объявленный как int вместо void, я получаю нарушение доступа в режиме x86, но отлично работает в x64!