I wonder if you have used such a program. They do not have the decompression function, but call the DOS program PKZIP to decompress the ZIP package. However, when the program is running, there is no DOS Console window and all the information that should have been displayed under DOS appears in the text box of the installer. This design is both beautiful and can prevent a few users who are eye-catching from closing your DOS window in advance.
Now let's discuss how to use the anonymous pipeline technology to implement this function.
Pipeline technology has been around for a long time. I believe many people are most familiar with the pipeline technology in the DOS command. When we type a file, we can input it if we want it to be paged.
C: \> type autoexec. bat | more
Here "|" is the pipeline operator. It uses the type output information as the Reading end and the more input end as the pipeline established by the writing end.
In Windows, a large number of pipelines are also anonymous pipelines, which are created using the CreatePipe API function.
BOOL CreatePipe (
PHANDLE hReadPipe, // pointer to the read-side handle
PHANDLE hWritePipe, // pointer to the write handle
LPSECURITY_ATTRIBUTES lpPipeAttributes, // pointer to the Security Attribute Structure
DWORD nSize // MPs queue capacity
);
In the preceding parameters, note that hReadPipe, hWritePipe, is the pointer to the handle, rather than the handle (I used it wrong for the first time ). NSize is generally set to 0 so that the system determines the pipeline capacity. Now let's look at the security attribute structure, SECURITY_ATTRIBUTES.
Typedef struct _ SECURITY_ATTRIBUTES {// sa
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;
NLength is the size of the struct, which is naturally obtained using sizeof. LpSecurityDescriptor is a security descriptor (a C-Style string ). BInheritHandle indicates whether the security description object can be inherited by the newly created process. Don't worry about their specific meaning. You will naturally know it when using it.
Okay. Now let's create a pipeline.
HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES sa;
Sa. nLength = sizeof (SECURITY_ATTRIBUTES );
Sa. lpSecurityDescriptor = NULL; // use the default security descriptor of the system.
Sa. bInheritHandle = TRUE; // The value must be TRUE. Otherwise, the handle cannot be inherited.
CreeatePipe (& hReadPipe, & hWritePipe, & sa, 0 );
OK. Our pipeline has been built. Of course, this is not the ultimate goal. Our goal is to redirect the output of a program on DOS to the Edit Control of a Windows program. So we still need to start a DOS program first, and there is no window in the DOS Console (otherwise, we will not reveal it ). We use CreateProcess to create a DOS program process.
BOOL CreateProcess (
Lptstr lpApplicationName, // C-style string: Application name
LPTSTR lpCommandLine, // C-style string: executed command
LPSECURITY_ATTRIBUTES lpProcessAttributes, // process Security Attribute
LPSECURITY_ATTRIBUTES lpThreadAttributes, // thread Security Attribute
BOOL bInheritHandles, // whether to inherit the HANDLE flag
DWORD dwCreationFlags, // create a flag
LPVOID lpEnvironment, // C-Style string: Environment Settings
Maid directory, // C-Style string: Execution directory
LPSTARTUPINFO lpStartupInfo, // start information
LPPROCESS_INFORMATION lpProcessInformation // Process Information
);
Don't go first. The parameter is a little more, but most of them do not need to fill in or leave a NULL parameter. LpApplication can be used at any time. LpCommandLine but the commands you want to execute must be carefully written. Let's see how to set lpProcessAttributes and lpThreadAttributes. Ah? This is the one you just mentioned. Yes, but it is simpler than just now. Since we only create a process and whether it can be inherited or not, the two values are all NULL. BInHeritHandles must also be set to TRUE, because since we want a new process to output information to the process that calls it, we must let the new process inherit the handle of the calling process. We have no requirement on the newly created process, so dwCreationFlags is NULL. According to your own requirements, lpEnvironment and lpCurrentDirectory are just a bit simple. Generally, they are also NULL. The next lpStartupInfo is the key. Let's take a closer look.
Typedef struct _ STARTUPINFO {// si
DWORD cb;
LPTSTR lpReserved;
LPTSTR lpDesktop;
LPTSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFO, * LPSTARTUPINFO;
Inverted! If there are so many parameters, one write will surely be exhausted. That's right, MS has long thought it would be exhausting. Therefore, it provides the life-saving API function GetStartupInfo.
VOID GetStartupInfo (
LPSTARTUPINFO lpStartupInfo
);
This function is used to obtain the StartupInfo of the current process. The newly created process is basically the same as the StartupInfo of the current process. just borrow it. Then you can make a small modification.
Here are a few changes: cb, dwFlags, hStdOutput, hStdError, and wShowWindow. Let's talk about cb. It refers to the size of STARTUPINFO or the sizeof method. Besides, wShowWindow sets the real state of the window when the new process is created. This property is of course for SW_HIDE. Aren't we going to hide the newly created DOS process. Haha, see hStdOutput and hStdError, standard output and error output handle. The key point is that as long as we set these two handles to hWrite, our processes will be written into the newly established anonymous pipeline once there is a standard output, we can use the hReadPipe handle of the pipeline to read the content and write it into the Edit Control. It's really easy to say. After these key parameters are completed, do not forget dwFlags. This parameter is used to specify the value of this heap parameter in STARTUPINFO. Now that we use hStdOutput, hStdError, and wShowWindow, dwFlags will give STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES.
Now, return to the last parameter lpProcessInformation (tired!) of CreateProcess !). You don't need to fill in this parameter. It is the information returned by CreateProcess. You just need to give him the address of the PROCESS_INFORMATION structure case.
This is a big success. one end of the pipeline is connected to the standard output end of the new process, and one end can be read by itself using the API function ReadFile. Wait, no. Our pipeline is still faulty. After hWrite is given to hStdOutput and hStdError, a pipeline write end will be opened in the new process when the new process starts, when CreatePipe is used in the current process to create an MPS queue, hWrite is also available in the current process. Well, there is a malformed pipe with two writing ends and one reading end. There must be a problem with such pipelines. Since the current process does not use the write end, we must disable the write end of the current process. In this way, our pipeline is truly successful. Let's take a look at the source program written by VC ++:
/*
* Use the MPs queue technology to change the dir /? To a CEdit control of the MFC application.
* VC ++ 6.0 + WinXP
*
* Detrox, 2003.
*/
Void CPipeDlg: OnButton1 ()
{
SECURITY_ATTRIBUTES sa;
HANDLE hRead, hWrite;
Sa. nLength = sizeof (SECURITY_ATTRIBUTES );
Sa. lpSecurityDescriptor = NULL;
Sa. bInheritHandle = TRUE;
If (! CreatePipe (& hRead, & hWrite, & sa, 0 ))
{
MessageBox ("Error On CreatePipe ()");
Return;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
Si. cb = sizeof (STARTUPINFO );
GetStartupInfo (& si );
Si. hStdError = hWrite;
Si. hStdOutput = hWrite;
Si. wShowWindow = SW_HIDE;
Si. dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
If (! CreateProcess (NULL, "c: \ windows \ system32 \ cmd.exe/c dir /? ", NULL, NULL, TRUE, NULL, & si, & pi ))
{
MessageBox ("Error on CreateProcess ()");
Return;
}
CloseHandle (hWrite );
Char buffer [4096] = {0 };
DWORD bytesRead;
While (true)
{
If (ReadFile (hRead, buffer, 4095, & bytesRead, NULL) = NULL)
Break;
M_Edit1 + = buffer;
UpdateData (false );
Sleep (200 );
}
}