Tag Archives: C#

c#itunes_icon

iTunes Integration with .NET

Inspiration

A few months back, I took a few lessons at a Ballroom dance studio in Knoxville. During their parties, the owner played the music to dance to on his laptop (which was connected to the sound system). He had just come up with an interesting system to let the students/guests what dance style was playing at the time. He had printed and laminated cards with each dance style, and taped them on one of columns. They were sorted top to bottom in the same order that his playlist was in. Each time a song came to an end, he would go out and pull down the card associated with the song had just played. As the night progressed, he had trouble getting back to take down the cards in time. I wondered if I could design a digital signage solution for this.

Concept

This gave me an idea – I suspected that iTunes or some other music playing software would have an API to allow you to detect when songs changed. It turns out I was right. The play and track-changed events are a little finicky at first, but I managed to get it working consistently. This isn’t incredibly fancy – more like just a proof of concept. When you play a song, it displays the artist and song title at the top, as well as the dance style (which I have stored in the comments field) in the middle. In addition, if iTunes isn’t running when you launch the program, it launches iTunes for you. When you close iTunes, the application closes as well.

Implementation

I think the ideal implementation would involve an LCD screen being mounted in the appropriate position, connected to the laptop using a 50ft VGA cable. The application would have additional code to make it launch in the secondary window and resize to full-screen. Some graphics associated with either the current song or the dance style would also be a nice addition.

httpv://www.youtube.com/watch?v=MP2MJaOK7w8

Here’s some code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using iTunesLib;

namespace WindowsFormsApplication1
{

    public partial class Form1 : Form
    {

        delegate void StringParameterDelegate(string value);
        Label statusIndicator;
        Label CategoryIndicator;
        String comment;
        iTunesApp itunes;
        String curArtist;
        String curAlbum;
        String curTitle;
        Timer Clock;
        public Form1()
        {
            InitializeComponent();
            itunes = new iTunesApp();
            itunes.OnPlayerPlayEvent += new _IiTunesEvents_OnPlayerPlayEventEventHandler(track_changed);
            itunes.OnQuittingEvent += new _IiTunesEvents_OnQuittingEventEventHandler(onItunesExit);
            itunes.OnPlayerPlayingTrackChangedEvent += new _IiTunesEvents_OnPlayerPlayingTrackChangedEventEventHandler(track_changed);
            itunes.OnQuittingEvent += new _IiTunesEvents_OnQuittingEventEventHandler(itunes_quit);
        }

        private void itunes_quit()
        {
            Application.Exit();
        }
        private void Form1_Load(object sender, EventArgs e)
        {

            statusIndicator = new Label();
            //statusIndicator.AutoSize = true;
            statusIndicator.TextAlign = ContentAlignment.MiddleCenter;
            statusIndicator.Size = new Size(this.Width, 50);
            Font font = new Font(FontFamily.GenericSansSerif, 20);
            statusIndicator.Font = font;
            statusIndicator.Location = new Point(0, 0);

            Controls.Add(statusIndicator);
           // statusIndicator.Text = "test";
            statusIndicator.BorderStyle = BorderStyle.None;

            CategoryIndicator = new Label();
            CategoryIndicator.TextAlign = ContentAlignment.MiddleCenter;
            CategoryIndicator.Font = font;
            CategoryIndicator.Location = new Point(0, this.Height / 2);
            CategoryIndicator.Size = new Size(this.Width, 50);
            CategoryIndicator.BorderStyle = BorderStyle.None;
            Controls.Add(CategoryIndicator);

            // iTunes classes

            IITPlaylist mainLibrary = itunes.CurrentPlaylist;
            IITTrackCollection tracks = mainLibrary.Tracks;

            Clock=new Timer();
            Clock.Interval=1000;
            Clock.Start();
            Clock.Tick+=new EventHandler(Timer_Tick);

        }

        private Point CenterControl(int Width, int Height, int vPos)
        {
            Point centered = new Point();
            int wWidth = this.Width;
            int wHeight = this.Height;
            centered.X = wWidth / 2 - Width / 2;
            centered.Y = vPos;
            return centered;
        }
        private Point CenterControl(int Width, int Height)
        {
            Point centered = new Point();
            int wWidth = this.Width;
            int wHeight = this.Height;
            centered.X = wWidth / 2 - Width / 2;
            centered.Y = wHeight / 2 - Height / 2; 

            return centered; 

        }

        public void Timer_Tick(object sender, EventArgs e)
        {
            try
            {
                if (itunes.CurrentTrack.Artist != curArtist | itunes.CurrentTrack.Album != curAlbum | itunes.CurrentTrack.Name != curTitle)
                {
                    track_changed(itunes.CurrentTrack);
                }
            }
            catch
            {
                //Nothing Here! this is just so your the app doesn't blow up if iTunes is busy. instead it will just try again in 1 second
            }
        }
        public void track_changed(object iTrack)
        {
            IITTrack currentTrack = (IITTrack)iTrack;
            string trackName = currentTrack.Name.ToString();
            string artist = currentTrack.Artist;
            string album = currentTrack.Album;
            curTitle = trackName;
            curArtist = artist;
            curAlbum = album;
            comment = currentTrack.Comment.ToString();

          //  if (!itunes.PlayerState.ToString().Contains("Playing")) MessageBox.Show("not playing");
            UpdateStatus(trackName + " - " + artist);

            Application.DoEvents();
        }

        void onItunesExit()
        {
            Application.Exit();
        }
        void UpdateStatus(string value)
        {
            if (InvokeRequired)
            {
                // We're not in the UI thread, so we need to call BeginInvoke

                BeginInvoke(new StringParameterDelegate(UpdateStatus), new object[] { value});
                return;
            }

           // IITTrack currentTrack = itunes.CurrentTrack;

            //statusIndicator.Width = value.Length;
            // Must be on the UI thread if we've got this far
            statusIndicator.Text = value;
           CategoryIndicator.Text = comment;
            //  statusIndicator.Invalidate();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            itunes.Quit();

        }

        private void button2_Click(object sender, EventArgs e)
        {

            if (itunes.PlayerState.ToString().Contains("Playing")) itunes.Pause();
            else
            itunes.Play();
           // MessageBox.Show(itunes.PlayerState.ToString());
        }

        private void button3_Click(object sender, EventArgs e)
        {

            itunes.NextTrack();
        }

        private void button4_Click(object sender, EventArgs e)
        {

            itunes.PreviousTrack();
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                // Remove any handlers from the iTunes COM object.
                itunes.OnPlayerPlayEvent -= track_changed;
                itunes.OnPlayerPlayingTrackChangedEvent -= track_changed;
                // Release the COM object.
                //Marshal.ReleaseComObject(itunes);
            }
            catch (Exception ex)
            {

            }

        }
    }
}
dual-monitor1

Multi-monitor slideshow

 

This was originally designed to display photos taken by a roaming photographer at a local attraction. The photos are relayed wirelessly to a networked computer. The images are displayed in the order in which they are taken, and are filtered so that only images taken at least 15 minutes ago and no more than 45 minutes ago (these are configurable variables) based on the average amount of time it takes for people to walk from where the photo was taken and where they’re displayed/sold. Kodak, which is the vendor used for the attraction’s photo system, has a solution for public photo display, but it only allows the display of a single photo at a time.

My solution displays two photos at a time. The application automatically runs as full-screen on both monitors, and resizes the images to fill either the length or width of the monitor, maintaining the photo’s aspect ratio. The photos are displayed for a predetermined length of time (default is 3 seconds).

The path to the photos can be either UNC or a local path. Below is the code for the Form class. The program class creates instances of this class and starts the individual threads running, but this is where all the work gets done. There’s also a Taskbar class which calls the user32.dll and disables the taskbar – this is completely different from autohide and it’s very useful for digital signage applications. You can still launch the start menu using your keyboard’s window key, but the task bar will still not be displayed.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Linq;
using System.Threading;
using System.IO;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PhotoDisplay
{
    public partial class Form1 : Form
    {
        private String[] filepaths;
        static string[] imagepaths;
        static String[] adpaths;
       private string mode="ads";
       static string mode1 = "ads";
       static string mode2 = "ads";
      // Cursor cursor;
       public struct IconInfo
       {
           public bool fIcon;
           public int xHotspot;
           public int yHotspot;
           public IntPtr hbmMask;
           public IntPtr hbmColor;
       }
        int screenNum = 0;
        public Form1()
        {

            Taskbar.Hide(); // this disables the taskbar it gets re-enabled when the application is closed
            InitializeComponent();
            Bitmap bitmap = new Bitmap(1, 1);
            Graphics g = Graphics.FromImage(bitmap);
            this.Cursor = CreateCursor(bitmap, 1, 1); // this makes the mouse pointer invisible

            this.FormBorderStyle = FormBorderStyle.None;

            Thread thread0 = new Thread(new ThreadStart(getimages)); // this gets the arrays of images to be displayed
            Thread thread1 = new Thread(new ThreadStart(RotateImages)); // this displays the images in sequence

            thread0.Start();
            Thread.Sleep(5000);
            thread1.Start();

        }
        public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
        {
            IconInfo tmp = new IconInfo();
            GetIconInfo(bmp.GetHicon(), ref tmp);
            tmp.xHotspot = xHotSpot;
            tmp.yHotspot = yHotSpot;
            tmp.fIcon = false;
            return new Cursor(CreateIconIndirect(ref tmp));
        }
        [DllImport("user32.dll")]
        public static extern IntPtr CreateIconIndirect(ref IconInfo icon);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
        public void setScreenNum(int num)
        {
            screenNum = num;
        }
        void getimages()
        {
            while (mode != "exit")
            {
                try
                {
                   // mode = "refreshing";
                    adpaths = Directory.GetFiles(Properties.Settings.Default.AdPath, "*.JPG");
                        DirectoryInfo Dir = new DirectoryInfo(Properties.Settings.Default.ImagePath);
                        FileInfo[] FileList = Dir.GetFiles("*.JPG", SearchOption.TopDirectoryOnly);

                        var query = from FI in FileList
                                    where FI.LastWriteTime.Date == DateTime.Now.Date
                                    where FI.LastWriteTime.TimeOfDay > DateTime.Now.TimeOfDay.Add(System.TimeSpan.FromMinutes(-1 * Properties.Settings.Default.Oldest))
                                    where FI.LastWriteTime.TimeOfDay < DateTime.Now.TimeOfDay.Add(System.TimeSpan.FromMinutes(-1 * Properties.Settings.Default.Newest))
                                    select FI.FullName;
                        imagepaths = query.ToArray();
                      //  MessageBox.Show(filepaths.Length.ToString() + " images found");
                        // if the number of photos found is less than the pre-defined minimum, just rotate through the ad images
                        if (filepaths.Length < Properties.Settings.Default.Minimumimages)
                        {
                            mode = "ads";

                        }
                        else this.mode = "images";

                    adpaths = Directory.GetFiles(Properties.Settings.Default.AdPath, "*.JPG");
                    // MessageBox.Show("finished getting images Mode:" + mode);
                }
                catch (System.IO.IOException io)
                {
                    Console.WriteLine(io.Message);
                    if (Properties.Settings.Default.debug) MessageBox.Show("offline");
                   // status = "offline";
                   // break;
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    this.mode = "ads";
                    if (Properties.Settings.Default.debug) MessageBox.Show(e.Message + e.StackTrace, "getimages: " + e.ToString());

                }
                //status = "idle";
                Thread.SpinWait(30);
            }
        }
        void RotateImages()
        {

            while (mode != "exit")
            {
               // Point p;

                try
                {

                    int cycle = 0;

                    if (screenNum == 0)
                        mode1 = this.mode;
                    else mode2 = this.mode;
                    if (this.mode == "images") filepaths = imagepaths;
                    else filepaths = adpaths;

                        while (mode1 != mode2)
                        {
                            Thread.Sleep(100);
                            cycle++;
                            if (cycle > 100) break;
                        }

                    if (!String.IsNullOrEmpty(filepaths[0]))
                    {
                        for (int i = screenNum; i < filepaths.Length; i = i + 2)
                        {

                            //MessageBox.Show(filepaths[i]);
                            Image img = Image.FromFile("aqLogo.jpg");
                            img = Image.FromFile(filepaths[i]);
                            img = resizeImage(img, this.Size);
                            pictureBox1.Invoke(new MethodInvoker(delegate { resizePicturebox(img.Size); }));
                            pictureBox1.Invoke(new MethodInvoker(delegate { pictureBox1.Image = img; }));

                            if (this.mode == "ads") Thread.Sleep(Properties.Settings.Default.AdDelay);
                            else Thread.Sleep(Properties.Settings.Default.ImageDelay);

                        }
                        if (this.mode == "images")
                            this.mode = "ads";
                        else if (this.mode == "ads") this.mode = "images";
                    }
                }
                catch (System.NullReferenceException nr)
                {
                    if (Properties.Settings.Default.debug)
                        MessageBox.Show("Rotate Null Reference: " + nr.Message);
                    mode = "ads";
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    if (Properties.Settings.Default.debug) MessageBox.Show(e.Message + " from: " + e.StackTrace, "rotate: " + e.ToString());
                    //break;
                }
                //MessageBox.Show("rotation complete");

            }

        }
        private static Image resizeImage(Image imgToResize, Size size)
        {
            int sourceWidth = imgToResize.Width;
            int sourceHeight = imgToResize.Height;

            float nPercent = 0;
            float nPercentW = 0;
            float nPercentH = 0;

            nPercentW = ((float)size.Width / (float)sourceWidth);
            nPercentH = ((float)size.Height / (float)sourceHeight);

            if (nPercentH < nPercentW)
                nPercent = nPercentH;
            else
                nPercent = nPercentW;

            int destWidth = (int)(sourceWidth * nPercent);
            int destHeight = (int)(sourceHeight * nPercent);
            Bitmap c = new Bitmap(destWidth, destHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            Graphics g = Graphics.FromImage((Image)c);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
            g.Dispose();
            return (Image)c;
        }

        private void Form1_Resize(object sender, EventArgs e)
        {

            this.pictureBox1.Width = (this.Height / this.pictureBox1.Height * this.pictureBox1.Width);
            this.pictureBox1.Height = this.Height;

        }

        private void pictureBox1_Resize(object sender, EventArgs e)
        {
            Point location = new Point();
            location.Y = pictureBox1.Location.Y;
            location.X = (this.Width - pictureBox1.Width) / 2;
            pictureBox1.Location = location;
        }

        private void resizePicturebox(Size size){
            pictureBox1.Size=size;
            Point location = new Point();
            location.Y = 0;
            location.X = (this.Width - pictureBox1.Width) / 2;
            pictureBox1.Location = location;
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                mode = "exit";
                Taskbar.Show();
                Process[] processlist = Process.GetProcesses();
                foreach (Process p in processlist)
                {

                    if (p.ProcessName.Contains("PhotoDisplay")) p.Kill();
                }

            }
        }

    }
}
Active Directory Search Tool

Active Directory Search Tool

This is one of my projects that went more smoothly. It was originally based on a demo found on CodeProject, and I later rewrote it in C#.NET and added more features. This is the latest incarnation of it.

You can search for computers or contacts (or both) by name and/or location (if you have the proper naming convention for object descriptions). The second textbox displays the LDAP query, which is generated dynamically as the you type the search string in the first box. Below, there are a few settings:

  • Max objects per page
  • Per page time limit
  • Max objects to fetch
  • Total time limit
  • Sort Results
  • Cache results
  • Ping Timeout

All these settings can be saved by clicking the blue Save button above.

There are also context menus for computer and contact objects. First, there’s a “copy list” for the top-level nodes.

Next, there’s a context menu for computer objects:

  • Ping – pings the computer in question and appends “(online)” or “(offline)” to the displayed description
  • Connect via Dameware – pings computer and if online, opens dameware set to connect to the computer
  • Same with
    RDP
  • Copy Service Tag / Express Service Tag – pings computer and if online, queries computer for service tag and calculates express service tag (Dell only)
  • Check Dell Website – obtains service tag and opens IE, navigating to the Dell Support site displaying info on the selected computer
  • Computer Management – opens computer management console for selected computer
  • Edit Description in Active Directory
  • Navigate to c:\ – navigates to \\<computername>\c$

For Contact objects:

  • Send E-Mail – opens Outlook and sends email to email address of the selected contact
  • Copy Cell# – place the number listed as mobile phone# in clipboard
  • Copy Office# – same as above
  • Copy Username
  • Edit Description – Edit’s Contact’s description in Active Directory