2

I have a C# winform application in which an optimization model is solved by OR-Tools. The optimization solver has the capability of sending the whole optimization process as stdout.This is done by:

    Slvr.EnableOutput();
    Solver.ResultStatus restatus = Slvr.Solve(); 

However, the solver does not automatically open up the console. Currently, what I have done is:

Projects properties --> Application --> Output type --> Console Application

and the console is ready from the beginning till the end of the application run. Hence, that process stdout is automatically displayed.

What I want is to open the console exactly when the above part of code is run and display the stdout from the solver. Then wait for a key from the user to close the console and continue with the main application.

  • Just a thought. You could get the process number of the solver. Then add a pipe (stream) to standard output of the process. – jdweng Aug 31 '19 at 16:40
  • Thank you jdweng. Your suggestion was my last bullet! If I cannot find an easier way, I will go for it. – Behnam Alizadeh Aug 31 '19 at 17:30

2 Answers2

1

I guess your problem is you are trying to run the solver as part of the Winforms application, inside the GUI process right? But Console output is usually disabled in a Winforms application. You have basically two options:

  • use one of the options described here in this older SO answer to attach a console window for output to a Winforms application

  • split the application into two exe files: one command line program which runs the solver, and a Winforms part, just containing the UI. Then run the command line part as a separate process by System.Diagnostics.Process.Start, which allows finegrained control about output redirection. You may need the UI to pass parameters to the command line program, for example, by using a temporary file.

The second option is more work, especially for the communication between the GUI and the command line tool, but can be implemented easier in a way the GUI is not blocked, is more robust against bugs / program crashes in the solver part and performs usually better in case you want to introduce parallelization / run multiple solver processes at once.

Doc Brown
  • 19,739
  • 7
  • 52
  • 88
  • Thanks Doc. You are right; The solver acts as a part of a Winform application. The main problem with the second approach is that I do not know how to catch the process by the solver. Please note that I do not run an .exe file to launch the solver. I do it inside code as explained. Thus, I do not know how to access the process. I have also tried AllocConsole(); both before and after the solve statement with no success. – Behnam Alizadeh Aug 31 '19 at 17:33
  • @BehnamAlizadeh: you probably did not get my point about the second approach: it means you need create **two** exe files, one Winforms application and one command line application. Yes, I understood that currently you do not run such a second exe file, but that is exactly what you could do here to solve your issue. – Doc Brown Aug 31 '19 at 18:10
  • Oh, I get it! you mean running the solver from a self-created .exe in the console mode. right? Yes, this is possible and I will give it a try. However, ain't there another approach which does not need the second exe? – Behnam Alizadeh Aug 31 '19 at 18:38
  • @BehnamAlizadeh: when you read all the answers to the link I gave in my answer, and look at the comments about those "almost" working solutions which do not work under Windows 8 and Windows 10 any more, I guess creating a second exe is the only solution which works reliable. – Doc Brown Aug 31 '19 at 18:54
  • Yes, I agree. I will pass the parameters through a temp file as you suggested. Many thanks. – Behnam Alizadeh Aug 31 '19 at 19:01
  • This is really my response except creating 2nd application to get to standard output. – jdweng Aug 31 '19 at 19:01
  • @BehnamAlizadeh: and thanks to you to pointing me to google's OR tools, I guess I have some application for those. – Doc Brown Aug 31 '19 at 19:04
1

Doc Brown has already answered your question, I'm only adding this to provide some code of how we implemented it here-- it's exactly what he suggests. We have a separate testPlugin.exe that get's started here. The communication is via files read and written on the file system. The console output gets captured in the "output handlers"

using System;
using System.Diagnostics;
using System.IO;

...

        private void startTest()
        {
            int result = 2;
            setFormStatus("working...");   // My method to inform the user with the form to wait.

            getFormData();  // My method to get the data from the form

            string errorMessage = null;
            System.Diagnostics.Process testPlugInProcess = new System.Diagnostics.Process();
            try
            {
                using (testPlugInProcess)
                {
                    testPlugInProcess.StartInfo.UseShellExecute = false;
                    testPlugInProcess.StartInfo.FileName = System.IO.Path.Combine(assemblyDirectory, TestPlugInExe); // The name of the exe file
                    testPlugInProcess.StartInfo.CreateNoWindow = false;
                    testPlugInProcess.StartInfo.Arguments = getModelTestCommandLineArgs(); // My method to create the command line arguments
                    testPlugInProcess.StartInfo.RedirectStandardError = true;
                    testPlugInProcess.StartInfo.RedirectStandardOutput = true;
                    testPlugInProcess.OutputDataReceived += pluginTestOutputHandler;
                    testPlugInProcess.ErrorDataReceived += pluginTestOutputHandler;
                    testPlugInProcess.Start();
                    testPlugInProcess.BeginErrorReadLine();
                    testPlugInProcess.BeginOutputReadLine();
                    testPlugInProcess.WaitForExit();
                    result = testPlugInProcess.ExitCode;
                }
                setFormStatus("");
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
            }
            testPlugInProcess = null;
        }

Both the console and error output get written to the same file here, but you could separate them.

The plug-in handler looks like this:

        private static void pluginTestOutputHandler(object sendingProcess,
           DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                for (int i = 0; i < numberOfTriesForWriting; i++) 
                {
                    try
                    {
                        using (StreamWriter sw = File.AppendText(lastPlugInTestTraceFilePath)) // The file name where the data is written.
                        {
                            sw.WriteLine(outLine.Data);
                            sw.Flush();
                            return;
                        }
                    }
                    catch (IOException)
                    {
                        System.Threading.Thread.Sleep(msToWaitBetweenTries);
                    } 
                }
            }
        }
Christopher Hamkins
  • 1,442
  • 9
  • 18