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

时间:2017-07-13 12:06:28

标签: c++ winapi unicode

首先,我是新手使用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 = QString{argv[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字符:

enter image description here

我应该更改什么才能正确处理unicode?

1 个答案:

答案 0 :(得分:4)

问题在于您依赖于argv的窄版main()数组,该数组无法在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;
    }
}