Printing .PDF from Winform Application - c#

I have an application that executes an SSRS report to create a file, which is saved locally in PDF. I would like to print the file, but we have been experiencing problems with the Acrobat Reader security settings, which are stopping the print from happening - our security guys and app support have said that Acrobat is not an option and I need to find another way to print the report.
All of the computers that will be used to run the app have Google Chrome installed, so it seems sensible to me to use this to open and print the PDF. We can set chrome as the default application for opening PDF files, and with this done, I had the following code snippet running (after some research online):
Process process = new Process();
PrintDialog printDialog = new PrintDialog();
numberOfCopies = printDialog.PrinterSettings.Copies;
ProcessStartInfo starter = new ProcessStartInfo();
starter.FileName = "\"" + printFileName + "\"";
starter.Arguments = "\"" + printDialog.PrinterSettings.PrinterName + "\"";
//starter.Verb = "printto";
for (short s = 1; s <= numberOfCopies; s++)
{
starter.CreateNoWindow = true;
starter.WindowStyle = ProcessWindowStyle.Hidden;
starter.UseShellExecute = true;
process.StartInfo = starter;
process.Start();
//... more code
With the line starter.Verb = "printto"; commented, chrome opens the PDF, but adding the verb causes an error to be returned saying there is no default app for that file type. I have been doing some research and it seems maybe I should be using headless mode, but all I have found there is command line stuff and ASP.NET.
I believe that a previous developer had problems with MS Word as well, so would prefer to avoid that route if possible - I think there was something to do with Citrix, but not sure.
Does Chrome support the use of the print command like this, or am I on a hiding to nothing? If it does support, what am I doing wrong / how could I do it better (e.g. headless mode?)? If it does not support, is there some other way that I can use to print a PDF without using acrobat?

Google chrome has no command line switch for "printto". Hence when you use the verb "printto" there is an error. Below are the list of command line switches supported by Chrome.
https://peter.sh/experiments/chromium-command-line-switches/
There is a switch "--print" in chrome. But that doesn't print pdf file.
If you are able to print pages using Acrobat reader then it should work. It would be easier for me to help if you can give more details on the security settings issue that you were facing. Having said that, I have been using Acrobat reader to initiate PDF printing in my Windows forms application for a fair amount of time . They have the "print" and "printto" verbs implemented. And it works well in all of production deployment.
System.Diagonstics.Process class does what "Start-Process" does on Powershell. We can test printing using "Start-Process" without having to run the C# code. Set the default application for opening pdf as Acrobat reader. Run the below sample code by changing pdf location.
Start-Process "C:\DevOps-with-ASP.NET-Core-and-Azure.pdf" -Verb printto "OneNote"
By this we can identify the security issue and do necessary changes to allow printing through Acrobat Reader.
Printing pdf through a browser using Process class is not feasible as of now. Even the Microsoft's own edge browser doesn't support printing through Process. Below is the error we get when we try to do same in powershell
PS C:\Users\darsh> Start-Process "C:\DevOps-with-ASP.NET-Core-and-Azure.pdf" -Verb printto "OneNote"
Start-Process : This command cannot be run due to the error: No application is associated with the specified file for
this operation.
At line:1 char:1
+ Start-Process "C:\DevOps-with-ASP.NET-Core-and-Azure.pdf" -Verb print ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand

Related

Run "Find" (ctrl + F) in Google Chrome using C#

I'm trying to make an app, which could open a pdf file in a browser (Chrome) and search for a certain word automatically. However, I can't find anything about passing commands to Google from C# whatsoever.
Process process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = "chrome";
process.StartInfo.Arguments = "console.log(\"TEST1\")";
process.Start();
I've tried running Chrome with StartInfo.FileName = "chrome", which went well. After that I thought I could add some Arguments: StartInfo.Arguments = "something" and that's where I ran into two problems:
The browser takes the Arguments as a URL and tries to open it as a web page (which of course fails)
I couldn't find the console command to run the search function in Chrome
Is there maybe some google API capable of this thing? Also please note that I'm not trying to use "search". I need the browser to literally focus on the word it found
If anyone here knows how to solve even one of these problems, I would be really grateful. Also this is my first question on StackOverflow (and I might have forgotten something) - I can give some additional info, if you ask

using C# Process.Start("msedge", "--app=\"https://www.google.com\"") opens in blank browser window

I'm developing .NET Windows From application in C#. As per my requirement, I need to open a URL in the browser's Pop up (not the browser main window) same as it is opened using javascript's "window.open" method. This was a little tricky but I managed by using the below CMD command.
C:\Window\system32>start msedge --app="https://www.google.com/"
I parsed the above command into C# code to trigger a similar result. Process.Start("msedge", "--app=\"https://www.google.com\"");
The code works fine for Chrome only. But when it runs for Edge or Firefox it opens the browser (main browser window) with an empty address bar. Whereas I'm expecting this should open the URL in the mentioned browser popup window.
Which version of Edge are you using? I use code below and it works well. You can try it to see if it works on your machine:
Process process = new Process();
string arg = "--app" + "=" + "https://www.google.com";
process.StartInfo.FileName = "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe";
process.StartInfo.Arguments = arg;
process.Start();

Converting .xls to .xlsx using excelcnv.exe or ofc.exe in C#

I am trying to run a 'EXE' using c# code to convert .xls to .xlsx. It is running successfully and working as expected if I run using a console application or cmd.
But when I am running it in server side (code hosted in IIS i.e code running using w3wp.exe worker process) it unable to convert files to .xlsx
First, I used Excelcnv.exe to convert
if (System.IO.File.Exists(pstrIPDTemplatePath))
{
string pstrOpenXMLIPDTemplatePath = pstrIPDTemplatePath.Replace(".xls", ".xlsx");
string processArguments = "-oice \"" + pstrIPDTemplatePath + "\" \"" + pstrOpenXMLIPDTemplatePath + "\"";
ProcessStartInfo Excelcnv = new ProcessStartInfo();
Excelcnv.FileName = mstrExcelCnvExePath;
Excelcnv.Arguments = processArguments;
Process.Start(Excelcnv);
}
It converted few files successfully but for few files it is creating excelcnv.exe*32 process in task manager, but it is not converting excel file. I even used event handlers for output, but the result is null. Process is going to indefinite state.
But if I am Running it through console application it converted the file successfully it also popped up a dialog after running it to convert multiple files that Excel did not launch correctly and asking whether to start in Safe Mode.
To avoid this safe mode issue, I tried to use ofc.exe
So, I used ofc.exe to convert as below:
Process prc = new Process();
prc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
prc.StartInfo.FileName = #"OFC.exe";
prc.StartInfo.Arguments = #"ofc.ini";
prc.StartInfo.UseShellExecute = false;
prc.StartInfo.RedirectStandardOutput = true;
prc.Start();
Same case with this code. It is working successfully and converting as expected when running via console application.
It is unable to convert to .xlsx but creating the process in task manager and log file is empty when running using w3wp process hosted in IIS.
Even after giving IIS_IUSRS permission to 'EXE' location, it is not working but its working in console application.
Any ideas why this is happening, and how to get around it?
Try to add your w3wp running user to 'Administrator' group. It will work.
Otherwise use this which is mentioned here

XpsDocument GetFixedDocumentSequence return null for files generated by a specific computer

Solidworks generate "EDRWX" files. Usually, those are opened with Microsoft XPS viewer.
Issue :
An application use .net XPSDocument to open them and do some manipulation with them before printing. The faulting method is "GetFixedDocumentSequence". It returns null with files generate from a specific computer. It does so even when the application is running on the specific computer. But it is working fine with file generated by other computer.
When trying to open the file with XPS document viewer, it's fail to open saying it can't be opened. However, it does open on the specific computer that created it. If this computer print a normal XPS file from microsoft word, others computer are able to open it normally with XPS document viewer. Also, "EDRWX" file generated by others computers are easily opened with XPS document viewer on every computer including the specific one.
More specification :
The specific computer run under windows 8.1.
Others computers run under windows 7.
The application run with .net Framework 4.0.
Here is a "EDRWX" dummy file generated by the specific computer.
Here is a "EDRWX" dummy file generated by one of the others computers.
What is the cause of the issue and how can it be fixed?
Sidenote : I have been investigating for almost 3 days....So I'm getting angry at the problem. May be your eyes can see what I don't anymore for being sucked into it this much.
In my digging, I found this difference between the two files.
The failing EDRWX files are not XPS files, they are Open XPS files. The two formats are very similar, however as you have discovered, XPSDocument doesn't support OpenXPS. I am not aware of any .net API that directly supports Open XPS, however microsoft did release standalone converter tools that you may be able to leverage.
Another alternative, which may or may not work, is to try to force SolidWorks to generate xps instead of oxps on the failing machine. It is possible that solidworks is using the xps document writer to generate these files, in which case changing the output format as described here may resolve the issue.
Like Jon pointed out, the issue is that starting with windows8, solidworks use OXPS format no matter what. Changing default format in Group Policy worked for others applications but Solidworks didn't care. So I ended up using the only tool provided by Microsoft thus far to handle the situation. Which is the converter application that come with a windows update package. Once installed, the converting application can be found here :
C:\Program Files (x86)\Windows Kits\8.0\bin
There is a x86 version and a x64 one.
Here is an usage example in the console.
So added the converter appplication next to my application and in my application I silently launch the converter. Here is a method that show how I did it. The method convert the given file to XPS format if necessary. But the resulted conversion must be another file, because the tool can't overwrite the file it is converting.
private string EnsureFileIsUnderXPSFormat(string fileName)
{
string retValue = fileName;
string convertedFileName = System.IO.Path.GetDirectoryName(fileName).TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar + System.IO.Path.GetFileNameWithoutExtension(fileName) + "_converted" + System.IO.Path.GetExtension(fileName);
Version win8version = new Version(6, 2, 9200, 0);
if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version >= win8version || true)
{
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.Arguments = "/XPS /InputFile=" + fileName + " /OutputFile=" + convertedFileName;
string applicationFolderPath = AppDomain.CurrentDomain.BaseDirectory.TrimEnd(System.IO.Path.DirectorySeparatorChar) + System.IO.Path.DirectorySeparatorChar;
startInfo.FileName = applicationFolderPath + "xpsconverter.exe";
//if (Environment.Is64BitOperatingSystem)
// startInfo.FileName = #"C:\Users\maxiveil\Desktop\testConversion\xpsconverter.exe";
//else
// startInfo.FileName = #"C:\Users\maxiveil\Desktop\testConversion\xpsconverter.exe";
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.CreateNoWindow = true;
using (System.Diagnostics.Process processus = System.Diagnostics.Process.Start(startInfo))
{
processus.WaitForExit();
if (processus.ExitCode != 0)
{
throw new Exception("Failed to convert OXPS file(" + fileName + ") to XPS format. Error code : " + processus.ExitCode.ToString());
}
}
retValue = convertedFileName;
}
return retValue;
}
N.B : The converter application doesn't care if you try to convert a XPS format file to XPS format file. So the "windows8 if" is optional.

Starting serverside print job via PHP

This is most likely not an easy one but here is the situation:
I have written a C# command line application which:
creates a PDF using ITextSharp
writes it to disk
uses Acrord32.exe (this is Acrobat Reader) via System.Diagnostics.Process in order to silently print the generated PDF
If I build my solution and double click the pdfGen.exe, it works as expected. The PDF is created and printed.
Now, my app has to be deployed on a internal server with Windows Vista running IIS 7. This server has some PHP webapp running. And it will be called via PHP using shell_exec() so that the resulting PDF will be printed on the printer attached to the server.
So my PHP page looks basically like this:
shell_exec('/path/to/pdfGen.exe');
But here things go wrong. What happens is according to task manager etc.:
pdfGen.exe starts
the PDF is created
Acrord32.exe starts
pdfGen.exe hangs forever (and so does the PHP script) and nothing is printed
I am pretty sure it is some permission related problem. I already gave IIS_IUSRS access to the default printer, and to the directory where Acrord32.exe is located. But still, no printing. However, if I start my pdfGen.exe manually it works.
Any idea what I am missing?
EDIT:
I am not bound to use Acrobat Reader in order to print the PDF. If there is another way in order to silently print the created PDF serverside, I would not mind at all.
In order to check what is going on, try to run
the process monitor from Sysinternals and filter the events to the adobe acrobat process. You will see the system calls of acrobat and it will allow you to know more or less what is going wrong.
I know a small improvement to your solution: SumatraPDF has a nice command-line interface that can be used to auto-close Sumatra after printing.
I used PHP "system" or "exec" functions to execute a batch file to open SumatraPDF:
sumatrapdf.exe -print-to-default -exit-on-print <path_to_PDF_file>
(you can also specify the printer name to print on)
that's an interesting program.
IIS_IUSRS
seems to have no permission to print, try adding IIS_IUSRS to Print Operators Group / grant Print permission to the user.
Shell_exec() is almost intended for shell commands (ls/dir, cp, etc.)
Have you tried to use exec() instead of shell_exec() ?
Thanks all for your comments. Unfortunately this "php start printjob" thing was part of a larger project that was cancelled today because of, well... I dont know... political reasons. Guess the project is pretty much dead.
Anyway, I tried myself a few more times in the last days and could not get it to work with IIS. My solution that I implemented and tested already: remove IIS, install a XAMPP or WAMPP package with a local apache and PHP that runs with admin access rights.
This did the trick. I used pclose(popen('...command...', 'r')); in PHP in order to start the .exe and so that PHP does not wait until the PDF is finished. It all worked great.
Here is my C# code which starts the print job using Acrobat Reader
public void Print(string pathname, string acrobatDirectory)
{
var proc = new Process
{
StartInfo =
{
Arguments = String.Format("/t \"{0}\"", pathname),
FileName = acrobatDirectory,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = false,
RedirectStandardError = false,
}
};
proc.Start();
}
The first argument is the path to the PDF that should be printed, the second parameter is the absolute path to the AcroRd32.exe.
The only problem left was that AcroRd32.exe was started, printed and never got closed again. So every printjob started a new instance of AcroRd32.exe (I am using Acrobat Reader 9.0). So if you printed 10 times, 10 acrobat reader instances were created.
What I did was starting the print job, then waiting X seconds, hoping that the printer was finished and then killing all AcroRd32.exe instances:
public void Print(string pathname, string acrobatDirectory)
{
Debug.WriteLine("Printing...");
Printer.Print(pathname, acrobatDirectory);
Thread.Sleep(30000);
try
{
Debug.WriteLine("Trying to kill runnung AcroRd32.exe's ");
FindAndKillProcess("AcroRd32");
}
catch (Exception)
{
Debug.WriteLine("AcroRd32.exe could not be killed...");
}
}
private bool FindAndKillProcess(string name)
{
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith(name))
{
clsProcess.Kill();
return true;
}
}
return false;
}
This worked out quite well.
Note that the above (killing all AcroRd32.exe and running PHP with admin privilegs) was only doable because: The whole thing is only used by one user at a time and has a very limited area of use.
It should be used on a touchscreen application deployed at the clients POS. A salesman would use the PHP app in order to configure a product, and then PHP would call my .exe which would create and print a PDF in the background. The printed document is then handed to the client. So security etc. was not really a concern in this case.
If anyone has a solution in order to use it with IIS, I am still willing to accept it as an answer.

Categories

Resources