ASP.NET gets InvalidOperationException exception on System.Diagnostic.Process object

Member_2_8198376
Member_2_8198376 used Ask the Experts™
on
I have an ASP.NET MVC Framework application in C#. In one of my Controller functions, I get the following errors:
BasePriority = 'process.BasePriority' threw an exception of type 'System.InvalidOperationException'
HandleCount = 'process.HandleCount' threw an exception of type 'System.InvalidOperationException'
MainModule = 'process.MainModule' threw an exception of type 'System.ComponentModel.Win32Exception'
MainWindowHandle = 'process.MainWindowHandle' threw an exception of type 'System.InvalidOperationException'
Modules = 'process.Modules' threw an exception of type 'System.ComponentModel.Win32Exception'
ProcessName = 'process.ProcessName' threw an exception of type 'System.InvalidOperationException'
Position = '((System.IO.FileStream)process.StandardError.BaseStream).Position' threw an exception of type 'System.NotSupportedException'
Threads = 'process.Threads' threw an exception of type 'System.InvalidOperationException'

There are other errors also, Most of them are InvalidOperationException errors.

Here is the code.
[AllowAnonymous]
public ActionResult PrintSummaryPDF()
{
    Process process = null;
    ProcessStartInfo processStartInfo = new ProcessStartInfo();
    processStartInfo.FileName = ConfigurationManager.AppSettings["WkHtmlToPdfExePath"];
    processStartInfo.Verb = "runas";
    processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    processStartInfo.RedirectStandardError = true;
    processStartInfo.RedirectStandardOutput = true;
    processStartInfo.CreateNoWindow = true;
    processStartInfo.UseShellExecute = false;
    string fileNameDatePart = DateTime.Now.ToLongTimeString().Replace(":", string.Empty).Replace(" ", string.Empty);
    string pdfFileName = "Survey" + fileNameDatePart + ".pdf";
    processStartInfo.Arguments = HttpContext.Request.Url.AbsoluteUri.Replace("PrintSummaryPDF", "PrintSummary") + " " +
        " --load-error-handling ignore" + " --cookie .ASPXAUTH " + Request.Cookies["DCCCDCookie"].Value + " \"" + ConfigurationManager.AppSettings["WkHtmlToPdfOutputPath"] + pdfFileName + "\"";
    process = Process.Start(processStartInfo);
    process.WaitForExit(Convert.ToInt32(ConfigurationManager.AppSettings["TimeOut"]));

    int exitCode = process.ExitCode;
    string stdout = process.StandardOutput.ReadToEnd();
    string stderr = process.StandardError.ReadToEnd();

    return File("../pdfoutput/" + pdfFileName, "application/pdf");
}

Open in new window


The browser page then states:
Can’t reach this page
•Make sure the web address http://localhost:54635 is correct

The StandardOutput, stdout, reads:

Qt: Untested Windows version 6.2 detected!
Loading pages (1/6)
[>                                                           ] 0%
[======>                                                     ] 10%
[================>                                           ] 27%
[===================>                                        ] 33%
[=======================>                                    ] 39%
[============================>                               ] 47%
[=====================================>                      ] 62%
[========================================>                   ] 68%
[============================================>               ] 74%
[================================================>           ] 81%
[============================================================] 100%
Counting pages (2/6)                                               
[============================================================] Object 1 of 1
Resolving links (4/6)                                                       
[============================================================] Object 1 of 1
Loading headers and footers (5/6)                                           
Printing pages (6/6)
[>                                                           ] Preparing
[==============================>                             ] Page 1 of 2
[============================================================] Page 2 of 2
Done    

Open in new window


I suspected that the path to, WkHtmlToPdfExePath perhaps did not have sufficient permissions for iisexpress.exe; but when I check the permissions, it has full permissions for my account and the admin account, and I do have admin privileges on this machine.
I also checked the, WkHtmlToPdfOutputPath

This application is running in Visual Studio Enterprise 2017, version 15.8.7

Please advise. Thanks.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
The problem doesn't appear to be your call to the external process; rather, it appears to be happening during the printing phase of the external process's execution.

Of the Stack Trace you've shared, this appears to be the important part:
ProcessName = 'process.ProcessName' threw an exception of type 'System.InvalidOperationException'
Position = '((System.IO.FileStream)process.StandardError.BaseStream).Position' threw an exception of type 'System.NotSupportedException'

Open in new window


... the exceptions after that are subsequent failures because of this one.

Given the exception I'd wager that the stream being passed into the third-party library is from an HttpWebResponse... because those aren't searchable and as such don't support Position or Length. In the code that calls the library, instead of directly passing the stream from the response, try coping it into a MemoryStream first and pass that to the library. You can read more about the scenario here.

Author

Commented:
It turns out that the path to the output path was different on the machine of the person who created this app than it was on mine. For that person, the path was as shown in the code above in my question.
I changed it to :
 . . .
 var outputPath = ConfigurationManager.AppSettings["WkHtmlToPdfOutputPath"] + pdfFileName;
 . . .
return File(outputPath, "application/pdf");

Open in new window

And that fixed it.

Kelvin McDaniel,
When you say, library, do you mean the WkHtmlToPdfExePath executable, wkhtmltopdf.exe?
What is the code the calls the library? Is that, Process.Start(processStartInfo);
What code is the stream from the response that you mentioned?
thanks
That change makes sense. Depending on how wkhtmltopdf.exe works an incorrect path could result in a null Stream, which would of course not have a value for Postion -- hence the error you were seeing.

- The library is whatever wkhtmltopdf.exe itself is using to handle the PDFs. If it's not using a library I'd be very surprised.
- The code that calls the library is wkhtmltopdf.exe.
- The stream is something that wkhtmltopdf.exe would create internally and use to generate the output PDF.
Exploring ASP.NET Core: Fundamentals

Learn to build web apps and services, IoT apps, and mobile backends by covering the fundamentals of ASP.NET Core and  exploring the core foundations for app libraries.

Author

Commented:
Hello Kelvin,
I hope you had a nice weekend. Thank you for your answers. I suspect that if I encounter this again, it will more likely be because of what you said about the memory stream.
Could you provide a code example of what you suggested: "In the code that calls the library, instead of directly passing the stream from the response, try coping it into a MemoryStream first and pass that to the library."
I looked at your link, but I didn't see how to relate it to your suggestion. Thanks.
Sure. If you run into the problem that I was describing, it would be because whatever is actually making the request to the URL that you're passing in is using a HttpWebRequest. You would then use HttpWebResponse.GetResponseStream() to get the Stream that has the content you requested. Herein lies the "problem".

The fix is relatively simple; once you obtain the handle on the response stream, simply push it into a MemoryStream and use that instead of the response stream itself.

Here's a .NET Fiddle that illustrates what I'm describing.

Author

Commented:
Hello Kelvin,
   Since my code starts a process, I don't know how to "obtain the handle on the response stream."
   For example, in my code, I start the process like this:
process = Process.Start(processStartInfo);
process.WaitForExit(Convert.ToInt32(ConfigurationManager.AppSettings["TimeOut"]));

Open in new window

Is 'process' the handle on the response stream? In that case, I would do something like,
		process = Process.Start(processStartInfo);
           process.WaitForExit(Convert.ToInt32(ConfigurationManager.AppSettings["TimeOut"]));
		using (process) 
                {
			using(var stream = new MemoryStream()) {
				using(var responseStream = process.GetResponseStream()) {
					responseStream.CopyTo(stream);
				}
				
				// now do whatever you're going to do; in this case, let's tell a little joke...
				stream.Position = 0;
				
				using(var reader = new StreamReader(stream)) {
				   // . . . 
				}				
			}
		}

Open in new window

Is that how I obtain the handle on the response stream?
thanks
You would need the source code of the wkhtmltopdf.exe to do this.
It turns out that the path to the output path defined in the web.config file was different on the machine of the person who created this app than it was on mine. When I downloaded the code from TFS, it still had the path that worked on the machine of the person who had previously worked on the app.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial