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。您可能想使用 ShellExecuteShellExecuteEx 而不是 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);

【讨论】:

认为更好地调用 GetEnvironmentVariableWL"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

Win32 API 类似于 CreateProcess,但适用于 bat/cmd/等。 (即使用 PATHEXT)

Hook CreateProcess

CreateProcess的前两个参数究竟怎么用

CreateProcess 和奇怪的 nslookup 错误