为啥 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::wstringstd::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() 不执行提供的命令?的主要内容,如果未能解决你的问题,请参考以下文章

使用jna和CreateProcessW时如何获取进程输出

Flutter Web:CreateProcessW 失败 2

通过 CreateProcessW 使用“mkdir”创建的目录名称中的垃圾?

为啥我的 Qt 应用程序在执行时不显示 GUI?

如果集合视图已经存在以执行任务,为啥 Xcode 提供 Tableview [重复]

为啥这个for循环不执行?