Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C#
Article

Show/Hide Outlook Express Tray App

Rate me:
Please Sign up or sign in to vote.
4.38/5 (10 votes)
28 Jan 20034 min read 142.7K   3.1K   57   12
An application to effectively place an application in the system tray

Introduction

With this application it is possible to effectively load another application (in this case Outlook Express) into the System Tray

Background

I usually load my Email Client, usually Outlook Express on my Microsoft OS, at startup. I have it set to poll the Email Server every 3 minutes. The problem I find is that OE takes up room on the taskbar, and I sometimes inadvertantly shut it down.

The other day I downloaded a C++ application called Window, written by by Steve Kemp at http://www.steve.org.uk/, which Hides and Unhides the main window of any program, and decided I could do something like that in C#. So I decided to write an application that would allow me to load OE with a hidden window and hide or unhide that window on demand.

This is my first real C# program (at least one that does something useful). I usually code in VB.NET, and the code for the About box was copied in from my VB code and converted to C#.

It was at this point that I realised how much VB lets you get away with, for example the lines like

C#
oCompany = (System.Reflection.AssemblyCompanyAttribute)
    oCustomAttributes[Index];

which in VB reads as

VB.NET
oCompany = oCustomAttributes(Index)

because VB will perform this implicitly, whereas C# will not (this threw me at first, I couldn't understand why a perfectly good peice of code wouldn't work).

My first attempt used DLLImport and called various Win32 library functions (EnumWindows, GetWindowText and ShowWindow) and used ShellExecute to launch an application. I then discovered System.Diagnostics.Process and decided to use this to load and control the behaviour of the applications Window.

However, I soon discovered a couple of problems with this approach, while I could load any application with any of 4 window styles (Hidden, Maximized, Minimized and Normal) using Process.Start, and later kill that application using Process.Kill, I couldn't find a way change the Window style using the Process object, and to make this worse the Process.MainWindowHandle property was returning zero, so I was unable to locate a valid Window handle for the recently launched application.

To overcome these problems I resorted to DLLImport and the Win32 library functions EnumWindows, GetWindowText and ShowWindow.

Then I realised that with the window hidden at startup the process might not be able to locate a window handle, a quick test starting Process with the window minimised proved this to be the case.

The main class is ExternalApplication which inhertits from System.Diagnostics.Process. I extended this class by overloading the Constructor and the Start method, these additions are for convenience only. I added the public methods HideApplication and ShowApplication.

Initially these methods called EnumWindows and attempted to locate a Window with the specified window title, the Property WindowTitle was initialised with the required window title or partial window title, this was parsed using System.Text.RegularExpressions.Regex (necessary to locate windows that match a partial window title string). The variable hWND is set to the value of the located windows hwnd and the Enum exited.

However by starting the process with the application's window minimised it is possible to locate a valid window handle, using Process.MainWindowHandle, if this is then stored in the variable hWND, it is then possible to eliminate the call to EnumWindows, and the WindowTitle property becomes redundant.

If the window is not already in the state required, as determined by a call to IsWindowVisible, ShowWindow is called and the hWND value and the appropriate style value (SW_HIDE or SW_RESTORE) passed to it.

When the Window is to be shown SetForegroundWindow is also called to ensure that the window will be the top most.

C#
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;


namespace ExternalApplication
{
    /// <summary>
    /// Summary description for OutlookExpress.
    /// </summary>
    public class ExternalApplication : System.Diagnostics.Process
    {
//        private delegate bool CallBack(int hwnd, int lParam);

        private const int SW_HIDE = 0;
        private const int SW_RESTORE = 9;

        [DllImport("User32")]
        private static extern int SetForegroundWindow(int hwnd);

        [DllImport("User32")]
        private static extern int ShowWindow(int hwnd, int nCmdShow);

        [DllImport("User32")]
        private static extern int IsWindowVisible(int hwnd);

//        [DllImport("User32")]
//        private static extern int GetWindowText(int hwnd, 
//               System.Text.StringBuilder lpString, int cch);
//
//        [DllImport("User32")]
//        private static extern int EnumWindows(CallBack  lpEnumFunc, 
//                  int lParam);

        private int hWND;

//        private    string mvarWindowTitle;
//
//        //The Window Title, may be a partial window Title.
//        //It is used to locate the window when we want to 
//        //Show or hide the window
//        public string WindowTitle
//        {
//            get
//            {
//                if ( mvarWindowTitle == "" )
//                {
//                    MessageBox.Show ("The window title string is missing.",
//                        "Unknown Window Title", 
//                        MessageBoxButtons.OK, MessageBoxIcon.Error);
//                }
//                return mvarWindowTitle;
//            }
//            set
//            {
//                 mvarWindowTitle = value;
//            }
//        }

        public ExternalApplication()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        public ExternalApplication(string FileName)
        {
            //
            // TODO: Add constructor logic here
            //
            //Initalise the Process passing it the Application Name
            //or a file name associated with the application
            this.StartInfo.FileName = FileName;
        }

        public ExternalApplication(string FileName, 
             ProcessWindowStyle WindowStyle)
        {
            //
            // TODO: Add constructor logic here
            //
            //Instantiate the application, using the specified Window Style
            this.StartInfo.FileName = FileName;
            StartTheApplication(WindowStyle);
        }

        public void Start(ProcessWindowStyle WindowStyle)
        {
            if ( this.StartInfo.FileName != "" )
            {
                //Instantiate the application, using the specified
                //Window Style
                StartTheApplication(WindowStyle);
            }
            else
            {
                MessageBox.Show ("The name of the application," + 
                    " or a valid file type for the application is missing.", 
                    "Unknown External Application", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        public void Start(string FileName, ProcessWindowStyle WindowStyle)
        {
            //Instantiate the application, using the specified Window Style
            this.StartInfo.FileName = FileName;
            StartTheApplication(WindowStyle);
        }

        private void StartTheApplication(ProcessWindowStyle WindowStyle)
        {
            if ( WindowStyle == ProcessWindowStyle.Hidden )
            {
                //Starting the application with the Window 
                //Style set to Hidden will return a mainWindow Handle of 0
                //So we set the Window Style to Minimised
                this.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
                this.StartInfo.CreateNoWindow = true;
                this.Start();
                this.WaitForInputIdle();
                //By starting the application with the window minimised 
                //I am able to obtain a handle to the window
                hWND = (int)this.MainWindowHandle;
                //We can then set the Widnow hidden, using the 
                //MainWindow Handle
                this.HideApplication();
            }
            else
            {
                this.StartInfo.WindowStyle = WindowStyle;
                this.StartInfo.CreateNoWindow = true;
                this.Start();
                this.WaitForInputIdle();
                hWND = (int)this.MainWindowHandle;
            }
        }

        public void HideApplication()
        {
            // Hide the window
            //CallBack EnumWindowProcCallback = new CallBack(
                 //this.EnumWindowProc);
            //EnumWindows(EnumWindowProcCallback, 0);

            if ( IsWindowVisible( this.hWND ) != 0 )
            {
                //Hide the window if it is not already hidden
                ShowWindow( this.hWND, SW_HIDE );
            }
        }

        public void ShowApplication()
        {
            // Restore the window
            //CallBack EnumWindowProcCallback= new CallBack(
                     //this.EnumWindowProc);
            //EnumWindows(EnumWindowProcCallback, 0);

            if ( IsWindowVisible( this.hWND ) == 0 )
            {
                //Show the window, if it not already displaying
                ShowWindow( this.hWND, SW_RESTORE );
                //Make sure the window is the top window
                SetForegroundWindow(this.hWND);
            }
        }

//        // Callback routine that is called with the handle
//        //    of all the windows found.
//        //
//        private bool EnumWindowProc(int hwnd, int parm)
//        {
//
//            // Buffer to store the title of the window found
//            System.Text.StringBuilder windowTitle = 
//                   new System.Text.StringBuilder(2050);
//
//            // Get the window title bar text
//            GetWindowText(hwnd, windowTitle, 2048);
//
//            // If the window title is empty ignore it,
//            if ( windowTitle.Length < 1   )
//            {
//                return true;
//            }
//
//            string WindowTitelResult = windowTitle.ToString().Trim();
//
//            System.Text.RegularExpressions.Regex GRE = 
//              new System.Text.RegularExpressions.Regex(WindowTitle);
//            System.Text.RegularExpressions.MatchCollection Result;
//            Result = GRE.Matches(WindowTitelResult);
//            string MatchString = "";
//            if ( Result.Count > 0 )
//            {
//                MatchString = Result[0].Value;
//            }
//
//            if (MatchString == WindowTitle)
//            {
//                hWND = hwnd;
//                return false;
//            }
//            return true;
//        }
    }
}

Changes

  • 31/01/2003 - I have added funtionality to this application. It is now possible to restart the child application (Outlook Express, in this case), when the child application has been closed down outside of the control of the Shoe_Tray application. To achieve this I made the following changes to the Show and Hide Methods.
C#
public void HideApplication()
{
    //Find Out if the Application is actually loaded,
    //if it is we get it's current hWnd
    //CallBack EnumWindowProcCallback = new CallBack(
            //this.EnumWindowProc);
    //EnumWindows(EnumWindowProcCallback, 0);

    //If the application has been closed external to
    //this application
    //hWnd will be the old hWnd and the windowTitle
    //will not be found, so we will
    //attempt to restart the application
    //Otherwise we will set the CurrentWindowTitle
    string windowTitle = GetWindowTitle( this.hWND );
    if (windowTitle == "")
    {
        StartTheApplication(this.WindowStyle);
    }
    else
    {
        CurrentWindowTitle = windowTitle;
        //Then we will check to see if the applications
        //window is InVisible
        //If it's not we will make it so
        if ( IsWindowVisible( this.hWND ) != 0 )
        {
            //Hide the window if it is not already hidden
            int rtn = ShowWindow( this.hWND, SW_HIDE );
        }
    }
}

public void ShowApplication()
{
    //Find Out if the Application is actually loaded,
    //if it is we get it's current hWnd
    //CallBack EnumWindowProcCallback = new CallBack(
        //this.EnumWindowProc);
    //EnumWindows(EnumWindowProcCallback, 0);

    //If the application has been closed external
    //to this application
    //hWnd will be the old hWnd and the windowTitle
    //will not be found, so we will
    //attempt to restart the application
    //Otherwise we will set the CurrentWindowTitle
    string windowTitle = GetWindowTitle( this.hWND );
    if (windowTitle == "")
    {
        StartTheApplication(this.WindowStyle);
    }
    else
    {
        CurrentWindowTitle = windowTitle;
        //Then we will check to see if the applications
        //window is Visible
        //If it's not we will make it so
        if ( IsWindowVisible( this.hWND ) == 0 )
        {
            //Show the window, if it not already displaying
            int rtn = ShowWindow( this.hWND, SW_RESTORE );
        }
        //Make sure the window is the top window
        SetForegroundWindow(this.hWND);
    }

Added the following properties.

C#
// The main form
public Form CallingForm
{
    get
    {
        return mvarCallingForm;
        }
    set
    {
        mvarCallingForm = value;
    }
}

//The message Form
public Form MessageForm
{
    get
    {
        return mvarMessageForm;
    }
    set
    {
        mvarMessageForm = value;
    }
}

And changed the Start Method as below

C#
private void StartTheApplication(ProcessWindowStyle WindowStyle)
{
    if ( WindowStyle == ProcessWindowStyle.Hidden )
    {
        //Starting the application with the Window Style set
        //to Hidden will return a mainWindow Handle of 0
        //So we set the Window Style to Minimised
        this.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
        this.StartInfo.CreateNoWindow = true;
        this.Start();
        if (ReStarting)
        {
            //By displaying a message we can ensure
            //that Input is idle when we reatart after
            //a premature closedown of the Called Application.
            //Otherwise the wrong hWnd is returned
            MessageForm.ShowDialog(CallingForm);
        }
        this.WaitForInputIdle();
        //By starting the application with the window minimised
        //I am able to obtain a handle to the window
        this.hWND = (int)this.MainWindowHandle;
        //Instead of getting the current hWnd, we now Set the
        //variable CurrentWindowTitle - I use this later on
        CurrentWindowTitle = mvarWindowTitle;
        //We can then set the Widnow hidden, using the MainWindow
        //Handle
        this.HideApplication();
    }
    else
    {
        this.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
        this.StartInfo.CreateNoWindow = true;
        this.Start();
        if (ReStarting)
        {
            //By displaying a message we can ensure that
            //Input is idle when we reatart after
            //a premature closedown of the Called Application.
            //Otherwise the wrong hWnd is returned
            MessageForm.ShowDialog(CallingForm);
        }
        this.WaitForInputIdle();
        this.hWND = (int)this.MainWindowHandle;
        //Instead of getting the current hWnd, we now Set the
        //variable CurrentWindowTitle - I use this later on
        CurrentWindowTitle = mvarWindowTitle;
        //Hide it first other wise it will not come to the front
        this.HideApplication();
        this.ShowApplication();
    }
    ReStarting = false;
}

The Message Form not only provides useful information when the child application is restarted, but also provides a decent timeout while the reloaded child application completes loading so that this - the ExternalApplication object can retrieve the correct hWnd for the reloaded Child application. I have replaced the Demo Project and the Source code.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralCrash Pin
Dogfight19-Sep-04 23:41
Dogfight19-Sep-04 23:41 
GeneralRe: Crash Pin
Tracy Barlow20-Sep-04 12:42
sussTracy Barlow20-Sep-04 12:42 
GeneralRe: Crash Pin
Jolly Josh15-Oct-06 10:14
Jolly Josh15-Oct-06 10:14 
GeneralRe: Crash - VS 2005 Pin
Armoghan Asif7-Jun-07 1:17
Armoghan Asif7-Jun-07 1:17 
GeneralOutlook plugin or addin Pin
Anonymous11-Mar-04 16:42
Anonymous11-Mar-04 16:42 
GeneralSmacked It!!! Pin
kolo83a7-Mar-03 10:15
kolo83a7-Mar-03 10:15 
GeneralVB & C# difference Pin
Mika30-Dec-02 19:59
Mika30-Dec-02 19:59 
GeneralRe: VB & C# difference Pin
Tracy Anne Barlow31-Dec-02 14:13
Tracy Anne Barlow31-Dec-02 14:13 
GeneralBeen looking for something like this for a while! Pin
Swinefeaster30-Dec-02 12:07
Swinefeaster30-Dec-02 12:07 
GeneralRe: Been looking for something like this for a while! Pin
Anonymous30-Dec-02 12:39
Anonymous30-Dec-02 12:39 
GeneralGreat article Pin
Marc Clifton30-Dec-02 1:53
mvaMarc Clifton30-Dec-02 1:53 
GeneralRe: Great article Pin
terriblecow19-Nov-03 9:12
terriblecow19-Nov-03 9:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.