为啥 CreateProcessW() 不执行提供的命令?
Posted
技术标签:
【中文标题】为啥 CreateProcessW() 不执行提供的命令?【英文标题】:why isn't CreateProcessW() performing the Command provided?为什么 CreateProcessW() 不执行提供的命令? 【发布时间】:2020-03-17 22:15:52 【问题描述】:为了呈现最少的可重现代码,我编写了一个代码,使用 CreateProcessW() 从给定位置删除文件。该文件不会被删除。一些帮助对于了解为什么这不起作用非常有用。
dprintf(("Error %d", GetLastError()));
STARTUPINFO si = sizeof(STARTUPINFO), 0 ;
si.cb = sizeof(si);
PROCESS_INFORMATION pi = 0 ;
LPWSTR AppName = L"C:\\Windows\\System32\\cmd.exe";
string bstr = "C:\\Windows\\System32\\cmd.exe /C del"+trans_loc+"a.rtf";
LPWSTR Command = new WCHAR[bstr.length()];
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
define TRANSCRIPT_LOCATION "C:\Users\Administrator\Desktop\" 这是要删除的文件的位置
GetLastError() 不断返回 50(ERROR_NOT_SUPPORTED) 和 res = 1 的值
【问题讨论】:
你看过GetLastError()吗?你调试过这个吗? 是的,它一直给 50 (ERROR_NOT_SUPPORTED)。 当你真正想要执行 gcloud 时,为什么要运行 cmd.exe 并不明显。暂时使用 /k 而不是 /c,这样您就可以看到错误消息。可以像需要双引号的文件路径一样简单。并修复错误处理,GetLastError() 仅在 CreateProcess 返回 FALSE 时有效。 【参考方案1】:我的第一个想法是
LPWSTR Command = new WCHAR[bstr.length()];
不对。也许
LPWSTR Command = new WCHAR[bstr.length() + 1];
会起作用。更好的选择是使用wchars_num
来分配内存。
而不是
LPWSTR Command = new WCHAR[bstr.length()];
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
使用
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, NULL, 0);
LPWSTR Command = new WCHAR[wchars_num];
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), -1, Command, wchars_num);
DWORD res = CreateProcessW(AppName, Command, 0, 0, 0, DETACHED_PROCESS, 0, 0, &si, &pi);
第二个问题是您在编写del
命令时可能遗漏了一个空格字符。
string bstr = "C:\\Windows\\System32\\cmd.exe /C del " + trans_loc + "a.rtf";
// ^^
【讨论】:
谢谢,我添加了更改。但问题依然存在 好的,在“del”命令后添加空格就可以了。非常感谢。我已经对问题进行了一些编辑以回到我原来的问题(del 只是为了最小的可重现示例)。 bstr 是运行 gcloud 并在文本文件中接收输出的命令。仅创建文本文件。 gcloud 提示永远不会出现。 @PrafulSrivastava 更改问题以使答案不再适合是不好的。您应该保留原来的问题并接受此答案,然后提出一个新问题。如果您单击edited 链接,您可以恢复到早期版本。 哦,好吧。我会提出一个新问题并重新编辑这个问题。【参考方案2】:我发现您的代码存在许多问题:
LPWSTR AppName = L"C:\\Windows\\System32\\cmd.exe";
无法在 C++11 及更高版本中编译。您需要(并且应该)改用LPCWSTR
,因为字符串文字是const
数据,而LPCWSTR
是指向const
WCHAR 数据的指针,但LPWSTR
是指向非const
WCHAR 数据的指针。
在string bstr = "C:\\Windows\\System32\\cmd.exe /C del"+trans_loc+"a.rtf";
中,您在del
命令和要删除的文件名之间缺少必需的空格字符。
在LPWSTR Command = new WCHAR[bstr.length()];
中,您没有为空终止符分配足够的空间。此外,无论如何,您都不应该将bstr.length()
用于转换后的长度,因为不能保证转换后的字符串不会大于原始字符串。您应该使用NULL
输出缓冲区调用MultiByteToWideChar()
一次以计算实际转换后的长度(您正在这样做),然后分配内存(您没有这样做 - 您分配得太快了!),然后调用@ 987654336@再次进行实际转换。
您正在泄漏分配的内存(您没有调用delete[] Command;
)。我建议使用std::wstring
或std::vector<WCHAR>
而不是new WCHAR[]
。
你说res
被设置为1,这意味着CreateProcessW()
实际上成功运行cmd.exe
(现在,cmd.exe
是否成功执行你的命令是另一回事 - 使用 GetExitCodeProcess()
找出它),因此 GetLastError()
的返回值是毫无意义!在调用CreateProcessW()
GetLastError()
当然是有意义的
无论CreateProcessW()
是成功还是失败,您都在调用WaitForSingleObject()
。
试试这个:
STARTUPINFO si = ;
si.cb = sizeof(si);
PROCESS_INFORMATION pi = ;
std::string bstr = "C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + "a.rtf\"";
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), NULL, 0);
if (wchars_num == 0)
dprintf(("MultiByteToWideChar Error %d", GetLastError()));
else
std::vector<WCHAR> Command(wchars_num + 1);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), Command.data(), wchars_num);
if (!CreateProcessW(nullptr, Command.data(), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
dprintf(("CreateProcessW Error %d", GetLastError()));
else
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
或者,如果您使用的是 Windows 10 build 17035 或更高版本,并且在您的 Windows 设置中启用了“测试版:使用 Unicode UTF-8 支持全球语言”选项(或者,如果 trans_loc
不包含任何非ASCII,非用户语言环境字符),则根本不需要 MultiByteToWideChar()
转换:
STARTUPINFO si = ;
si.cb = sizeof(si);
PROCESS_INFORMATION pi = ;
std::string Command = "C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + "a.rtf\"";
if (!CreateProcessA(nullptr, const_cast<char*>(Command.c_str()), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
dprintf(("CreateProcessA Error %d", GetLastError()));
else
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
话虽如此,更好的选择是简单地使用 std::wstring
而不是 std::string
开头:
STARTUPINFO si = ;
si.cb = sizeof(si);
PROCESS_INFORMATION pi = ;
// make sure trans_loc is std::wstring instead of std::string...
std::wstring bstr = L"C:\\Windows\\System32\\cmd.exe /C del \"" + trans_loc + L"a.rtf\"";
if (!CreateProcessW(nullptr, Command.data(), nullptr, nullptr, FALSE, DETACHED_PROCESS, nullptr, nullptr, &si, &pi))
dprintf(("CreateProcessW Error %d", GetLastError()));
else
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwExitCode = 0;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
dprintf(("cmd.exe Exit Code %d", dwExitCode));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
当然,最简单的解决方案是根本不使用cmd.exe / C del
,而是使用DeleteFileW()
:
// make sure trans_loc is std::wstring instead of std::string...
std::wstring bstr = trans_loc + L"a.rtf";
if (!DeleteFileW(bstr.c_str()))
dprintf(("DeleteFileW Error %d", GetLastError()));
或者,如果您坚持使用 UTF-8 编码的std::string
:
std::string bstr = trans_loc + "a.rtf";
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), NULL, 0);
if (wchars_num == 0)
dprintf(("MultiByteToWideChar Error %d", GetLastError()));
else
std::vector<WCHAR> wstr(wchars_num + 1);
MultiByteToWideChar(CP_UTF8, 0, bstr.c_str(), bstr.length(), wstr.data(), wchars_num);
if (!DeleteFileW(wstr.c_str()))
dprintf(("DeleteFileW Error %d", GetLastError()));
或者,如果您使用的是启用了 UTF-8 支持的 Windows 10(或者,如果 trans_loc
不包含任何非 ASCII、非用户语言环境字符):
std::string bstr = trans_loc + "a.rtf";
if (!DeleteFileA(bstr.c_str()))
dprintf(("DeleteFileA Error %d", GetLastError()));
【讨论】:
除此之外,bstr
对于不是 Windows BSTR 的东西来说是一个糟糕的名字。以上是关于为啥 CreateProcessW() 不执行提供的命令?的主要内容,如果未能解决你的问题,请参考以下文章
Flutter Web:CreateProcessW 失败 2
通过 CreateProcessW 使用“mkdir”创建的目录名称中的垃圾?