views:

2018

answers:

3

At the risk of sounding like a total noob, how do I implement ISynchronizeInvoke on a System.Timers.Timer?

I have a class (no UI) that is making calls to mciSendString. I have a timer that is supposed to poll for the current status. All of the calls from the class work, but not the ones coming from the timers elapsed event. I've tracked it down to being on a different thread, but I've not gotten any further than that. I think I need to invoke a delegate on the same thread as the class, but I haven't had any luck accomplishing it yet.

Code Sample:

    [DllImport("winmm.dll")]
    private static extern Int32 mciSendString(string command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);

    public cPlayer()
    {
        tmrPoll = new System.Timers.Timer(1000);
        tmrPoll.Enabled = false;
        tmrPoll.Elapsed += new ElapsedEventHandler(tmrPoll_tick);
    }

    public void tmrPoll_tick(object source, ElapsedEventArgs e)
    {
        Poll();
    }

    private void Poll()
    {
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
        tmrPoll.Stop();
        int res = 0;

        res = mciSendString("status MediaFile position", _sbBuffer, _sbBuffer.Capacity, IntPtr.Zero);
        if (res == 0) _position = int.Parse(_sbBuffer.ToString());
        if (res == 0)
        {
            Console.WriteLine("Position = " + _sbBuffer.ToString());
        } else {
            Console.WriteLine("Failed:  Error " + res.ToString());
        }

        res = mciSendString("status MediaFile length", _sbBuffer, _sbBuffer.Capacity, IntPtr.Zero);
        if (res == 0) Console.WriteLine("Length = " + _sbBuffer.ToString());
        if (res == 0) _length = int.Parse(_sbBuffer.ToString());

        res = mciSendString("status MediaFile mode", _sbBuffer, _sbBuffer.Capacity, IntPtr.Zero);
        if (res == 0) Console.WriteLine("Mode = " + _sbBuffer.ToString());
    }

    private void SendCommand(string cmd)
    {
        mciSendString(cmd, null, 0, IntPtr.Zero);
        Poll();

    }

For clarification, if I call from SendCommand (whatever it may be, Play, Stop, etc.) it works and the result of Poll() is what I'd expect. When the timer fires, the result (res) is 263 which is MCIERR_INVALID_DEVICE_NAME. The threadID on the failed call is different from the one that succeeds, that's why I figured I needed to use ISynchronizeInvoke.

A: 

What is the error code that you are getting? I imagine part of it has to do with the hwndCallback parameter, but without seeing any code samples, it's hard to tell.

However, you don't need to implement ISynchronizeInvoke here. You might have to call it if you are passing a window handle for the callback. In that case, you will need to call the Invoke method on the managed representation that implements ISynchronizeInvoke. In other words, the control/form that you got the handle from.

casperOne
A: 

I made the following changes, but they didn't help. I'm still at a loss.

    //Added this delegate
    delegate void PollDelegate(); 
    private PollDelegate pd;

    [DllImport("winmm.dll")] 
    private static extern Int32 mciSendString(string command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback); 

    public cPlayer() 
    { 
        tmrPoll = new System.Timers.Timer(1000); 
        tmrPoll.Enabled = false; 
        tmrPoll.Elapsed += new ElapsedEventHandler(tmrPoll_tick); 

        // instansiated
        pd = new PollDelegate(() => Poll()); 
    } 

    public void tmrPoll_tick(object source, ElapsedEventArgs e) 
    { 
        // And Invoke
        pd.Invoke(); 
        //Poll(); 
    } 

    private void Poll() 
    { 
        Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()); 
        tmrPoll.Stop(); 
        int res = 0; 

        res = mciSendString("status MediaFile position", _sbBuffer, _sbBuffer.Capacity, IntPtr.Zero); 
        if (res == 0) _position = int.Parse(_sbBuffer.ToString()); 
        if (res == 0) 
        { 
            Console.WriteLine("Position = " + _sbBuffer.ToString()); 
        } 
        else 
        { 
            Console.WriteLine("Failed:  Error " + res.ToString()); 
        } 

        res = mciSendString("status MediaFile length", _sbBuffer, _sbBuffer.Capacity, IntPtr.Zero); 
        if (res == 0) Console.WriteLine("Length = " + _sbBuffer.ToString()); 
        if (res == 0) _length = int.Parse(_sbBuffer.ToString()); 

        res = mciSendString("status MediaFile mode", _sbBuffer, _sbBuffer.Capacity, IntPtr.Zero); 
        if (res == 0) Console.WriteLine("Mode = " + _sbBuffer.ToString()); 
    } 

    private void SendCommand(string cmd) 
    { 
        mciSendString(cmd, null, 0, IntPtr.Zero); 
        Poll(); 

    }

The result is still the same, a 263 from the mciSendString, and no status. I did try calling Poll() from outside the class, and that worked. I'm convinced it is related to the timer thread, but I'm at a loss how to correct it.

Krakerjak
Krakerjak: Then this is not an answer, please delete it and incorporate the updates in your question instead. Answers should only be used to *answer* the question, not to say you couldn't solve it.
chiccodoro
+2  A: 

I got an answer over at the msdn forum. Someone left me a message with a link to codeplex (nito.async). I used the GenericSynchronizingObject to get the job done.

Krakerjak