2013年1月20日日曜日

外部コマンドの実行とリダイレクト(Windows API)

CreateProcess関数で外部コマンドを実行できる。
CUIアプリならsystem関数を使えばよいが、GUIの場合コンソール画面が表示されてしまう。その為、CreateProcess関数で新たに作成したプロセスでコマンドを実行してもらい、終了を待機するという処理を行う。
しかし、実行したコマンドの出力結果をファイルへ書き込みたい場合、CreateProcess関数では"cmd > log.txt" のようなリダイレクトが使えない。">"がコマンドライン引数として扱われてしまうからだ。CreateProcess関数でリダイレクトを実現する為には、CreateFile関数で取得したファイルハンドルを出力先に設定する必要がある。下記にCreateFile関数とCreateProcess関数で必要な手続きを記載する。

CreateFile関数

  1. セキュリティ属性でハンドルを起動したプロセスで使用する事を許可する。
  2. 新たに起動させるプロセスに書き込みさせる為、共有モードで開く。
  3. 起動したプロセスが終了したら、ファイルハンドルを閉じる。

CreateProcess関数

  1. セキュリティ属性で作成するプロセスとスレッドでのハンドル使用を許可する。
  2. 標準出力ハンドルにファイルハンドルを渡す。
  3. CreateProcess関数を実行し、WaitForSingleObject関数で終了を待機する。
  4. コマンドの終了コードを取得する。
  5. 使い終わったハンドルを閉じる。
DWORD execute(LPCTSTR lpCommand, LPCTSTR lpRedirectFile) {
    DWORD dwExidCode = -1;

    TCHAR* pCmd;
    size_t nSize = _tcsclen(lpCommand);
    pCmd  = new TCHAR[nSize +1];
    _tcscpy_s(pCmd, nSize+1, lpCommand);

    SECURITY_ATTRIBUTES sa;
    sa.nLength              = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle       = TRUE;        

    HANDLE hFile = CreateFile(
        lpRedirectFile,
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        &sa,                    
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hFile != INVALID_HANDLE_VALUE){

        STARTUPINFO si;
        ZeroMemory(&si, sizeof(si));
        si.cb         = sizeof(si);    
        si.dwFlags    = STARTF_USESTDHANDLES;
        si.hStdOutput = hFile;

        PROCESS_INFORMATION pi;
        ZeroMemory(&pi, sizeof(pi));

        if(CreateProcess(NULL, pCmd, &sa, &sa, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)){
            WaitForSingleObject(pi.hProcess, INFINITE );
            GetExitCodeProcess(pi.hProcess, &dwExidCode); 
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
        CloseHandle(hFile);
    }

    delete[] pCmd;
    return dwExidCode;
}
※CreateProcess関数に渡すコマンド文字列はconst付きの文字列を取らないので、ヒープ上にコピーしたうえで渡す。

0 件のコメント:

コメントを投稿