2010. 7. 19. 23:25

WshShell.Exec 사용시 블락되는 문제 해결

WshShell.Exec 사용시 가끔 블락되는 경우가 있다.

이러한 현상의 원인은 StdOut 에 출력되는 내용이 너무 많을 경우, Exec 가 리턴되지 않고 데드락에 빠진다.

이를 해결하기 위해서는 출력을 파일로 redirection 해야 한다. 주의할 점은 실행시 에러가 발생한 경우 StdOut 이 아니라 StdErr 에 출력되기 때문에 두 경우에 대해 모두 redirection 을 지정하는 것이 좋다.

아래는 스크립트 예이다.

/** Run a command, in a separate process and retrieve its output.
 *
 * This is a safer, slower, alternative to WshShell.Exec that supports
 * retrieving the output (to stdout and stderr) only after the command
 * has completed execution.  It does not support writing to the standard
 * input of the command.  It's only redeeming quality is that it will
 * not cause deadlocks due to the blocking behavior of attempting to read
 * from StdOut/StdErr.
 *
 * @param cmd The name/path of the command to run
 * @param winstyle The window style (see WshShell.Run) of the command, or null
 * @return An object with an exitcode property set to the exit code of the
 * command, an output property set to the string of text written by the
 * command to stdout, and an errors property with the string of text written
 * by the command to stderr.
 */

function run(cmd) {
    var tmpdir = FSO.GetSpecialFolder(2 /* TemporaryFolder */);
    if (!/(\\|\/)$/.test(tmpdir))
        tmpdir += "\\";

    var outfile = tmpdir + FSO.GetTempName();
    var errfile = tmpdir + FSO.GetTempName();

    // Note:  See KB278411 for this recipe
    // Note2:  See cmd.exe /? for interesting quoting behavior...
    var runcmd = '%comspec% /c "
' + cmd + ' > "' + outfile + '" 2> "' + errfile + '""';
    var wshexec = WShell.Exec(runcmd);

    // Write stuff to the standard input of the command (through cmd.exe)
    // Note:  This will block until the program exits if significant amounts
    // of information are written and not read.  But no deadlock will occur.
    // Note2:  This will error if the program has exited
    try {
        wshexec.StdIn.Write("
stuff\n");
    } catch (ex) {
        WScript.Echo("
Unable to write to program.");
    }

    // Do stuff, or write more stuff while cmd executes, or wait...
    while (wshexec.Status == 0)
        WScript.Sleep(100);

    exitcode = wshexec.ExitCode;

    var output = "
";
    try {
        var outfs = FSO.OpenTextFile(outfile, 1 /* ForReading */);
        output = outfs.ReadAll();
        outfs.Close();
        FSO.DeleteFile(outfile);
    } catch (ex) { }

    var errors = "
";
    try {
        var errfs = FSO.OpenTextFile(errfile, 1 /* ForReading */);
        errors = errfs.ReadAll();
        errfs.Close();
        FSO.DeleteFile(errfile);
    } catch (ex) { }

    return { exitcode: exitcode, output: output, errors: errors };
}

result = run("
dir");
WScript.Echo("
Exit Code: " + result.exitcode);
WScript.Echo("
Output:\n" + result.output);
WScript.Echo("
Error Output:\n" + result.errors);