Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

After testing under windows, it is found that QT program will kill the process created by CreateProcess when it exits again



  • MinGW32,After testing under windows, it is found that QT program will kill the process created by CreateProcess when it exits again,I tested the following situations, but there were no exceptions(windows下经过测试发现QT编写的程序再退出的时候会把CreateProcess创建的进程杀死,我测试了以下各种情况但都没有例外),following code:
    #include "bccmainwindow.h"
    #include "ui_bccmainwindow.h"
    #include <QProcess>
    #include <QDebug>
    #include <QDir>
    //#include <windows.h>
    #include "qsystemlibrary.h"

    static QString qt_create_commandline(const QString &program, const QStringList &arguments);
    static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
    const QString &workingDir, qint64 *pid);
    bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid);

    using namespace std;
    BccMainWindow::BccMainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::BccMainWindow)
    {
    ui->setupUi(this);
    }

    BccMainWindow::~BccMainWindow()
    {
    delete ui;
    }

    void BccMainWindow::on_pushButton_clicked()
    {

    QProcess* process  = new QProcess;
    //QString strpath = qApp->applicationDirPath();
    //process->setWorkingDirectory(strpath + QString("/softwares/com2tcp"));
    //QString str_exePath = "\"" + str_exePath + "\"";
    QStringList arguments;
    
    arguments << "-new" << "http://www.qq.com";
    
    
    
    if (process->state() != QProcess::Running)
    {   /// 只允许开启一个进程
         process->startDetached("C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe ", arguments);     // 将以阻塞的方式启动
         //process->start("C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe ", arguments);
         if(!process->waitForStarted())
         {
             qDebug() << "waitForStarted false";
         }
         //process->waitForFinished(5000);
         QString strResult = QString::fromLocal8Bit(process->readAllStandardOutput());
         qDebug() << strResult;
    }
    
    
    if(process)
    {
        //process->close();
        delete process;
        process = NULL;
    }
    

    }

    void BccMainWindow::on_pushButton_2_clicked()
    {
    QDir::setCurrent("C:\Program Files (x86)\Internet Explorer\");

    QStringList arguments;
    arguments << "-new" << "http://www.qq.com";
    if(!QProcess::startDetached("iexplore.exe",arguments))
    {
        qDebug() << "startDetached false";
    }
    

    }

    void BccMainWindow::on_pushButton_3_clicked()
    {
    /*
    QDir::setCurrent("C:\Program Files (x86)\Internet Explorer\");
    int code=QProcess::execute("iexplore.exe -new www.qq.com");

    qDebug() << "execute code "<<code;
    */
    /*
    

    #define SW_NORMAL 1 //普通方式运行
    #define SW_SHOWMINIMIZED 2 //运行时最小化显示
    #define SW_SHOWMAXIMIZED 3 //运行时最大化显示
    #define SW_MAXIMIZE 3 //最大化运行
    */
    WinExec(""C:\Program Files (x86)\Internet Explorer\iexplore.exe" -new www.qq.com",1);

    }

    void BccMainWindow::on_pushButton_4_clicked()
    {
    QDir::setCurrent("C:\Program Files (x86)\Internet Explorer\");

    QStringList arguments;
    arguments << "-new" << "http://www.qq.com";
    
    if(!QProcess::execute("iexplore.exe",arguments))
    {
        qDebug() << "execute false";
    }
    

    }
    /*
    QProcess ps;

    connect(&ps,SIGNAL(readyReadStandardOutput()),this,SLOT(readProcessStandardOutput()));

    QStringList args;

    args<<"-c";

    args<<"ps | grep 'processName";

    ps.start("sh",args);

    readProcessStandardOutput()

    {
    ps.readAllStandardOutput() // 得到sh -c "ps | grep 'processName‘“ 的输出结果,查看这个结果就可以得到这个结果是否在运行。

    }
    */

    void BccMainWindow::on_pushButton_cmd_clicked()
    {
    QDir::setCurrent("C:\Program Files (x86)\Internet Explorer\");

    QStringList arguments;
    arguments<<"/c"<<"iexplore.exe -new \"http://www.qq.com\"";
    
    qint64 pid;
    //if(!QProcess::startDetached("cmd",arguments,"C:\\Program Files (x86)\\Internet Explorer\\",&pid))
    if(!startDetached("cmd",arguments,"C:\\Program Files (x86)\\Internet Explorer\\",&pid))
    {
        qDebug() << "startDetached false";
    }
    
    /*
    qint64 ProcessId;
    
    HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
    if (hProcess == NULL)
    {
        //return FALSE;
    }
    if (!TerminateProcess(hProcess, 0))
    {
        //return FALSE;
    }
    */
    //return TRUE;
    

    }

    static QString qt_create_commandline(const QString &program, const QStringList &arguments)
    {
    QString args;
    if (!program.isEmpty()) {
    QString programName = program;
    if (!programName.startsWith(QLatin1Char('"')) && !programName.endsWith(QLatin1Char('"')) && programName.contains(QLatin1Char(' ')))
    programName = QLatin1Char('"') + programName + QLatin1Char('"');
    programName.replace(QLatin1Char('/'), QLatin1Char('\'));

        // add the prgram as the first arg ... it works better
        args = programName + QLatin1Char(' ');
    }
    
    for (int i=0; i<arguments.size(); ++i) {
        QString tmp = arguments.at(i);
        // Quotes are escaped and their preceding backslashes are doubled.
        tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
        if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
            // The argument must not end with a \ since this would be interpreted
            // as escaping the quote -- rather put the \ behind the quote: e.g.
            // rather use "foo"\ than "foo\"
            int i = tmp.length();
            while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
                --i;
            tmp.insert(i, QLatin1Char('"'));
            tmp.prepend(QLatin1Char('"'));
        }
        args += QLatin1Char(' ') + tmp;
    }
    return args;
    

    }

    // Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails
    // with ERROR_ELEVATION_REQUIRED.
    static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments,
    const QString &workingDir, qint64 *pid)
    {
    typedef BOOL (WINAPI *ShellExecuteExType)(SHELLEXECUTEINFOW *);

    static const ShellExecuteExType shellExecuteEx = // XP ServicePack 1 onwards.
        reinterpret_cast<ShellExecuteExType>(QSystemLibrary::resolve(QLatin1String("shell32"),
                                                                     "ShellExecuteExW"));
    if (!shellExecuteEx)
        return false;
    
    const QString args = qt_create_commandline(QString(), arguments); // needs arguments only
    SHELLEXECUTEINFOW shellExecuteExInfo;
    memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW));
    shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
    shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI;
    shellExecuteExInfo.lpVerb = L"runas";
    const QString program = QDir::toNativeSeparators(programIn);
    shellExecuteExInfo.lpFile = reinterpret_cast<LPCWSTR>(program.utf16());
    if (!args.isEmpty())
        shellExecuteExInfo.lpParameters = reinterpret_cast<LPCWSTR>(args.utf16());
    if (!workingDir.isEmpty())
        shellExecuteExInfo.lpDirectory = reinterpret_cast<LPCWSTR>(workingDir.utf16());
    shellExecuteExInfo.nShow = SW_SHOWNORMAL;
    
    if (!shellExecuteEx(&shellExecuteExInfo))
        return false;
    if (pid)
        *pid = qint64(GetProcessId(shellExecuteExInfo.hProcess));
    CloseHandle(shellExecuteExInfo.hProcess);
    return true;
    

    }

    bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid)
    {
    static const DWORD errorElevationRequired = 740;

    QString args = qt_create_commandline(program, arguments);
    bool success = false;
    PROCESS_INFORMATION pinfo;
    
    DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
    dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
    dwCreationFlags |= DETACHED_PROCESS;
    STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
                                 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
                                 (ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
                                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
                               };
    success = CreateProcess(0, (wchar_t*)args.utf16(),
                            0, 0, FALSE, dwCreationFlags, 0,
                            workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
                            &startupInfo, &pinfo);
    
    if (success) {
        CloseHandle(pinfo.hThread);
        CloseHandle(pinfo.hProcess);
        if (pid)
            *pid = pinfo.dwProcessId;
    } else if (GetLastError() == errorElevationRequired) {
        success = startDetachedUacPrompt(program, arguments, workingDir, pid);
    }
    
    return success;
    

    }

    void BccMainWindow::on_pushButton_CreateP_clicked()
    {
    char cWindowsDirectory[MAX_PATH] = { 0 };

        //LPTSTR 与 wchar_t* 等价(Unicode环境下)
        LPTSTR cWinDir = new TCHAR[MAX_PATH];
        memset(cWinDir, MAX_PATH, 0);
        //GetCurrentDirectory(MAX_PATH, cWinDir);
    
        
        wcscpy_s(cWinDir, MAX_PATH, L"D:\\Tool\\DevMng.exe");
        LPTSTR sConLin = cWinDir;
    
        STARTUPINFO si;
        PROCESS_INFORMATION pi;
    
        ZeroMemory(&si, sizeof(si));
        ZeroMemory(&pi, sizeof(pi));
    
        //创建一个新进程
        if (CreateProcess(
            NULL,   //  指向一个NULL结尾的、用来指定可执行模块的宽字节字符串
            sConLin, // 命令行字符串
            NULL, //    指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。
            NULL, //    如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。<同上>
            false,//    指示新进程是否从调用进程处继承了句柄。
            0,  //  指定附加的、用来控制优先类和进程的创建的标
                //  CREATE_NEW_CONSOLE  新控制台打开子进程
                //  CREATE_SUSPENDED    子进程创建后挂起,直到调用ResumeThread函数
            NULL, //    指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境
            NULL, //    指定子进程的工作路径
            &si, // 决定新进程的主窗体如何显示的STARTUPINFO结构体
            &pi  // 接收新进程的识别信息的PROCESS_INFORMATION结构体
        ))
        {
            qDebug() << "create process success" << endl;
    
            //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能不小心调用TerminateProcess函数关掉子进程
    //      CloseHandle(pi.hProcess);
    //      CloseHandle(pi.hThread);
        }
        else {
            qDebug() << "failed to create process" << endl;
        }
        ::GetCurrentProcess();
        Sleep(1000);
    
        //终止子进程
        //TerminateProcess(pi.hProcess, 300);
    
        //终止本进程,状态码
        //ExitProcess(1001);
    
        //return 0;
    

    }



  • I hope that when the main process exits, it does not exit other processes it started,It's useful in some scenarios, such as program upgrades(我希望主进程退出时不会退出它启动的其它进程,在某些场景下还是有用的,比如程序升级),Now I have to use other development tools to finish it.


  • Moderators

    @legahero said in After testing under windows, it is found that QT program will kill the process created by CreateProcess when it exits again:

    I hope that when the main process exits, it does not exit other processes it started

    Then your other child process must be detached.

    Anyway, I see that your code contains both QProcess and CreateProcess. It doesn't make sense to mix both in the same application, so you should just choose one. (I think QProcess is a lot simpler to use)

    If you have further questions, please clean up and simplify your code.



  • @JKSH ,thanks,I find that this happens when debugging, not when publishing。


Log in to reply