如何将事件附加到动态对象或COM对象

3
我认为这篇文章和我的问题相同。然而,对于我的情况没有可行的解决方案。
我在我的程序中使用Windows Media Player ActiveX。
由于某些原因,我不想添加它的引用并通过IDE自动转换为AxHost。
我通过ActivatorProgID创建实例。
protected const string WMP_PROG_ID = "WMPlayer.OCX.7";

private dynamic _wmp;

protected virtual bool init(){
    try{
        _wmp = Activator.CreateInstance(Type.GetTypeFromProgID(WMP_PROG_ID));
    }
    catch{ return false; }
    return true;
}

我曾试图通过Reflection来实现这一点,但我发现dynamic更适合我的情况。

每个属性和方法都正常工作,比如:

protected override bool setSpeed(float speed){
    try{
        _wmp.settings.rate = speed;
    }
    catch { return false; }
    return true;
}

protected override int getLength(){
    double res;
    try{
        res = _wmp.currentMedia.duration;
    }
    catch { return 0; }
    return (int)(res * 1000);
}

很不幸,当我想像我在顶部指出的文章那样附加事件时,它没有起作用。

我的代码如下:

protected bool connectEvent(){
_wmp.StatusChange += new EventHandler(_wmp_StatusChange);
    return true;
}

protected void _wmp_StatusChange(object sender, EventArgs e){
    Console.WriteLine(_wmp.Status);
}

我检查了StatusChange事件处理程序的类型,它是EventHandler
这些代码编译得很好,我可以加载一些音乐,播放,暂停,...做任何我想做的事情。
但是StatusChange事件从未触发过。
我尝试在connectEvent设置断点。
当运行_wmp.StatusChange += new EventHandler(...)时,IntelliTrace向我提供了一些信息。
这些信息已经用繁体中文写出来了,我认为它的意思是: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:无法将操作符"+="应用于类型System.Dynamic.DynamicObjectSystem.EventHandler 即使有异常,但就像我说的,编译通过了,一切仍然有效-除了我无法监听事件。
那么,如何在动态对象_wmp中成功附加事件?
对我来说,任何可能的解决方案(如Reflection)都是有用的。
另外,在上面的情况下,StatusChange处理程序的类型是EventHandler
但是,如果我想处理PlayStateChange事件,则如果我不添加对wmp.dll的引用,则会出现“未知句柄”。
我希望解决方案也适用于此情况。
提前感谢大家的支持,并请原谅我拙劣的英语。

你是如何确保 WMPlayer 状态正在改变的? - Srikanth Venugopalan
根据MSDN(http://msdn.microsoft.com/en-us/library/windows/desktop/dd564082.aspx)的说明,当我的“_wmp.Status”更改时,会引发此事件。我已经检查了状态字符串,并且它已经更改,因此我认为事件也应该被触发。 - J.C
同意-我想知道您是否知道状态属性被更改的情况,由于它是只读属性,您将如何使状态更改? - Srikanth Venugopalan
@SrikanthVenugopalan:在一开始,StatusString.Empty。当我通过 _wmp.URL = "some media" 加载媒体后,它会变成 _Connecting_。当媒体开始播放时,状态将更改为 _Playing some media in mm:ss_。 - J.C
看起来还不错,需要检查一下动态类型的事件绑定。你可以尝试一下这个答案中的方法:https://dev59.com/rlLTa4cB1Zd3GeqPXiJ-#4264616。反射似乎是一个选项。 - Srikanth Venugopalan
@SrikanthVenugopalan:感谢您的建议。事实上,在我提出问题之前,我已经阅读了那篇文章,但我不知道如何编写ActiveX的包装器。顺便说一下,我刚刚从MVP那里得到了一些提示,正在尝试解决这个问题。一旦我达到目标,我会在这里发布答案 :) - J.C
1个回答

1
通用策略是将使用COM对象的程序从早期绑定调用转换为晚期绑定调用,首先编写早期绑定代码。智能感知将帮助您成功地完成此操作,确保您使用正确命名的方法、传递正确类型的参数,并帮助您找出事件处理程序签名应该是什么样子的。
这会生成以下测试代码:
    void testEarlyBound() {
        var wmp = new WMPLib.WindowsMediaPlayer();
        wmp.StatusChange += new WMPLib._WMPOCXEvents_StatusChangeEventHandler(wmp_StatusChange);
    }

    void wmp_StatusChange() {
        throw new NotImplementedException();
    }

通过 IntelliSense 完全自动生成的 StatusChange 事件处理程序分配和方法体。请注意事件处理程序的签名,它不是 EventHandler,只是一个返回 void 并且不带参数的方法,它与 Action 委托类型匹配。现在你有机会编写晚期绑定版本而无需不可诊断的运行时异常:

    void testLateBound() {
        dynamic wmp = Activator.CreateInstance(Type.GetTypeFromProgID("WMPlayer.OCX"));
        wmp.StatusChange += new Action(wmp_StatusChange);
    }

我从一位MVP在Trad. Chinese MDSN Social上得到了相同的提示/答案。我已经实现了StatusChangePlayStateChange,目前为止还不错。但是,我无法附加到wmp.Error,异常描述为“无法将运算符'+='应用于'System.__ComObject'和'Action'”。我仍在努力解决这个问题。我会按照您的提示尝试解决这个问题。谢谢。 - J.C
根据MSDN文档,似乎存在一个错误事件。但是当我使用WMPLib.WindowsMediaPlayer类型时,Error标记是一个属性。我正在寻找一种方法来附加错误事件。 - J.C
假设事件处理程序有这样的参数 public void wmp_StatusChange(object sender , MyCustomArgument e){ } 在这种情况下,事件注册代码将是什么? - Aneesh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接