I am trying to print an xps document to printers (network printer, some virtual local printers, xps and non xps based) with the following code.
C# Source:
static void Main(string[] args)
{
PrintServer printServer = new PrintServer(#"\\printserver.csez.zohocorpin.com");
foreach (PrintQueue queue in printServer.GetPrintQueues())
{
Console.WriteLine("Printer: {0}, Port: {1}, ShareName: {2}, status: {3}, PrintingIsCancelled: {4}",
queue.Name, queue.QueuePort.Name, queue.ShareName, queue.QueueStatus, queue.PrintingIsCancelled);
Program program = new Program();
Thread printingThread = new Thread(() => program.Print_XPXFile(queue, #"D:\Assist\RemotePrint\Spool\Donalduck.xps"));
// Set the thread that will use PrintQueue.AddJob to single threading.
printingThread.SetApartmentState(ApartmentState.STA);
printingThread.Start();
printingThread.Join();
}
}
public void Print_XPXFile(PrintQueue pQueue, String FilePath)
{
// Create print server and print queue.
bool fastCopy = pQueue.IsXpsDevice;
FileInfo file = new FileInfo(FilePath);
if (!file.Exists)
{
Console.WriteLine("There is no such file.");
}
else
{
Console.WriteLine("Adding {0} to {1} queue. share name : {2}", FilePath, pQueue.Name, pQueue.ShareName);
try
{
// Print the Xps file while providing XPS validation and progress notifications.
PrintSystemJobInfo xpsPrintJob = pQueue.AddJob(file.Name, FilePath, fastCopy);
Console.WriteLine("Done adding.");
}
catch (PrintJobException e)
{
Console.WriteLine("\n\t{0} could not be added to the print queue.", file.Name);
if (e.InnerException.Message == "File contains corrupted data.")
{
Console.WriteLine("\tIt is not a valid XPS file."); // Use the isXPS Conformance Tool to debug it.
}
else
{
Console.WriteLine("\tmessage : {0}", e.InnerException.Message);
}
}
}
}
When printing to Microsoft XPS Document Writer, Microsoft Print to PDF, etc it works fine.
I found that it is working fine with all XPS based printers. I even installed a XPS sample printer driver and added a virtual local printer to confirm this claim and as expected it worked.
For non-xps based printers, it actually gets stuck in the AddJob function. It neither throws any exception, nor it moves to the next statement.
I developed the code based on this msdn resource.
What is the cause and solution?
All thoughts are welcome.
I can't seem to find the problem. But here is a more promising way to print XPS files:
public static void PrintXPSToDefaultPrinter(string FilePath)
{
try
{
// Create the print dialog object and set options
PrintDialog pDialog = new PrintDialog();
pDialog.PageRangeSelection = PageRangeSelection.AllPages;
pDialog.UserPageRangeEnabled = true;
FileInfo file = new FileInfo(FilePath);
XpsDocument xpsDocument = new XpsDocument(FilePath, FileAccess.ReadWrite);
FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence();
pDialog.PrintDocument(fixedDocSeq.DocumentPaginator, file.Name);
}
catch (System.IO.IOException ex)
{
Console.WriteLine("The file is being used by some other process.");
}
catch (Exception ex)
{
Console.WriteLine("Exception occured : {0}", ex.Message);
}
}
You should call this in STA mode just like you have used:
static void Main(string[] args)
{
Thread printingThread = new Thread(() => PrintXPSToDefaultPrinter(#"D:\Assist\RemotePrint\Spool\Donalduck.xps"));
printingThread.SetApartmentState(ApartmentState.STA);
printingThread.Start();
}
You can also mention any printqueue u want in the pDialog.PrintQueue property.
Hope this helps. Enjoy man !!!
I, too, got stuck in the AddJob() method. The problem seemed to be more prevalent as Windows 10 platforms became involved.
Breaking the execution in debug, the stack trace showed the STA thread was blocked on a call to
MS.Internal.PrintWin32Thunk.XpsCompatiblePrinter.JobIdentifier.get
and this was further blocking on a low-level WaitOne() on some unknown synchronization object.
Even though details on this problem are thin (this is the only post I have found on the topic), THANKFULLY the accepted solution works extremely well (and I have been trying all kinds of things to print in WPF for years).
AddJob() is fully replaced in all respects. One can even control the PrintTicket more easily through the PrintDialog (to set paper size, orientation, one/two sided printing, etc.)
Related
I have an application that displays a pdf in a webBrowser control, using the following code
webBrowser1.Navigate(filename + "#toolbar=0");
It works perfectly if Adobe Reader is installed
I would like to check if Adobe Acrobat Reader is installed before displaying the window, or at least when trying to display the pdf.
I have adapted the following code from here Check Adobe Reader is installed (C#)?
As mentioned in the comments, unfortunately, it flags uninstalled versions as well.
I have also tried the 64 bit code in the same article but find errors I can't easily resolve and suspect would give the same result anyway as it simmply looks at the registry in a similar way.
using System;
using Microsoft.Win32;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
RegistryKey adobe = Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Adobe");
if(null == adobe)
{
var policies = Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Policies");
if (null == policies)
return;
adobe = policies.OpenSubKey("Adobe");
}
if (adobe != null)
{
RegistryKey acroRead = adobe.OpenSubKey("Acrobat Reader");
if (acroRead != null)
{
string[] acroReadVersions = acroRead.GetSubKeyNames();
Console.WriteLine("The following version(s) of Acrobat Reader are installed: ");
foreach (string versionNumber in acroReadVersions)
{
Console.WriteLine(versionNumber);
}
}
}
}
}
}
If Adobe pdf Reader is not loaded, a prompt appears to open(in any other installed reader), save the file or cancel.
I would like to be able to intercept this so as to Indicate that Adobe's reader is not available.
I have tried
private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
string url = e.Url.ToString();
if (url.StartsWith("res://ieframe.dll/navcancl.htm") && url.EndsWith("pdf"))
{
e.Cancel = true;
MessageBox.Show("Cannot open PDF!");
}
}
at the following https://social.msdn.microsoft.com/Forums/en-US/46aaeecd-5317-462a-ac36-9ebb30ba90e7/load-pdf-file-using-webbrowser-control-in-windows-form-c?forum=csharpgeneral
but found the open, save cancel event precedes the webBrowser1_Navigating.
I would appreciate any help in a reliable solution that will not flag uninstalled versions, or a seperate solution that will stop the open, save, cancel prompt, and allow me to create a message to install the reader
Thanks
The Simple Way is to use try / catch
in the try give the code.
The web browser will give error if there is an error so in the catch then say the user to install adobe reader.
Here is the sample
try
{
//your code goes her
}
catch(Exception ex)
{
if(ex.Message != null)
{
Console.WriteLine("Adobe Reader not Installed");
//this is where you say adobe reader is not installed
}
}
I am developing a Windows application where I manipulate Word Application. More specific, I am opening a Word Document but when I quit it and try to open another Word Document this Error comes out.
How to handle
System.Runtime.InteropServices.COMException (0x800706BA): The RPC server is unavailable. (Exception from HRESULT: 0x800706BA) at Microsoft.Office,Word.ApplicationClass.set_Visible(Boolean Prop)**
If I don't quit the Word Application this error does not come out.
Below I show you the functions that I open and quit the Word Application.
//function to open word Document located in a specific path
public static void openWordDocument(string fileName)
{
try
{
wordApplication.Visible = true;
string filePath = myPath + fileName;
WordApi.Document docx = wordApplication.Documents.Open(filePath);
}
catch (Exception ex)
{
MyLogger.Error(ex.ToString());
}
}
//function to quit wordApplication
public static void CloseWordApp() {
try {
Object wordAppObject = Marshal.GetActiveObject("Word.Application");
WordApi.Application wordApp = (WordApi.Application)wordAppObject; //cast Object to its actual type
wordApp.Quit();
}
catch (Exception ex) {
MyLogger.Error(ex.ToString());
}
I finally figured it out what is the problem.
The main problem was that when I quit it and try to open another Word Document,which opening another Word Document means get/create an Object of Word Application. In my case wordApp != null, after finalizing the application, so I had to create another Word Application Object and return it for the case.
//open word Document located in a specific path
public static void openWordDocument(string fileName)
{
try
{
wordApplication = createWordApplicationObject(wordApplication);
wordApplication.Visible = true;
string filePath = myPath + fileName;
WordApi.Document docx = wordApplication.Documents.Open(filePath);
}
catch (Exception ex)
{
MyLogger.Error(ex.ToString());
}
}
private static WordApi.Application createWordApplicationObject(WordApi.Application wordApp)
{
WordApi.Application wordAppFirstTime;
WordApi.Application wordApp1;
if (wordApp == null)
{
wordAppFirstTime = new WordApi.Application();
return wordAppFirstTime;
}
else
{
wordApp1 = new WordApi.Application();
return wordApp1;
}
}
With CloseWordApp() remain the same.
Most probably the exception is fired by the following line of code:
wordApplication.Visible = true;
You need to make sure the COM server is alive. Because after quitting the object becomes unavailable. I'd suggest setting such object references to null, so later we could check whether the application object is still alive. For example:
try
{
if (wordApplication == null)
{
wordApplication = new Word.Application();
}
wordApplication.Visible = true;
string filePath = myPath + fileName;
WordApi.Document docx = wordApplication.Documents.Open(filePath);
}
catch (Exception ex)
{
MyLogger.Error(ex.ToString());
}
I wanted to add a solution that works for me. We had this issue in a .net web service, along with other errors, like "the remote procedure call failed" on Word.Documents.Open(). i'll list all the things we tried, and finish with the solution.
we tried:
Make sure RPC service is up. Word is not corrupted, opens properly,
including the file we were opening.
restart server and service hosting the web application.
Rollback a windows update that occured the same day it stopped working.
Uninstalled the antivirus software.
We isolated the code to a third party app to validate it was the open()
method that caused the problem, and using different files as well. We
created a win form app, and consol app. We ran that small app as win
admin, a regular account as well as the account that runs the web
app.
We ran procMon.
we did a repair on word.
we installed Office all over, we tried 32 and 64bits version
Finale solution:
we deleted the user profile that runs the web app.
4 days to find that out. I'd thought i'd share my paine with the world. lol
while posting these lines, we are not sure why the local profile created this issue.
I'm trying to send a pdf to the printer without the print dialog coming up using GhostScript.NET. My understanding is if I provide the exact name of the printer in the -sOutputFile switch, the user will not be prompted..
The exact name of my printer is 101-XER4250, and in debugging, the name that the processor receives is: "-sOutputFile=%printer%101-XER4250-E". Is there something that I am missing regarding this? Also if it's worth mentioning, I'm using a Xerox machine with PCL6 drivers.
Here's my example code:
private static void PrintWithGSNET(byte[] pdfFormBytes, string printer, int copies)
{
try
{
var fileName = #"c:\temp\" + $"{DateTime.Now:yyyyMMddhhmmssffff} - {Security.CurrentUser}";
using (var file = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite))
{
file.Write(pdfFormBytes, 0, pdfFormBytes.Length);
using (GhostscriptProcessor processor = new GhostscriptProcessor(GhostscriptVersionInfo.GetLastInstalledVersion(), true))
{
List<string> switches = new List<string>();
switches.Add("-empty");
switches.Add("-dPrinted");
switches.Add("-dBATCH");
switches.Add("-dPDFFitPage");
switches.Add("-dNOPAUSE");
switches.Add("-dNOSAFER");
switches.Add("-dNOPROMPT");
switches.Add("-dQUIET");
switches.Add("-sDEVICE=mswinpr2");
switches.Add("-sOutputFile=%printer%" + printer.Trim());
switches.Add("-dNumCopies=1");
switches.Add(fileName);
processor.StartProcessing(switches.ToArray(), null);
}
file.Close();
}
File.Delete(fileName);
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error printing. [Printer: {printer.Trim()}] [Copies: {copies}", ex);
}
}
If you want the user should not notice that the file is being printed you can add the no cancel command
switches.Add("-dNoCancel");
Is it a network printer? I had to qualify my printer name with the server name.
If I set the printer to be "printerName" the dialog popped up. When I changed it to #"\servername\printerName" the dialog went away and it printed silently.
I am attempting to open an Imanage document, in MS Word, within a temporary test application (for debugging) to later copy over into an ActiveX control project. The error that is popping up is:
Exception thrown at 0x7618851A (msvcrt.dll) in w3wp.exe: 0xC0000005: Access >violation reading location 0x09801000.
If there is a handler for this exception, the program may be safely continued.
The error occurs when running the cmd.Execute line and I am unsure as to why I am getting the error.
using IManage;
using IMANEXTLib;
using System;
namespace WebApplication3
{
public partial class WebForm2 : System.Web.UI.Page
{
IManDatabase imanagedatabase;
IManDMS myDMS = new ManDMSClass();
protected void Page_Load(object sender, EventArgs e)
{
openImanageDoc("docNumber", "versionNumber", "server", "database", ReadOnly);
}
public void imanageLogin(string server, string database)
{
try
{
IManSession session = myDMS.Sessions.Add(server);
IManWorkArea oWorkArea = session.WorkArea;
session.TrustedLogin();
foreach (IManDatabase dbase in session.Databases)
{
if (dbase.Name == database)
{
imanagedatabase = dbase;
}
}
}
catch (Exception ex)
{
throw ex;
}
}
public void openImanageDoc(string docNo, string versionNo, string server, string database, bool isReadOnly = true)
{
IManDocument doc;
try
{
imanageLogin(server, database);
int iDocNo = int.Parse(docNo);
int iVersion = int.Parse(versionNo);
doc = imanagedatabase.GetDocument(iDocNo, iVersion);
openNRTDocument(ref doc, isReadOnly);
imanagedatabase.Session.Logout();
myDMS.Close();
}
catch (Exception Ex)
{
imanagedatabase.Session.Logout();
throw Ex;
}
finally
{
imanagedatabase = null;
myDMS = null;
}
}
public void openNRTDocument(ref IManDocument nrtDocument, Boolean isReadonly)
{
OpenCmd cmd = new OpenCmd();
ContextItems objContextItems = new ContextItems();
objContextItems.Add("NRTDMS", myDMS);
objContextItems.Add("SelectedNRTDocuments", new[] { (NRTDocument)nrtDocument.LatestVersion });
objContextItems.Add("IManExt.OpenCmd.Integration", false);
objContextItems.Add("IManExt.OpenCmd.NoCmdUI", true);
cmd.Initialize(objContextItems);
cmd.Update();
cmd.Execute();
}
}
}
Due to the nature of the error, I am presuming it is a configuration issue rather than a code error although I could be completely wrong as I am very new to programming.
I have found out that w3wp.exe is an IIS worker process created by the app pool but other than that I have no idea what the numeric code represents. Any help or advice is greatly appreciated.
The error is being raised by the OpenCmd instance because it is most likely trying to access resources such as local registry settings. It's not possible to do that in a web application, unless you host your code in a proprietary technology like ActiveX (which is specific to Internet Explorer)
Actually, it is not appropriate for you to use OpenCmd here. Those type of commands (iManage "ICommand" implementations) are intended to be used in regular Windows applications that have either the iManage FileSite or DeskSite client installed. These commands are all part of the so-called Extensibility COM libraries (iManExt.dll, iManExt2.dll, etc) and should not be used in web applications, or at least used with caution as they may inappropriately attempt to access the registry, as you've discovered, or perhaps even display input Win32 dialogs.
For a web app you should instead just limit yourself to the low-level iManage COM library (IManage.dll). This is in fact what iManage themselves do with their own WorkSite Web application
Probably what you should do is replace your openNRTDocument method with something like this:
// create a temporary file on your web server..
var filePath = Path.GetTempFileName();
// fetch a copy of the iManage document and save to the temporary file location
doc.GetCopy(filePath, imGetCopyOptions.imNativeFormat);
In an MVC web application you would then just return a FileContentResult, something like this:
// read entire document as a byte array
var docContent = File.ReadAllBytes(filePath);
// delete temporary copy of file
File.Delete(filePath);
// return byte stream to web client
return File(stream, MediaTypeNames.Application.Octet, fileName);
In a Web Forms application you could do something like this:
// set content disposition as appropriate - here example is for Word DOCX files
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
// write file to HTTP content output stream
Response.WriteFile(filePath);
Response.End();
We have an application that have a print function that works well on all printers except some products from Epson on Windows 8.
I've manage to make a minimal sample that reproduce the problem.
Call the following method, providing it with the full path to a correct xps file.
private void Print(string xpsFilename)
{
if (string.IsNullOrEmpty(xpsFilename))
{
return;
}
PrintDialog printDialog = new PrintDialog();
printDialog.ShowDialog();
PrintQueue defaultPrintQueue = printDialog.PrintQueue;
try
{
// This is were it seems to fail for some Epson printers: no job in spooler, no print ...
PrintSystemJobInfo xpsPrintJob = defaultPrintQueue.AddJob("Print through 'AddJob' on PrintQueue", xpsFilename, false);
}
catch (PrintJobException printJobException)
{
Console.WriteLine("{0} Could not be added to the print queue.", xpsFilename);
Console.WriteLine(printJobException.Message);
}
catch (Exception exception)
{
Console.WriteLine("{0} Unknown error:", xpsFilename);
Console.WriteLine(exception.Message);
}
}
You will see that if you choose an Epson printer no job will appear in the spooler whereas it will work with any other printer.
Does anyone has an idea why it's not printing (the job does not even appears in the spool) ?
In the real application we do not use xps file but rather use a paginator, but for the sample purpose it's simpler and fail too ...
Thanks for any help.