将 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>
【问题讨论】:
你打电话给ShellExecuteExW
或ShellExecuteExA
?
你可以在我调用的代码中看到ShellExecuteEx
不,ShellExecuteEx
不存在。这是扩展为ShellExecuteExW
或ShellExecuteExA
的宏。你需要打电话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 函数