使用双管道在后台连接CMD

  cheney

    起初先是看到 linux 的 SHELL 中用到的管道" | " ,把两个程序连接起来,看了一下Windows 下也有这个管道功能,随后又想到如果通过管道后台连接到CMD,那不就可以为所欲为了么。(后来发现这么做的人还不在少数,果然英雄所见略同)。为了方便以后使用,直接封装成类——多线程双管道后台连接CMD。

    ##管道

    DualPipe .h 文件如下

    /* 
    多线程双管道后台连接CMD的类定义
     Use Multi-Byte Character Set
     2012年月日19:20:36 V 0.1
     */
    #pragma once 
    
     // 常数定义
    #define AXB_ME 3333
    #define SEND_BUF_LEN 32
    #define RECV_BUF_LEN 512
    #define WM_DPCOM (WM_USER+AXB_ME+1)    //自定义消息 
    #define SendRight 1                //消息参数 
    #define SendError 2
    #define ReadRight 3
    #define ReadError 4
    
    // 通过双通道连接cmd 的类 
    class DualPipe {
    
    public:
    
        ~DualPipe(void);
        DualPipe(CWnd* pOwner,int MaxSendlen=SEND_BUF_LEN,int Maxrecvlen=RECV_BUF_LEN);
        //子线程 static void ThreadInputProc(LPVOID lpParam);
        static void ThreadOutputProc(LPVOID lpParam);
    
    public:    
    
        // 主窗口指针用来通信 CWnd* m_pOwner;
        // 子线程状态 BOOL m_ComThreadEnable;
        BOOL m_InputExit;
        BOOL m_OutputExit;
        // 用来输入的管道 HANDLE m_hReadPipe1 , m_hWritePipe1 ;    
        // 用来输出的管道 HANDLE m_hReadPipe2 , m_hWritePipe2 ;    
        // 线程句柄 HANDLE m_hThreadOutput , m_hThreadInput ;
        // 待发往CMD的指令 char *m_SendBuf;
        DWORD m_senddatalen;
    
        // 待读取的数据 char *m_RecvBuf;
        DWORD m_Maxrecvlen;
    
    private:
        DualPipe(void);
    };
    

    DualPipe.cpp 文件如下

    #include "StdAfx.h"
    #include "DualPipe.h"
    
    STARTUPINFO si = {0};	
    SECURITY_ATTRIBUTES sa = {0};
    PROCESS_INFORMATION pi = {0};
    int TheCMDProcess(HANDLE Pipe1,HANDLE Pipe2);
    
    void CDualPipeCMD::ThreadInputProc(LPVOID lpParam)
    {
    	DWORD len; 
    	CDualPipeCMD *p=(CDualPipeCMD *)lpParam;
    	
    	TRACE("Input Thread start.\n");
    	p->m_InputExit=FALSE;
    
    	for (;;)
    	{
    		// 有数据需要写入
    		if ( p->m_senddatalen > 0 )
    		{
    
    			WriteFile(p->m_hWritePipe1, p->m_SendBuf, p->m_senddatalen, &len, NULL);
    			if(p->m_senddatalen == len)
    			{
    				p->m_senddatalen = 0;	
    				::SendMessage(p->m_pOwner->m_hWnd, WM_DPCOM, (WPARAM) SendRight, (LPARAM) len);
    			}
    			else
    			{
    				::SendMessage(p->m_pOwner->m_hWnd, WM_DPCOM, (WPARAM) SendError, (LPARAM) len);		
    			}	
    		}
    		
    		// 响应退出
    		if (p->m_ComThreadEnable==FALSE)
    		{
    			WriteFile(p->m_hWritePipe1, "exit\r\n", sizeof("exit\r\n"), &len, NULL);
    			p->m_InputExit = TRUE;
    			TRACE("Input Thread end.\n");
    			break;
    		}
    		
    		Sleep(50);
    	}
    	return ;
    }
    
    void CDualPipeCMD::ThreadOutputProc(LPVOID lpParam)
    {
    	DWORD dwReadLen = 0, dwTotalAvail = 0;
    	BOOL bRet = FALSE;
    	CDualPipeCMD *p=(CDualPipeCMD *)lpParam;
    	int n=0;
    	
    	TRACE("Output Thread start.\n");
    	p->m_InputExit=FALSE;
    
    	for (;;)
    	{
    		dwTotalAvail = 0;
    
    		bRet = PeekNamedPipe(p->m_hReadPipe2, NULL, 0, NULL, &dwTotalAvail, NULL);	//预览管道数据
    		if ( bRet && dwTotalAvail > 0 ) 
    		{
    			n=0;
    			bRet = ReadFile(p->m_hReadPipe2, p->m_RecvBuf, p->m_Maxrecvlen, &dwReadLen, NULL);
    			if ( bRet && dwReadLen > 0 ) 
    			{
    
    				p->m_RecvBuf[dwReadLen]=0;		//转换为字符串
    				::SendMessage(p->m_pOwner->m_hWnd, WM_DPCOM, (WPARAM) ReadRight, (LPARAM) dwReadLen);	
    			}
    		}
    		
    		if (p->m_ComThreadEnable==FALSE)
    		{
    			TRACE("Output Thread end.\n");
    			break;
    		}
    		
    		Sleep(50);
    	}
    	
    	return ;
    }
    
    int TheCMDProcess(HANDLE Pipe1,HANDLE Pipe2)
    {
    	// 配置进程参数
    	GetStartupInfo(&si);
    	si.cb = sizeof(STARTUPINFO);
    	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    	si.hStdInput = Pipe1;					// 命令从 管道 1 输入
    	si.hStdOutput = si.hStdError = Pipe2;			// 错误和结果 从管道 2 输出
    	si.wShowWindow = SW_HIDE;				// 不打开CMD的窗口
    
    	// 创建进程
    	TCHAR szCmdLine[MAX_PATH] = {0};
    	GetSystemDirectory(szCmdLine, MAX_PATH);
    	_tcscat_s(szCmdLine, MAX_PATH, _T("\\cmd.exe"));
    	if ( !CreateProcess(szCmdLine, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) )
    	{
    		return -1;
    	}
    	return 0;
    };
    
    CDualPipeCMD::CDualPipeCMD(CWnd* pOwner,int MaxSendlen,int Maxrecvlen)
    {
    	//初始化数据
    	m_pOwner = pOwner;
    
    	m_senddatalen = 0;
    	m_Maxrecvlen = Maxrecvlen;
    
    	m_SendBuf = new char[MaxSendlen];
    	m_RecvBuf = new char[Maxrecvlen];
    
    	m_ComThreadEnable = false;
    	m_InputExit=true;
    	m_OutputExit=true;
    
    	m_hReadPipe1 = NULL;
    	m_hWritePipe1 = NULL;	
    	m_hReadPipe2 = NULL;
    	m_hWritePipe2 = NULL;	
    	m_hThreadOutput = NULL;
    	m_hThreadInput = NULL;
    	
    	// 创建管道
    	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    	sa.lpSecurityDescriptor = NULL;
    	sa.bInheritHandle = TRUE;				//一定要为TRUE,不然句柄不能被继承。 
    
    	if ( !CreatePipe(&m_hReadPipe1, &m_hWritePipe1, &sa, 0) || !CreatePipe(&m_hReadPipe2, &m_hWritePipe2, &sa, 0) ) 
    	{
    		return ;
    	}
    
    	if (TheCMDProcess(m_hReadPipe1,m_hWritePipe2))		//后台开启一个CMD进程
    	{
    		return ;
    	}
    
    	//开启读写线程
    	m_ComThreadEnable = TRUE;
    	DWORD dwThreadRead = 0, dwThreadWrite = 0;		// 线程ID 不需要
    	m_hThreadOutput = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadOutputProc, this, 0, &dwThreadWrite);
    	m_hThreadInput = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadInputProc, this, 0, &dwThreadRead);
    }
    
    
    CDualPipeCMD::CDualPipeCMD(void)
    {
    	delete this;
    }
    
    CDualPipeCMD::~CDualPipeCMD(void)
    {
    	m_ComThreadEnable = FALSE;		//让线程先退出 // 同时退出CMD进程
    	
    	Sleep(100);
    	if (m_InputExit)
    	{
    		TRACE("Input Thread end. OK\n");
    	}
    	else
    	{
    		TRACE("Input Thread end. Error\n");	
    	}
    	if (m_OutputExit)
    	{
    		TRACE("Output Thread end. OK\n");
    	}
    	else
    	{
    		TRACE("Output Thread end. Error\n");		
    	}
    	
    
    	TRACE("析构完成\n");
    }
    
    

    所有的东西都在构造函数中一次性开启。通过响应消息 WM_DPCOM 来控制线程。任务管理器中可以看到正在执行中的CMD进程,如果要达到隐身目的,可以执行完之后迅速关闭。

    提供一个MFC的测试程序:

    cmd

    ##SUBST

    subst [盘符] [路径] 将指定的路径替代盘符,该路径将作为驱动器使用。

    subst /d 解除替代

    例如:

    C:\>subst a: c:\temp 用c盘temp目录替代a盘
    C:\>ubst a: /d 解除替代

    既然有这么有趣的命令,我第一个想到的就是把它和MFC做的皮连接起来,就是一个小软件。通过双管道后台连接CMD的类,我可以很快就把程序做出来。用MFC做界面简直就是傻瓜化操作。于是TortoiseSubst这个软件诞生了。

    几乎就要完成的时候我发现一个致命的缺陷,Subst命令映射出来的盘不能改名字。于是我的电脑下就出现了各种相同名字的盘,感觉反而更乱了。

    于是我放弃了继续做下去,当前基本功能都已实现,通过操作 ini 文件记录用户操作,同时后台执行命令。添加和删除都测试成功,但只是 Debug 版本,我在 CString转Char * 的时候是直接转的,所以 Realse 还没做。当然还有其他 BUG 。这个项目暂时就这样吧,除非以后有什么别的发现。

    ##链接
    http://pan.baidu.com/s/1gdh4gsr
    - (DualPipe test 0.1.rar)[]
    - (TortoiseSubst)[]