来自 C++ 的回调COM DLL 到 C# 应用程序

发布于 2024-12-14 13:58:18 字数 3195 浏览 0 评论 0原文

这将是一篇很长的文章,因为我想向您展示我尝试完成此工作的所有步骤:)

我有 C++ COM dll,其中包含一个 VideoPlayer 类,该类使用 Media Foundation API 来显示视频。

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 对象内的视频帧。

自定义Presenter需要一个IEVRPresenterCallback类型的回调,它的定义如下:

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

正如你所看到的,它不是在IDL文件中定义的,而是在头文件中声明的。

我需要向 VideoPlayer 类添加一个新函数,该函数允许调用 C# 代码传递继承自 IEVRPresenterCallback 的类的实例,该实例将设置为自定义演示者。

我尝试将此行添加到 VideoPlayer 的 IDL 文件中:

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

但出现错误:

错误 MIDL2025:语法错误:期望附近有类型规范 “IEVRPresenterCallback”

我想这是正常的,因为我没有在IDL中导入任何东西。这是正常的,因为 IEVRPresenterCallback 是在头文件中定义的。

我尝试导入头文件,但 IEVRPresenterCallback 定义的 MIDL_INTERFACE 宏生成错误:

错误 MIDL2025:语法错误:需要接口名称或 DispatchInterfaceName 或 CoclassName 或 ModuleName 或 LibraryName 或 ContractName 或“MIDL_INTERFACE”附近的类型规范

然后我尝试转发声明接口,但出现此错误:

错误 MIDL2011:未解析的类型声明:IEVRRPresenterCallback [过程“RegisterCallback2”的参数“p_PresenterCallback”(接口“IVideoPlayer”)]

我最后一次尝试是更改 RegisterCallback 的定义,将指针指向 IUnknown 而不是 IEVRPresenterCallback,并在声明中在该函数中,我将指针投射到正确的接口。

这使得 C++ dll 可以正确编译。

在 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:\Users\Public\Videos\Sample Videos\wildlife.wmv");
    }
}

我遇到的问题是,尽管自定义演示者调用了回调,但我从未返回到 C# PresentSurfaceCB 函数。

我现在完全陷入困境,我不知道问题出在哪里,也不知道如何解决:(

有什么想法吗?

提前致谢

This is going to be a long post, as I want to expose you all the steps I tried to make this work :)

I have C++ COM dll which contains a VideoPlayer class which uses the Media Foundation API to display a video.

The VideoPlayer class is defined using an IDL file:

[
    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);
};

This class internally uses a custom presenter (which is based on the WPFMediaKit project), which outputs the video frames inside a IDirect3DSurface9 object.

The custom presenter needs a callback of type IEVRPresenterCallback, which is defined as follow:

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

As you can see, it is not defined in a IDL file, but is declared in a header file.

I need to add a new function to the VideoPlayer class, which allows the calling C# code to pass an instance of a class inheriting from IEVRPresenterCallback, which will be set to the custom presenter.

I've tried to add this line to the IDL file of the VideoPlayer:

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

But I get an error:

error MIDL2025: syntax error : expecting a type specification near
"IEVRPresenterCallback"

I guess it is normal, because I didn't import anything in the IDL. Which is normal, as IEVRPresenterCallback is defined in a header file.

I tried to import the header file, but the MIDL_INTERFACE macro of the IEVRPresenterCallback definition generates an error:

error MIDL2025: syntax error : expecting an interface name or DispatchInterfaceName or CoclassName or ModuleName or LibraryName or ContractName or a type specification near "MIDL_INTERFACE"

I then tried to forward declare the interface, but I got this error:

error MIDL2011: unresolved type declaration : IEVRPresenterCallback [ Parameter 'p_PresenterCallback' of Procedure 'RegisterCallback2' ( Interface 'IVideoPlayer' ) ]

My last attempt was to change the definition of RegisterCallback, to have a pointer to IUnknown instead of IEVRPresenterCallback, and in the declaration of the function, I cast the pointer to the correct interface.

This makes the C++ dll compile correctly.

In the C# application, I set the callback as follow:

[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:\Users\Public\Videos\Sample Videos\wildlife.wmv");
    }
}

The problem I get is despite the custom presenter calling the callback, I never get back in the C# PresentSurfaceCB function.

I'm completely stuck right now, and I don't know where the problem is, nor how to solve it :(

Any ideas?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦里兽 2024-12-21 13:58:18

感谢汉斯,我可以让它发挥作用。

我移动了 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 对象。

Thanks to Hans, I could make it work.

I moved the interface in the IDL file, and instead of returning a ID3D9Surface pointer in the callback, I return a 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);
};

In my WPF application, I create a class deriving from IEVRCallback:

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

and I give this instance to the VideoPlayer object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文