天天看點

Qt執行dos指令并擷取控制台輸出

應用場景與問題描述:

問題是這樣的,我寫了很多指令行程式用于處理遙感影像,這種方式很友善,可以通過dos或shell腳本來實作批處理。但這也引起一個問題,當我在內建時偷懶,不想做界面,而使用者又必須要求有個界面時,我不得不做一個視窗來展現我是有界面的,于是我決定直接調用我的可執行程式或者批處理腳本,但這些指令和腳本不能在終端或dos視窗裡執行。這個問題簡單的抽象為執行一個"ping localhost"的指令,并且把輸出重定向到我的消息視窗裡。

我們習慣于先看看别人有沒有做過這個事,于是先百度一下,就直接看到這篇部落格:點選打開連結,參照這個部落格的例子,我做了如下的實驗:

頭檔案:

#ifndef QTWINMSG_H
#define QTWINMSG_H

#include <QtWidgets/QMainWindow>
#include "ui_qtwinmsg.h"
#include "Worker.h"

class QtWinMsg : public QMainWindow
{
	Q_OBJECT

public:
	QtWinMsg(QWidget *parent = 0);
	~QtWinMsg();

public slots:
	void onTest();

private:
	Ui::QtWinMsgClass ui;
};

#endif // QTWINMSG_H
           

源檔案:

#include "qtwinmsg.h"
#include <QMessageBox>

#include <conio.h>  
#include <stdio.h>  
#include <windows.h> 

QtWinMsg::QtWinMsg(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
	ui.lineEdit->setText(tr("ping localhost"));
	ui.textEdit->setLineWrapMode(QTextEdit::NoWrap);

	connect(ui.pushButton,SIGNAL(clicked()),this,SLOT(onTest()));
}

QtWinMsg::~QtWinMsg()
{
}


void QtWinMsg::onTest()
{
	QString qsCmd =  ui.lineEdit->text();
	QByteArray qbCmd = qsCmd.toLocal8Bit();
	char* pszCmd = qbCmd.data();
	LPWSTR ppCmd = new TCHAR[100];
	LPSTR p = pszCmd;
	MultiByteToWideChar(CP_ACP, 0, p, -1, ppCmd, 100);

	SECURITY_ATTRIBUTES   sa;   
	HANDLE   hRead,hWrite;   

	sa.nLength   =   sizeof(SECURITY_ATTRIBUTES);   
	sa.lpSecurityDescriptor   =   NULL;   
	sa.bInheritHandle   =   TRUE;   
	if   (!CreatePipe(&hRead,&hWrite,&sa,0))     
	{   
		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;   
	//關鍵步驟,CreateProcess函數參數意義請查閱MSDN   
	if   (!CreateProcess(NULL,   ppCmd   
		,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi))     
	{   
		return  ;   
	}   
	CloseHandle(hWrite);   

	char   buffer[4096]   =   {0};   
	DWORD   bytesRead;     
	//ofstream outfile("log.txt");  

	while   (true)     
	{   
		if   (ReadFile(hRead,buffer,4095,&bytesRead,NULL)   ==   NULL)   
			break;   
		//buffer中就是執行的結果,可以儲存到文本,也可以直接輸出   
		//printf(buffer);   
		//outfile << buffer << endl;  
		//Sleep(1000);   


		QString qsMsg = QString::fromLocal8Bit(buffer);
		ui.textEdit->append(qsMsg);
		this->update();
	}  

	//outfile.close(); 
}

           

運作效果:

Qt執行dos指令并擷取控制台輸出

這樣可以實作目标,但是在Qt程式裡使用了Win32 API,這是有點别扭,另外輸出的消息并不是動态的輸出,而是在執行完畢後一下子刷出來,即使我們通過QThread,把處理過程放到其它線程裡,然後通過異步的事件來重新整理消息輸出也是實作不了動态消息效果。總體來說,這種方式是不完美的。

我想Qt應該有自己的方式。通過檢視Qt的幫助,很快就可以找到做這個事情的類QProcess,QProcess可以同于執行外部程式和指令,并且支援消息重定向和标準輸入、輸出,有了QProcess類,前面講到的問題就可以簡單的通過下面的方式來實作了:

頭檔案:

#ifndef TEST2_H
#define TEST2_H

#include <QWidget>
#include <QProcess>
#include "ui_Test2.h"

class Test2 : public QWidget
{
	Q_OBJECT
public:
	Test2(QWidget *parent = 0);
	~Test2();
	public slots:
		void onTest();
		void onOutput();
private:
	Ui::Test2 ui;
	QProcess  *m_Process;
};

#endif // TEST2_H
           

源檔案:

#include "Test2.h"
#include <QTextEdit>

Test2::Test2(QWidget *parent)
	: QWidget(parent),m_Process(new QProcess)
{
	ui.setupUi(this);
	ui.lineEdit->setText(tr("ping localhost"));
	ui.textEdit->setLineWrapMode(QTextEdit::NoWrap);
	m_Process->setProcessChannelMode(QProcess::MergedChannels);
	connect(ui.pushButton,SIGNAL(clicked()),this,SLOT(onTest()));
	connect(m_Process,SIGNAL(readyReadStandardOutput()),this,SLOT(onOutput()));
}

Test2::~Test2()
{
	m_Process->terminate();
}

void Test2::onTest()
{
	QString qsCmd = ui.lineEdit->text();
	m_Process->start( qsCmd);
}

void Test2::onOutput()
{
	QByteArray qbt = m_Process->readAllStandardOutput();
	QString msg = QString::fromLocal8Bit(qbt);
	ui.textEdit->append(msg);
	ui.textEdit->update();
}
           

運作效果:

Qt執行dos指令并擷取控制台輸出
Qt

繼續閱讀