Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to detect, and react when an External sub Process, invokes an error dialog

I am aware that this may on first glance look like a question you have seen before: Knowing when an external process' window shows

But this is slightly different.

I have an C# asp.net web application, for helping people create an installer for their programs. (The developers here are mostly mechanical engineers scripting equations in some calculation tools, they are not software people, so we don't want them spending time learning wix, debugging the installers, maintaing GUID's between releases, and so on..)

The serverside will be running the console application "heat.exe" (a tool that is shipped with the wix tools), to harvest information on how to register dll's etc., if and only if they have a dll in their repository..

I do it like this:

    public int runHeat(string filePath, string outputFile, ref string response)
    {
        response += "run heat.exe to harvest file data" + '\r' + '\n';

        string args = "file " + '"' + filePath + '"' + " -srd -out" + '"' + outputFile + '"';
        string command = Path.Combine(WixBinariesPath, "heat.exe");
        string workPath = Path.GetDirectoryName(filePath);

        StringBuilder outputBuilder;
        ProcessStartInfo processStartInfo;
        Process process;

        outputBuilder = new StringBuilder();

        processStartInfo = new ProcessStartInfo();
        processStartInfo.CreateNoWindow = true;
        processStartInfo.RedirectStandardOutput = true;
        processStartInfo.RedirectStandardInput = true;
        processStartInfo.UseShellExecute = false;
        processStartInfo.WorkingDirectory = workPath;
        processStartInfo.Arguments = args;
        processStartInfo.FileName = command;
        processStartInfo.ErrorDialog = false;

        //create the process handler
        process = new Process();
        process.StartInfo = processStartInfo;

        // enable raising events because Process does not raise events by default
        process.EnableRaisingEvents = true;

        // attach the event handler for OutputDataReceived before starting the process
        process.OutputDataReceived += new DataReceivedEventHandler
        (
            delegate(object sender, DataReceivedEventArgs e)
            {
                // append the new data to the data already read-in
                outputBuilder.AppendLine(e.Data);
            }
        );

        // start the process
        // then begin asynchronously reading the output
        // then wait for the process to exit
        // then cancel asynchronously reading the output
        process.Start();
        process.BeginOutputReadLine();
        process.WaitForExit();

        // use the output
        response += outputBuilder.ToString();

        if (process.ExitCode != 0)
            response += '\r' + '\n' + "heat.exe exited with code: " + process.ExitCode;

        process.CancelOutputRead();
        return process.ExitCode;
    }

I thought this worked.. It passed tests, it's been running for a while without problems, and then suddenly, a developer called, that the webtool I made, no longer produces wix xml for him..

When I logged into the server, I found this dialog:

The runtime error message

and then clicked [OK] - the web application then continued, and produced the xml, and stuff worked..

I have now found the dll that, makes heat throw this error. It doesn't really need registering (typical right?). So I could probably just write a timeout thing, to kill heat.exe if it takes to long, and thus unlock the waiting script, (and basicly fix the issue untill it happens again with a dll that actually needs registering) But that is not really detecting the error, that is just detecting that stuff takes time...

On this error, I would like to continue the script, but present a warning to the user, that heat.exe failed to run on that particular file. But to do this I need my asp.net application to know that this error was invoked, and dispose it, so that the script can continue..

how the *? do I get information that this runtime error occurred, so I can handle it from the server script?

Have you tried using the -sreg command line option to heat?

I now have, and as a result, heat.exe no longer chrashes, but this is not a solution, as heat also avoids harvesting the registry information that I need for autoregistering the dll's shipped with the code in question.

like image 423
Henrik Avatar asked Apr 13 '15 08:04

Henrik


1 Answers

Working with external "uncooperative" executables often requires some trickery. I'd try the following:

  • Start the program on the command line, and check if there is any output when the error occurs. Probably it writes to standard error, and you could use RedirectStandardError, read the stream and hopefully get a clue when the error occurs.

  • Check if there is any logging-possibility in heat.exe that you could enable, and use this to detect the error-case. Maybe a verbose setting, or a log-file...

  • If none of the above worked, I'd use process monitor (e.g. https://technet.microsoft.com/de-at/sysinternals/bb896645.aspx). Start process monitor and then your application and bring it to the point of error. Filter the enormous output in process monitor to just your application (which is still quite a lot) and search at the end, whether there is any access where the programm might log the error. Maybe some log-files, or a logging-service. You could check this file after your timeout.

  • But something the would work in any case is the hack you already suggested in your question. To detect whether the dialog opened. There are also possibilities to browse through the content of the dialog box, so you could also read the text and check which kind of error it is. I used this once in production code to get the progress of an external program, which was written in a text field inside the form. I used spy++ (bundled with Visual Studio) to get the name/id of the text field, and accessed it using the (native) windows API. An ugly hack, but worked fine unless the external program's UI is changed. In your case it is a standard Error Dialog, so it should stay quite consistent.

like image 110
azt Avatar answered Nov 10 '22 19:11

azt