【C++/Win32】外部アプリケーションを起動する

 アプリケーションから他のアプリケーションを起動する場合、最低限必要な記述は以下の通り。

開発環境Visual Studio 2022
言語C++
フレームワークWin32 API
動作確認Windows 11
Windows 10
Windows 7

#include <windows.h>


    // 起動したいアプリケーションのフルパス(あるいはカレントフォルダーからの相対パス)と、
    // 必要に応じて引数も空白区切りにて付加する。
    wchar_t commandLine[] = L"アプリケーションのパスと引数";

    STARTUPINFO si{};
    PROCESS_INFORMATION pi{};

    si.cb = sizeof(si);

    // コマンドラインの実行(成功すると0以外が戻る)
    if (CreateProcess(nullptr, commandLine, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi))
    {
        // 起動成功
        //

        // アプリケーションの終了まで待つ
        // (待たずに処理を先に進める場合、WaitForSingleObjectとGetExitCodeProcessは不要)
        WaitForSingleObject(pi.hProcess, INFINITE);

        // アプリケーションの終了コードの取得
        // (終了コードを使用しない場合は不要)
        unsigned long exitCode;
        GetExitCodeProcess(pi.hProcess, &exitCode);

        // 終了コードが負の値になる場合もあるので、signedにキャストする
        long ec = static_cast<long>(exitCode);

        // ハンドルを閉じる
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
    else
    {
        // 起動失敗
        //
    }

 CreateProcessにアプリケーション(*.exe)とその引数を設定する方法は、以下の4パターンが存在する。

1番目の引数2番目の引数
(A)アプリケーションnullptr
(B)アプリケーション引数
(C)nullptrアプリケーション
(D)nullptrアプリケーションと引数

 お勧めは、アプリケーションのみなら(A)(C)どちらでもよく、引数を指定する場合は(D)パターンである。なぜなら、起動したいアプリケーションのソースがC/C++にて記述されていると仮定した場合、argc、argvの値は以下のようになるからである。

argcargv[0]argv[1]argv[2]
(A)1アプリケーションnullptrnullptr
(B)引数の個数1番目の引数2番目の引数3番目の引数
(C)1アプリケーションnullptrnullptr
(D)引数の個数+1アプリケーション1番目の引数2番目の引数

 常識的なC/C++プログラマーなら(A)(C)(D)パターンを前提としてソースを記述しているので、(B)パターンにて呼び出すと意図した動きにならない。 さらに(B)パターンでは、argv[0]に別のアプリケーションを設定することも可能なので、(具体的な悪用方法は思いつかないが)セキュリティホールとなるかもしれない。

 他のプログラミング言語については確認していないが、CreateProcessの2番目の引数がコマンドライン指定と同じと考えると、無難なのは(C)(D)パターンである。

 その他の注意点は、以下の通り。

  • CreateProcessの2番目の引数は、const不可
  • パスに空白が含まれる場合、パスをダブルクォートで括る
  • Windowsバッチファイル(*.bat)を起動したい場合、「C:\Windows\System32\cmd.exe /C バッチファイルのパス 引数」

リファレンス

CreateProcessW function
https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw