We help IT Professionals succeed at work.

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

532 Views
Last Modified: 2019-03-01
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

Kelvin McDanielSr. Developer
CERTIFIED EXPERT

Commented:
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
Kelvin McDanielSr. Developer
CERTIFIED EXPERT

Commented:
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.

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.
Kelvin McDanielSr. Developer
CERTIFIED EXPERT

Commented:
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
Kelvin McDanielSr. Developer
CERTIFIED EXPERT

Commented:
You would need the source code of the wkhtmltopdf.exe to do this.
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.