使用 CreateProcess 运行批处理文件

Posted

技术标签:

【中文标题】使用 CreateProcess 运行批处理文件【英文标题】:Use CreateProcess to Run a Batch File 【发布时间】:2014-09-18 18:03:29 【问题描述】:

我正在尝试使用 CreateProcess 来启动一个新的环境块并在新的环境块中运行一个批处理文件。我已经阅读了 CreateProcess 的 msdn 示例,并提出了如下所示的代码。

发生了什么,它将打开新的命令提示符,然后停在那里。由于某种原因,它不会运行我的 .bat 文件。使用 system("CALL path") 将调用 .bat 文件。

#include <iostream>

#define WINDOWS_LEAN_AND_MEAN
#include <Windows.h>

#include <strsafe.h>

#define BUFSIZE 4096

int main()

    //system("CALL C:\\HFSS\\setup_vars.bat");

    //return 0;

    LPWCH chNewEnv;
    LPTSTR lpszCurrentVariable;
    DWORD dwFlags = 0;
    TCHAR szAppName[] = TEXT("C:\\windows\\system32\\cmd.exe");
    TCHAR cmdArgs[] = TEXT("C:\\HFSS\\setup_var.bat");

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    BOOL fSuccess;

    // Copy environment strings into an environment block. 
    chNewEnv = GetEnvironmentStrings();

    lpszCurrentVariable = (LPTSTR)chNewEnv;
    if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, TEXT("MySetting=A"))))
    
        printf("String copy failed\n");
        return FALSE;
    

    lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;
    if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, TEXT("MyVersion=2"))))
    
        printf("String copy failed\n");
        return FALSE;
    

    // Terminate the block with a NULL byte. 

    lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1;
    *lpszCurrentVariable = (TCHAR)0;

    // Create the child process, specifying a new environment block. 

    SecureZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);

#ifdef UNICODE
    dwFlags = CREATE_UNICODE_ENVIRONMENT;
#endif

    fSuccess = CreateProcess(szAppName, cmdArgs, NULL, NULL, TRUE, dwFlags,
        (LPVOID)chNewEnv,   // new environment block
        NULL, &si, &pi);

    if (!fSuccess)
    
        printf("CreateProcess failed (%d)\n", GetLastError());
        return FALSE;
    

    std::cout << "In new environment\n";
    WaitForSingleObject(pi.hProcess, INFINITE);

    return TRUE;

【问题讨论】:

在哪一行触发断点?您不确定的行正在尝试将两个新的环境变量添加到即将启动的进程的环境块中,并且您已经表明这就是您要执行的操作,所以我不确定您的关于他们的问题可能是。 我实际上是在尝试在批处理文件中设置环境变量。但是,当我进入批处理文件(没有创建新进程)时,%PATH% 变量具有来自 Visual Studio 的所有树数据。因此,当我尝试将新路径附加到 %PATH% 变量时,它包含冗余和多余信息。另外,我发现崩溃是由于使用“\”而不是“\\” 您的编辑没有解决我提出的所有问题。 如果您只需要系统环境变量,您可以调用CreateEnvironmentBlockhToken 作为NULLbInherit 作为FALSE 【参考方案1】:

一些问题:

    您需要将/C 选项传递给cmd.exe 以使其执行.bat 文件。 CreateProcess 的第二个参数必须是可修改的字符串。不是字面意思。 您需要对文字中的反斜杠字符进行转义。 lpszCurrentVariable 指向GetEnvironmentStrings 返回的缓冲区。您不能修改该缓冲区。您需要分配一个足够长的新缓冲区并将环境复制到其中。然后添加您的修改。 环境块是双空终止的。标准字符串函数对双空终止字符串没有用处。 使用像StringCchCopy 这样的函数而不是C 运行时函数只会令人困惑。不要将 MSDN 示例代码视为风格的典范。 C 字符串是可以使用的绑定。但是你使用 C++,所以使用 std::wstring 和其他标准库类和函数。 您需要在导入Windows.h之前定义WINDOWS_LEAN_AND_MEAN。 对于 C++,int main(void) 不正确。没有参数mainint main()

以下代码向您展示了如何做到这一点:

#include <cstring>
#include <string>
#include <iostream>

#define WINDOWS_LEAN_AND_MEAN
#include <Windows.h>

std::wstring GetEnvString()

    wchar_t* env = GetEnvironmentStrings();
    if (!env)
        abort();
    const wchar_t* var = env;
    size_t totallen = 0;
    size_t len;
    while ((len = wcslen(var)) > 0)
    
        totallen += len + 1;
        var += len + 1;
    
    std::wstring result(env, totallen);
    FreeEnvironmentStrings(env);
    return result;


int main()

    std::wstring env = GetEnvString();
    env += L"myvar=boo";
    env.push_back('\0'); // somewhat awkward way to embed a null-terminator

    STARTUPINFO si =  sizeof(STARTUPINFO) ;
    PROCESS_INFORMATION pi;

    wchar_t cmdline[] = L"cmd.exe /C C:\\Desktop\\MyBatFile.bat";

    if (!CreateProcess(NULL, cmdline, NULL, NULL, false, CREATE_UNICODE_ENVIRONMENT,
        (LPVOID)env.c_str(), NULL, &si, &pi))
    
        std::cout << GetLastError();
        abort();
    

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

【讨论】:

感谢大卫的所有帮助,但是,运行它仍然给我原来的问题:我的批处理文件将 %PATH% 变量读取为本地副本(来自 Visual Studio)。这是原始问题的链接:***.com/questions/25917796/… 在这里工作正常。此外,您会发现我展示的代码比您的要简单得多,并向您展示了如何避免所有可怕的 C 样板。 无论如何,从我的确切程序开始。用系统上的有效文件名替换 .bat 文件名。在您的 .bat 文件中放置列出环境变量的set 命令。当你这样做时会发生什么? 我看到了 PATH 变量,最后它有 3 倍的我要添加的路径,然后是 AMD64 的 Visual Studio 路径和 Visual Studio 12.0 文件夹本身。 所以我的程序运行正常并显示修改后的环境?

以上是关于使用 CreateProcess 运行批处理文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 CreateProcess 运行带空格的 bat

CreateProcess 执行批处理文件

用于批处理脚本的 Visual Studio 2010 CreateProcess()

如何将带有空格的路径作为参数添加到 CreateProcess 批处理文件?

使用 CreateProcess 运行游戏可执行文件

cmd.exe 在使用 CreateProcess 调用后立即关闭