How to attach event to dynamic object or COM objec

2019-05-26 14:06发布

问题:

I think this article has the same problem with me. However, there's no workable solution for my case.

I'm using Windows Media Player ActiveX in my program.

For some reason, I don't want to add a reference of it and convert to AxHost automatically by IDE.

I create the instance by Activator and ProgID

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

I was tried to do this by Reflection, but I found that dynamic is suitable to my case.

Every property and method works alright, like these:

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

Unfortunately, when I want to attach event like the article I indicated in the top, it got no work.

My code like this:

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

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

I've checked the type of event handler of StatusChange, it's EventHandler.

These codes compiled well, and I can load some music, play it, pause it, ...do anything I like.

But the StatusChange event never triggered.

I tried to set a break-point at connectEvent.

When run at _wmp.StatusChange += new EventHandler(...), the IntelliTrace give me some information.

Those information had written in Trad. Chinese, I think it means:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Could not apply operator "+=" to type System.Dynamic.DynamicObject and System.EventHandler

Even though there's an exception, but just like I said, compile was passed, everything still work -- except I could not listen event.

So, how can I attach event successfully in the dynamic object _wmp?

Any possible solution (like Reflection) is useful to me.

Also, in the case above, the handler type of StatusChange is EventHandler.

But if I want to handle PlayStateChange event, it is an "Unknown handle" if I don't add a reference of wmp.dll.

I hope the solution is suitable to this case, too.

Thanks everyone in advance for all of your support, and please forgive me for my poor English.

回答1:

The generic strategy to turn a program that uses a COM object from early bound to late bound calling is to first write it early bound. IntelliSense will help you fall in the pit of success, ensuring that you use correctly named methods, pass the right kind of arguments and particularly useful to help you find out what the event handler signatures should look like.

Which produces this bit of test code:

    void testEarlyBound() {
        var wmp = new WMPLib.WindowsMediaPlayer();
        wmp.StatusChange += new WMPLib._WMPOCXEvents_StatusChangeEventHandler(wmp_StatusChange);
    }

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

With the StatusChange event handler assignment and method body completely auto-generated by IntelliSense. Note the signature of the event handler, it is not an EventHandler. Just a method that returns void and takes no arguments, it matches the Action delegate type. Now you have a good shot at writing the late-bound version without the undiagnosable runtime exceptions:

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