QT中QProcess呼叫命令列的痛苦經歷

薰衣草的旋律發表於2015-06-30

在QT程式中需要將某些目錄和檔案壓縮為一個rar的壓縮包,於是想到了在QT中通過QProcess類呼叫命令列的rar.exe來達到效果,但是沒想到QProcess類用起來很麻煩,而且達不到效果,折騰了2天仍然沒找到原因,使用另外一種辦法解決了。

建立壓縮包的方法

在windows平臺建立壓縮包,可以直接使用rar.exe,該程式在安裝winrar之後,在其安裝目錄下就可以找到。該程式是winrar對應的命令列版本,其語法例子如下:

rar.exe a -k -r -s -m1 test.rar direct1/   direct2/  test.txt

例子對應的目錄結構如下:

s

上面的命令表示在當前目錄下建立壓縮包 test.rar ,將當前目錄下的 direct1目錄以及其所有子目錄和檔案、direct2目錄以及其子目錄和檔案、當前目錄下的檔案test.txt 都新增到test.rar壓縮包中。

其中引數a表示新增到壓縮包

引數-r表示遞迴新增

命令的問題解決了之後,那麼下面就是如何在QT中呼叫該命令,實際上在QT中呼叫該命令就出現了很多問題。

在QT中呼叫命令列

QT中呼叫外部命令一般使用QProcess類提供的成員函式,其使用的具體程式碼如下:

QProcess p(0);
p.start(command,args); //command是要執行的命令,args是引數
p.waitForFinished();//等待完成
qDebug()<<QString::fromLocal8Bit(p.readAllStandardError());

套用上面的程式碼得到如下:

QProcess p(0);
QString command = "E:/test_rar_course/rar.exe";
QStringList args;
args.append("a");
args.append("-k");
args.append("-r");
args.append("-s");
args.append("-m1");
args.append("E:/test_rar_course/test.rar");
args.append("E:/test_rar_course/direct1/");
args.append("E:/test_rar_course/direct2/");
args.append("E:/test_rar_course/test.txt");
p.execute(command,args);//command是要執行的命令,args是引數
p.waitForFinished();
qDebug()<<QString::fromLocal8Bit(p.readAllStandardError());

可以生成test.rar但是,該壓縮包中將路徑E:/test_rar_course也壓縮排去了,而我需要的是開啟壓縮包之後僅僅看到direct1,direct2,test.txt3個專案,那麼是不是設定一下工作目錄就可以了呢:

QProcess p(0);
p.setWorkingDirectory("E:/test_rar_course/");//指定程式的工作目錄
QString command = "E:/test_rar_course/rar.exe";
QStringList args;
args.append("a");
args.append("-k");
args.append("-r");
args.append("-s");
args.append("-m1");
args.append("-wE:/test_rar_course/");//指定rar.exe的工作目錄
args.append("test.rar");
args.append("direct1/");
args.append("direct2/");
args.append("test.txt");
p.execute(command,args);//command是要執行的命令,args是引數
p.waitForFinished();
qDebug()<<QString::fromLocal8Bit(p.readAllStandardError());//獲取輸出

我不僅新增-w引數(該引數為rar.exe的命令列引數,用於指定工作目錄)為rar.exe命令指定工作目錄,同時利用p.setWorkingDirectory()為啟動的程式指定工作目錄,執行之後報錯,說找不到檔案,我猜可能還是工作目錄的問題,但是不知道問題在哪裡,查了很多資料都無濟於事,最終還是放棄了這種嘗試,改成了下面的嘗試:

QProcess p(0);
p.setWorkingDirectory("E:/test_rar_course/");//指定程式的工作目錄
QString command = "E:/test_rar_course/test.bat";
p.start(command);
p.waitForFinished();
qDebug()<<QString::fromLocal8Bit(p.readAllStandardError());

而test.bat的內容為如下:

cd /d E:/test_rar_course/
E:/test_rar_course/rar.exe a -k -r -s -m1 -wE:/test_rar_course/ test.rar direct1/ direct2/ test.txt

我直接在bat中通過cd命令切換工作目錄,然後進行壓縮,其中為了避免出現壓縮絕對路徑的情況,direct1,direct2,test.txt使用的都是相對路徑,直接滑鼠雙擊該test.bat執行OK,放在QT中執行OK,似乎完美的解決了問題。

但是我發現,如果目錄中出現()括號字元就不行了,當有括號字元的時候在QProcess執行的報錯中顯示路徑被括號截斷,此後我把路徑用引號引起來沒效果:

"\"E:/test_rar_course(xx)/test.bat\""

根據網上搜尋到的資訊,用^符號對括號進行轉義沒有截斷的報錯了,但是命令執行還是沒有效果,控制檯也沒有報錯:

"E:/test_rar_course^(xx^)/test.bat"

到這裡我不知道該怎麼樣去達到我的效果,唯一的感覺QProcess怎麼這麼難用,如果有知道的QT大神,煩請告訴一下。我想到用另外一種方式來實現,就是用C++寫一個dll實現,然後QT中呼叫。

在QT中呼叫C++建立的dll

主要程式碼如下,實際上就是呼叫system函式,但是如果路徑中有圓括號,還是需要用^符號進行轉義,否則system執行也有問題:

void SystemTool::GenerateIndexRar(char * command)
{
    if(command == NULL) return ;
    system(command);
}

但是除了圓括號要轉義以外,還存在一個很不舒服的問題,就是每次執行都會彈出cmd的黑視窗,執行完成之後,視窗消失,程式碼改成下面的就好了:

#include <windows.h>
void SystemTool::GenerateIndexRar(char * command)
{
         if(command == NULL) return ; 
         /**
            WinExec 的windows 呼叫,可以通過引數SW_HIDE隱藏命令列黑視窗
            並且命令的路徑是可以帶括號的
          */
         WinExec(command,SW_HIDE);
}

到此完美解決該問題,既不需要對圓括號進行轉義,同時也隱藏了黑視窗了。當然前述的test.bat的內容要在程式中動態生成,利用合適的路徑替換掉test.bat中的路徑。

最後呼叫如:SystemTool::GenerateIndexRar("E:/test_rar_course(xx)/test.bat");

 

相關文章