C++ 利用管道操作本机命令行命令_完成一个使用管道命令的实例-程序员宅基地

技术标签: 学习笔记  

目录

1.知识要点

1.1 进程创建与关闭

1.2 代码示例:

1.2 管道的创建与关闭

1.2.1 管道

1.2.2匿名管道

1.2.3相关函数

1.2.4 匿名管道创建方法

1.2.5 管道示例

2.利用MFC创建命令行命令执行工具

2.1 界面创建

2.2 命令执行函数

2.3 测试

存在问题:



1.知识要点


1.1 进程创建与关闭

在C++中,创建一个进程,需要利用WINDOWS API函数,该函数的原型如下:

BOOL CreateProcessA(
  LPCSTR                lpApplicationName,  //启动进程的程序模块
  LPSTR                 lpCommandLine,        //程序执行的命令行参数
  LPSECURITY_ATTRIBUTES lpProcessAttributes,  //进程继承属性
  LPSECURITY_ATTRIBUTES lpThreadAttributes,    //线程继承属性
  BOOL                  bInheritHandles,       //句柄继承标志
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCSTR                lpCurrentDirectory,
  LPSTARTUPINFOA        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

参数说明:

lpApplicationName

此处的程序指的是基于windows的可执行程序,也可以是在当前系统中安装的其他子系统的可执行文件。

此处的字符串必须包含一个扩展名,系统不会默认任何扩展名。

此处的路径可以是相对路径,也可以是绝对路径。如果是相对路径,需要指出的是程序默认会在当前目录下寻找相应的程序。

此处为NULL,则会启动cmd.exe进程。同时,lpCommandLine参数必须以一个空格开头,执行lpCommandLine的命令

lpCommandLine:

执行的命令行参数。

lpProcessAttributes:

指向SECURITY_ATTRIBUTES 结构的指针,用来决定返回的进程对象能否被子进程继承,如果为NULL,则不可继承。

lpThreadAttributes:

指向SECURITY_ATTRIBUTES 结构的指针,用来决定返回的线程对象能否被子进程继承,如果为NULL,则不可继承。

在使用CreateProcess创建进程的时候,会创建一个进程和一个主线程。在其他子线程中,根据该进程创建时的标志来决定是否可以继承此函数创建的进程与线程。

bInheritHandles:

指示新进程是否从调用进程处继承了句柄。

如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原进程拥有完全相同的值和访问权限。

dwCreationFlags:

指定附加的、用来控制优先类和进程的创建的标志。以下的创建标志可以以除下面列出的方式外的任何方式组合后指定。

⑴值:CREATE_DEFAULT_ERROR_MODE

含义:新的进程不继承调用进程的错误模式。CreateProcess函数赋予新进程当前的默认错误模式作为替代。应用程序可以调用SetErrorMode函数设置当前的默认错误模式。

这个标志对于那些运行在没有硬件错误环境下的多线程shell程序是十分有用的。

对于CreateProcess函数,默认的行为是为新进程继承调用者的错误模式。设置这个标志以改变默认的处理方式。

⑵值:CREATE_NEW_CONSOLE

含义:新的进程将使用一个新的控制台,而不是继承父进程的控制台。这个标志不能与DETACHED_PROCESS标志一起使用。

⑶值:CREATE_NEW_PROCESS_GROUP

含义:新进程将是一个进程树的根进程。进程树中的全部进程都是根进程的子进程。新进程树的用户标识符与这个进程的标识符是相同的,由lpProcessInformation参数返回。进程树经常使用GenerateConsoleCtrlEvent函数允许发送CTRL+C或CTRL+BREAK信号到一组控制台进程。

⑷值:CREATE_SEPARATE_WOW_VDM

如果被设置,新进程将会在一个私有的虚拟DOS机(VDM)中运行。另外,默认情况下所有的16位Windows应用程序都会在同一个共享的VDM中以线程的方式运行。单独运行一个16位程序的优点是一个应用程序的崩溃只会结束这一个VDM的运行;其他那些在不同VDM中运行的程序会继续正常的运行。同样的,在不同VDM中运行的16位Windows应用程序拥有不同的输入队列,这意味着如果一个程序暂时失去响应,在独立的VDM中的应用程序能够继续获得输入。

⑸值:CREATE_SHARED_WOW_VDM

如果WIN.INI中的Windows段的DefaultSeparateVDM选项被设置为真,这个标识使得CreateProcess函数越过这个选项并在共享的虚拟DOS机中运行新进程。

⑹值:CREATE_SUSPENDED

含义:新进程的主线程会以暂停的状态被创建,直到调用ResumeThread函数被调用时才运行。

⑺值:CREATE_UNICODE_ENVIRONMENT

含义:如果被设置,由lpEnvironment参数指定的环境块使用Unicode字符,如果为空,环境块使用ANSI字符。

⑻值:DEBUG_PROCESS

含义:如果这个标志被设置,调用进程将被当做一个调试程序,并且新进程会被当做被调试的进程。系统把被调试程序发生的所有调试事件通知给调试器。

如果你使用这个标志创建进程,只有调用进程(调用CreateProcess函数的进程)可以调用WaitForDebugEvent函数。

⑼值:DEBUG_ONLY_THIS_PROCESS

含义:如果此标志没有被设置且调用进程正在被调试,新进程将成为调试调用进程的调试器的另一个调试对象。如果调用进程没有被调试,有关调试的行为就不会产生。

⑽值:DETACHED_PROCESS

含义:对于控制台进程,新进程没有访问父进程控制台的权限。新进程可以通过AllocConsole函数自己创建一个新的控制台。这个标志不可以与CREATE_NEW_CONSOLE标志一起使用。

〔11〕值:CREATE_NO_WINDOW

含义:系统不为新进程创建CUI窗口,使用该标志可以创建不含窗口的CUI程序。

dwCreationFlags:

还用来控制新进程的优先类,优先类用来决定此进程的线程调度的优先级。如果下面的优先级类标志都没有被指定,那么默认的优先类是NORMAL_PRIORITY_CLASS,除非被创建的进程是IDLE_PRIORITY_CLASS。在这种情况下子进程的默认优先类是IDLE_PRIORITY_CLASS

可以选择下面的标志中的一个:

优先级:HIGH_PRIORITY_CLASS

含义:指示这个进程将执行时间临界的任务,所以它必须被立即运行以保证正确。这个优先级的程序优先于正常优先级或空闲优先级的程序。一个例子是Windows任务列表,为了保证当用户调用时可以立刻响应,放弃了对系统负荷的考虑。确保在使用高优先级时应该足够谨慎,因为一个高优先级的CPU关联应用程序可以占用几乎全部的CPU可用时间。

优先级:IDLE_PRIORITY_CLASS

含义:指示这个进程的线程只有在系统空闲时才会运行并且可以被任何高优先级的任务打断。例如屏幕保护程序。空闲优先级会被子进程继承。

优先级:NORMAL_PRIORITY_CLASS

含义:指示这个进程没有特殊的任务调度要求。

优先级:REALTIME_PRIORITY_CLASS

含义:指示这个进程拥有可用的最高优先级。一个拥有实时优先级的进程的线程可以打断所有其他进程线程的执行,包括正在执行重要任务的系统进程。例如,一个执行时间稍长一点的实时进程可能导致磁盘缓存不足或鼠标反映迟钝。

lpEnvironment:

指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。

一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的。每个字符串都是name=value的形式。

因为相等标志被当做分隔符,所以它不能被环境变量当做变量名。

与其使用应用程序提供的环境块,不如直接把这个参数设为空,系统驱动器上的当前目录信息不会被自动传递给新创建的进程。对于这个情况的探讨和如何处理,请参见注释一节。

环境块可以包含Unicode或ANSI字符。如果lpEnvironment指向的环境块包含Unicode字符,那么dwCreationFlags字段的CREATE_UNICODE_ENⅥRONMENT标志将被设置。如果块包含ANSI字符,该标志将被清空。

请注意一个ANSI环境块是由两个零字节结束的:一个是字符串的结尾,另一个用来结束这个快。一个Unicode环境块是由四个零字节结束的:两个代表字符串结束,另两个用来结束块。

lpCurrentDirectory:

指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录。这个选项是一个需要启动应用程序并指定它们的驱动器和工作目录的shell程序的主要条件。

lpStartupInfo:

指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体

lpProcessInformation

指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体

1.2 代码示例:

// CreateProcess.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"


void _tmain( int argc, TCHAR *argv[] )
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    CString s_Cmd = "ipconfig /?";
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    //if( argc != 2 )
    //{
    //    printf("Usage: %s [cmdline]\n", argv[0]);
    //    return;
    //}

    // Start the child process. 
    if( !CreateProcess( NULL,   // No module name (use command line)
		s_Cmd.GetBuffer(),        // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        0,              // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
    ) 
    {
        printf( "CreateProcess failed (%d).\n", GetLastError() );
        return;
    }

    // Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );

    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );

    getchar();
    system("pause");

}
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//

#pragma once

#include "targetver.h"
#include <afxwin.h>
#include <windows.h>
#include <stdio.h>
#include <tchar.h>


// TODO: 在此处引用程序需要的其他头文件

运行结果如下:

 

1.2 管道的创建与关闭

1.2.1 管道

管道是一块共享内存,用来进行进程间的通信。有两种方式的管道,一种是匿名管道(anonymous pipes),一种是命名管道( named pipes)。匿名管道比命名管道需求较小,但同样,其功能也受到更多的限制。

1.2.2匿名管道

匿名管道是一种未命名的单向管道,通常在父进程和子进程之间传输数据。匿名管道始终是本地的;它们不能用于通过网络进行通信。

1.2.3相关函数

创建管道函数

BOOL CreatePipe(
  PHANDLE               hReadPipe,    //指向接收管道的读取句柄的变量的指针
  PHANDLE               hWritePipe,   //指向接收管道的写句柄的变量的指针。
  LPSECURITY_ATTRIBUTES lpPipeAttributes,//指向SECURITY_ATTRIBUTES结构体的指针 
  DWORD                 nSize            //管道的大小
);

读取管道函数

BOOL ReadFile(
  HANDLE       hFile,        //读取句柄
  LPVOID       lpBuffer,    //读取之后的数据保存在该缓冲区中
  DWORD        nNumberOfBytesToRead, //指示最大的读取字符数
  LPDWORD      lpNumberOfBytesRead,    //实际的读取字节数
  LPOVERLAPPED lpOverlapped    //OVERLAPPED数据结构的指针
);

写入管道函数

BOOL WriteFile(
  HANDLE       hFile,    //可供写入的句柄
  LPCVOID      lpBuffer, //写入数据的来源
  DWORD        nNumberOfBytesToWrite, //最大的写入字节
  LPDWORD      lpNumberOfBytesWritten, //实际写入的字节
  LPOVERLAPPED lpOverlapped  //OVERLAPPED数据结构的指针
);

1.2.4 匿名管道创建方法

CreatePipe函数可以创建一个匿名管道,同时返回两个句柄:一个读管道句柄和一个写管道句柄。写句柄仅能向管道写,读句柄仅能从管道中读。

通常,管道服务端会通过继承的方式传递一个管道句柄给另一个进程,当然,也可以通过DuplicateHandle函数在两个不相关的进程间通过其他技术(DDE或共享内存)复制管道句柄。

根据另一个进程是接收信息还是发送信息,管道服务端即可以将写句柄传递给另一个进程,也可以将读句柄传递给另一个进程。

要从管道中读取,调用ReadFile函数使用管道的读取句柄从管道中读取数据;在另一端写完或写句柄关闭、出错的情况,会调用ReadFile从管道中读取数据。

要向管道中写数据,调用WriteFile函数使用管道的写入句柄向管道中写入数据;写入句柄不会关闭直到数据写完或出现错误,如果写入的数据缓冲区写满,写入句柄不会关闭,会在另一端读取数据之后,利用空闲的空间继续写入。

缓冲区的大小在CreatePipe函数中指定。

管道句柄被继承的方式:

  •  在使用CreatePipe函数创建管道时,如果在接收的SECURITY_ATTRIBUTES结构体中,如果结构体中的成员bInheritHandle被设置为TRUE,则管道可被继承。
  • 管道服务端可以通过DuplicateHandle函数来改变管道的继承关系。可以通过该函数创建一个不可以继承管道句柄的可继承复制或者是可继承管道句柄的不可继承复制。此处的理解就是利用该函数改变管道句柄的继承性
  • 利用CreateProcess函数来指定管道句柄句柄的继承性。

1.2.5 管道示例

在本例中,是利用CreateProcess通过父进程创建一个子进程,通过管道,将子进程的标准输入与输出重定位到父进程中来,其基本的思路如下图。需要两个管道将子进程的写入与读取分别重定向。

在下图中,

父进程持有 ChildStd_OUT 管道的读句柄,ChildStd_IN 的写句柄

子进程持有 ChildStd_OUT 管道的写句柄,ChildStd_IN 的读句柄

 

 

代码如下:

// ParentProcess.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h> 
#include <strsafe.h>

#define BUFSIZE 4096 

//定义全局句柄

//子进程标准输入的读、写句柄 
HANDLE g_hChildStd_IN_Rd = NULL; //子进程持有 
HANDLE g_hChildStd_IN_Wr = NULL; // 父进程持有

//子进程标准输出的读、写句柄 
HANDLE g_hChildStd_OUT_Rd = NULL; // 父进程持有
HANDLE g_hChildStd_OUT_Wr = NULL;  //子进程持有 

HANDLE g_hInputFile = NULL;  //声明一个文件

//子进程创建
void CreateChildProcess(void); 

//写入管道
void WriteToPipe(void); 

//从管道读出
void ReadFromPipe(void); 

//错误处理
void ErrorExit(PTSTR); 
 
int _tmain(int argc, TCHAR *argv[]) 
{ 
   SECURITY_ATTRIBUTES saAttr; 
 
   printf("\n->Start of parent execution.\n");

// 设置 bInheritHandle 标志,以使管道句柄可被继承 
 
   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
   saAttr.bInheritHandle = TRUE; 
   saAttr.lpSecurityDescriptor = NULL; 

// 创建一个子进程标准输出的管道
 
   if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) 
      ErrorExit(TEXT("StdoutRd CreatePipe")); 

// 我们只允许将子进程的输出重定向到父进程,因此,对于标准输出管道的读句柄,不能被继承

   if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
      ErrorExit(TEXT("Stdout SetHandleInformation")); 

// 创建一个子进程标准输入的管道
 
   if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) 
      ErrorExit(TEXT("Stdin CreatePipe")); 

// 我们只允许将子进程的输入重定向到父进程,因此,对于标准输入管道的写句柄,不能被继承
 
   if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
      ErrorExit(TEXT("Stdin SetHandleInformation")); 
 
//创建子进程
   
   CreateChildProcess();

// Get a handle to an input file for the parent. 
// This example assumes a plain text file and uses string output to verify data flow. 
 
   if (argc == 1) 
      ErrorExit(TEXT("Please specify an input file.\n")); 

   g_hInputFile = CreateFile(
       argv[1], 
       GENERIC_READ, 
       0, 
       NULL, 
       OPEN_EXISTING, 
       FILE_ATTRIBUTE_READONLY, 
       NULL); 

   if ( g_hInputFile == INVALID_HANDLE_VALUE ) 
      ErrorExit(TEXT("CreateFile")); 
 
// Write to the pipe that is the standard input for a child process. 
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
 
   WriteToPipe(); 
   printf( "\n->Contents of %s written to child STDIN pipe.\n", argv[1]);
 
// Read from pipe that is the standard output for child process. 
 
   printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
   ReadFromPipe(); 

   printf("\n->End of parent execution.\n");

// The remaining open handles are cleaned up when this process terminates. 
// To avoid resource leaks in a larger application, close handles explicitly. 

   return 0; 
} 
 
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{ 
   TCHAR szCmdline[]=TEXT("ChildProcess");
   PROCESS_INFORMATION piProcInfo; 
   STARTUPINFO siStartInfo;
   BOOL bSuccess = FALSE; 
 
// Set up members of the PROCESS_INFORMATION structure. 
 
   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
 
// Set up members of the STARTUPINFO structure. 
// This structure specifies the STDIN and STDOUT handles for redirection.
 
   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
   siStartInfo.cb = sizeof(STARTUPINFO); 
   siStartInfo.hStdError = g_hChildStd_OUT_Wr;
   siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
   siStartInfo.hStdInput = g_hChildStd_IN_Rd;
   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
 
// Create the child process. 
    
   bSuccess = CreateProcess(NULL, 
      szCmdline,     // command line 
      NULL,          // process security attributes 
      NULL,          // primary thread security attributes 
      TRUE,          // handles are inherited 
      0,             // creation flags 
      NULL,          // use parent's environment 
      NULL,          // use parent's current directory 
      &siStartInfo,  // STARTUPINFO pointer 
      &piProcInfo);  // receives PROCESS_INFORMATION 
   
   // If an error occurs, exit the application. 
   if ( ! bSuccess ) 
      ErrorExit(TEXT("CreateProcess"));
   else 
   {
      // Close handles to the child process and its primary thread.
      // Some applications might keep these handles to monitor the status
      // of the child process, for example. 

      CloseHandle(piProcInfo.hProcess);
      CloseHandle(piProcInfo.hThread);
      
      // Close handles to the stdin and stdout pipes no longer needed by the child process.
      // If they are not explicitly closed, there is no way to recognize that the child process has ended.
      
      CloseHandle(g_hChildStd_OUT_Wr);
      CloseHandle(g_hChildStd_IN_Rd);
   }
}
 
void WriteToPipe(void) 

// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data. 
{ 
   DWORD dwRead, dwWritten; 
   CHAR chBuf[BUFSIZE];
   BOOL bSuccess = FALSE;
 
   for (;;) 
   { 
      bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
      if ( ! bSuccess || dwRead == 0 ) break; 
      
      bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
      if ( ! bSuccess ) break; 
   } 
 
// Close the pipe handle so the child process stops reading. 
 
   if ( ! CloseHandle(g_hChildStd_IN_Wr) ) 
      ErrorExit(TEXT("StdInWr CloseHandle")); 
} 
 
void ReadFromPipe(void) 

// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT. 
// Stop when there is no more data. 
{ 
   DWORD dwRead, dwWritten; 
   CHAR chBuf[BUFSIZE]; 
   BOOL bSuccess = FALSE;
   HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

   for (;;) 
   { 
      bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
      if( ! bSuccess || dwRead == 0 ) break; 

      bSuccess = WriteFile(hParentStdOut, chBuf, 
                           dwRead, &dwWritten, NULL);
      if (! bSuccess ) break; 
   } 
} 
 
void ErrorExit(PTSTR lpszFunction) 

// Format a readable error message, display a message box, 
// and exit from the application.
{ 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(1);
}

子进程代码如下:

// ChildProcess.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>

#define BUFSIZE 4096 



int _tmain(int argc, _TCHAR* argv[])
{

 
   CHAR chBuf[BUFSIZE]; 
   DWORD dwRead, dwWritten; 
   HANDLE hStdin, hStdout; 
   BOOL bSuccess; 
 
   hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
   hStdin = GetStdHandle(STD_INPUT_HANDLE); 
   if ( 
       (hStdout == INVALID_HANDLE_VALUE) || 
       (hStdin == INVALID_HANDLE_VALUE) 
      ) 
      ExitProcess(1); 
 
   // Send something to this process's stdout using printf.
   printf("\n ** This is a message from the child process. ** \n");

   // This simple algorithm uses the existence of the pipes to control execution.
   // It relies on the pipe buffers to ensure that no data is lost.
   // Larger applications would use more advanced process control.

   for (;;) 
   { 
   // Read from standard input and stop on error or no data.
      bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL); 
      
      if (! bSuccess || dwRead == 0) 
         break; 
 
   // Write to standard output and stop on error.
      bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL); 
      
      if (! bSuccess) 
         break; 
   } 
	return 0;
}

生成文件如下:

运行程序

2.利用MFC创建命令行命令执行工具

2.1 界面创建

利用MFC创建如下界面的程序:

2.2 命令执行函数

添加命令执行函数,代码如下:

CString CMyMFCPipeDemoDlg::ExecCMD(CString str_Cmd)
{
	//声明四个句柄,分别代表cmd的输入、输出,将CMD的标准输入与输出重定向到管道
	HANDLE hCmdStdIN_Rd = NULL ;//cmd 标准输入的读句柄
	HANDLE hCmdStdIN_Wr = NULL ;//cmd 标准输入的写句柄
	HANDLE hCmdStdOUT_Rd = NULL ;//cmd 标准输出的读句柄
	HANDLE hCmdStdOUT_Wr = NULL ;//cmd 标准输出的写句柄

	//---------------创建管道-------------------------
	//声明SECURITY_ATTRIBUTES 结构体变量
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.bInheritHandle = TRUE;
	sa.lpSecurityDescriptor = NULL;
	//标准输入的管道
	if(!CreatePipe(&hCmdStdIN_Rd,&hCmdStdIN_Wr,&sa,0))
	{
		MessageBox(_T("标准输入管道创建失败"),_T("tips"),MB_OK);
		ExitProcess(1);
	}
		

	//标准输出管道
	if(!CreatePipe(&hCmdStdOUT_Rd,&hCmdStdOUT_Wr,&sa,0))
	{
		MessageBox(_T("标准输入管道创建失败"),_T("tips"),MB_OK);
		ExitProcess(1);
	}
		

	//-------------------创建子进程-------------------------
	//声明STARTUPINFO 结构体变量
	STARTUPINFO si;
	ZeroMemory(&si,sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.hStdError = hCmdStdOUT_Wr;
	si.hStdOutput = hCmdStdOUT_Wr;
	si.hStdInput = hCmdStdIN_Rd;
	si.dwFlags = STARTF_USESTDHANDLES;

	CString strCmd=_T("");

	if(str_Cmd.GetLength() < 1)
	{ 
		strCmd+=TEXT("ipconfig /?");
	}
	else
	{
		strCmd+=str_Cmd;
	}
	int len = strCmd.GetLength();
	TCHAR* tch_Cmd = strCmd.GetBuffer(len);
	strCmd.ReleaseBuffer();

	//声明 PROCESS_INFORMATION结构体变量
	PROCESS_INFORMATION pi;
	ZeroMemory(&pi,sizeof(PROCESS_INFORMATION));
	if(!CreateProcess(
		NULL,		//此处指向windows cmd程序
		tch_Cmd,  //获得命令行程序
		NULL,
		NULL,
		TRUE,
		0,
		NULL,
		NULL,
		&si,
		&pi
		))
	{
		ErrorExit(_T("子进程创建失败"));
	}
	else
	{
		//关闭不需要的句柄 
		CloseHandle(hCmdStdOUT_Wr);
		CloseHandle(hCmdStdIN_Rd);
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
	}

	//-------------------读取数据-------------------------

	DWORD dwRead,dwWrite;
	CHAR chBUF[BUFSIZ];
	BOOL bSuccess = FALSE;
	CString output;
	while(TRUE){
		memset(chBUF, 0, BUFSIZ); 
		bSuccess=ReadFile(hCmdStdOUT_Rd,chBUF,BUFSIZ,&dwRead,NULL);
		if(!bSuccess || dwRead ==0 )
		{
				break;
		}
		output +=chBUF;
	}
	CloseHandle(hCmdStdOUT_Rd);
	CloseHandle(hCmdStdIN_Wr);
	
	return output;
}

2.3 测试

存在问题:

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq490765184/article/details/104341858

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签