//popen 进程管道
import process;
namespace process

class popen{
	ctor( exefile,parameters="", startInfo ){ 
		var sa = ..table.mixin(..process.STARTUPINFO(),startInfo);
		sa.flags = (sa.flags:0) | 0x100/*_STARTF_USESTDHANDLES*/ | 0x1/*_STARTF_USESHOWWINDOW*/ 
		//sa.showWindow = 0x0/*_SW_HIDE*/  
		
		saPipe =SECURITY_ATTRIBUTES();
		saPipe.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.creationFlag = (sa.creationFlag:0) | 0x8/*_DETACHED_PROCESS*/  //禁止子进程附加到当前进程的控制台
		sa.inheritHandles = true; //允许子进程继承句柄
		//sa.suspended = true;  
	
		//下面我们创建进程
		var prcs = ..process( exefile,parameters, sa);
		this.process = prcs;
		//你也可以有其他的写法,总之很自由 var prcs = process( "ping","www.baidu.com" , { suspended = true } );
 		
		//子继承已经继承了此句柄,我们可以关掉了
		//这样子进程一结束,获取子进程输入就会失败停止,不然他就卡在那里一直读,因为当前进程的管道没关闭
		..raw.closehandle(sa.stdInput)
		..raw.closehandle(sa.stdOutput)
		..raw.closehandle(sa.stdError)
	
		this.stdWrite = ..io.open(inPipeWrite,"w+");
		this.stdRead = ..io.open(outPipeRead,"r")
	};
	
	write = function(...){ 
		this.stdWrite.write(...)
		this.stdWrite.flush() 
	}
	
	read = function(...){
		return this.stdRead.read(...);
	}
	
	peek = function(delay=1000){ 
		sleep(delay)
		var ret,read,total,leftmsg = PeekNamedPipe(outPipeRead, ,0,0,0,0)
		if(ret && total)
			return this.read(total) 
	} 
	
	peekTo = function(tag,proc){ 
		var str = ""
		var part;
		var tpart = {};
		do{ 
			part = this.peek(100);
			if(part){
				str = ..string.concat(str,part )
				if( proc) proc(part);
			}
		}while( ! ..string.endWith(str,tag) ) 
		 
		return str;
	} 
	
	close = function(){
		this.stdWrite.close();
		this.stdRead.close();
		prcs.free();
	} 
}

namespace popen{
	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)");

	//这个结构体在创建管道时会用到
	SECURITY_ATTRIBUTES = class{ 
    	INT nLength;
    	pointer lpSecurityDescriptor;
    	bool bInheritHandle;
	}
}
 
/**intellisense()
process.popen = 创建子进程,并支持管道读写
process.popen(.(执行文件, 命令行参数,启动参数) = 创建子进程,并支持管道读写\n参数与process()的参数用法一致
?process.popen = !process_popen.
!process_popen.read()
!process_popen.read() = 从当前位置，向后读取下一行
!process_popen.read("%s") = 从当前位置，向后读取下一行\n支持多参数
!process_popen.read("%d") = 从当前位置，向后读取下一个数值\n支持多参数
!process_popen.read(-1) = 向后读取到文件尾部\n负数表示从文件尾部倒计数位置\n支持多参数
!process_popen.peek(__) = 检测管道中可读取的数据长度\n有数据则读取数据,无数据则返回空值\n此函数不会因为等待输入而阻塞\n参数可指定等待目标进程执行的延时时间. 
!process_popen.peekTo("> ") = 读取数据,直到发现数据以指定的字符串结束.
!process_popen.read(0) = 检测是否读取到文件尾
!process_popen.read(__) = 正数参数表示从当前位置向后读取n个字节\n支持多参数
!process_popen.write(__) = 写数据，支持一个或多个参数
!process_popen.write(__, ) = 写数据，支持一个或多个参数
!process_popen.close() = 释放对象
!process_popen.process = !process.
end intellisense**/
