Turn: Write your own IDE_ if you capture the standard output of the console in the graphical interface

xiaoxiao2021-03-05  25

Write your own IDE

How to capture the standard output of the console program in real time in the graphical interface

(Author: Wang Yonggang)

IDE is an abbreviation for integrated development environment (Integrated Development Environment). There are many excellent IDEs in the impression, such as JBuilder and Kylix, such as Visual Studio. I don't know if you pay attention, most IDE itself only provides code editing, engineering management and other human-computer interaction, we compile code in IDE, when debugging the program, IDE needs to call the command line compiler, the debugger completes the corresponding operation. For example, when using the Visual Studio compiles the C program, we will see the whole process of compilation and connection in the Output window below the IDE. Although we can't see the pop-up DOS window, it is actually the IDE launched the Microsoft C compiler. CL.EXE and connector Link.exe These two command line programs, while the output of Cl.exe and Link.exe reflects in real time to the Output window of the IDE. Also, we can configure your own tool programs (such as special compilers) in Visual Studio, then let Visual Studio run these tools in the appropriate time and display the output of the toolpower to the Output window. The following figure is the case where I run J2SDK's JavaC.exe in the Output window of Visual Studio 6.0 and compile the Java source and display the syntax error in the program:

That is, most IDE tools can call specific command line programs in the integrated environment (more exactly in Win32 is console program), and then capture their output in real time (most of which is output to standard stdout and stderr) Running things) and display the captured information in the window of the graphical interface.

This is obviously a function of potential value. Using this technology, we can at least

1. Write your own IDE, if we have enough patience;

2. Embed a full-text search function (call the Grep.exe tool in Borland C ), or compress and decompress the compressed function (call the compressed decompression program of the console method, such as Arj.exe, PKzip.exe, etc.) ;

3. Connect other people prepared, or our console procedures we have previously prepared - I often worry because it is difficult to call a powerful but no source of console procedures.

How is this good feature implementation?

First, if we want to do another console program with a console program, then it is simple. We only turn down the stdout of the parent process to the WRITE end of an anonymous pipe, then start the child process, at this time, the STDOUT of the child process is also connected by the relationship between the pipeline, all the standard output of the child process. Write a pipeline, the parent process is "listening" at the other end of the pipe - this technology is called the redirection of the input and output.

The problem now is that the Windows program of the GUI mode has no console, there is no STDIN, STDOUT, and the child process is what others write, this is not possible to talk about it?

There is another trick: We can directly redirect the sub-process's standard output to a file in the command line when the sub-process is called, and then read the file content after the child process is running. This method is of course feasible, but its problem is that we have difficulty monitoring the output of the child process in real time. If the child process is not always refreshed with stdout, then we can only wait for a whole piece of data to actually write files to see the results. Moreover, and the overhead of accessing the disk file is also much higher than the pipe operation in the memory. The program given here is actually very simple: Since the console program can call another console program and complete the redirection of the input and output, we can write a multi-agency, this intermediary program calls the tools we need to call and always Get the output information of the program, then directly pass the information between information (such as anonymous pipeline) with the agreed process, like this:

In the figure, tools and intermediary procedures are running in hidden ways. The tool program originally output to Stdout is redirected into the pipeline opened by the intermediary program. The intermediary program is used to use the pipe created by the GUI program to instantly pass the information to a backend thread of the GUI program. The background thread is responsible for refreshing the user interface of the GUI program (The reason for using the background thread is that only this can guarantee that the information does not affect the other operations in progress at any time in the GUI interface, just like the time-consuming time-consuming compilation function in Visual Studio).

I wrote the intermediard program name called Wspawn, this name comes from Visual Studio to complete the similar functional intermediary program Vcspawn (you can find it in the Visual Studio installation directory). My WSPAWN is very simple, it uses system call _Popen () to complete the creation sub-process and the input and output redirection two works at the same time. The GUI program uses a special command line to call Wspawn:

Wspawn -h [arg1] [arg2] ...

Among them, the -H is followed by the pip handle provided by the GUI program. Automatically convert it to a decimal number, Wspawn is written to the handle, and the subsequent content is the command line of the GUI program that is really to be executed, for example The way to call C compiler Cl.exe is as follows:

Wspawn -h 1903 cl / id: / myinclude test.cpp

The list of procedures for Wspawn.cpp is as follows:

#include

#include

#include

#include

#include

Using namespace std;

Void EXIT_FRIENDLY (VOID)

{

PUTS ("Please do not run Wspawn.");

exit (0);

}

Int main (int Argc, char * argv [])

{

Handle hwrite = NULL;

DWORD DWWRITED;

INT i = 0, RET = 0, len = 0;

Char psbuffer [256];

FILE * Child_Output;

String command_line = ""

/ / Check the command line, if there is a pipe handle, convert it to a Handle Type

IF (Argc <2)

EXIT_FRIENDLY ();

IF (! Stricmp (Argv [1], "-h")))

{

IF (Argc <4)

EXIT_FRIENDLY (); hwrite = (handle) ATOI (Argv [2]);

i = 3;

}

Else

i = 1;

/ / Extract the command to be executed

For (; i

{

Command_line = argv [i];

COMMAND_LINE = ""

}

// Use _Popen to create a sub-process and redirect it to the file pointer

IF ((Child_Output = _Popen (Command_Line.c_Str (), "RT")) == NULL)

Exit (1);

While (! feof (child_output))

{

IF (Fgets (psbuffer, 255, child_output)! = NULL)

{

IF (hwrite)

{

// Write the standard output of the child process into the pipeline and provide your own parent process

// The format is the length of the data block (0 indicates the end), and then write the data block content.

LEN = Strlen (psbuffer);

Writefile (hwrite, & len, sizeof (int), & dwwrited, null);

Writefile (hwrite, psbuffer, len, & dwwrited, null);

}

Else

/ / If the pipe handle is not provided, the output is directly printed.

Printf (psbuffer);

}

}

// Write "0" means that all data has been written

LEN = 0;

IF (hwrite)

Writefile (hwrite, & len, sizeof (int), & dwwrited, null);

Return _pclose (child_output);

}

Below, we use the WSPAWN program to write a simple "IDE" tool. We choose Visual Studio 6.0 as a development environment (the code given in this article also tests in Visual Studio.net 7.0). First, create the Visual C Engineering Myide, the engineering type is the Dialog based type in the MFC AppWizard (exe), which creates a GUI program for the dialog box. Engineering Myide's master dialog class is CMYIDEDLG. Now we have to add a sufficiently big multi-line editing box (Edit Box) in the resource editor, and its control ID is IDC_EDIT1, you must set the following properties for IDC_EDit1:

Multiline, Horizontal Scroll, Auto Hscroll,

Vertical Scroll, Auto Vscroll, Want Return

Then add a corresponding member variable with ClassWizard for IDC_EDit1 (note the type of variable to select CEDIT, non-string CString)

Cedit m_edit1;

Add a message response method onok () to edit the method using classwizard to add a message response method onok ().

Void cmyidedlg :: onok ()

{

AfxBeginthread (Mythread, this);

INVALIDATERECT (NULL);

UpdateWindow ();

}

That is, when we press the "Determine" button, start the background thread mythread (), then what is mythread () what to do? Let's first add a member function declaration in the header file myidedlg.h of the CMYidedlg class: protected:

Static uint mythread (lpvoid pparam);

Then, add mythread () implementation code in the implementation file of the CMYIDEDLG class:

Uint cmyidedlg :: mythread (lpvoid pparam)

{

Process_information pi;

Startupinfo sistartinfo;

Security_attributes saattr;

CSTRING OUTPUT, TMP;

Char Command_Line [200];

DWORD DWREAD;

CHAR * BUF; INT LEN;

Handle Hread, hwrite;

CMYIDEDLG * PDLG = (cmyidedlg *) PPARAM;

// Create inheritable anonymous pipelines with Wspawn.exe communication

Saattr.nlength = sizeof (security_attributes);

Saattr.binherithandle = true;

Saattr.lpsecurityDescriptor = null;

IF (! CreatePipe (& Hread, & Hwrite, & Saattr, 0))

{

AFXMessageBox ("Create Pipe Failed");

Return 0;

}

/ / Prepare the command line of Wspawn, give the command line to the write pipe handle and command to be executed by Wspawn

MEMSET (& PI, 0, SIZEOF (PI));

Sprintf (Command_Line, "Wspawn -h% D CL /?", (unsigned int) hwrite;

// Sub process runs in hidden

ZeromeMory (& SistartInfo, SIZEOF (STARTUPINFO);

SistartInfo.cb = sizeof (startupinfo);

Sistartinfo.wshowwindow = sw_hide;

SistartInfo.dwflags = startf_useshowwindow;

// Create a WSPAWN process

IF (! CreateProcess (Null, Command_Line, Null, NULL, TRUE,

0, NULL, NULL, & SistartInfo, & Pi)

{

AFXMessageBox ("When you call WSPAWN");

Return 0;

}

// Read the pipe and display the output information returned from the pipeline from the pipeline.

IF (! Readfile (Hread, & Len, SizeOf (int), & dwread, null) || dwread == 0)

Return 0;

While (len)

{

BUF = New char [len 1];

MEMSET (BUF, 0, LEN 1);

IF (! Readfile (Hread, BUF, Len, & dwread, null) || dwread == 0)

Return 0;

/ / Replace "/ N" in the return information is "/ R / N" that can be identified by Edit Box

TMP = BUF;

TMP.REPLACE ("/ n", "/ r / n");

Output = TMP; // Display the result in the Edit Box and refresh the dialog

PDLG-> m_edit1.setwindowText (OUTPUT);

PDLG-> InvalIdateRect (null);

PDLG-> UpdateWindow ();

DELETE [] BUF;

IF (! Readfile (Hread, & Len, SizeOf (int), & dwread, null) || dwread == 0)

Return 0;

}

// Waiting for Wspawn to end

WaitforsingleObject (pi.hprocess, 30000);

// Turn off the pipe handle

CloseHandle (HREAD);

CloseHandle (HWRITE);

Return 0;

}

Very simple, isn't it? The background thread creates an anonymous pipe, then launches Wspawn.exe in a hidden manner and pass the pipe handle to Wspawn.exe through the command line, followed by reading information from the pipeline. Now we can try to compile MYIDE.EXE, remember to put myide.exe and wspawn.exe in the same directory. Also, I wrote the command line to be executed to Wspawn.exe in mythread () function, which simulates a typical compilation process, if you don't plan to change this line of code, That must be noted on your computer, the C compiler cl.exe must be in the path indicated by the environment variable Path, otherwise Wspawn.exe can not find CL.exe. Below is the running result of the Myide program:

In addition, the WSPAWN given above uses the _popen () completion sub-process to create and enter output redirection, which is simple, but can only redirect the STDOUT or Stdin of the sub-process. If it is also required to redirect STDERR If the Java Compiler Javac uses the STDERR output result information, then we can't take this way. According to the above discussion, you must use the traditional _pipe (), _ dup () and other system calls, write the new version of the new version of Wspawn, I am no longer coming here.

Sample code download

Back to top

Click here to download the sample code (ZIP format, 28kb) of this article.

[Wang Wei Gang, May 2002]

Supplement: Information transfer in the opposite direction

Back to top

The article has been released online after the online article has caused some response. Many netizens have to ask questions: Demonstration above is the output of the graphical interface program to capture the output of the console program; but many console programs are interactive (such as FTP client programs), they need people to input on the control interface. Specific instructions can complete the corresponding functions - Can you use a similar approach to enter a specific command line instruction to the console program?

If we want to enter the instruction sequence of the console program is fixed, you can use a simpler method: put the command sequence in a text file, then use the following redirect command to run the console program:

Foo.exe

However, if the command sequence you want to enter to the console is determined by the user when an operation graphical interface program is determined, or according to the output of the console program, we need to use a similar pipeline method similar to the above article. This idea is basically the same as above, but the passing direction of information is reversed: the graphical interface program passes the instruction sequence as a string to the intermediary program when needed, and the intermediary program writes the string to the console program. enter.

The code for this intermediary program proxy code is as follows:

#include #include

#include

#include

#include

Using namespace std;

Void EXIT_FRIENDLY (VOID)

{

PUTS ("Please do not run Proxy.");

exit (0);

}

Int main (int Argc, char * argv [])

{

Handle Hread = NULL;

DWORD dwread;

INT i = 0, RET = 0, len = 0;

Const int buffer_len = 256;

CHAR PSBUFFER [Buffer_Len];

File * child_INPUT;

String command_line = ""

/ / Check the command line, if there is a pipe handle, convert it to a Handle Type

IF (Argc <2)

EXIT_FRIENDLY ();

IF (! Stricmp (Argv [1], "-h")))

{

IF (Argc <4)

EXIT_FRIENDLY ();

Hread = (Handle) ATOI (Argv [2]);

i = 3;

}

Else

i = 1;

/ / Extract the command to be executed

For (; i

{

Command_line = argv [i];

COMMAND_LINE = ""

}

// Use _Popen to create a child process and redirect its standard input

IF ((Child_INPUT = _POPEN (Command_Line.c_STR (), "WT")) == NULL)

Exit (1);

IF (HREAD)

{

While (1)

{

MEMSET (psbuffer, 0, buffer_len);

IF (readfile (Hread, Psbuffer, Buffer_len, & dwreaded, Null)

&& dwreaded> 0)

{

FPUTS (Psbuffer, Child_Input);

fflush (child_input);

PSBuffer [4] = 0;

IF (! Stricmp (Psbuffer, "Quit"))

Break;

}

}

}

Return_pclose (child_input);

}

In the graphical interface program, the sample code for creating a pipe and initiate an intermediary program is as follows:

Handle Hread, hwrite;

Handle HPROCESS;

Process_information pi;

Startupinfo sistartinfo;

Security_attributes saattr;

CSTRING OUTPUT, TMP;

Char Command_Line [200];

// Create the inheritable anonymous pipe with proxy.exe communication

Saattr.nlength = sizeof (security_attributes);

Saattr.binherithandle = true;

Saattr.lpsecurityDescriptor = null;

IF (! CreatePipe (& Hread, & Hwrite, & Saattr, 0))

{

AFXMessageBox ("Create pipe failed"); enddialog (idcancel);

Return False;

}

// Prepare the command line of proxy.exe, give the command line to the command handle and command to proxy.exe

MEMSET (& PI, 0, SIZEOF (PI));

Sprintf (Command_Line, "Proxy -h% D ftp ...", (unsigned int) HREAD);

ZeromeMory (& SistartInfo, SIZEOF (STARTUPINFO);

//sistartinfo.cb = sizeof (startupinfo);

//sistartinfo.wshowwindow = sw_hide;

//sistartinfo.dwflags = startf_useshowwindow;

IF (! CreateProcess (Null, Command_Line, Null, NULL, TRUE,

0, NULL, NULL, & SistartInfo, & Pi)

{

AfxMessageBox ("Call Proxy.exe Failed");

Enddialog (IDCANCEL);

Return False;

}

HPROCESS = PI.hprocess;

In the graphical interface program, the sample code for sending a particular command to the console program is as follows:

Char command = "help";

DWORD DWWRITTEN

Writefile (HWrite, Command, Strlen (Command), & DWWritten, NULL

Obviously, use the two-way pipeline, we can easily add a layer of graphical user interface to a layer of graphical user interface.

[Wang Wei Gang, December 2003]

转载请注明原文地址:https://www.9cbs.com/read-35298.html

New Post(0)