I need to print a word document on server side to a network printer. My web page sends the document and file location to the server to open and replace mail merge items and then print the document to a preferred (not default) network printer. Preferred printer name changes when document selected on web page changes
I'm using Word 14.0 object library, Asp.Net MVC 4.0, .Net Framework 4.0, IIS 7 on windows server 2008 R2. On IIS I created an app pool that runs on an specific account Identity (accountName#DomainName). Load user profile set to true to load the network printer connections in to the registry. I allowed the Account to have permissions to run the word COM interop services. I was successful to open the document and replace the mail merge fields and save it as pdf to send the file as attachment to an email.
The word application has the default printer in its ActivePrinter property, so that I could print to default printer as well. But my final goal is to print the word to a preferred network printer before I close the word application and active document.
The following two Methods were causing exceptions if I try to change the ActivePrinter property.
Word.Application wordApp = new Word.Application();
First Method:
wordApp.ActivePrinter = "preferredPrinterName";
Second Method;
object[] oWordDialogParams = { "\\<serverName>\<PrinterName>", true };
string[] argNames = { "Printer", "DoNotSetAsSysDefault" };
object wordBasic = wordApp.WordBasic;
wordBasic.GetType().InvokeMember("FilePrintSetup"
, System.Reflection.BindingFlags.InvokeMethod
, null
, wordBasic
, oWordDialogParams
, null
, null
, argNames);
I found that the Word application object is not loading all the printers installed to the user account. It only loads the default printer. I'm assuming that it was the reason for exceptions when above two methods were attempting to change or add preferred printer to the application object because the printer I'm trying to set was never found in the active printers list.
How do I get all installed shared printers under user profile loaded in to the Word application object?
Using Office interop in a server-scenario (like ASP.NET, Windows Service etc.) is NOT supported by MS - see http://support.microsoft.com/default.aspx?scid=kb;EN-US;q257757#kb2
Additionally there have been several security-related changed since Windows Vista which basically make it really hard to do anything "desktop-like" in a Windows Service (IIS/ASP.NET is just a special case of Windows Service in this regard).
Another point is "printing" from a server-scenario is likely to cause problems since IIS is a (special) Windows Service... Windows Service usually don't have a "full/real" desktop which in turn is needed for printing robustly...
I don't think that there an easy solution for your scenario...
I would break it down to different components:
Word document handling (for example with Aspose.Words)
Create a PDF from the resulting Word file (for example with Aspose.Words)
Implement a HotFolder on the target network printer
Copy the PDF over to that HotFolder for printing
This would be a robust and supported option for your scenario...
Related
I have a Windows server running an ASP.NET application and a local printer connected to this machine. I need to print some documents from the server-side code.
So far I know, there's no managed API in .NET that is supported on server-side (service).
System.Printing namespace - is part of the WPF and is not supported to run on server-side as it may produce run-time exceptions (checked on msdn)
System.Drawing.Printing - is part of the WinForms and also not supported to run on server-side (checked on msdn)
The same problem was elaborated with help of Microsoft back in 2009 and the solution was to use an unmanaged XPS Print API as the only supported way back in that time. Problem described and solution with example posted is here: How to Print a Document on a Server via the XpsPrint API
However nowadays this is a problem as the XPS Print API is marked as not supported and may be unavailable in the future (msdn).
So, what is the supported way of printing from the server-side code?
It looks like there are more Win32 APIs that could be probably used, but there's no info on the web and it would probably be a nightmare...
Commercial solutions are accepted. Thank you.
So the best way would be to set up your printer on a print server and then install the print drivers on your web server with a reference to the printer you want to print to.
Then in code you can use System.Drawing.Printing to send the print job to whatever printer you just installed.
PrintDocument p1 = new PrintDocument();
p1.PrinterSettings.PrinterName = "\\PrintServer\NewPrinter";
p.PrintPage += new PrintPageEventHandler(this.Page_Print);
p1.Print();
protected void Page_Print(object sender, PrintPageEventArgs ev)
{
Brush b = new SolidBrush(Color.Black);
Font printFont = new Font("Lucida Sans Typewriter", 10);
ev.Graphics.DrawString("Hello World", printFont, b, x, y, StringFormat.GenericDefault);
}
Where this code will work technechally I would not recommend doing it this way since Microsoft itself says that this isn't supported.
I personally would push all print jobs to the client side. Install the printer locally like you did and then redirect the user to a page that they can print easily.
You can then write JavaScript if desired to call the browser specific print for them (if you want to automate that as well)
Or save a PDF server side and push the file as a download to the user making them download/save and then print the document via Adobe Reader or alternative app.
I'm warning you, that yhis question will seems to be very strange for a lot of people:) But I have to post it because my project manager is teeling me that a technical solution exist, even if for me it doesn't.
What we have:
A Windows 7 Console Application with no UI, with our C# application running and no Office and Interop on it
a Windows 2012 server, with Ms Office 2010 + Interop installed on it (also with IIS and .NET of course)
What my PM want (and I told him it is not possible) :
From my C# client application
Automate "Ms office" installed on the server
Automate means "Save" or "print" a doc file to a network printer.
Of course the Ms office process had to run on the server
This kind of solution of "remote Ms Office automation is possible" seems to be impossible for me. But maybe I am wrong, it can be possiblbe using DCOM, WCF, or something else?
Anyone can confirm I am right please ;)
As you already learned from the comments, automating the desktop version of any of the Office applications is bad for several reasons. The details can be found in the Knowledge base article KB257757 Considerations for server-side Automation of Office. The main take away from that article is:
Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.
But as you are still insisting, consider the following example as a very simple, naive, not to be used near production proof-of-concept that enables you to quickly run into all problems mentioned in the KB article.
In a fresh solution create a WCF Service application and a Console Application. In the WCF application add the following interface:
[ServiceContract]
public interface IPrintService
{
[OperationContract]
string Print(Stream wordDoc);
}
and have a service implement that. Make sure to add a reference to Microsoft.Office.Interop.Word that you can find in the COM tab of the Add Reference dialog.
public class PrintService : IPrintService
{
public string Print(Stream wordDocStream)
{
// copy our stream to a local file
var tempFile = Path.GetTempFileName();
using(var file = File.Create(tempFile))
{
wordDocStream.CopyTo(file);
}
// start word
var wordApp = new Microsoft.Office.Interop.Word.Application();
// setup printer
wordApp.ActivePrinter = "Canon LBP3010/LBP3018/LBP3050";
// open, collect data, print and close
var doc = wordApp.Documents.Open(tempFile);
doc.PrintOut();
var res = doc.Words.Count;
doc.Close(false);
// quit word
wordApp.Quit(false);
// delete temp file
File.Delete(tempFile);
return String.Format("{0} words", res);
}
}
You can see here a barebone solution to print a document that is sent as a stream to the Service. The service copies the stream to a file, starts Word, Opens the file, prints the document, get some data from the document and tears down and cleans up hen finished.
The client is straight forward:
using(var client = new PrintService.PrintServiceClient())
{
using(var file = File.Open(#"small.docx", FileMode.Open))
{
var response = client.Print(file);
Console.WriteLine(response);
}
}
This is technically all there is that is needed to print a Word document from a service. This runs without much problems on the dev server. If you run this on IIS you'll probably have to make sure that the account used as an identity in the AppPool is a "user" that can start Word, is allowed to access the printers etc. I already ran into one known issue: I used the XPS Print driver which caused a dialog to popup. That is something you can't have on a server and there is no real way to prevent or detect that.
Remember that this service interface only allows for a stream to be sent. If you want to add extra data you'll have to use a message contract as is explained on msdn in Large Data and Streaming. Your contract would have to look like this in that case:
[MessageContract]
public class UploadStreamMessage
{
[MessageHeader]
public string appRef;
[MessageBodyMember]
public Stream data;
}
If you run all this, (stress)test, consider deployment and install I'm sure you'll convince anyone that this isn't a good idea.
I want to build a Windows Service in C# which listens to an MSMQ queue for incoming print command messages. A message references a PDF document und contains the name of the destination printer (and maybe some other metadata like numbers of copies to print). The service should pick up the PDF and print it on the destination printer.
Question: Is it possible to print an arbitrary PDF on a printer from a Windows Service?
supplement:
The implementation should not depend on Acrobat Reader or any other "GUI tool". The Windows service runs headless. Besides I want to avoid to start a separate process for each print job when this is possible.
I use Spire.PDF library. The free version has a limit of 10 pages per file. No UI or Acrobat dependence
https://www.nuget.org/packages/Spire.PDF/
PdfDocument pdfdocument = new PdfDocument();
pdfdocument.LoadFromFile(path);
pdfdocument.PrinterName = printername;
pdfdocument.PrintDocument.PrinterSettings.Copies = copiesNumber;
pdfdocument.PrintDocument.Print();
pdfdocument.Dispose();
I have a problem with starting the Excel Application under a particular user.
I try to schedule this script (C#) through an application X (not Windows Task Scheduler. And this application will always use a service account to run services on the server). If I run the C# script in command prompt under the same user, it runs. Under the application X, which uses the exact same user, to initiate the C# script, it fails to open the Excel application (not sufficient permission?).
This script calls:
app.Workbooks.Open(ExcelFileName,0,false,Type.missing....), yet it gives the following error:
Microsoft Excel cannot access the file "...". There are several possible reasons:
-The file name or path does not exist.
-The file is being used by another program.
-The workbook you are trying to save has the same name as a currently open workbook.
I tried all the methods that I found online to no avail.
Create directory “C:\Windows\SysWOW64\config\systemprofile\Desktop” (for 64 bit Windows) or “C:\Windows\System32\config\systemprofile\Desktop” (for 32 bit Windows). Then Set full control permissions on Desktop directory above (for example in Win7 & IIS 7 & DefaultAppPool set permissions for user “IIS AppPool\DefaultAppPool”)
Changed the DCOM config for the Microsoft Excel application to include this user for Local/Remote Launch and Access
Enabled all macros in Excel and set the Trust Center.
Add the user to have full control on all folders that contain the Excel file.
Under DCOM config, Microsoft Excel Application, if I modify the Identity tab to check on "This User" and enter the username/password to let Excel always run under that user. Then the application runs perfectly. However, other users can't run the excel application on their own with the following error: "Cannot use object linking and embedding". If I check "Use the launching user", then Excel can't be launched. No errors in the logs or events anywhere to check.
Yet, still the same error. I think it's permission but I am not sure where and what to do for this to work.
Now, normally, when I run this excel report, I can double-click on the file and it'd automatically run, save the new parameters into the current file and generate a new excel file (with date attached to the file name). That means there is a change (save) to the original file.
I appreciate all your help!
I found the problem is the layer of security in the server!
Creating the Desktop folder inside the C:\Windows\System32\config\systemprofile and giving the service account permission to access the desktop folder is not enough.
I modified the C:\Windows\System32\config (or C:\windows\SysWOW64\config) folder to allow permission to the service account in security tab.
Then I also had to set up the same permission for the sub folder C:\Windows\System32\config\systemprofile (or C:\windows\SysWOW64\config\systemprofile) for the service account.
This works!
Microsoft Excel cannot access the file in Server 2012 Excel 2016.. checked various solutions online to create folder desktop in C:\Windows\SysWOW64\config\systemprofile\desktop.. didn't work for this but then i added a folder desktop in System32 and that definitely worked.
Since I have 64-bit Excel installed, the proper directory turned out to be the
c:\windows\system32\config\systemprofile\desktop
I ran across another way you can get this error: when you try to save a file with an "illegal" name, such as one with whacks (forward slashes) in it.
For that reason, it would seem reasonable that the err msg would give that as one possible explanation of the problem, but...no!
Specifically, I was trying to save a file named C:\RoboReporter\ABUELITOS\20160524_1327\ABUELITOS - Fill Rate - 4\1\61910B10
The whacks (shown as backwhacks in the copied exception text, but seen as forwardwhacks when hovering over the value when debugging) were the cause of the discombobulation. Once I fixed that by replacing whacks with underscores:
filename = String.Format("{0}\\{1} - Fill Rate - {2}.xlsx", uniqueFolder, _unit, _begDate.ToShortDateString());
filename = filename.Replace("/", "_");
...all went swimmingly.
I am using the WorkSite COM API to open an IMANEXT2Lib.IManFileOpenDlg which is working perfectly. Clicking on the document, I can retrieve the metadata, such as the document name, version, number etc.
Is there a method or technique available to read the document from WorkSite and prompt a download/open through a browser?
The IManage.IManDocument has the method GetCopy() available which will download a local copy to a specified directory, but this isn't really what I'm after.
If the clients have Worksite 8.5 or greater, you can use the IWL protocol in a hyperlink to force the client to use the IWL handler to download and launch the file themselves instead of streaming it from the browser. This has the added benefit of respecting the document's permissions and checking the document out to the user so they can make amends to the file if required.
An IWL formatted anchor tag in a web browser would look like the following:
Document x
Note that this will only work if the clients have Worksite 8.5 or newer installed.