CreateProcess api 在 Windows 10 上失败,错误代码为 122
Posted
技术标签:
【中文标题】CreateProcess api 在 Windows 10 上失败,错误代码为 122【英文标题】:CreateProcess api failing with error code 122 on windows 10 【发布时间】:2018-08-28 12:12:42 【问题描述】:我正在使用 CreateProcess api 来启动一个批处理文件。该代码在 Windows 7 上运行良好,但在 Windows 10 上失败。 下面是sn-p的代码:
CString param; //it holds the very long string of command line arguments
wstring excFile = L"C:\\program files\\BatchFile.bat";
wstring csExcuPath = L"C:\\program files";
wstring exeWithParam = excFile + _T(" ");
exeWithParam = exeWithParam.append(param);
STARTUPINFO si = sizeof(si) ;
PROCESS_INFORMATION pi;
TCHAR lpExeWithParam[8191];
_tcscpy_s(lpExeWithParam, exeWithParam.c_str());
BOOL bStatus = CreateProcess(NULL, lpExeWithParam, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB, NULL, csExcuPath.c_str(), &si, &pi);
DWORD err;
if (!bStatus)
err = GetLastError();
使用上面的代码,它正在调用一个批处理文件,该文件将使用给定的参数启动一个可执行文件。此代码在我们的产品中仅适用于 Windows 10。 GetLastError 返回错误代码 122,错误代码为“传递给系统调用的数据区域太小”。如何找出导致此错误的原因以及如何解决?
但是,当在示例测试应用程序中使用相同的代码时,不会出现任何错误并通过。 任何导致它在 Windows 10 上失败的线索/提示。
【问题讨论】:
粗略地说:不能用“.bat”文件创建进程,必须是“.exe”文件。我真的很想知道这适用于 Windows 7。您可能想使用ShellExecute
或 ShellExecuteEx
而不是 CreateProcess
应该可以在 CMD.exe
上创建进程,将批处理文件作为参数传递,例如 "cmd.exe /C path_to_batchfile.bat"
如果完全相同的代码在放置在一个应用程序中时似乎可以工作,但在放置在另一个应用程序中时却不行,那么您肯定不是使用完全相同的代码,或者你正在改变环境。通用文本映射类型与显式 Unicode 类型的混合是一个令人担忧的信号。无论如何,请提供minimal reproducible example。
似乎不太可能有人将 .bat 文件放在 c:\program 文件中。所以这个问题可能混淆了真正的原因,没有让任何人有机会看到它可能是 MAX_PATH 引起的问题。
@RbMm,我不会推测 OP 的问题。这太不寻常了。如果是我,我会附加一个调试器并逐步执行 CreateProcess
调用,以获取有关失败的内部调用的更多信息。
【参考方案1】:
你需要以.bat
文件为参数执行cmd.exe
,不要尝试直接执行.bat
。
另外,你不需要lpExeWithParam
,你可以将exeWithParam
直接传递给CreateProcess()
。
试试类似的方法:
CString param; //it holds the very long string of command line arguments
...
wstring excFile = L"C:\\program files\\BatchFile.bat";
wstring csExcuPath = L"C:\\program files";
wstring exeWithParam = L"cmd.exe /c \"" + excFile + L"\" ";
exeWithParam.append(param);
STARTUPINFOW si = sizeof(si) ;
PROCESS_INFORMATION pi = ;
BOOL bStatus = CreateProcessW(NULL, &exeWithParam[0]/*or exeWithParam.data() in C++17*/, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB, NULL, csExcuPath.c_str(), &si, &pi);
if (!bStatus)
DWORD err = GetLastError();
...
else
...
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
【讨论】:
认为更好地调用GetEnvironmentVariableW
为 L"ComSpec"
并将返回值作为 CreateProcessW
的第一个 (lpApplicationName) 参数传递,而不是在此处使用 0。但无论如何这不能解释为什么 OP 得到ERROR_INSUFFICIENT_BUFFER
,如果这是真的错误
我们不需要手动执行cmd.exe。从 NT 3.1 开始就是这种情况。直到 NT 5.x,CreateProcess
使用 "CMD /c"
。在 NT 6.0 中,它切换到使用 "%ComSpec% /c"
,并在未定义 ComSpec 时回退到 "%SystemRoot%\System32\cmd.exe /c"
。 IIRC 甚至 Windows 9x 都可以通过"COMMAND.COM /c"
直接运行批处理文件。在这种情况下,MSDN 是错误的。
@eryksun:MSDN 记录了支持的合同。保证实施遵守该合同。如果在其规范之外使用 API(例如通过传递批处理文件)似乎可以工作,那么这并不会使合同无效,并且 MSDN 不一定是错误的。请特别注意,CreateProcess
也可以从 UWP 应用程序中调用,因此实现在不同平台上可能会表现出不同的行为。
@IInspectable,很好,但 MSDN 是一个问题严重的合同。虚假声明:BY_HANDLE_FILE_INFORMATION
表示“标识符(低位和高位部分)和卷序列号唯一标识文件”。未经修改的公然破坏功能:FILE_FLAG_POSIX_SEMANTICS
(CreateFile
) 和 FIND_FIRST_EX_CASE_SENSITIVE
(FindFirstFileEx
) 自 XP 以来默认已被破坏。控制台 ReadFile
不再为 Ctrl+C (Win 8+) 设置 ERROR_OPERATION_ABORTED
。记录不充分的行为:NoDefaultCurrentDirectoryInExePath
已记录在案,但在重要的 CreateProcess
下没有记录。
无论如何,这无关紧要。这是什么都没有的答案。批处理文件可通过 NT 3.1-10.0 中的CreateProcess
直接执行。我不知道为什么有人会标记答案,但实际上并没有回答这个问题,并且绝对没有说明这个函数的奇怪错误代码ERROR_INSUFFICIENT_BUFFER
。这更像是对示例代码问题的扩展评论。【参考方案2】:
错误 122 等同于 ERROR_INSUFFICIENT_BUFFER,我认为这里的线索是“它包含很长的命令行参数字符串”。
到底有多长? Windows 10 上的限制可能会更低 - 我建议您进行实验(二进制印章)。
另外,CreateProcess
的 documentation 声明您必须明确启动 cmd.exe
才能运行批处理文件,所以我想您应该按照它说的去做。
【讨论】:
windows 允许命令行最长为0x7ffe
长度(终止为 0 - 这将是 0x7fff * 2=0xfffe
字节大小 - UNICODE_STRING
的最大大小)。当我们使用更多长度时——我们得到另一个错误ERROR_FILENAME_EXCED_RANGE
(文件名或扩展名太长。)——所以这不能解释错误ERROR_INSUFFICIENT_BUFFER
(这通常是我们在某些查询的输出缓冲区时得到的太小,但不适用于太大的输入缓冲区)。当然最好的查询"ComSpec"
并将其路径用作应用程序名称,OP 代码也必须工作,尽管效率不高。只有调试他的代码才能定位错误
What is the command line length limit?
@remy 谢谢。应该知道雷蒙德对此有话要说。有人对这里发生的事情有更好的了解吗?太多的命令行争论?不管它可能是 OP,实验,实验,实验。也许将缓冲区放在堆上而不是堆栈上(在这里抓住稻草!)
“有人对这里发生的事情有更好的了解吗?” - 也许环境块超出了它的限制 (What is the maximum length of an environment variable?)。可能是在一个用户帐户下运行,当与系统环境结合时,它只是超过了限制。
_tcscpy_s 不会溢出目标缓冲区。【参考方案3】:
我认为要运行批处理文件,您必须将 lpApplicationName 设置为 cmd.exe 并将 lpCommandLine 设置为以下参数:/c 加上批处理文件的名称
【讨论】:
那行不通。 Cmd.exe 不会将第一个参数解释为命令行的一部分(通用约定)。当您将/c BatchFile.bat
作为lpCommandLine 传递时,它将删除/c
部分,假设那是应用程序名称。无论如何,这个提议的答案并没有试图确定问题是什么。以上是关于CreateProcess api 在 Windows 10 上失败,错误代码为 122的主要内容,如果未能解决你的问题,请参考以下文章
尝试使用 createprocess (API) 在 cmd throw c++ 上运行命令?
CreateProcess api 在 Windows 10 上失败,错误代码为 122