views:

378

answers:

4

I'm learning C# event handling by writing an app that uses the iTunes COM API. I have a method that should run when iTunes stops playing a song, but the method is never getting called when I trigger the event in the app by hitting the "stop/pause" button.

EDIT: Based on dboarman's reply, I deleted the while loop. Now the event does get handled, but only if I restart iTunes prior to running PlayPlaylist(). If I run PlayPlaylist() a second time, the stop event no longer gets fired/handled.

void trayIcon_Click(object sender, EventArgs e)
{
    PlayPlaylist();
}

public static void PlayPlaylist()
{

    itapp = new iTunesApp();
    itapp.OnPlayerStopEvent += 
        new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);

    lastPlaylist = itapp.LibraryPlaylist;

    itapp.Play();            
}

static void itapp_OnPlayerStopEvent(object iTrack)
{
    Debug.WriteLine("Stop Event fired");
    //...
}

Updated source in Pastebin here (lines 59-68 are the relevant ones).

Spec: My app is supposed to play the songs in a Genius recommendations playlist from first to last (iTunes by default doesn't play Genius recommendations consecutively). The StopEvent should trigger the next song in the list to play.

+2  A: 

Here is the complete code that is in question:

public static void PlayPlaylist()
{
    itapp = new iTunesApp();
    itapp.OnPlayerStopEvent += new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);

    lastPlaylistID = itapp.LibraryPlaylist.playlistID;
    Debug.WriteLine("Last playlist:");
    Debug.WriteLine(lastPlaylistID);

    itapp.Play();

    while (true)
    {
        System.Threading.Thread.Sleep(1000);
    }
}

I suspect that the while loop is causing the event to never fire because the thread will sleep for a second and because true is, well...always true.

I would put your playlist in into a list. Something like:

static List<myTrack> Tracks;

public static void PlayPlaylist() 
{
    itapp = new iTunesApp();
    itapp.OnPlayerStopEvent += new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);

    foreach (myTrack track in Tracks)
    {
    // perform play
    }
}

See how that works for you.

dboarman
even before looping over songs in a playlist, maybe just delete the entire while loop. There isn't a need to sit there and wait -- presumably, this method is called from a main app that's still running?
Dave
+1: good point about the main app... ;)
dboarman
Thanks dboarman. My intention with the Sleep command was to keep my program running so that when the event fired, the process would be listening to handle it. Otherwise, my program just finishes and exits. Do you see what I mean? Is there a better way to achieve this goal?I considered a design like the one you propose, but what if I want to pause my playlist manually? The for loop will just keep running and override my command. Also, I want to learn what I'm doing wrong so that I can understand how event-based programming works.
RexE
Well, I would create an application (windows forms app) so that you have a loop. It's called Application.Run(). This will allow you to create other controls, like a listbox, fill it with your playlist...add a button with on_click() to play your list, etc. ;)
dboarman
Thanks; I realized that since I already have this in a WinForms app I don't need the Sleep command. Took that out; it's not working but I'll try debugging it a bit more.
RexE
+1  A: 

If you want the thread to block and wait for the event you can use the ManualResetEvent class.

private ManualResetEvent _resetEvent;

public void PlayPlaylist() 
{
    _resetEvent = new ManualResetEvent(false);
    itapp = new iTunesApp();
    itapp.OnPlayerStopEvent += new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);


    // Block until the resetEvent has been Set() or
    // give up waiting after 5 minutes
    _resetEvent.WaitOne(1000*5*60); 
}

Inside itapp_OnPlayerStopEvent() you must call: _resetEvent.Set();

To answer your original question, I'm pretty sure the while loop is not giving the thread any time to respond to the stop event, hence you are not seeing it being handled.

Phillip Ngan
+1 nice: I had thought of the ManualResetEvent, but I still have problems putting the .Set()/.Reset() in the right place. This should help me remember... ;)
dboarman
Thanks Philip, but I tried that and it still isn't working, unfortunately.
RexE
A: 

I'm wondering if the fact that the event handler doesn't unhook is causing an issue somewhere along the line (i.e. iTunes holds a singular reference to the initial instance of your app). This may solve it? I don't have the iTunes API so I'm flying a little blind; apologize if it's a waste of time.

private bool stopIt;

private void trayIcon_Click(object sender, EventArgs e)
{
    if (!stopIt)
    {
        PlayPlaylist();
        stopIt = true;
    }
    else
    {
        StopPlaylist();
        stopIt = false;
    }
}

public static void PlayPlaylist()
{

    itapp = new iTunesApp();
    itapp.OnPlayerStopEvent += 
        new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);

    lastPlaylist = itapp.LibraryPlaylist;

    itapp.Play();            
}

public static void StopPlaylist()
{
    itapp.Stop(); // don't know if this is the right method name

    // unhook the event so the reference object can free if necessary.
    itapp.OnPlayerStopEvent -= 
        new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);
}

private static void itapp_OnPlayerStopEvent(object iTrack)
{
    Debug.WriteLine("Stop Event fired");
    //...
}
Jesse C. Slicer
+1  A: 

When your itapp goes out of scope, be sure to release it with

System.Runtime.InteropServices.Marshal.ReleaseComObject(itapp);

or you'll have to restart iTunes for it to work again. Unregistering the event handlers with -= probably wouldn't hurt either.

JustABill