Outlook Inspector Activate doesn't bring in foreground - vba

I have a PowerShell script that search for an email and then open it
Once I have the MailItem object, I get his inspector with MailItem.GetInspector and then I try to display the mail in foreground thanks to Inspector.Display and Inspector.Activate. The doc for Activate method says :
Activates an inspector window by bringing it to the foreground and
setting keyboard focus.
But the Activate doesn't work, the mail is open, but it stays in background, it's not in foreground. And I don't know and don't find why.
My PowerShell script :
param(
[string] $Subject,
[string] $Path
)
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;
Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
$olFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
$subfolder = $Path.Split('/')
$folder = $namespace.Folders($subfolder[1])
for($i=2; $i -lt $subfolder.Length; $i++) {
$folder = $folder.Folders($subfolder[$i])
}
$filter = "#SQL=urn:schemas:httpmail:subject LIKE '%"+$Subject+"%'"
$mail = $folder.items.find($filter)
$inspector = $mail.GetInspector
$inspector.Display()
$inspector.Activate()
I try with only $inspector.Display() or inspector.Activate(), but the result is the same, the window is displayed, but stay in background.
Thanks for your help !!

Windows would not let a background process (such as outlook.exe) to set the foreground window - keep in mind that even through your process might be in the foreground, the call is marshalled into the outlook.exe address space, which it turn executes it.
To work around that, you'd need to attach the current foreground window to your thread using AttachThreadInput() Windows API function, but you cannot call it from PS.
If using Redemption is an option (I am its author), it exposes SafeInspector.Active method, which will work whether outlook or your process are in the background. In VBS:
Set sInspector = CreateObject("Redemption.SafeInspector")
sInspector.Item = inspector
sInspector.Activate

Related

Can't Print Specific Word Document Using VB.Net [duplicate]

I have a C# WinForm application that opens and fills out a MS Word dotx template by placing text at bookmarks, and then attempts to print it, all using MS Word Interop 15.
Everything seems to go fine, the print dialog shows and completes OK, the print job shows up in the print queue (i.e. the "See what's printing" window from "Devices and Printers" on MS Windows 10). But then the job immediately disappears from the queue before it can be spooled! (document appears very very briefly with "Spooling" status, and does not print - the printer never gets the job)
Here is my code (exception checking removed for brevity):
using Word = Microsoft.Office.Interop.Word;
private void Print_Click(object sender, EventArgs e)
{
// Open the MS Word application via Office Interop
Word.Application wordApp = new Word.Application();
Word.Document wordDoc;
// Open the template
wordDoc = wordApp.Documents.Add(Template: ContractTemplatePath, Visible: false);
// Ensure the opened document is the currently active one
wordDoc.Activate();
// Set the text for each bookmark from the corresponding data in the GUI
SetBookmarkText(wordDoc, "Foo", fooTextBox.Text);
// ... There's a whole bunch of these ... then:
// Instantiate and configure the PrintDialog
var pd = new PrintDialog()
{
UseEXDialog = true,
AllowSomePages = false,
AllowSelection = false,
AllowCurrentPage = false,
AllowPrintToFile = false
};
// Check the response from the PrintDialog
if (pd.ShowDialog(this) == DialogResult.OK)
{
// Print the document
wordApp.ActivePrinter = pd.PrinterSettings.PrinterName;
wordDoc.PrintOut(Copies: pd.PrinterSettings.Copies);
}
// Close the document without saving the changes (once the
// document is printed we don't need it anymore). Then close
// the MS Word application.
wordDoc.Close(SaveChanges: false);
wordApp.Quit(SaveChanges: false);
}
The only thing I can think of here is that maybe because I do away with the document as soon as I've sent it to the printer, then the job hasn't been completely sent so it removes itself or something. If this is the case then how can I determine how long I need to keep the document around for and what's the best way of waiting for that?
EDIT: Ive done another small bit of research (dont have time for more on this just at this moment) that suggests I may be able to use the PrintEnd event, but I couldn't immediately see if this would be applicable when using Interop. Would it be a method of achieving what I want without polling?
One solution is to poll the BackgroundPrintingStatus property of the Word application. It holds a count of the documents still waiting in the printing queue. While this count is greater than 0 there are still documents awaiting printing.
There are many ways you could achieve this. Here's a simple loop which blocks the UI:
// Send document to printing queue here...
while (wordApp.BackgroundPrintingStatus > 0)
{
// Thread.Sleep(500);
}
// Printing finished, continue with logic
Alternatively you may want to wrap it in a task so that you can do other things while waiting:
await Task.Run(async () => { while (wordApp.BackgroundPrintingStatus > 0)
{ await Task.Delay(500); } });

Cloning an outlook email to resend

I want to add a "resend" context menu in my Outlook 2016 add-in, to resend an email. The original email should be re-displayed to the user for him to make any modifications if necessary, and then press the 'send' button. It seems that I need to create a copy of the email, as calling Display() on the original message (or a copy created with MailItem.Copy()) just views the message, as opposed to showing it editable with a send button.
I got this so far - pretty straight forward:
Outlook.MailItem clone = Globals.ThisAddIn.Application.CreateItem(Outlook.OlItemType.olMailItem) as Outlook.MailItem;
clone.SendUsingAccount = email.SendUsingAccount;
clone.To = email.To;
clone.CC = email.CC;
clone.BCC = email.BCC;
clone.Subject = email.Subject;
clone.Body = email.Body;
clone.HTMLBody = email.HTMLBody;
for (int i = 1; i <= email.Attachments.Count; ++i)
clone.Attachments.Add(email.Attachments[i], email.Attachments[i].Type, email.Attachments[i].Position, email.Attachments[i].DisplayName);
However, I am getting a DISP_E_MEMBERNOTFOUND error when trying to copy the attachments. What am I doing wrong?
Attachments.Add only allows to pass a string pointing to a fully qualified path to a file or an Outlook item (such as MailItem). Also note that you code only copies the recipient display names, which may or may not be successfully resolved.
Outlook Object Model exposes MailItem.Copy method, but it creates a copy in the same sent/unsent state as the original.
If using Redemption (I am its author) is an option, you can use RDOMail.CopyTo() method - it will copy all the properties and sub-objects (such as recipients and attachments) but it will leave the sent state intact (since in MAPI it can only be set before the message is saved for the very first time).
Off the top of my head:
using Redemption;
...
RDOSession session = new RDOSession();
session.MAPIOBJECT = Globals.ThisAddIn.Application.Session.MAPIOBJECT;
RDOMail clone = session.GetDefaultFolder(rdoDefaultFolders.olFolderDrafts).Items.Add();
RDOMail original = (RDOMail)session.GetRDOObjectFromOutlookObject(email);
original.CopyTo(clone);
clone.Save();
MailItem OutlookClone = Globals.ThisAddIn.Application.Session.GetItemFromID(clone.EntryID);
OutlookClone.Display()

Accessing custom task pane in active window - Visual Basic, VSTO

I'm creating a COM add-in in VSTO for Ppt 2013 and am having a problem referencing the custom task pane in the active window.
My code is supposed to make the custom task pane visible for the active window only, however it currently runs for all document windows.
My code is:
For Each CTP As Microsoft.Office.Tools.CustomTaskPane In Globals.ThisAddIn.CustomTaskPanes
If CTP.Window Is Globals.ThisAddIn.Application.ActiveWindow Then
CTP.Visible = True
End If
Next
The taskpane is added to each new presentation created/ opened using the below code
AddIn_control1 = New AddIn_control
AddIn_taskpane = Me.CustomTaskPanes.add(AddIn_control1, "Add-in taskpane", Me.Application.ActiveWindow)
I conducted a little experiment and turns out CustomTaskPane.Window is always ActiveWindow. So to workaround it you can keep tracking of tackpanes in some dictionary:
Dictionary<CustomTaskPane, PowerPoint.Presentation> ctpDict = new Dictionary<CustomTaskPane, PowerPoint.Presentation>();
void Application_AfterNewPresentation(PowerPoint.Presentation Pres) {
AddIn_control AddIn_control1 = new AddIn_control();
CustomTaskPane AddIn_taskpane = this.CustomTaskPanes.Add(AddIn_control1, "Add-In Taskpane", this.Application.ActiveWindow);
ctpDict.Add(AddIn_taskpane, Pres);
}
and later you can use it:
if (cptDict[CTP] == Globals.ThisAddIn.Application.ActivePresentation) {
CTP.Visible = true;
}

Determine whether the user selected Refresh in WebBrowser Control

How can I detect if the user selected Refresh via the context menu? The NavigateComplete2 method does not get invoked when the user selects it.
More importantly, to set own custom user agent, one way to do it is to hook the BeforeNavigate2 event method and it is necessary to know if the user has selected Refresh or navigating a new url.
Any insight would be appreciated.
This demonstrates the NavigateComplete2 method does not get fired when Refresh is selected.
oWB := new WebBrowserControl("http://stackoverflow.com")
Class WebBrowserControl
{
__New(strURL) {
static WB
Gui, New, Resize
Gui, Add, ActiveX, vWB w780 h580, Shell.Explorer
Gui, show, w800 h600
ComObjConnect(WB, this)
WB.Navigate(strURL)
Loop
Sleep 10
Until (WB.readyState=4 && WB.document.readyState="complete" && !WB.busy)
Return
GuiClose:
ExitApp
}
NavigateComplete2(oParams*) {
ComObjError(false)
WB := oParams[1]
msgbox, 64, Navigate Completed
, % "WB.locationURL :`t`t" WB.locationURL "`n"
. "WB.Document.URL:`t`t" WB.Document.URL "`n"
. "windowlocation.href:`t" WB.document.parentWindow.location.href
}
BeforeNavigate2(oParams*) {
WB := oParams[8]
strURL := oParams[2]
msgbox % "Loading URL:`t`t" strURL "`n"
. "WB.locationURL :`t`t" WB.locationURL "`n"
. "WB.Document.URL:`t`t" WB.Document.URL "`n"
. "location.href:`t`t" WB.document.parentWindow.location.href "`n"
. "WB.ReadyState:`t`t" WB.readystate "`n"
. "WB.document.readystate:`t" WB.document.readystate "`n"
. "WB.Busy:`t`t`t" WB.Busy "`n"
}
}
One way to see if a refresh (or new page) has been initiated is to monitor the mouse state (in Chrome, not sure about other browsers) with: if/while (A_Cursor = "AppStarting"). This is true when the mouse cursor has turned into an hourglass.
Comparing the previous and the new URL would tell you if this is a new request or a refresh: ControlGetText CurrentURL, Chrome_OmniboxView1, Chrome
Hope this helps.

How do I grab hold of a pop-up that is opened from a frame?

I am testing a website using WatiN.
On one of the pages I get a "report" in an Iframe, within this I frame there is a link to download and save the report. But since the only way to get to the link is to use frame.Link(...) the pop-up closes immediately after opening; Code snippet below
//Click the create graph button
ie.Button(Find.ById("ctl00_ctl00_ContentPlaceHolder1_TopBoxContentPlaceHolder_btnCreateGraph")).Click();
//Lets export the data
ie.Div(Find.ById("colorbox"));
ie.Div(Find.ById("cboxContent"));
ie.Div(Find.ById("cboxLoadedContent"));
Thread.Sleep(1000);//Used to cover performance issues
Frame frame = ie.Frame(Find.ByName(frameNameRegex));
for (int Count = 0; Count < 10000000; Count++) {double nothing = (Count/12); }//Do nothing I just need a short pause
//SelectList waits for a postback which does not occur.
try
{
frame.SelectList(Find.ById("rvReport_ctl01_ctl05_ctl00")).SelectByValue("Excel");
}
catch (Exception)
{
//Do nothing
}
//Now click export
frame.Link(Find.ById("rvReport_ctl01_ctl05_ctl01")).ClickNoWait();
IE ieNewBrowserWindow = IE.AttachTo<IE>(Find.ByUrl(urlRegex));
fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(150);
fileDownloadHandler.WaitUntilDownloadCompleted(200);
I have tried using ie instead of frame which is why all those ie.Div's are present.
if I use frame the pop-up window opens and closes instantly.
If I use ie I get a link not found error.
If I click on the link manually, while the test is "trying to find the link" the file will download correctly.
I have changed the code to use a different page that doe not have the frame and I still get the same problem download pop-up closes instantly.
[STAThread]
public void TestForMeterDataExport()
{
// Open a new Internet Explorer window and
// goto the website.
IE ie = new IE("https://<URL>", true);
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler("C:\\Documents and Settings\\karnold\\Desktop\\MeterUsageReport_Large.xls");
Regex urlRegex = new Regex("<URL>\\?Mode=true&ReportID=[a-z A-Z 0-9]{30,33}&ControlID=[a-z A-Z 0-9]{30,33}&Culture=1033&UICulture=1033&ReportStack=1&OpType=Export&FileName=BuildingMeterDataReport&ContentDisposition=OnlyHtmlInline&Format=Excel");
//Find the Username text field and input the user ID
ie.TextField(Find.ByName("ctl00$ContentPlaceHolder1$txtUsername")).TypeText("<Name>");
//Find the Password text field and input the password
ie.TextField(Find.ByName("ctl00$ContentPlaceHolder1$txtPassword")).TypeText("PASS");
//Go ahead and login
ie.Button(Find.ByName("ctl00$ContentPlaceHolder1$butLogin")).Click();
//Let's use the Reports Tab
ie.Link(Find.ByUrl("https://<URL>")).Click();
// Let's get the meter data
ie.Link(Find.ByUrl("https://<URL>")).Click();
//Let's choose University of
ie.SelectList(Find.ById("ctl00_ctl00_ctl00_ContentPlaceHolder1_TopBoxContentPlaceHolder_TopBoxContentPlaceHolder_ucFacility_ddlFacility")).SelectByValue("5041");
//Set the date range for which we want to get data
ie.TextField(Find.ById("ctl00_ctl00_ctl00_ContentPlaceHolder1_TopBoxContentPlaceHolder_TopBoxContentPlaceHolder_DateRangePicker1_dpBeginDate_TextBox")).TypeText("12/09/10");
ie.TextField(Find.ById("ctl00_ctl00_ctl00_ContentPlaceHolder1_TopBoxContentPlaceHolder_TopBoxContentPlaceHolder_DateRangePicker1_dpEndDate_TextBox")).TypeText("12/10/10");
//Click the create report button
ie.Button(Find.ById("ctl00_ctl00_ctl00_ContentPlaceHolder1_TopBoxContentPlaceHolder_TopBoxContentPlaceHolder_btnSubmit")).ClickNoWait();
//Lets export the data
Thread.Sleep(2000);
//SelectList waits for a postback which does not occur.
try
{
ie.SelectList(Find.ById("ctl00_ctl00_ctl00_ContentPlaceHolder1_ContentAreaContentPlaceHolder_ContentAreaContentPlaceHolder_rvMain_ctl01_ctl05_ctl00")).SelectByValue("Excel");
}
catch (Exception)
{
ie.SelectList(Find.ById("ctl00_ctl00_ctl00_ContentPlaceHolder1_ContentAreaContentPlaceHolder_ContentAreaContentPlaceHolder_rvMain_ctl01_ctl05_ctl00")).FireEventNoWait("onchange");
//fire the postback event
}
//Now click export
ie.Link(Find.ById("ctl00_ctl00_ctl00_ContentPlaceHolder1_ContentAreaContentPlaceHolder_ContentAreaContentPlaceHolder_rvMain_ctl01_ctl05_ctl01")).ClickNoWait();
IE ieNewBrowserWindow = IE.AttachTo<IE>(Find.ByUrl(urlRegex));
fileDownloadHandler.WaitUntilFileDownloadDialogIsHandled(10);
fileDownloadHandler.WaitUntilDownloadCompleted(20);
}// close TestForMeterDataExport()
Hopefully some one can tell me what I am doing wrong. Thank you
Here is the error that I get when the program can't find the handle maybe it will help
TestCase 'M:WebTest.CommandLine.WatiNConsoleWebAndDB.TestForMeterDataExport'
failed: Error HRESULT E_FAIL has been returned from a call to a COM component.
System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been returned from a call to a COM component.
at SHDocVw.IWebBrowser2.get_Document()
at WatiN.Core.Native.InternetExplorer.IEBrowser.get_NativeDocument()
at WatiN.Core.Native.InternetExplorer.IEWaitForComplete.WaitForCompleteOrTimeout()
at WatiN.Core.WaitForCompleteBase.DoWait()
at WatiN.Core.DomContainer.WaitForComplete(IWait waitForComplete)
at WatiN.Core.Native.InternetExplorer.AttachToIeHelper.FinishInitializationAndWaitForComplete(IE ie, SimpleTimer timer, Boolean waitForComplete)
at WatiN.Core.Native.InternetExplorer.AttachToIeHelper.Find(Constraint findBy, Int32 timeout, Boolean waitForComplete)
at WatiN.Core.Browser.AttachTo(Type browserType, Constraint constraint, Int32 timeout)
at WatiN.Core.Browser.AttachTo(Type browserType, Constraint constraint)
at WatiN.Core.Browser.AttachTo[T](Constraint constraint)
Web+DB_test_app.cs(139,0): at WebTest.CommandLine.WatiNConsoleWebAndDB.TestForMeterDataExport()
Thanks to Baptiste for the pointer.
//Set the handles and the file save as name
FileDownloadHandler handler = new FileDownloadHandler("MeterUsageReport_Large_Iframe.xls");
// add a watcher to look for the save file local
ie.AddDialogHandler(handler);
//Do not close dialog boxes immediately
ie.DialogWatcher.CloseUnhandledDialogs = false;
//create a single use instance that will be easily cleaned up and avoid having windows open after we are done with them
using (new UseDialogOnce(ie.DialogWatcher, handler))
{
//Now click export
frame.Link(Find.ById("rvReport_ctl01_ctl05_ctl01")).ClickNoWait();
//Grab hold of the poup dialog and download the file
handler.WaitUntilFileDownloadDialogIsHandled(30);
handler.WaitUntilDownloadCompleted(35);
}
Now for the fun this will only work if all of the work is done on localhost.
if you need to hit a server that is not local and ou want to use IE well then
2) You will need to edit the security for "trusted sites to allow scripting of downloads and Iframes.
* a) open IE
* b) Tools -> Internet options
* c) Security tab.
* d) make sure "trusted site" is highlighted
* e) click custom level, Make sure all .Net and .Net reliant components are set to enabled.
* f) Enable or prompt all activeX components
* g) Enable all downloads
* h) Enable .Net framework setup
* i) Enable scripting of web browser controls
* j) Enable allow websites to open windows without address's or status bars.
* k) Enable Launching programs or files from an Iframe.
* l) Everything under scripting is set to enabled or prompt.
* Just so you know, localhost is treated as a "low" security area so tests run on localhost do not need these changes.
* Refer to http://support.microsoft.com/kb/174360