Place Outlook Reminders as top window using C#
This program subscribes to WMI events and therefore must be run as an admin account. It can be added to scheduled jobs so it runs automatically.It is created as a console application and hides its console window to run in the background.
Basically it watches for whenever Outlook kicks off a new thread and if the window heading has Reminder in it, it forces the window to be on top of all others.
using System; using System.Management; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; namespace OutlookReminder { class Program { ManagementEventWatcher startThreadWatch; static void Main(string[] args) { //create a processwatcher that will watch for outlook programs that will be getting opened. ProcessWatcher processWatcher = new ProcessWatcher("OUTLOOK.EXE"); //create a list of threadWatchers for each outlook program that is currently open, however...there really should only be one open at a time. List<ThreadsWatcher> threadsWatcher = Process.GetProcessesByName("OUTLOOK").ToList().Select(x => new ThreadsWatcher(x.Id)).ToList(); //Hide this console window and let it live in the background. WindowControl.ShowWindow(Process.GetCurrentProcess().MainWindowHandle, (int)WindowControl.WindowState.Hide); Console.WriteLine("Press ENTER to stop the Outlook reminders to be forced as the top most window."); Console.ReadLine(); //Dispose of the process watcher that is watching for the outlook program to get opened. processWatcher.Dispose(); //Dispose of all the threads that are not null and not disposed of yet. threadsWatcher.Where(x => x != null && !x.disposed).ToList().ForEach(x => x.Dispose()); } } #region watchers public class ProcessWatcher : IDisposable { ManagementEventWatcher startProcessWatch; ManagementEventWatcher stopProcessWatch; ThreadsWatcher threadWatcher; public string processName { get; private set; } public bool disposed { get; private set; } public ProcessWatcher(string _processName) { processName = _processName; disposed = false; startProcessWatch = new ManagementEventWatcher(new WqlEventQuery(string.Format("Select * from Win32_ProcessStartTrace WHERE ProcessName='{0}'", processName))); startProcessWatch.EventArrived += new EventArrivedEventHandler(startProcessWatch_EventArrived); startProcessWatch.Start(); stopProcessWatch = new ManagementEventWatcher(new WqlEventQuery(string.Format("Select * from Win32_ProcessStopTrace WHERE ProcessName='{0}'", processName))); stopProcessWatch.EventArrived += new EventArrivedEventHandler(stopProcessWatch_EventArrived); stopProcessWatch.Start(); } #region disposeAndFinalize ~ProcessWatcher() { if (startProcessWatch != null) { startProcessWatch.Stop(); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { startProcessWatch.Stop(); startProcessWatch.Dispose(); if (threadWatcher != null && !threadWatcher.disposed) { threadWatcher.Dispose(); } } disposed = true; } } #endregion disposeAndFinalize void stopProcessWatch_EventArrived(object sender, EventArrivedEventArgs e) { if (threadWatcher != null && !threadWatcher.disposed) { threadWatcher.Dispose(); } } void startProcessWatch_EventArrived(object sender, EventArrivedEventArgs e) { int processID = 0; int.TryParse(e.NewEvent.Properties["ProcessID"].Value.ToString(), out processID); threadWatcher = new ThreadsWatcher(processID); } } public class ThreadsWatcher : IDisposable { ManagementEventWatcher startThreadWatch; public bool disposed { get; private set; } public int processID { get; private set; } #region constructors public ThreadsWatcher(int _processID) { disposed = false; processID = _processID; startThreadWatch = new ManagementEventWatcher(new WqlEventQuery("Select * from Win32_ThreadStartTrace WHERE ProcessID=" + processID)); startThreadWatch.EventArrived += new EventArrivedEventHandler(startThreadWatch_EventArrived); startThreadWatch.Start(); } #endregion constructors #region disposeAndFinalize public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { startThreadWatch.Stop(); startThreadWatch.Dispose(); } disposed = true; } } ~ThreadsWatcher() { if (startThreadWatch != null) { startThreadWatch.Stop(); } } #endregion disposeAndFinalize void startThreadWatch_EventArrived(object sender, EventArrivedEventArgs e) { int processID = 0; int.TryParse(e.NewEvent.Properties["ProcessID"].Value.ToString(), out processID); Process outlookProcess = Process.GetProcessById(processID); if (outlookProcess.MainWindowTitle.Contains("Reminder")) { //Place window as TopMost window. WindowControl.SetWindowPos(outlookProcess.MainWindowHandle, WindowControl.SpecialWindowHandles.TopMost, 0, 0, 0, 0, WindowControl.SetWindowPosFlags.TopMostFlag); //The next 2 commands are use to ensure that if the window is minimized it will still be maximized and displayed. //force the window as the foreground window. WindowControl.SetForegroundWindow(outlookProcess.MainWindowHandle); //Restore the window to be maximized WindowControl.ShowWindow(outlookProcess.MainWindowHandle, (int)WindowControl.WindowState.Restore); } outlookProcess.Dispose(); } } #endregion watchers static class WindowControl { #region WindowControlEnums //First few classes are defined to replace an enum however defining it this way we can load any type. public static class SpecialWindowHandles { public static IntPtr NoTopMost = new IntPtr(-2), TopMost = new IntPtr(-1), Top = new IntPtr(0), Bottom = new IntPtr(1); } public static class SetWindowPosFlags { public static readonly uint NoSize = 0x0001, NoMove = 0x0002, NoZOrder = 0x0004, NoRedraw = 0x0008, NoActivate = 0x0010, DrawFrame = 0x0020, FrameChanged = 0x0020, ShowsWindow = 0x0040, HideWindow = 0x0080, NoCopyBits = 0x0100, NoOwnerZOrder = 0x0200, NoRrPosition = 0x0200, NoSendChanging = 0x0400, DeferErase = 0x2000, AsyncWindowPos = 0x4000, TopMostFlag = NoMove | NoSize; } //Loaded this great enum values from http://www.pinvoke.net/default.aspx/user32.showwindow. It loads the XML descriptions so you can read //the description in the code when you are using it. public enum WindowState :uint { Hide = 0, /// <summary>Activates and displays a window. If the window is minimized /// or maximized, the system restores it to its original size and /// position. An application should specify this flag when displaying /// the window for the first time.</summary> /// <remarks>See SW_SHOWNORMAL</remarks> ShowNormal = 1, /// <summary>Activates the window and displays it as a minimized window.</summary> /// <remarks>See SW_SHOWMINIMIZED</remarks> ShowMinimized = 2, /// <summary>Activates the window and displays it as a maximized window.</summary> /// <remarks>See SW_SHOWMAXIMIZED</remarks> ShowMaximized = 3, /// <summary>Maximizes the specified window.</summary> /// <remarks>See SW_MAXIMIZE</remarks> Maximize = 3, /// <summary>Displays a window in its most recent size and position. /// This value is similar to "ShowNormal", except the window is not /// actived.</summary> /// <remarks>See SW_SHOWNOACTIVATE</remarks> ShowNormalNoActivate = 4, /// <summary>Activates the window and displays it in its current size /// and position.</summary> /// <remarks>See SW_SHOW</remarks> Show = 5, /// <summary>Minimizes the specified window and activates the next /// top-level window in the Z order.</summary> /// <remarks>See SW_MINIMIZE</remarks> Minimize = 6, /// <summary>Displays the window as a minimized window. This value is /// similar to "ShowMinimized", except the window is not activated.</summary> /// <remarks>See SW_SHOWMINNOACTIVE</remarks> ShowMinNoActivate = 7, /// <summary>Displays the window in its current size and position. This /// value is similar to "Show", except the window is not activated.</summary> /// <remarks>See SW_SHOWNA</remarks> ShowNoActivate = 8, /// <summary>Activates and displays the window. If the window is /// minimized or maximized, the system restores it to its original size /// and position. An application should specify this flag when restoring /// a minimized window.</summary> /// <remarks>See SW_RESTORE</remarks> Restore = 9, /// <summary>Sets the show state based on the SW_ value specified in the /// STARTUPINFO structure passed to the CreateProcess function by the /// program that started the application.</summary> /// <remarks>See SW_SHOWDEFAULT</remarks> ShowDefault = 10, /// <summary>Windows 2000/XP: Minimizes a window, even if the thread /// that owns the window is hung. This flag should only be used when /// minimizing windows from a different thread.</summary> /// <remarks>See SW_FORCEMINIMIZE</remarks> ForceMinimized = 11 } #endregion [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.I4)] public static extern int SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); } }