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
oCompany = (System.Reflection.AssemblyCompanyAttribute)
oCustomAttributes[Index];
which in VB reads as
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.
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace ExternalApplication
{
public class ExternalApplication : System.Diagnostics.Process
{
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);
private int hWND;
public ExternalApplication()
{
}
public ExternalApplication(string FileName)
{
this.StartInfo.FileName = FileName;
}
public ExternalApplication(string FileName,
ProcessWindowStyle WindowStyle)
{
this.StartInfo.FileName = FileName;
StartTheApplication(WindowStyle);
}
public void Start(ProcessWindowStyle WindowStyle)
{
if ( this.StartInfo.FileName != "" )
{
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)
{
this.StartInfo.FileName = FileName;
StartTheApplication(WindowStyle);
}
private void StartTheApplication(ProcessWindowStyle WindowStyle)
{
if ( WindowStyle == ProcessWindowStyle.Hidden )
{
this.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
this.StartInfo.CreateNoWindow = true;
this.Start();
this.WaitForInputIdle();
hWND = (int)this.MainWindowHandle;
this.HideApplication();
}
else
{
this.StartInfo.WindowStyle = WindowStyle;
this.StartInfo.CreateNoWindow = true;
this.Start();
this.WaitForInputIdle();
hWND = (int)this.MainWindowHandle;
}
}
public void HideApplication()
{
if ( IsWindowVisible( this.hWND ) != 0 )
{
ShowWindow( this.hWND, SW_HIDE );
}
}
public void ShowApplication()
{
if ( IsWindowVisible( this.hWND ) == 0 )
{
ShowWindow( this.hWND, SW_RESTORE );
SetForegroundWindow(this.hWND);
}
}
}
}
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.
public void HideApplication()
{
string windowTitle = GetWindowTitle( this.hWND );
if (windowTitle == "")
{
StartTheApplication(this.WindowStyle);
}
else
{
CurrentWindowTitle = windowTitle;
if ( IsWindowVisible( this.hWND ) != 0 )
{
int rtn = ShowWindow( this.hWND, SW_HIDE );
}
}
}
public void ShowApplication()
{
string windowTitle = GetWindowTitle( this.hWND );
if (windowTitle == "")
{
StartTheApplication(this.WindowStyle);
}
else
{
CurrentWindowTitle = windowTitle;
if ( IsWindowVisible( this.hWND ) == 0 )
{
int rtn = ShowWindow( this.hWND, SW_RESTORE );
}
SetForegroundWindow(this.hWND);
}
Added the following properties.
public Form CallingForm
{
get
{
return mvarCallingForm;
}
set
{
mvarCallingForm = value;
}
}
public Form MessageForm
{
get
{
return mvarMessageForm;
}
set
{
mvarMessageForm = value;
}
}
And changed the Start
Method as below
private void StartTheApplication(ProcessWindowStyle WindowStyle)
{
if ( WindowStyle == ProcessWindowStyle.Hidden )
{
this.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
this.StartInfo.CreateNoWindow = true;
this.Start();
if (ReStarting)
{
MessageForm.ShowDialog(CallingForm);
}
this.WaitForInputIdle();
this.hWND = (int)this.MainWindowHandle;
CurrentWindowTitle = mvarWindowTitle;
this.HideApplication();
}
else
{
this.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
this.StartInfo.CreateNoWindow = true;
this.Start();
if (ReStarting)
{
MessageForm.ShowDialog(CallingForm);
}
this.WaitForInputIdle();
this.hWND = (int)this.MainWindowHandle;
CurrentWindowTitle = mvarWindowTitle;
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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.