将 unicode 参数传递给 ShellExecuteEx 中的子进程

Posted

技术标签:

【中文标题】将 unicode 参数传递给 ShellExecuteEx 中的子进程【英文标题】:Passing unicode arguments to subprocess in ShellExecuteEx 【发布时间】:2017-07-13 12:06:27 【问题描述】:

首先,我是使用 Windows 的 C++ API 的新手,所以我可能缺少一些明显的东西。

我正在尝试在 Windows 的 C++ 中启动提升的子进程。我设法编写了这段代码,它启动了一个提升的子进程,将 2 个参数传递给它,然后子进程会弹出一个带有这些参数的窗口:

#include <shlobj.h>
#include <shlwapi.h>
#include <objbase.h>
#include <string>
#include <QString>
#include <QDebug>
#include <QMessageBox>
#include <QApplication>

auto getWinError()

    auto dw =GetLastError();
    LPTSTR* lpMsgBuf;
    FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dw,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR) &lpMsgBuf,
                0, NULL );
    auto str = QString::fromWCharArray(*lpMsgBuf);
    LocalFree(*lpMsgBuf);
    return str;


int main(int argc, char** argv)

    if (argc == 1)
        // start self as admin with 2 arguments
        SHELLEXECUTEINFO info = ;
        info.cbSize = sizeof(SHELLEXECUTEINFO);
        info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE;
        info.lpVerb = L"runas";
        auto filestr = QStringargv[0].toStdWString();
        info.lpFile = filestr.c_str();
        info.lpParameters = LR"("first parameter" "żółć")";
        info.nShow = SW_SHOW;

        auto success = ShellExecuteEx(&info);
        if (!success || (int)info.hInstApp <= 32)
            qDebug()<<getWinError();
            return -1;
        
        HANDLE handle = info.hProcess;

        auto exitCode = [handle]
            DWORD status;
            GetExitCodeProcess(handle, &status);
            return status;
        ;

        while (exitCode() == STILL_ACTIVE) Sleep(100);

        qDebug()<<"process exited with exit code "<<exitCode();

        CloseHandle(handle);
        return 0;
     else 
        // show popup with arguments
        QApplication a(argc, argv);
        QStringList s;
        for (int i = 1; i<argc; i++)
            s += argv[i];
        
        QMessageBox::information(0, "", s.join('\n'));
        return 42;
    

它大部分都有效,但它会破坏非 ASCII 字符:

我应该改变什么以使其正确处理 unicode?​​p>

【问题讨论】:

你打电话给ShellExecuteExWShellExecuteExA 你可以在我调用的代码中看到ShellExecuteEx 不,ShellExecuteEx 不存在。这是扩展为ShellExecuteExWShellExecuteExA 的宏。你需要打电话ShellExecuteExW 感谢您的提示。我打电话给ShellExecuteW,当我把它改成ShellExecuteA它工作了。 但调用任何 api 的 A 版本 - 糟糕的选择 - 你几乎总是需要调用 W 版本并将宽字符串参数传递给它(蝙蝠不是ansi 字符串) 【参考方案1】:

问题是您依赖main() 的窄版本的argv 数组,它无法在Windows 上接收Unicode 输入。您需要改用 wmain() 的宽版本。

但是,QApplication 会为您解析命令行。在 Windows 上,QApplication 忽略 argv 而是使用 Win32 API GetCommandLine() 函数,因此它可以接收 Unicode 输入。但是您将直接访问argv,而不是使用 Qt 解析的内容。您应该使用QApplication::arguments() 方法将解析的命令行检索为QStringList

您还错误地使用了FormatMessage()。你应该使用WaitForSingleObject() 而不是GetExitCodeProcess() 循环。

试试这个:

#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <objbase.h>

#include <string>

#include <QString>
#include <QDebug>
#include <QMessageBox>
#include <QApplication>

QString getWinError()

    DWORD dw = GetLastError();
    LPWSTR lpMsgBuf = NULL;
    FormatMessageW(
                FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dw,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPWSTR) &lpMsgBuf,
                0, NULL );
    QString str = QString::fromWCharArray(lpMsgBuf);
    LocalFree(lpMsgBuf);
    return str;


int main(int argc, char** argv)

    if (argc == 1)
    
        // start self as admin with 2 arguments
        SHELLEXECUTEINFOW info = ;
        info.cbSize = sizeof(info);
        info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE;
        info.lpVerb = L"runas";

        WCHAR szFileName[MAX_PATH] = ;
        GetModuleFileNameW(NULL, szFileName, MAX_PATH);
        info.lpFile = szFileName;

        /* alternatively:

        std::wstring filePath = QCoreApplication::applicationFilePath().toStdWString();
        info.lpFile = filePath.c_str();

        or:

        QString filePath = QCoreApplication::applicationFilePath();
        info.lpFile = (LPCWSTR) filePath.utf16();
        */

        info.lpParameters = L"\"first parameter\" \"żółć\"";
        info.nShow = SW_SHOW;

        if (!ShellExecuteEx(&info))
            qDebug() << getWinError();
            return -1;
        

        WaitForSingleObject(info.hProcess, INFINITE);

        DWORD status = 0;
        GetExitCodeProcess(info.hProcess, &status);

        CloseHandle(info.hProcess);

        qDebug() << "process exited with exit code " << status;

        return 0;
    
    else
    
        // show popup with arguments
        QApplication a(argc, argv);
        QStringList s = a.arguments();
        QMessageBox::information(0, "", s.join('\n'));
        return 42;
    

【讨论】:

但是如果我不想在进程完成之前阻塞线程,那么通过GetExitCodeProcess 进行轮询就可以了,对吧? 你可以用WaitForSingleObject()做同样的事情,使用非无限超时,甚至是0。进程退出时,进程句柄会发出信号。但是在您之前的示例中,您只是在循环中调用Sleep(),直到进程退出,因此您阻塞了调用线程。最好通过一次调用 WaitForSingleObject(INFINITE) 而不是 while (STILL_ACTIVE) 循环来处理。

以上是关于将 unicode 参数传递给 ShellExecuteEx 中的子进程的主要内容,如果未能解决你的问题,请参考以下文章

如何将 POST 参数传递给 Durable Function,然后将此参数传递给 Timer Triggered 函数

如何将参数传递给 Java 线程?

如何将参数传递给进程

如何通过反射将参数传递给方法

如何将参数传递给 erlang os:cmd()?

将 NSDictionary 作为参数传递给 UITapGestureRecognizer