//popen 进程管道 import process; import fsys.wow64; namespace process class popen{ ctor( exefile,parameters="", startInfo,... ){{ if( type(startInfo) == type.string ){ parameters = {parameters;startInfo;...}; startInfo = null; } var sa = ..table.assign(..process.STARTUPINFO(),startInfo); //startInfo 参数指定{ showWindow = 1/*_SW_NORMAL*/ } 可显示控制台以外的窗口 sa.flags = (sa.flags:0) | 0x100/*_STARTF_USESTDHANDLES*/ | 0x1/*_STARTF_USESHOWWINDOW*/; sa.createNoWindow = true;//不显示控制台窗口 var saPipe = { INT nLength; pointer lpSecurityDescriptor; bool bInheritHandle = true; }; var ok,outPipeRead,outPipeWrite = CreatePipe(,,saPipe,0) var ok,errPipeRead,errPipeWrite = CreatePipe(,,saPipe,0) var ok,inPipeRead,inPipeWrite = CreatePipe(,,saPipe,0) if ( ! SetHandleInformation(outPipeRead, 0x00000001/*_HANDLE_FLAG_INHERIT*/, 0) ) error( "Stdout SetHandleInformation",2); if ( ! SetHandleInformation(inPipeWrite, 0x00000001/*_HANDLE_FLAG_INHERIT*/, 0) ) error( "Stdin SetHandleInformation",2); // sa.flags 需要指定 _STARTF_USESTDHANDLES 下面的参数才起作用 sa.stdInput = ..process.dup( inPipeRead ) sa.stdOutput = ..process.dup( outPipeWrite ) sa.stdError = ..process.dup( errPipeWrite ) sa.inheritHandles = true; var prcs = ..process( exefile,parameters, sa); if(!prcs) return null,..lasterr(); this.process = prcs; // 子继承已经继承了此句柄,我们可以关掉了,不然子进程结束会因为等待这些句柄关闭而卡住 ..raw.closehandle(sa.stdInput) ..raw.closehandle(sa.stdOutput) ..raw.closehandle(sa.stdError) this.outPipeRead = outPipeRead; this.errPipeRead = errPipeRead; this.inPipeWrite = inPipeWrite; this.stdIn = ..io.file(inPipeWrite,"w+"); this.stdOut = ..io.file(outPipeRead,"r"); this.stdErr = ..io.file(errPipeRead,"r"); this.codepage = ::Kernel32.GetACP() = 65001? 65001 : 0; ..table.gc(this,"close"); }}; textMode = function(o,i){ this.stdOut.mode( o ? 0x4000/*_O_TEXT*/ : 0x8000/*_O_BINARY*/); this.stdErr.mode( o ? 0x4000/*_O_TEXT*/ : 0x8000/*_O_BINARY*/); this.stdIn.mode( i ? 0x4000/*_O_TEXT*/ : 0x8000/*_O_BINARY*/); }; writeUtf8 = function(...){ if( this.stdIn ){ var ok,e,en = this.stdIn.write(...); if(ok) return this.stdIn.flush(); return ok,e,en; } else { return null,"管道已关闭",6 } }; write = function(...){ var ok,e,en; if( this.stdIn ){ if(this.codepage == 65001){ ok,e,en = this.stdIn.write(...); } else{ for i,arg in ..table.eachArgs({...}){ if( type(arg) == type.string ){ arg = ..string.fromto( arg,65001,this.codepage); } ok,e,en = this.stdIn.write(arg); } } if(ok) return this.stdIn.flush(); return ok,e,en; } else { return null,"管道已关闭",6 } }; writeClose = function(...){ var ok,e,en = true; if(...) ok,e,en = this.write(...); if(this.stdIn){ this.stdIn.close(); this.stdIn = null; } return ok,e,en; }; print = function(...){ if( this.stdIn ){ args = {...}; for(k,v in ..table.eachArgs(args)){ args[k] = ..string.fromto( tostring(v),65001,this.codepage); } this.peek(0); var ok,e,en = this.stdIn.write(..string.join(args,' '),'\n'); if(ok) return this.stdIn.flush(); return ok,e,en; } else { return null,"管道已关闭",6 } }; printf = function(s,...){ if( this.stdIn ){ if(...) s = ..string.fromto( ..string.format(s,...),65001,this.codepage);; this.peek(0); var ok,e,en = this.stdIn.write(s,'\n'); if(ok) return this.stdIn.flush(); return ok,e,en; } else { return null,"管道已关闭",6 } }; readUtf8 = function(...){ if(!this.stdOut) return; if(...==-1) this.writeClose(); return this.stdOut.read(...); }; read = function(...){ if(!this.stdOut){ return; } if(...==-1) this.writeClose(); var out = { this.stdOut.read(...) } if(this.codepage!==65001){ for i,v in ..table.eachArgs(out){ out[i] = ..string.fromto(v,( (v[2]===0) && ( #v>=2) )? 1200 : this.codepage,65001); } } else { var str = out[1]; if(str){ if( str[1] == 0xEF){ out[1] = ..string.removeBom(str); } elseif( (str[2]===0) && ( #str>=2) ){ out[1] = ..string.fromto(str,1200,65001); this.readUtf8 = this.read; } } } return ..table.unpackArgs(out); }; lines = function(pattern,usePeek){ var read = this.read; if(usePeek){ read = function(){ return this.peekTo('\n'); } } if(!pattern) return function(){ var l = read() if(l) return ..string.trimright(l); } return function() { while( var l = read() ) { l = ..string.trimright(l); if(..string.find(l,pattern)){ return ..string.match(l,pattern); } } } }; readErr = function(...){ if(!this.stdErr){ return; } var out = { this.stdErr.read(...) } for i,v in ..table.eachArgs(out){ out[i] = ..string.fromto(v,( (v[2]===0) && ( #v>=2) ) ? 1200 : this.codepage,65001); } return ..table.unpackArgs(out); }; peek = function(delay){ if(!this.process) return; if( delay === null ) delay = 1000; if( this.process.waitOne(delay) ){ var o,e = this.read(-1),this.readErr(-1); if(#o || #e) return o,e; return; } var out,err; var ret,read,total,leftmsg = PeekNamedPipe(this.outPipeRead, ,0,0,0,0) if(ret && total) out = this.read(total); ret,read,total,leftmsg = PeekNamedPipe(this.errPipeRead, ,0,0,0,0) if(ret && total && this.stdErr) err = this.readErr(total); return out,err; }; peekErr = function(delay){ if(!this.process) return; if( delay === null ) delay = 1000; if( this.process.waitOne(delay) ){ var e = this.readErr(-1) return #e ? e : null; } var ret,read,total,leftmsg = PeekNamedPipe(this.errPipeRead, ,0,0,0,0) if(ret && total && this.stdErr) return this.readErr(total); }; peekTo = function(tag,proc,timeout){ var str,err = this.$peekToBuffer:"",""; this.$peekToBuffer = null; var pos = ..string.indexOf(str,tag) if(pos){ var ret = ..string.slice(str,1,pos-1); if(#ret){ var remain = ..string.slice(str,pos+#tag+1); if(#remain) this.$peekToBuffer = remain; if(!#err) err = null; return ret,err; } } var s,e; var time = 0; var endWith = ..string.endsWith; var closed; if(!tag) tag = ">"; while(this.process){ closed = this.process.waitOne(1000); s,e = this.peek(0); if( s || e ){ if(s){ str = ..string.concat(str,s ) } if(e){ err = ..string.concat(err,e ) } if( proc) { proc(s,e); } } elseif(timeout ? timeout > 0 ) { time = time + 1000; if( time > timeout ){ if(!#err) err = null; return str,err; } } if(closed || ..win[["closed"]] ){ return null,..string.concat(str,err); } var pos = ..string.indexOf(str,tag) if(pos){ var ret = ..string.slice(str,1,pos-1); var remain = ..string.slice(str,pos+#tag+1); if(#remain) this.$peekToBuffer = remain; if(!#err) err = null; return ret,err; } } return null,..string.concat(str,err); }; each = function(interval,timeout){ if( interval === null ) interval=1; var tick = 0; return function(){ var a,o,e; do{ o,e = this.peek(0); if( o || e ) return ..string.concat(o,e):"",o,e; if(timeout){ tick = tick + interval; if( tick > timeout ) return; } }while(this.process && !..thread.waitOne(this.process.handle,interval)); if(!this.process) return; if( this.stdOut && this.stdErr ){ o,e = this.read(-1),this.readErr(-1); a = ..string.concat(o,e); if(#a) return a,o,e; } } }; stopLogResponse = function(){ if(this.echoEditor && this.echoTimerId){ this.echoEditor.clearInterval(this.echoTimerId); this.echoTimerId = null; this.echoEditor = null; } }; logResponse = function(echo,interval){ this.stopLogResponse(); if(echo[["hwnd"]] && echo.setInterval && echo.log){ this.echoEditor = echo; this.echoTimerId = this.echoEditor.setInterval( function(){ var s = ..string.concat(this.peek(0)); if(s) echo.log( ..string.crlf( s ) ); if( !( this.process && this.process.stillActive() ) ) { this.echoTimerId = null; if(this.onResponseEnd) this.onResponseEnd(); return 0; } },interval : 500 ); return; } if(!echo){ if( this.logger ){ if( this.logger.echo) echo = function(all) this.logger.echo(all); elseif( this.logger.log) echo = function(all) this.logger.log(all); elseif( this.logger.write ) echo = function(all) this.logger.write(all); } else { if( ..console && ! ..console.getWindow() ) ..console.open(); echo = function(all,out,err){ if(out) ..io.stdout.write( out ); if(err) ..io.stderr.write( err ); } } } for( all,out,err in this.each(interval) ){ echo( all,out,err );//回显 } if(this.onResponseEnd) this.onResponseEnd(); }; expect = function(p,timeout,max){ var s = ""; var match = ..string.match; if( max === null ) max = 1024; if(!p) error("请指定参数 @1",2); for( all,out,err in this.each(,timeout) ){ if( out ){ s = s ++ out; if( match( s,p ) ) return match( s,p ); if( #s > max ) s = ..string.right(s,max/2,true); } elseif(err){ return null,err; } } }; expect2 = function(p,timeout,max){ var s = ""; var match = ..string.match; if( max === null ) max = 1024; if(!p) error("请指定参数 @1",2); for( all in this.each(,timeout) ){ if( all ){ s = s ++ all; if( match( s,p ) ) return match( s,p ); if( #s > max ) s = ..string.right(s,max/2,true); } } }; readAll = function(pattern,close){ this.writeClose(); var ret,retErr = {},{}; for( all,out,err in this.each() ){ ..table.push(ret,all); if(#err) ..table.push(retErr,err); } ret = ..string.join(ret); ..thread.delay(10); var exitCode = this.process.getExitCode(); if(close || close===null ) this.close(); if(ret) { if(pattern) return ..string.match(ret,pattern); ret = ..string.trim(ret); } if(#retErr){ retErr = ..string.join(retErr); } else { retErr = null; } return ret,retErr,exitCode; }; json = function(){ var ret,errMsg = this.expect("[\{\[]\N*[\]\}][\r]*\n"); if(ret) return ..JSON.tryParse(ret); return null,errMsg; }; jsonAll = function(close){ var ret,retErr = this.readAll(,close) if(ret &&!retErr) return ..JSON.tryParse(ret); return null,retErr; }; ndjsonAll = function(close){ var ret,retErr = this.readAll(,close) if(ret &&!retErr) return ..table.filter(..JSON.ndParse(ret),lambda(v)type(v)=="table"); return null,retErr; }; jsonWrite = function(obj){ return this.write(..JSON.stringify(obj),'\n') }; wait = function(timeout){ if(! ( this.process ? this.process.handle ) ) return true; var ret = ..thread.wait(this.process.handle,timeout) sleep(10); if( ret ){ return true,this.read(-1),this.readErr(-1),(this.process.getExitCode()); } }; waitOne = function(timeout){ if(! ( this.process ? this.process.handle ) ) return true; var ret = ..thread.waitOne(this.process.handle,timeout) sleep(10); if(ret){ if(this.echoTimerId) { while(this.echoTimerId){ ..thread.delay(10); } return ret; } return true,this.read(-1),this.readErr(-1),(this.process.getExitCode()); } }; getExitCode = function(){ if(this.process) return this.process.getExitCode(); }; stillActive = function(){ if(this.process) return this.process.stillActive(); }; assignToJobObject = function(hJob){ if( this.process ){ return this.process.assignToJobObject(hJob); } }; killOnExit = function(){ return this.assignToJobObject(..process.job.limitKill); }; terminate = function(){ this.process.terminate(); this.close(); }; ctrlEvent = function(event){ this.stopLogResponse(); return this.process.ctrlEvent(event); }; sendCtrlC = lambda () this.ctrlEvent(0); stillActive = function(){ if(this.process) return this.process.stillActive(); }; suspend = function(){ if(this.process) return this.process.suspend(); }; resume = function(){ if(this.process) return this.process.resume(); }; getMainWindow = function(cls,tilte){ if(this.process) return this.process.getMainWindow(cls,tilte); }; waitMainWindow = function(waitCls,waitHwnd){ if(this.process) return this.process.waitMainWindow(waitCls,waitHwnd); }; closeMainWindow = function(){ if(this.process) return this.process.closeMainWindow(); }; close = function(){ this.stopLogResponse(); if( this.process ){ if( this.beforeClose ) this.beforeClose(); if(this.stdIn)..raw._release(this.stdIn); if(this.stdOut)..raw._release(this.stdOut); if(this.stdErr)..raw._release(this.stdErr); this.stdIn = null; this.stdOut = null; this.stdErr = null; this.process.free(); this.process = null; } }; @_meta; } namespace popen{ _meta = {_topointer = lambda() owner.process} is = lambda(v) v@ ==_meta; CreatePipe = ::Kernel32.api( "CreatePipe", "bool(pointer& hRead,pointer& hWrite,struct attributes,INT size)"); SetHandleInformation = ::Kernel32.api( "SetHandleInformation", "bool(pointer object,INT mask,INT flags)"); PeekNamedPipe = ::Kernel32.api( "PeekNamedPipe", "bool(pointer pipe,pointer buf,INT size,INT &read,INT & total,INT & leftMsg)"); win = lambda(exefile,param,... ) ..process.popen(exefile,...?{param,...}:param, { showWindow = 1/*_SW_NORMAL*/ }); cmd = function(cmdline,...){ var m; cmdline = ..string.trim(cmdline); cmdline,m = ..string.replace(cmdline,"[\r\n]+","&"); if(..io.exist(cmdline)){ return ..process.popen(cmdline,...) } return ( type(...)==type.string ? ..process.popen( "cmd.exe","/c",cmdline,...) : ..process.popen( "cmd.exe"," /c " + cmdline ) ) } cmd64 = function(...){ return ..fsys.wow64.disableRedirection(lambda(...) cmd(...),... ) } wow64 = function(...){ return ..fsys.wow64.disableRedirection(lambda(...) ..process.popen(...),...) } bind64 = function(fileName,codepage){ return function(cmdline,...){ var prcs,err = ..fsys.wow64.disableRedirection(lambda(cmdline,...) ..process.popen(fileName,cmdline,...),cmdline,...); if(!prcs) return null,err; prcs.codepage = codepage : 0; return prcs; } } ps = bind64("PowerShell"); ps1 = lambda(path,...) ps(`-NoLogo`,`-ExecutionPolicy`,`bypass`,`-File`,..io.fullpath(path),...); wsl = bind64("wsl",65001); detached = function(exefile,...){ return ..process.popen(exefile,type.isString(...)?{...}:...,{ creationFlag = 8/*_DETACHED_PROCESS*/; }) } } /**details(相关范例) [范例程序/进程/管道](doc://example/Process/Pipe/QuickStart.aardio) [范例程序/调用其他语言/批处理与命令行](doc://example/Languages/Bat/popen.aardio) [范例程序/文件操作/文件权限](doc://example/File/Permissions/cacls.html) end details**/ /**intellisense() process.popen = 创建进程管道并调用目标程序,\n可隐藏子进程的控制台黑窗口。\n\n成功返回可读写的进程管道对象,\n失败返回 3 个值:null,错误信息,错误代码 process.popen( = 创建进程管道并调用目标程序,\n可隐藏子进程的控制台黑窗口。\n\n成功返回可读写的进程管道对象,\n失败返回 3 个值:null,错误信息,错误代码。\n\n个别系统 64 位程序,需要改用 process.popen.wow64 以避免目录重定向。 process.popen(.( ,系统命令行) = 创建可读写的进程管道对象。\n如果省略第一个参数,并仅指定命令行,则作为系统命令行启动运行 process.popen(.(执行文件,命令行参数,更多命令行参数,->->->) = 命令行参数可以是一个数组、一个或多个字符串参数,\n\n数组或多个命令行参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号,\n命令参数最大长度为 32765 个字符 process.popen(.(执行文件,命令行参数,startupInfo) = 命令行参数可以是字符串或由多个字符串组成的数组,\n数组参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号,\n命令参数最大长度为 32765 个字符。\nSTARTUPINFO参数为 process.STARTUPINFO 结构体,可选参数\n如果该参数是普通 table 对象.将自动转换为 STARTUPINFO 结构体。\n//@startupInfo 指定{ showWindow = 1/*_SW_NORMAL*/ } 可显示控制台以外的窗口 process.popen.win( = 创建进程管道调用目标程序。\n隐藏控制台窗口,但允许显示其他图形界面。\n\n成功返回可读写的进程管道对象(process.popen 对象),\n失败返回 3 个值:null,错误信息,错误代码。 process.popen.win(.(执行文件,命令行参数,->->->) = 命令行参数可以是一个数组、一个或多个字符串参数,\n\n数组或多个命令行参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号,\n命令参数最大长度为 32765 个字符。 process.popen.cmd( = 创建管道按命令行方式调用控制台类型应用程序,\n可隐藏子进程的控制台黑窗口,并支持管道读写。 process.popen.cmd(.(命令行参数,->->->) = 如果第一个参数包含换行,则替换为批处理命令分隔符&&,\n如果传入多个命令行参数,或者命令行参数是个数组,则合并为单个命令行,\n合并时不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号 process.popen.cmd64( = 创建管道按命令行方式调用控制台类型应用程序,\n可隐藏子进程的控制台黑窗口,并支持管道读写。\n\n此函数禁用64位系统文件与注册表重定向,\n在64位系统会返回64位进程,兼容32位系统。\n\n如果不需要读写管道,应当改用 process.wow64 process.popen.cmd64(.(命令行参数,->->->) = 如果第一个命令行参数包含换行,则替换为批处理命令分隔符&&,\n如果传入多个命令行参数,或者命令行参数是个数组,则合并为单个命令行,\n合并时不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号 process.popen.wow64(.(执行文件,命令行参数,->->->) = 创建子进程,返回可读写进程管道,\n此函数禁用64位系统文件与注册表重定向,\n在64位系统会返回64位进程,兼容32位系统,\n命令行参数可以是一个数组、一个或多个字符串参数,\n\n数组或多个命令行参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号 process.popen.ps(.(命令行参数,->->->) = 创建进程执行 PowerShell 命令,返回可读写进程管道,\n命令行参数可以是一个数组、一个或多个字符串参数,\n数组或多个命令行参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号 process.popen.ps(.(`-Command`,`&{}`) = @.ps(`-Command`,`&{__/*请输入要执行的 PowerShell 语句,\n返回 process.popen 进程管道对象。 */}`) process.popen.ps1((.(ps1脚本路径,->->->) = 创建进程执行 PowerShell *.ps1 脚本,返回可读写进程管道,\n可添加一个或多个其他命令行参数,\n所有命令行参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号 process.popen.wsl(.(命令行参数,->->->) = 创建进程执行 WSL 命令,并支持管道读写,\n命令行参数可以是一个数组、一个或多个字符串参数,\n数组或多个命令行参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号 process.popen.bind64( = 禁用 64 位重定向绑定并返回执行命令行的函数,\n该函数的命令行参数可以是一个数组、一个或多个字符串参数,\n数组或多个命令行参数调用 process.joinArguments 合并,\n不在双引号内、且包含空白或需要转义的参数转义处理后首尾添加双引号 process.popen.bind64(fileName,codepage) = fileName 指定要执行的命令文件名,\n可选用 codepage 参数指定代码页,默认为 0(ANSI)。 process.popen.is(__) = 传入参数是否 process.popen 对象 process.popen() = !process_popen. process.popen.cmd() = !process_popen. process.popen.cmd64() = !process_popen. process.popen.wow64() = !process_popen. process.popen.ps() = !process_popen. process.popen.ps1() = !process_popen. process.popen.detached() = !process_popen. process.popen.wsl() = !process_popen. !process_popen.codepage = 进程输入输出使用的代码页,默认为 0(系统默认 ANSI 编码)。\nUTF-8 编码的程序必须指定为 65001 ,否则可能会出现乱码。\n\n传统命令行程序通常使用 ANSI 编码。\n而一些跨平台的语言编写的命令行程序通常会使用 UTF-8 编码。 !process_popen.textMode(.(output,input) = 参数 @output 指定进程输出是否使用文本模式\n参数 @input 指定进程输入是否使用文本模式\n输入输出默认都是二进制模式\n\n文本模式下'\x1A'(CTRL+Z),'\0'表示终止文本,\n并且读入时自动转换回车换行为换行,\n写出时自动转换换行符为回车换行 !process_popen.reaUtf8(__) = 所有参数用法与 read 函数相同。\n此函数直接读入,不作任何编码转换。 !process_popen.read(0) = 检测是否读取到文件尾 !process_popen.read(__) = 正数参数表示从当前位置向后读取n个字节,\n支持多参数。 !process_popen.read() = 从当前位置,向后读取下一行\n默认为二进制模式,行尾可能有回车符号,\n可使用 textMode 函数修改 !process_popen.read("%s") = 从当前位置,向后读取下一行\n支持多参数 !process_popen.read("%d") = 从当前位置,向后读取下一个数值\n支持多参数 !process_popen.read(-1) = 向后读取到文件尾部\n负数表示从文件尾部倒计数位置\n如果第一个参数为-1,则自动调用 writeClose 关闭输出流,\n支持多参数 !process_popen.readErr(-1) = 读取所有错误输出 !process_popen.readAll( = 关闭进程输入,并读取所有输出。\n返回值 1 包含标准输出与错误输出。\n不阻塞当前线程窗口消息 !process_popen.readAll(.(匹配模式串,是否关闭) = 读取所有输出,所有参数可选。\n返回值 1 包含标准输出与错误输出。\n\n不指定参数 @1 时会清除返回值 1 文本首尾的空白字符,\n返回值 2 为错误输出(无错为 null),返回值 3 为进程退出代码。\n如果有输出文本且指定了参数@1,\n则按指定的模式串返回匹配的输出文本,此时无返回值 2、返回值 3。\n\n参数 @2 指定是否关闭进程对象,默认为 true !process_popen.each( 间隔时间,超时时间 ) = @for( all,out,err in ??.each() ){ io.stdout.write( out,err );__/*读取进程输出直到进程关闭,不阻塞界面线程消息*/ } !process_popen.lines(模式串,允许处理窗口消息) = @for i,k,v in ??.lines("^(\s*)(\S.*?)[\s.]*\:\s*(.*)"){ __/*按行读取进程输出,移除每行尾部的空白字符,\n指定模式串则返回匹配结果,否则直接返回当前读取的文本。\n如果模式匹配用圆括号创建捕获分组,则每个捕获分组对应一个返回值。\n\n以上示例模式串匹配用 : 号分隔键值对的行,\n其中 \s 匹配空白字符,大写的 \S 匹配非空白字符\n\n如果参数 @2 为 true 则读取进程输出时不会阻塞窗口消息*/ } !process_popen.beforeClose = @.beforeClose = function(){ owner.process.terminate();__/*释放管道以前触发此事件,可强制关闭进程*/ } !process_popen.logger = 指定 logResponse 的默认回显对象\n该对象必须有log或write成员函数用于输出回显\nlogger 对象用于logResponse 时将同步获取输出并等待进程结束\n\n如果不指定此属性,且已导入 console 则默认输出到控制台 !process_popen.logResponse( = 回显进程的标准输出以及错误输出 !process_popen.logResponse(.(回显函数,间隔时间) = 自动同动获取进程输出,并直到进程结束,所有参数可省略。\n\n回显函数有 3 个回调参数 all,out,err。\nall 为所有输出,out 为标准输出,err 为错误输出。\n可用 #err 判断是否出错。\n\n如果不指定回显函数,且之前定义了 logger 属性,则调用 logger 对象输出进程回显。\n未指定 logger 对象则默认回显到控制台。\n此函数不会自动打开控制台 !process_popen.logResponse(.(异步回显文本框,间隔时间) = 不会阻塞并等待进程结束,\n而是在传入文本框控件上创建定时器异步回显进程输出,\n回显文本框必须指定edit或richedit控件,间隔时间可省略。\n\n如果正在进行异步回显,则调用 waitOne 函数不会读取任何输出。\n并且 waitOne 函数会等待异步回显结束 !process_popen.stopLogResponse() = 清除异步回显文本框的定时器。\n关闭进程对象时也会自动清除此定时器 !process_popen.peek(__) = 检测标准输出管道中可读取的数据长度。\n有数据则读取数据,无数据则返回空值\n如果 stderr 中有错误信息,则第二个返回值为错误信息\n此函数不会因为等待输入而阻塞\n参数可指定等待目标进程执行的延时时间 !process_popen.peekTo( = 读取数据直到数据以指定的字符串结束 !process_popen.peekTo(.(">",回显函数,超时) = 读取数据,直到数据以 参数@1 指定的字符串结束。\n不阻塞当前线程窗口消息。\n\n返回标准输出,第二个返回值为错误信息\n可选使用参数@2指定回显函数,回显函数有两个参数,分别为标准输出,错误信息\n可选用参数@3指定无输出时的超时限制\n该函数在进程退出后也会返回 !process_popen.peekErr(__) = 检测标准错误输出管道中可读取的数据长度。\n有数据则读取错误信息,无数据则返回空值\n此函数不会因为等待输入而阻塞\n参数可指定等待目标进程执行的延时时间 !process_popen.expect( = 等待直到模式匹配标准输出成功,返回匹配结果。\n如果遇到错误输出返回null以及错误信息。\n此函数不阻塞界面线程消息 !process_popen.expect(.("模式串",超时,文本最大长度) = 超时参数是以毫秒为单位的估算值,可省略。\n参数@3可省略,默认为1024 !process_popen.expect2( = 等待直到模式匹配标准输出成功,返回匹配结果。\n注意 expect2 函数会匹配标准输出与错误输出,\n而不是像 expect 函数那样遇到错误输出会返回 null。 !process_popen.expect2(.("模式串",超时,文本最大长度) = 超时参数是以毫秒为单位的估算值,可省略。\n参数@3可省略,默认为1024 !process_popen.print(__) = 清空标准输出然后写数据\n多个参数中间添加空格,尾部添加换行\n\n成功返回 true,失败返回 null ,错误代码,错误信息。 !process_popen.printf(__) = 清空标准输出然后写数据\n调用string.format函数格式化全部参数,尾部添加换行\n\n成功返回 true,失败返回 null ,错误代码,错误信息。 !process_popen.write(__) = 写数据,支持一个或多个参数。\n成功返回 true,失败返回 null ,错误代码,错误信息。\n如果目标进程已关闭,但标准输入管道未释放仍可能返回 true。\n\n\nwaitOne 函数返回 true,或 each ,expect 等函数检测到进程退出,\n或readAll 或 read 函数参数为 -1 时\n会自动关闭目标进程标准输入管道\n并导致 write 函数返回 null 值 !process_popen.writeUtf8(__) = 写入 UTF8 编码数据,不作编码转换,支持一个或多个参数。\n成功返回 true,失败返回 null ,错误代码,错误信息。\n如果目标进程已关闭,但标准输入管道未释放仍可能返回 true。\n\n\nwaitOne 函数返回 true,或 each ,expect 等函数检测到进程退出,\n或readAll 或 read 函数参数为 -1 时\n会自动关闭目标进程标准输入管道\n并导致 write 函数返回 null 值 !process_popen.writeClose(__) = 写数据并关闭进程输入流,\n可选指定一个或多个参数\n不指定参数则直接关闭输入流\n注意:有些进程会一直等待输入流关闭再继续下一下操作 !process_popen.waitOne() = 等待进程关闭,不会阻塞当前线程窗口消息。\n可用一个毫秒值参数设定超时。\n\n超时或失败返回 null,进程已退出则返回值1为true,\n返回值2为标准输出,返回值3为错误输出,返回值4为退出代码\n\n如果被调用进程写满输出缓冲区,而调用进程没有读取,\n则被调用进程会一直等待,此函数将无法返回。\n改用 readAll 函数读取输入可避免该问题。\n\n注意如果正在调用异步 logResponse 回显到窗口对象。\n则 waitOne 函数仅返回单个值,不读取任何输出 !process_popen.wait() = 等待进程关闭,可选使用一个毫秒值参数设定超时。\n超时或失败返回 null,进程退出则返回值1为true,\n返回值2为标准输出,返回值3为错误输出,返回值4为退出代码\n\n如被调用进程写满输出缓冲区,而调用进程没有读取,\n则被调用进程会一直等待,此函数将无法返回。\n改用 readAll 函数读取输入可避免该问题。 !process_popen.stillActive() = 检查进程是否仍在运行,仍在运行返回 true !process_popen.getExitCode() = 该函数调用成功有两个返回值:进程退出代码,进程是否已退出 !process_popen.close() = 关闭当前对象,释放读写管道。\n当前对象释放时会自动调用此函数。\nreadAll,terminate 等函数会自动调用此函数\n\n注意此函数并不会关闭目标进程。 !process_popen.terminate() = 强制关闭进程并释放进程对象 !process_popen.assignToJobObject(.(process->job->limitKill) = 绑定到作业对象,成功返回 true\n作业对象示例请参考标准库 process.job.limitKill 的源码。\n也可直接调用 killOnExit 函数绑定 process.job.limitKill !process_popen.killOnExit() = 主进程退出时自动退出此进程 !process_popen.ctrlEvent(0) = 发送 `Ctrl+C`(SIGINT 信号),等价于调用 sendCtrlC 方法。 \n信号将传递到与目标进程控制台连接的所有非分离控制台进程,\n64 位目标进程会导致当前控制台暂时关闭 !process_popen.ctrlEvent(1) = 发送 `Ctrl+Break`(SIGBREAK 信号)。\n信号将传递到与目标进程控制台连接的所有非分离控制台进程\n64 位目标进程会导致当前控制台暂时关闭 !process_popen.sendCtrlC() = 发送 `Ctrl+C`(SIGINT 信号),等价于调用进程对象的 ctrlEvent(0) 方法。 !process_popen.resume() = 恢复运行 !process_popen.suspend() = 暂停进程 !process_popen.process = !process. !process_popen.stdErr = 进程标准错误输出\n!ioFile. !process_popen.stdOut = 进程标准输出\n!ioFile. !process_popen.stdIn = 进程标准输入\n\nwaitOne 函数返回 true,或 each ,expect 等函数检测到进程退出,\n或readAll 或 read 函数参数为 -1 时\n会自动关闭目标进程标准输入管道\n!ioFile. !process_popen.json() = 等待并读取进程输出的一行 JSON ,并解析为对象。\n成功返回对象,失败返回 null,错误信息。\n此函数不阻塞界面线程消息。\n\nJSON 最外层必须是大括号 {} 或中括号 [],JSON不能包含非转义换行,且必须以换行结束。\n自动忽略不符合要求的其他输出。\n\n使用此函数前必须导入 JSON 库,\n注意 process.popen 并不导入 JSON !process_popen.jsonAll() = 读取进程的全部输出,并用 JSON 解析为 aardio 对象。\n成功返回对象,失败返回 null,错误信息。\n此函数会等待进程结束,但不会阻塞当前线程窗口消息。\n\n进程输出应符合 JSON 语法。\n如果参数 @1 为 true 或省略,读取后关闭进程管道。\n\n使用此函数前必须导入 JSON 库,\n注意 process.popen 并不导入 JSON !process_popen.ndjsonAll() = 读取进程的全部输出,并将多行 JSON 解析为 aardio 对象。\n有效的 JSON 必须是JSON 对象或数组,忽略其他 JSON 类型或无效的行。\n成功返回对象,失败返回 null,错误信息。\n此函数会等待进程结束,但不会阻塞当前线程窗口消息。\n如果参数 @1 为 true 或省略,读取后关闭进程管道。\n\n使用此函数前必须导入 JSON 库,\n注意 process.popen 并不导入 JSON !process_popen.jsonWrite() = 将对象转换为 JSON 并写入进程标准输入,\nJSON 不包含换行,且以换行结尾。\n最好统一为输入对象生成的JSON 最外层是大括号 {} 或中括号 []\n\n使用此函数前必须导入 JSON 库,\n注意 process.popen 并不导入 JSON\n\n成功返回 true,失败返回 null ,错误代码,错误信息。 !process_popen.onResponseEnd() = @.onResponseEnd = function(){ __/*被调用进程结束,异步或同步调用 logResponse 函数结束后触发此回调*/ } !process_popen.getMainWindow(.() = 返回进程的主窗口以及窗口进程 ID,找不到则搜寻子进程主窗口。\n查找时忽略隐藏窗口。 !process_popen.getMainWindow(.(类名,标题) = 返回进程的指定类名的主窗口以及窗口进程 ID,找不到则搜寻子进程主窗口。\n类名与标题支持完全匹配与模式匹配,忽略大小写。\n所有参数可选,不指定类名或标题时忽略隐藏窗口。 !process_popen.waitMainWindow( = 等待并返回进程主窗口以及窗口进程ID。 !process_popen.waitMainWindow(.(类名,等待窗口句柄) = 等待并返回进程主窗口以及窗口进程ID。\n所有参数可选。\n可选指定要等待的类名,类名支持完全匹配与模式匹配,忽略大小写。\n不指定类名时忽略隐藏窗口,\n可选指定等待窗口句柄,该窗口关闭时些函数不再等待并直接返回结果 !process_popen.closeMainWindow() = 关闭进程的主窗口,忽略隐藏窗口 process.popen.win() = !process_popen. end intellisense**/