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); } });
Related
I am trying to create a new instance of Excel using VBA using:
Set XlApp = New Excel.Application
The problem is that this new instance of Excel doesn't load all the addins that load when I open Excel normally...Is there anything in the Excel Application object for loading in all the user-specified addins?
I'm not trying to load a specific add-in, but rather make the new Excel application behave as though the user opened it themself, so I'm really looking for a list of all the user-selected add-ins that usually load when opening Excel.
I looked into this problem again, and the Application.Addins collection seems to have all the addins listed in the Tools->Addins menu, with a boolean value stating whether or not an addin is installed. So what seems to work for me now is to loop through all addins and if .Installed = true then I set .Installed to False and back to True, and that seems to properly load my addins.
Function ReloadXLAddins(TheXLApp As Excel.Application) As Boolean
Dim CurrAddin As Excel.AddIn
For Each CurrAddin In TheXLApp.AddIns
If CurrAddin.Installed Then
CurrAddin.Installed = False
CurrAddin.Installed = True
End If
Next CurrAddin
End Function
Using CreateObject("Excel.Application") would have the same result as using New Excel.Application, unfortunately.
You will have to load the Addins that you need individually by file path & name using the Application.Addins.Add(string fileName) method.
I'm leaving this answer here for anyone else who ran into this problem, but using JavaScript.
A little background... In my company we have a 3rd party web app that used JavaScript to launch Excel and generate a spreadsheet on the fly. We also have an Excel add-in that overrides the behavior of the Save button. The add-in gives you the option of saving the file locally or in our online document management system.
After we upgraded to Windows 7 and Office 2010, we noticed a problem with our spreadsheet-generating web app. When JavaScript generated a spreadsheet in Excel, suddenly the Save button no longer worked. You would click save and nothing happened.
Using the other answers here I was able to construct a solution in JavaScript. Essentially we would create the Excel Application object in memory, then reload a specific add-in to get our save button behavior back. Here's a simplified version of our fix:
function GenerateSpreadsheet()
{
var ExcelApp = getExcel();
if (ExcelApp == null){ return; }
reloadAddIn(ExcelApp);
ExcelApp.WorkBooks.Add;
ExcelApp.Visible = true;
sheet = ExcelApp.ActiveSheet;
var now = new Date();
ExcelApp.Cells(1,1).value = 'This is an auto-generated spreadsheet, created using Javascript and ActiveX in Internet Explorer';
ExcelApp.ActiveSheet.Columns("A:IV").EntireColumn.AutoFit;
ExcelApp.ActiveSheet.Rows("1:65536").EntireRow.AutoFit;
ExcelApp.ActiveSheet.Range("A1").Select;
ExcelApp = null;
}
function getExcel() {
try {
return new ActiveXObject("Excel.Application");
} catch(e) {
alert("Unable to open Excel. Please check your security settings.");
return null;
}
}
function reloadAddIn(ExcelApp) {
// Fixes problem with save button not working in Excel,
// by reloading the add-in responsible for the custom save button behavior
try {
ExcelApp.AddIns2.Item("AddInName").Installed = false;
ExcelApp.AddIns2.Item("AddInName").Installed = true;
} catch (e) { }
}
Problem: I routinely receive PDF reports and annotate (highlight etc.) some of them. I had the bad habit of saving the annotated PDFs together with the non-annotated PDFs. I now have hundreds of PDF files in the same folder, some annotated and some not. Is there a way to check every PDF file for annotations and copy only the annotated ones to a new folder?
Thanks a lot!
I'm on Win 7 64bit, I have Adobe Acrobat XI installed and I'm able to do some beginner coding in Python and Javascript
Please ignore the following suggestion, since the answers already solved the problem.
EDIT: Following Mr. Wyss' suggestion, I created the following code for Acrobat's Javascript console to be run only once at the beginning:
counter = 1;
// Open a new report
var rep = new Report();
rep.size = 1.2;
rep.color = color.blue;
rep.writeText("Files WITH Annotations");
Then this code should be applied to all PDFs:
this.syncAnnotScan();
annots = this.getAnnots();
path = this.path;
if (annots) {
rep.color = color.black;
rep.writeText(" ");
rep.writeText(counter.toString()+"- "+path);
rep.writeText(" ");
if (counter% 20 == 0) {
rep.breakPage();
}
counter++;
}
And, at last, one code to be run only once at the end:
//Now open the report
var docRep = rep.open("files_with_annots.pdf");
There are two problems with this solution:
1. The "Action Wizard" seems to always apply the same code afresh to each PDF (that means that the "counter" variable, for instance, is meaningless; it will always be = 1. But more importantly, var "rep" will be unassigned when the middle code is run on different PDFs).
2. How can I make the codes that should be run only once run only at the beginning or at the end, instead of running everytime for every single PDF (like it does by default)?
Thank you very much again for your help!
This would be possible using the Action Wizard to put together an action.
The function to determine whether there are annotations in the document would be done in Acrobat JavaScript. Roughly, the core function would look like this:
this.syncAnnotScan() ; // updates all annots
var myAnnots = this.getAnnots() ;
if (myAnnots != null) {
// do something if there are annots
} else {
// do something if there are no annots
}
And that should get you there.
I am not completely positive, but I think there is also a Preflight check which tells you whether there are annotations in the document. If so, you would create a Preflight droplet, which would sort out the annotated and not annotated documents.
Mr. Wyss is right, here's a step-by-step guide:
In Acrobat XI Pro, go to the 'Tools' panel on the right side
Click on the 'Action Wizard' tab (you must first make it visible, though)
Click on 'Create New Action...', choose 'More tools' > 'Execute Javascript' and add it to right-hand pane > click on 'Execute Javascript' > 'Specify Settings' (uncheck 'prompt user' if you want) > paste this code:
.
this.syncAnnotScan();
var annots = this.getAnnots();
var fname = this.documentFileName;
fname = fname.replace(",", ";");
var errormsg = "";
if (annots) {
try {
this.saveAs({
cPath: "/c/folder/"+fname,
bPromptToOverwrite: false //make this 'true' if you want to be prompted on overwrites
});
} catch(e) {
for (var i in e)
{errormsg+= (i + ": " + e[i]+ " / ");}
app.alert({
cMsg: "Error! Unable to save the file under this name ('"+fname+"'- possibly an unicode string?) See this: "+errormsg,
cTitle: "Damn you Acrobat"
});
}
;}
annots = 0;
Save and run it! All your annotated PDFs will be saved to 'c:\folder' (but only if this folder already exists!)
Be sure to enable first Javascript in 'Edit' > 'Preferences...' > 'Javascript' > 'Enable Acrobat Javascript'.
VERY IMPORTANT: Acrobat's JS has a bug that doesn't allow Docs to be saved with commas (",") in their names (e.g., "Meeting with suppliers, May 11th.pdf" - this will get an error). Therefore, I substitute in the code above all "," for ";".
We have developed a Thunderbird (11) plugin that allows us to save the content of a message to disk. Now we are extending this extension to allow automatic processing of a message when you close it. We run into a number of issues:
We cannot find a way to hook into a 'close tab' event. We are also having trouble getting the Message URI of the currently open tabs (we are trying catching click and keyboard events now). This information does not appear to be available in the DOM of the tab container.
Is there a way to detect closing of a mail message tab or window in a generic way, together with retrieving the URI of the closed mail message for further processing?
We have looked at the documentation of the tab container, the NsIWindowMediator, tried various event listeners, but no luck so far.
Edit: We are getting some results using the most recently closed tabs list. Not a very elegant solution but at least we have a reference to the tab. Now we only have to get the URI to the message that was contained inside the tab.
We cannot find a way to hook into a 'close tab' event.
The (badly documented) <tabmail> element allows registering tab monitors. Something like this should work:
var tabmail = document.getElementById("tabmail");
var monitor = {
onTabClosing: function(tab)
{
...
}
};
tabmail.registerTabMonitor(monitor);
We are also having trouble getting the Message URI of the currently open tabs
The <tabmail> element has a property tabInfo containing information on the currently open tabs. You probably want to look only at the tabs where mode.name is "message" (there is a bunch of other modes as well, e.g. "folder" or "contentTab"). This mode has a getBrowser() method, so something like this should do:
var tabmail = document.getElementById("tabmail");
for (var i = 0; i < tabmail.tabInfo.length; i++)
{
var tab = tabmail.tabInfo[i];
if (tab.mode.name == "message")
alert(tab.mode.getBrowser().currentURI.spec);
}
Edit: As Peter points out in the comments, the approach to get the URI for a message will only work the currently loaded message - all tabs reuse the same browser element for the mail messages. Getting the URI properly is more complicated, you have to get the nsIMsgDBHdr instance for the message via TabInfo.folderDisplay.selectedMessage and then use nsIMsgFolder.getUriForMsg() to construct the URI for it:
var tabmail = document.getElementById("tabmail");
for (var i = 0; i < tabmail.tabInfo.length; i++)
{
var tab = tabmail.tabInfo[i];
if (tab.mode.name != "message")
continue;
var message = tab.folderDisplay.selectedMessage;
alert(message.folder.getUriForMsg(message));
}
For the second part of the question:
The following example code will at provide you the msgDBHdr objects of all opened tabs. You should do some checks on the type to avoid accessing a message in a calendar tab.):
tabInfos = window.document.getElementById("tabmail").tabInfo;
for (i = 0; i < tabInfos.length; i++) {
msgHdr = tabInfos[i].folderDisplay.selectedMessage;
alert(
msgHdr.mime2DecodedSubject+"\n"
+msgHdr.messageId+"\n"
+"in view type "+tabInfos[i].mode.type
);
}
The tabinfo entries have some further interesting information. Just open the ErrorConsole and run
top.opener.window.document.getElementById("tabmail").tabInfo[0].toSource()
and read through it carefully.
any expert here got any idea how I can determine a word document if created from blank document or open from an existing document.
I am using Globals.ThisAddIn.Application.ActiveDocument.Saved but it seems not enough.
This should do
if (string.IsNullOrEmpty(WordInstance.ActiveDocument.Path))
{
throw new ApplicationException("Document must be saved first");
}
for vsto developers, please go here
if (Globals.ThisAddIn.Application.ActiveDocument.Path == String.Empty)
{
Word.Dialog dlg;
Object timeout = 3000;
dlg = Globals.ThisAddIn.Application.Dialogs[
Word.WdWordDialog.wdDialogFileSaveAs];
int result = dlg.Display(ref timeout);
}
else
{
Globals.ThisAddIn.Application.ActiveDocument.Save();
}
The result will store which button is pressed (0- cancel, 1- ok, 2- close)
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