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

我有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对象中输出视频帧。

自定义演示者需要类型为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);

但我得到一个错误:



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

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



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



我的最后一次尝试是更改RegisterCallback的定义,以指向IUnknown的指针而不是IEVRPresenterCallback,在函数的声明中,我将该指针转换为正确的接口(interface)。

这使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函数中。

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

有任何想法吗?

提前致谢

最佳答案

多亏汉斯,我才能使它工作。

我将接口(interface)移到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对象。

关于c# - 从C++ COM DLL回调到C#应用程序,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8063202/

10-13 06:57