This is a common problem in Outlook. You might have tried to override the Ribbon settings for Print in Outlook to find that your code never gets run when the user clicks Print.
There is also not any events in the Outlook object model to detect Print either. So if you need to detect the user pressing the print button, you are out of luck.
While it is still not possible to detect the print button being pressed, you can at least detect when the user has selected the Print tab on the backstage.
The following code uses a background thread and a series of Windows API calls to FindWindow/FindWindowEx to detect when the Print tab on the backstage is opened:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[DllImport("user32.dll")] | |
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); | |
[DllImport("user32.dll", SetLastError = true)] | |
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle); | |
/// <summary> | |
/// Startup for Outlook Add-in | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
private void ThisAddIn_Startup(object sender, System.EventArgs e) | |
{ | |
// we start by creating a background thread and look for a specific | |
// set of windows to appear, then we know the user clicked print | |
new Thread(() => | |
{ | |
while (true) | |
{ | |
Thread.Sleep(1000); | |
CheckForPrint(); | |
} | |
}).Start(); | |
} | |
/// <summary> | |
/// Checks to see if the user has opened backstage and | |
/// selected the Print tab | |
/// </summary> | |
private void CheckForPrint() | |
{ | |
try | |
{ | |
// depending on whether we have an inspector active, or the explorer | |
// active we will need to get the caption to FindWindow | |
string LstrCaption = ""; | |
if(Application.ActiveWindow() is Outlook.Inspector) | |
{ | |
// Active inspector caption | |
LstrCaption = ((Outlook.Inspector)Application.ActiveWindow()).Caption; | |
} | |
else if(Application.ActiveWindow() is Outlook.Explorer) | |
{ | |
// Active explorer caption | |
LstrCaption = ((Outlook.Explorer)Application.ActiveWindow()).Caption; | |
} | |
// get the window handle | |
IntPtr LintHostHandle = FindWindow(null, LstrCaption); | |
if (LintHostHandle == IntPtr.Zero) return; // if we cannot find it – nevermind | |
// create a list of windows to find (in reverse order) | |
// 4) rctrl_renwnd32 – is the print preview window | |
// 3) NetUICtrlNotifySink – is whole Print options and preview | |
// 2) NetUIHWND – is the the entire print tab | |
// 1) FullpageUIHost – is the backstage page | |
Stack<string> LobjWindowClasses = new Stack<string> ( | |
new string[] { "rctrl_renwnd32", "NetUICtrlNotifySink", "NetUIHWND", "FullpageUIHost" }); | |
// recursive call back to find each window in the stack. | |
// if all of them are found, then present a message to the user | |
if(FindWindowStack(LintHostHandle, LobjWindowClasses)) | |
{ | |
MessageBox.Show("You have clicked on the Print Tab in Outlook."); | |
} | |
} | |
catch { } | |
} | |
/// <summary> | |
/// RECURSIVE | |
/// This function will take the window classnames in the provided stack | |
/// and then find each one in order via recursive calls. If all of them | |
/// are found – we return true = found | |
/// </summary> | |
/// <param name="PintHandle"></param> | |
/// <param name="PobjStack"></param> | |
/// <returns></returns> | |
private bool FindWindowStack(IntPtr PintHandle, Stack<string> PobjStack) | |
{ | |
try | |
{ | |
// get the window with the classname being popped off the stack | |
IntPtr LintNewHandle = FindWindowEx(PintHandle, IntPtr.Zero, PobjStack.Pop(), ""); | |
if(LintNewHandle != IntPtr.Zero && PobjStack.Count == 0) | |
{ | |
return true; // found it | |
} | |
else if(LintNewHandle!= IntPtr.Zero) | |
{ | |
// found a window, but the stack still has items, call next one | |
return FindWindowStack(LintNewHandle, PobjStack); | |
} | |
else | |
{ | |
// did not find it | |
return false; | |
} | |
} | |
catch | |
{ | |
// oops | |
return false; | |
} | |
} |
The only reliable way to detect a print is to hook the Windows API (C/C++)