问题描述
这可能是 2 个问题,但它们大致上都围绕着 CreateProcess 并且它的功能不太正常。
我一直在开发一个应用程序,它将文件收集在一起,处理它们,然后作为最后一步压缩它们,使用我压缩的目录的散列重命名压缩文件。为了实现这一点,我使用 7zip (7za.exe) 的独立副本,通过使用 CreateProcess 创建/压缩存档,以及一个名为 DirHash 的单独程序来生成我正在尝试制作的存档的名称。
我遇到的问题是这两个程序都不能正常工作。我目前正在运行带有 -t "temp.txt" -Nowait -quiet -overwrite"
标志的 DirHash,它确实创建了一个名为 temp.txt
的文件,但是,使用 CreateProcess 运行它时,该文件始终为空。当我在标准命令行上使用完全相同的参数时,它会产生正确的输出。
另一个问题是 7zip 在尝试压缩我的目录时似乎出错。在运行我的 CreateProcess 时,我收到“不支持的命令”错误,并且文件没有被压缩。但是,当我在命令行上使用完全相同的参数时,成功创建了存档。
这里是 DirHash 的相关代码,同样的代码用于 7za。我已经确认给 CreateProcess 的值是正确的,参数与我在命令行中使用的相同。
auto hashLocation = searchPath + "\\" + DIRHASH_NAME;
auto params = "\"" + targetDir + "\" MD5" + " -quiet -t \"temp.txt\" -Nowait -overwrite";
// I kNow this part is gross and if there's any suggestions for how to do it better I'm willing to hear it.
std::wstring intermediate;
intermediate.assign(params.begin(),params.end());
LPWSTR trueParams = &intermediate[0];
std::wstring intermediate_ex;
intermediate_ex.assign(hashLocation.begin(),hashLocation.end());
LPCWSTR trueLocation = intermediate_ex.c_str();
STARTUPINFO si;
PROCESS_informatION pi;
ZeroMemory(&si,sizeof(STARTUPINFO));
ZeroMemory(&pi,sizeof(PROCESS_informatION));
si.cb = sizeof(STARTUPINFO);
// Ripped from stack overflow
bool success = CreateProcess(
trueLocation,trueParams,NULL,TRUE,// No creation flags
NULL,// Use parent's environment block
NULL,// Use parent's starting directory
&si,// Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_informatION structure (removed extra parentheses)
);
// Close process and thread handles.
WaitForSingleObject(pi.hProcess,INFINITE);
if (!success)
{
addLog("Failed to run DirHash process.",ErrorLevel::ERROR_MESSAGE);
return ERROR_STR;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
解决方法
当同时使用 lpApplicationName
的 lpCommandLine
和 CreateProcess()
参数时,lpCommandLine
应包括 EXE 路径作为命令行中的第一个标记。这甚至在 CreateProcess()
文档中都有说明:
如果lpApplicationName
和lpCommandLine
都为非NULL,则lpApplicationName
指向的空终止字符串指定要执行的模块,lpCommandLine
指向的空终止字符串指定要执行的模块{1}} 指定命令行。新进程可以使用 GetCommandLine
来检索整个命令行。 用 C 编写的控制台进程可以使用 argc
和 argv
参数来解析命令行。由于 argv[0]
是模块名称,C 程序员通常在命令行中重复模块名称作为第一个标记。
因此,如果您打算在命令行中包含 EXE 路径,则根本不需要使用 lpApplicationName
参数:
如果 lpApplicationName
为 NULL,则命令行的第一个以空格分隔的标记指定模块名称。如果您使用包含空格的长文件名,请使用带引号的字符串来指示文件名的结束位置和参数的开始位置(请参阅 lpApplicationName
参数的说明)...
此外,与其将 char
字符串转换为 std::wstring
(您没有正确执行)只是为了调用 CreateProcess()
(在这种情况下被映射到 CreateProcessW()
),您可以改用 CreateProcessA()
。
试试这个:
std::string hashLocation = searchPath + "\\" + DIRHASH_NAME;
std::string cmd = "\"" + hashLocation + "\" \"" + targetDir + "\" MD5 -quiet -t \"temp.txt\" -nowait -overwrite";
STARTUPINFOA si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
bool success = CreateProcessA(
NULL,&cmd[0],// or const_cast<char*>(cmd.c_str()),or params.data() in C++17...
NULL,NULL,FALSE,// No creation flags
NULL,// Use parent's environment block
NULL,// Use parent's starting directory
&si,// Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
if (!success)
{
addLog("Failed to run DirHash process.",ErrorLevel::ERROR_MESSAGE);
return ERROR_STR;
}
// Close process and thread handles.
WaitForSingleObject(pi.hProcess,INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
或者这个:
// change searchPath,DIRHASH_NAME,and targetDir to wide strings...
std::wstring hashLocation = searchPath + L"\\" + DIRHASH_NAME;
std::wstring cmd = L"\"" + hashLocation + L"\" \"" + targetDir + L"\" MD5 -quiet -t \"temp.txt\" -nowait -overwrite";
STARTUPINFOW si = {};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {};
bool success = CreateProcessW(
NULL,// or params.data() in C++17...
NULL,INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);