【问题标题】:How do I call the unicode function CreateProcessW in c++ to launch a Windows executable?如何在 c++ 中调用 unicode 函数 CreateProcessW 来启动 Windows 可执行文件?
【发布时间】:2017-10-19 14:07:24
【问题描述】:

有一个如何在堆栈交换here 上调用 CreateProcess 的示例,但是这似乎在 Windows 10 上不再受支持,您必须使用 unicode 版本的 CreateProcessW。

与 ASCI 版本类似,我正在寻找一个示例:

  1. 启动一个 EXE
  2. 等待 EXE 完成。
  3. 在可执行文件完成时正确关闭所有句柄。

【问题讨论】:

  • 我已经回答了我自己的问题,因为我花了一段时间研究如何做到这一点,并决定使用外部库 (Poco) 来代替。据我所知,没有关于如何使用 CreateProcessW 的示例,所以我在这里发布了一个。我也不想浪费我写这篇文章的时间。
  • 问题是CreateProcess 是具有W 版本的大量函数之一。如何使用宽字符串并不特定于这些。您可以单独学习并将其应用于这些函数中的任何一个,包括CreateProcess
  • 确实如此。如果您认为它不适合堆栈交换,您可以关闭此问题。老实说,我只是不想浪费时间编写代码。

标签: c++ unicode createprocess


【解决方案1】:

在这方面,Windows 10 并没有什么新鲜事。您的问题实际上是关于 Unicode 与 ANSI,以及 Visual Studio 使用 Unicode 的新默认设置。

CreateProcess是一个宏,它被定义为

#ifdef UNICODE
#define CreateProcess  CreateProcessW
#else
#define CreateProcess  CreateProcessA
#endif // !UNICODE

另外STARTUPINFOSTARTUPINFOASTARTUPINFOW 的宏

CreateProcessA 使用 ANSI 字符字符串 char*STARTUPINFOA

CreateProcessW 使用 Unicode 宽字符字符串 wchar_t*STARTUPINFOW

如果您坚持使用 ANSI(不推荐),请转到项目设置 -> 字符集并禁用 Unicode。

如果你坚持使用带有Unicode设置的ANSI版本(仍然不推荐),你需要

//Using ANSI when UNICODE is defined (not recommended):
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi;
std::string path = "c:\\windows\\notepad.exe \"c:\\test\\_text.txt\"";

//if not using C++11 or later, force a zero at the end 
//to make sure path is null-ternminated
path.push_back(0);

if(CreateProcessA(NULL, &path[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
    WaitForSingleObject(pi.hProcess, INFINITE); 
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

只要目录名称与 ANSI 兼容,上述代码就可以工作。但推荐的版本是:

//recommended:
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    std::wstring path = L"c:\\windows\\notepad.exe \"c:\\test\\_text.txt\"";

    //if not using C++11 or later, force a zero at the end 
    //to make sure path is null-ternminated
    path.push_back(0);

    if(CreateProcess(0, &path[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
}

另外,不要将常量字符串转换为字符串,如下所示:

wchar_t* arg_concat = const_cast<wchar_t*>( input.c_str() );

CreateProcess 中的第二个参数应该是wchar_t*,因为进程可能会修改命令行。

【讨论】:

  • 请注意 &amp;path[0] 在 C++11 之前不能保证为空终止。
  • @RemyLebeau 写path.push_back(0) 以确保字符串以空值结尾就足够了吗?
【解决方案2】:

这个例子已经过注释,希望能自我解释。

#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <cstdlib>
#include <string>
#include <algorithm>

class process
{
public:

    static PROCESS_INFORMATION launchProcess(std::string app, std::string arg)
    {

        // Prepare handles.
        STARTUPINFO si;
        PROCESS_INFORMATION pi; // The function returns this
        ZeroMemory( &si, sizeof(si) );
        si.cb = sizeof(si);
        ZeroMemory( &pi, sizeof(pi) );

        //Prepare CreateProcess args
        std::wstring app_w(app.length(), L' '); // Make room for characters
        std::copy(app.begin(), app.end(), app_w.begin()); // Copy string to wstring.

        std::wstring arg_w(arg.length(), L' '); // Make room for characters
        std::copy(arg.begin(), arg.end(), arg_w.begin()); // Copy string to wstring.

        std::wstring input = app_w + L" " + arg_w;
        wchar_t* arg_concat = const_cast<wchar_t*>( input.c_str() );
        const wchar_t* app_const = app_w.c_str();

        // Start the child process.
        if( !CreateProcessW(
            app_const,      // app path
            arg_concat,     // Command line (needs to include app path as first argument. args seperated by whitepace)
            NULL,           // Process handle not inheritable
            NULL,           // Thread handle not inheritable
            FALSE,          // Set handle inheritance to FALSE
            0,              // 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
        )
        {
            printf( "CreateProcess failed (%d).\n", GetLastError() );
            throw std::exception("Could not create child process");
        }
        else
        {
            // Return process handle
            return pi;
        }


    }

    static bool checkIfProcessIsActive(PROCESS_INFORMATION pi)
    {
        // Check if handle is closed
            if ( pi.hProcess == NULL )
            {
                printf( "Process handle is closed or invalid (%d).\n");
                return FALSE;
            }

        // If handle open, check if process is active
        DWORD lpExitCode = 0;
        if( GetExitCodeProcess(pi.hProcess, &lpExitCode) == 0)
        {
            printf( "Cannot return exit code (%d).\n", GetLastError() );
            throw std::exception("Cannot return exit code");
        }
        else
        {
            if (lpExitCode == STILL_ACTIVE)
            {
                return TRUE;
            }
            else
            {
                return FALSE;
            }
        }
    }

    static bool stopProcess( PROCESS_INFORMATION &pi)
    {
        // Check if handle is invalid or has allready been closed
            if ( pi.hProcess == NULL )
            {
                printf( "Process handle invalid. Possibly allready been closed (%d).\n");
                return 0;
            }

        // Terminate Process
            if( !TerminateProcess(pi.hProcess,1))
            {
                printf( "ExitProcess failed (%d).\n", GetLastError() );
                return 0;
            }

        // Wait until child process exits.
            if( WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_FAILED)
            {
                printf( "Wait for exit process failed(%d).\n", GetLastError() );
                return 0;
            }

        // Close process and thread handles.
            if( !CloseHandle( pi.hProcess ))
            {
                printf( "Cannot close process handle(%d).\n", GetLastError() );
                return 0;
            }
            else
            {
                pi.hProcess = NULL;
            }

            if( !CloseHandle( pi.hThread ))
            {
                printf( "Cannot close thread handle (%d).\n", GetLastError() );
                return 0;
            }
            else
            {
                 pi.hProcess = NULL;
            }
            return 1;
    }
};//class process
#endif //win32

【讨论】:

  • stdio.h 是已弃用的 C 兼容性标头。在 C++ 中使用 cstdio 代替。此外,您确实应该尽可能使用nullptr 类型而不是NULL。另外使用类型 truefalse 而不是为它们定义的 C 样式。
  • @tambre:最好传递 WinAPI 适当的类型。它们通常映射到您认为的那些,但不能保证。
猜你喜欢
  • 2010-09-07
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
  • 1970-01-01
  • 2011-05-07
相关资源
最近更新 更多