2008/02/07(木)Win32 コンソール向きパイプ通信モジュール

はてブ数 2008/02/07 01:33 プログラミング::HSP3つーさ

何で今更こんなモノ、とか思わないでもない。
というか、誰か似たようなモノ作ってるんじゃないのかなぁ、
という感は否めないんだけど(笑
しかも、何に使うんだコレ(笑x2

今回は思ってたより時間かかってしまったなぁ。
予想+1時間くらい。コード量も+100行くらい。

なんで、pipe2なのかといえば、hspextのpipeの代替だから。
特にpipeputは呼び出しただけでシステムエラーに^^;

このモジュールはスクリプトに組み込んで自由に使用できます。

/* pipe2.hsp */
//
// Win32 コンソール向きパイプ通信モジュール
//      月影とも 2008. 2. 7 v0.02
// 
//      パイプを使ったリダイレクトで
//      標準入力(STDIN), 標準出力(STDOUT), 標準エラー出力(STDERR)
//      を操作できるモジュール。以下の命令を定義してます。
// 
// #deffunc pipe2exec str cmdline
//      子プロセスをパイプ付き実行。
//        cmdline : 実行するコマンドライン。
//        statの値 : 成功時 procid (0以上), 失敗時 -1
//
// #deffunc pipe2get int procid, var buf, int size, int offset
// #deffunc pipe2err int procid, var buf, int size, int offset
//      標準出力/標準エラー出力の(文字列/バイナリ)取得。
//        procid : pipe2exec の戻り値
//        buf : 内容取得先のバッファ
//        size : 取得する最大サイズ
//        offset : バッファの書き込み開始位置
//        statの値 : 取得したバイト数
//        備考 : size 以降を省略した場合、必要なサイズを勝手にbufに確保する(内容はクリアされる)。
//          取得内容が文字列とわかっている場合は便利。ただしoffsetの指定は無効。
//        
// #deffunc pipe2write int procid, var buf, int size, int offset
//      標準入力へバイナリ書き込み。
//        procid : pipe2exec の戻り値
//        buf : 書き込み元のデータが入っているバッファ
//        size : 書き込むバイト数
//        offset : 書き込み元データの開始位置
//        statの値 : 実際に書き込めたサイズ
//
// #deffunc pipe2put int procid, str string
//      標準入力へ文字列書き込み。
//        procid : pipe2exec の戻り値
//        string : 書き込む内容
//        statの値 : 実際に書き込めたサイズ
//        
// #deffunc pipe2check int procid
//      パイプの状況をチェック。
//        procid : pipe2exec の戻り値
//        statの値 : 以下のビットの組み合わせ。
//            if stat & 1 : プログラムは実行中
//            if stat & 2 : STDOUTにデータあり(pipe2getで取得)
//            if stat & 4 : STDERRにデータあり(pipe2errで取得)
//
// #deffunc pipe2term int procid
//      子プロセスの強制終了+パイプを閉じる
//        procid : pipe2exec の戻り値
//        statの値 : 常に 0
//

#module mod_execconsole m_hStdOut, m_hStdErr, m_hStdIn, m_hProcess
#uselib "kernel32.dll"
#func ReadFile "ReadFile" int,int,int,int,nullptr
#func WriteFile "WriteFile" int,int,int,int,nullptr
#cfunc GetCurrentProcess "GetCurrentProcess"
#func CreatePipe "CreatePipe" int,int,int,int
#func PeekNamedPipe "PeekNamedPipe" int,nullptr,nullptr,nullptr,int,nullptr
#func DuplicateHandle "DuplicateHandle" int,int,int,int, int,int,int
#func CloseHandle "CloseHandle" int
#cfunc GetLastError "GetLastError"

#func CreateProcess "CreateProcessA" int,int,int,int,int,int,int,int,int,int
#cfunc WaitForSingleObject "WaitForSingleObject" int,int
#func TerminateProcess "TerminateProcess" int,int

#defcfunc local _GetModIndex var p1, local hspctx, local vptr
    mref hspctx, 68
    dupptr vptr, hspctx.207, 8, 4
    return vptr.1

#modinit
    return _GetModIndex@mod_execconsole(thismod)

#modfunc local _ExecProcessWithPipe str _cmdline
    ret = 0
    
    thisProcess = GetCurrentProcess()
    security_attributes = 12, 0, 1
    
    // for STDOUT
    CreatePipe varptr(hTemp), varptr(hStdout), varptr(security_attributes), 0
    DuplicateHandle thisProcess, hTemp, thisProcess, varptr(m_hStdOut), 0, 0, 2
    CloseHandle hTemp

    // for STDERR
    CreatePipe varptr(hTemp), varptr(hStderr), varptr(security_attributes), 0
    DuplicateHandle thisProcess, hTemp, thisProcess, varptr(m_hStdErr), 0, 0, 2
    CloseHandle hTemp

    // for STDIN
    CreatePipe varptr(hStdin), varptr(hTemp), varptr(security_attributes), 0
    DuplicateHandle thisProcess, hTemp, thisProcess, varptr(m_hStdIn), 0, 0, 2
    CloseHandle hTemp

    // CreateProcess
    dim process_information, 4
    dim startupinfo, 17
    startupinfo.0 = 68
    startupinfo.11 = 0x101
    startupinfo.14 = hStdin, hStdout, hStderr
    cmdline = _cmdline
    CreateProcess 0, varptr(cmdline), 0, 0, 1, 0, 0, 0, varptr(startupinfo), varptr(process_information)
    if stat = 0 : ret = -1 : else : m_hProcess = process_information.0 : CloseHandle process_information.1

    // CloseHandle
    CloseHandle hStdOut
    CloseHandle hStdErr
    CloseHandle hStdIn
    return ret

#modfunc local _WriteStdin var buf, int size, int offset, local written
    written = 0
    WriteFile m_hStdIn, varptr(buf)+offset, size, varptr(written)
    if stat=0 : return 0
    return written

#modfunc local _readPipe int hPipe, var buf, int size, int offset, local read, local readable
    read = 0 : readable = 0
    PeekNamedPipe hPipe, varptr(readable) : if readable = 0 : return 0
    if size : sz = size : else : sz = readable : sdim buf, readable+1
    ReadFile hPipe, varptr(buf)+offset, sz, varptr(read)
    if stat=0 : return 0
    return read

#modfunc local _ReadStdout var buf, int size, int offset
    _readPipe@mod_execconsole thismod, m_hStdout, buf, size, offset : return

#modfunc local _ReadStderr var buf, int size, int offset, local read
    _readPipe@mod_execconsole thismod, m_hStderr, buf, size, offset : return

#modfunc local _GetProcessStatus local status, local readable
    status = 0
    readable = 0 : PeekNamedPipe m_hStderr, varptr(readable) : if readable : status += 4
    readable = 0 : PeekNamedPipe m_hStdout, varptr(readable) : if readable : status += 2
    if WaitForSingleObject(m_hProcess, 0) : status += 1
    return status

#modfunc local _Dispose
    CloseHandle m_hStdout
    CloseHandle m_hStderr
    CloseHandle m_hStdin
    if WaitForSingleObject(m_hProcess, 0) : TerminateProcess m_hProcess
    return 0

#modterm
    _Dispose@mod_execconsole thismod
    return

#deffunc pipe2exec str _cmdline
    newmod children, mod_execconsole : idx = stat
    _ExecProcessWithPipe@mod_execconsole children.idx, _cmdline
    if stat != 0 : delmod children.idx : return -1
    return idx

#deffunc pipe2err int procid, var buf, int size, int offset
    _ReadStderr@mod_execconsole children.procid, buf, size, offset
    return

#deffunc pipe2get int procid, var buf, int size, int offset
    _ReadStdout@mod_execconsole children.procid, buf, size, offset
    return

#deffunc pipe2write int procid, var buf, int size, int offset
    _WriteStdin@mod_execconsole children.procid, buf, size, offset
    return

#deffunc pipe2put int procid, str string
    buf = string
    _WriteStdin@mod_execconsole children.procid, buf, strlen(buf)
    return

#deffunc pipe2check int procid
    _GetProcessStatus@mod_execconsole children.procid
    return

#deffunc pipe2term int procid
    _Dispose@mod_execconsole children.procid
    return

#global

//// モジュ終わり

#if 0 // サンプル

#define CMDLINE "cmd.exe"

    pipe2exec CMDLINE : ChildID = stat
    if stat<0: dialog "実行に失敗:\\n"+CMDLINE : end
    wait 10
    pipe2get ChildID, buf : if stat : mes buf
    pipe2put ChildID, "dir /w \\\\\\n"
    pipe2put ChildID, "exit\\n"

    wait 10

    // 子が終了後でも受け取るモノは受け取れます。
    pipe2get ChildID, buf : if stat : mes buf
    pipe2check ChildID : title ""+stat

#endif

#if 0 // サンプル その2

#define CMDLINE "netstat"
    pipe2exec CMDLINE : ChildID = stat
    if stat<0: dialog "実行に失敗:\\n"+CMDLINE : end
    repeat
        pipe2check ChildId : c = stat : title ""+c
        if c & 4 : pipe2err ChildID, buf : if stat : mes buf
        if c & 2 : pipe2get ChildID, buf : if stat : mes buf
        if c = 0 : break
        wait 10
    loop
    mes "終了しました。"
    
#endif