//外部进程窗口操作
namespace winex;
import win; //winex基于win库扩展，重用普通窗口操作模块
 
//AllowSetForegroundWindow := ::User32.api( "AllowSetForegroundWindow", "int(INT dwProcessId)" ) 

AttachThreadInput := ::User32.api( "AttachThreadInput", "int(INT idAttach,INT idAttachTo,int fAttach)" )  
EnumWindows := User32.api( "EnumWindows", "int(POINTER lpEnumFunc ,int lParam )" )
EnumChildWindows := User32.api( "EnumChildWindows", "int(int hWndParent,POINTER lpEnumFunc ,int lParam )" ) 
isUnicode := User32.api( "IsWindowUnicode", "int(int hwnd)" )

/**intellisense(winex)
isUnicode(__/*窗口句柄*/) = 判断窗口是否Unicode窗口
end intellisense**/


var getWindow = win.GetWindow;
match = function(hwnd,text,cls,id){ 

	if( cls  ){ 
		if( not ..string.find( win.getClass(hwnd), cls ) ) {
			return false;  
		}
	}

	if( text ) {  
		if( not  ..string.find( win.getText(hwnd,30/*不要取太长的标题*/),text ) ){ 
			return false;
		} 
	}
	
	if( id ) { 
		if( win.getId(hwnd) != id) { 
			return false;  
		}
	}
	
	return true;
}

//枚举所有子窗口 - 深度递归遍历
var enumWindows
enumWindows=function( onfind, parent, cls ,text, id,depth  ){   
    //取到第一个子窗口
	var next = getWindow( parent ,0x5/*_GW_CHILD*/ ) ;
	if(!next)
		return;
	
	
    do{
		sleep(0);   
		
		if( enumWindows( onfind, next, cls ,text, id ,depth+1 ) === false )
			return;
		 
		do{
		
			if( ! match(next,text,cls,id ) )
				break;
			
			if( onfind( next,depth ) === false )
				return false;
		
		}while(0) 
		
		 
		next = getWindow( next ,0x2/*_GW_HWNDNEXT*/ )
		
	}while( next  )
}

enum = function( onfind, parent, cls ,text, id  ){
	parent := ..win.getDesktop(); 
	enumWindows(onfind, parent, cls ,text, id,2)
}
 
function enumTop( onfind ) {  
    // 须是一个字符串
    assert( where===null || type(where) == type.string ,"查询条件必须是字符串")
 
    var EnumwndProc = function( hwnd,lparam ){ 
                    
		if( onfind( hwnd ) === false)
			return 0;
		else
			return 1 ;  
    } 
    
    pEnumwndProc = ..raw.tostdcall(EnumwndProc,"int(int hwnd,int lparam )"/* ,{ tag='crane' }  */ ); 
    EnumWindows(pEnumwndProc ,0); //回调函数声明了_topointer无方法，因此可以自动转换为pointer指针
    pEnumwndProc = null;//确认不会再用到的回调函数，可以显示声明为null进行释放 
    
}


/**intellisense(winex)
enum(onfind,parent)=@.enum( \n	function(hwnd,depth){\n		..io.print( depth/*深度*/,hwnd/*窗口*/,win.getText(hwnd)/*标题*/ )\n		/*return false*/\n	} \n	,__/*输入父窗口*/\n	,要查找的类名\n	,要查找的标题\n	,要查找的控件ID\n)
enum(onfind)=@.enum( \n	function(hwnd,depth){\n		..io.print( depth/*深度*/,hwnd/*窗口*/,win.getText(hwnd)/*标题*/ )\n		/*return false*/\n	} \n)
enumTop=@.enumTop(\n	function(hwnd){\n 		..io.print(hwnd)\n	}\n)\n//枚举桌面顶层窗口;
end intellisense**/


//迭代指定父窗口的子级窗口 - 广度迭代遍历
function each( mcls,mtitle,parent ) {
    parent := ..win.getDesktop(); 
    
	return function(hwnd) { //接收for循环传递的参数得到迭代器控制变量  
        var title;
        
        do{
        	hwnd = getWindow( hwnd  ,0x2/*_GW_HWNDNEXT*/ );
        	if(!hwnd)
        		return null;
        	
        	if( ! match(hwnd,mtitle,mcls ) )
				continue; 
			
			if(!title)
				title = ..win.getText(hwnd); 
				
			return hwnd,title,  ..win.getThreadProcessId(hwnd); 
        
        	
        }while(1) 
       
	} , , getWindow( parent ,0x5/*_GW_CHILD*/ ) ;

} 

/**intellisense(winex) 
each("类名","查找标题") =  @for hwnd,title,theadId,processId in winex.each( "", ".*__/*请输入标题包含字符串\n可使用模式匹配语法*/" ) { \n//遍历所有进窗口\n	\n}
each() =  @for hwnd,title,theadId,processId in winex.each( __/*无参数则返回桌面所有顶层窗口\n可在此输入标题包含字符串\n可使用模式匹配语法*/ ) { \n//遍历所有进窗口\n	\n}
each("类名","查找标题",父窗口) =  @for hwnd,title,theadId,processId in winex.each("",__/*请输入父窗口句柄*/) { \n//遍历所有进窗口\n	\n}
end intellisense**/
 
//模糊匹配查找顶层窗口，可选参数(类名,标题,进程ID,线程ID)
find = function(cls,title,pid,tid){
	for hwnd,title,theadId,processId in  each( cls,title ) { 
		if( pid ){
		 	if( pid != processId )
		 		continue;
		}
		if( tid ){
		 	if( tid != theadId )
		 		continue;
		} 
		return hwnd,theadId,processId
	} 
}

findEx = function(parent,count,cls ,text, id  ){
	var count2=0;
	var result;
	
	enum( 
		function( hwndfind,depth ){
			result = hwndfind; 
			if( count ){
				count2++;
				if( count2 < count ) 
					return true; //接着找 
			}
			return false;//停止查找
			
		} 
		,parent
		,cls
		,text
		,id
	)
	
	return result;
}
 
findExists = function(title,text,cls,ctrlcls,ctrlid){
	var ctrl;
	for parent,title,theadId,processId in  each( cls,title ) { 
		ctrl = findEx( parent,null,ctrlcls ,text, ctrlid ) 
		if( ctrl )
			return parent,ctrl,theadId,processId
	} 
} 

findActivate = function(title,text,cls,ctrlcls,ctrlid){
	parent,ctrl,theadId,processId = findExists(title,text,cls,ctrlcls,ctrlid);
	if(parent){  
		..win.setForeground(parent)
		sleep(1)
	}
	return parent,ctrl,theadId,processId;
}

waitTimeout = null;
waitDelay = 100;
wait = function( title,text,cls,ctrlcls,ctrlid){
	var tk = ..time.tick();
	var parent,ctrl,theadId,processId ;
	while(!parent){
		 parent,ctrl,theadId,processId = findExists(title,text,cls,ctrlcls,ctrlid)
		 
		 if( waitTimeout ){
		 	if( ..time.tick() - tk > waitTimeout ){
		 		return parent,ctrl,theadId,processId
		 	}
		 }
		 sleep(0)
		 ..win.delay(waitDelay)
	}
	return parent,ctrl,theadId,processId;
}

waitClose = function( title,text,cls,ctrlcls,ctrlid){
	var tk = ..time.tick(); 
	var waitfunc = type(title)==type.number? ..win.isWindow : findExists
	while( waitfunc(title,text,cls,ctrlcls,ctrlid) ){ 
	
		 if( waitTimeout ){
		 	if( ..time.tick() - tk > waitTimeout ){
		 		return false
		 	}
		 }
		 sleep(0)
		 ..win.delay(waitDelay)
	}
	return true;
}


waitActive = function( title,text,cls,ctrlcls,ctrlid){
	var tk = ..time.tick(); 
	var hwnd = type(title)==type.number ? title;
	var f;
	while( 1 ){
		f = ..win.getForeground();
		if( hwnd ){
			if(hwnd == f )
				return f;
		}
		else {
			if( match(f,title,cls ) ){
				var ctrl = findEx(f,null,ctrlcls ,text, ctrlid  )
				if(  ctrl )
					return f,ctrl,..win.getThreadProcessId(f);
			}
		}
		
		if( waitTimeout ){
		 	if( ..time.tick() - tk > waitTimeout ){
		 		return;
		 	}
		 }
		 sleep(0)
		 ..win.delay(waitDelay)
	}
	  
}

//MenuItemFromPoint = ::User32.api("MenuItemFromPoint","int(int hWnd,int hMenu,int x,int y)")

//叶级子窗口
fromPoint= User32.api("WindowFromPoint", "int(int x, int y)");
 
//叶级子窗口,并穿透groupbox 
fromPointReal = function(x,y){
	var hwnd = fromPoint(x,y);
	if(hwnd){
		var parent = ..win.getParent(hwnd);
		if(parent)
			hwnd= fromClientPointReal(parent,
				..win.toClient(parent,x,y) );
	}
	return hwnd;
}

//子窗口,而非叶级窗口
var childWindowFromPointEx = User32.api("ChildWindowFromPointEx","int(int hwnd,int x,int y,INT flags)")
fromClientPoint = function(hwnd,x,y,un=0x0001/*_CWP_SKIPINVISIBLE*/){ 
	return childWindowFromPointEx(hwnd,x,y,un);
} 

//子窗口,而非叶级窗口,穿透groupbox
fromClientPointReal = User32.api("RealChildWindowFromPoint","int(int hwnd,int x,int y )")


attachThread = function(tid,att = true){
	var cid = ..thread.getId(); 
	if(tid==cid)
		return att;
		
	return AttachThreadInput(tid, cid, att?1:0)
}

attach = function(hwnd,att = true){
	if(!hwnd)
		error("无效的窗口句柄",2)
	var tid,pid = ..win.getThreadProcessId(hwnd); // 当前窗口的线程ID  
	return attachThread(tid,att);
}

getFocus = function(hwnd){
    hwnd := win.getForeground();
    if(type(hwnd)!=type.number)
    	error("错误的句柄",2)
    var tid,pid = ..win.getThreadProcessId(hwnd); // 当前窗口的线程ID  
    var cid = ..thread.getId();
    
    var hwndChildAfter = ..win.findEx(hwnd,0);
    if(hwndChildAfter){ 
		if(tid == cid ) 
			return ..win.getFocus(),cid,pid; 
		else if(  AttachThreadInput(tid, cid, 1)) {
			var hCtrl = ..win.getFocus();
			AttachThreadInput(tid, cid, 0);
			return hCtrl,tid,pid;
		} 
	}
	else
	    return hwnd,tid,pid;
	
} 
 

/**intellisense(winex) 
match(.(句柄,文本,类名,ID) = 指定一个窗口句柄,检测是否符合给定参数\n所有参数可选,类名与文本支持模式表达式
find(.(类名模式串,标题模式串,进程ID,线程ID)=查找顶层窗口，所有参数都是可选参数\n返回值为：句柄,线程ID,进程ID
findEx(.(父窗口句柄,返回第几个匹配句柄,类名模式串 ,标题模式串, 控件ID )=查找子窗口，除父窗口句柄外所有参数可选\n返回值为句柄
findExists(.(父窗口标题,控件文本,父窗口类名,控件类名,控件ID) = 查找包含指定控件窗口的父窗口,所有参数可选\n返回值为:窗口句柄,控件句柄,线程ID,进程ID
findActivate(.(父窗口标题,控件文本,父窗口类名,控件类名,控件ID) = 调用winex.findExists查找并激活包含指定控件窗口的父窗口,所有参数可选\n返回值为:窗口句柄,控件句柄,线程ID,进程ID
wait(.(父窗口标题,控件文本,父窗口类名,控件类名,控件ID) = 等待指定窗口创建,所有参数可选\n成功返回窗口句柄,控件句柄,线程ID,进程ID,超时返回空值
waitClose(.(父窗口标题,控件文本,父窗口类名,控件类名,控件ID) = 等待指定窗口关闭,所有参数可选\n成功返回true,超时返回false
waitClose(窗口句柄) = 等待指定窗口关闭,所有参数可选\n成功返回true,超时返回false
waitActive(.(父窗口标题,控件文本,父窗口类名,控件类名,控件ID) = 等待指定窗口激话,所有参数可选\n成功返回窗口句柄,控件句柄,线程ID,进程ID,超时返回false
waitActive(窗口句柄) = 等待指定窗口激话,所有参数可选\n成功返回窗口句柄,超时返回false
waitTimeout = 指定winex库所有wait前缀的等待函数的默认超时值\n以毫秒为单位,默认为null表示永不超时.
waitDelay = 指定winex库所有wait前缀的等待函数的的检测时间间隔;\n以毫秒为单位,默认为100毫秒

fromPoint(.(x坐标,y坐标)=从指定的屏幕坐标查找窗口\n获取叶级子窗口。
fromPointReal(.(x坐标,y坐标)=从指定的屏幕坐标查找窗口\n获取叶级子窗口,穿透groupbox。
fromPointEx(.(窗口句柄,x坐标,y坐标) = 从指定的屏幕坐标查找子窗口
fromPointEx(.(窗口句柄,x坐标,y坐标,_CWP_ALL) = 从指定的屏幕坐标查找子窗口\n_CWP_ALL指定包含隐藏、禁用窗口
fromClientPoint(.(窗口句柄,x坐标,y坐标) = 从指定的窗口坐标查找子窗口\n仅子窗口,非叶级窗口
fromClientPoint(.(窗口句柄,x坐标,y坐标,_CWP_ALL) = 从指定的窗口坐标查找子窗口\n_CWP_ALL指定包含隐藏、禁用控件
fromClientPointReal(.(窗口句柄,x坐标,y坐标) = 从指定的窗口坐标查找子窗口\n仅子窗口,非叶级窗口,穿透groupbox。 
attach(__/*窗口句柄*/) = 附加到输入线程\n返回值为是否成功附加
attach(__/*窗口句柄*/,false) = 解除附加\n返回值为是否成功解除
attachThread(__/*线程ID*/) = 附加到输入线程\n返回值为是否成功附加
attachThread(__/*线程ID*/,false) = 解除附加\n返回值为是否成功解除
getFocus(__/*输入父窗口句柄*/)=获取输指定窗口输入焦点所在的控件句柄
getFocus()=获取前台窗口、输入焦点所在的控件句柄
end intellisense**/
 
say2  = function(str,hwnd){ 
    //注入对方进程，并发送文本
	hwnd = getFocus(hwnd); 
	
	for(i=1;#str ) {  
		//双字节字符
		if(str[i]>0x80){
			::PostMessage(hwnd, 0x102/*_WM_CHAR*/,  (  ( ( str[i+1] << 8) | str[i]  ) ) , 1); 
			i++
		}
		else 
			::PostMessage(hwnd, 0x102/*_WM_CHAR*/,  str[i] , 1); 
	} 
	..win.delay(1) 
}

sayIme = function(str,hwnd){
	hwnd = getFocus(hwnd); 
	for(i=1;#str ) {  
		//双字节字符
		if(str[i]>0x80){
			::PostMessage(hwnd, 0x286/*_WM_IME_CHAR*/,  (  ( ( str[i] << 8) | str[i+1]  ) ) , 1); 
			i++
		}
		else 
			::PostMessage(hwnd, 0x286/*_WM_IME_CHAR*/,  str[i] , 1); 
	}  
	..win.delay(1) 
}


say = function(str,hwnd){
	//自动发送文本
	hwnd = getFocus(hwnd); 
	if(  ..win.isWindow(hwnd) ){   
		for(i=1;#str) { 
			//这里如果用SendMessage，就发不出字体 
			::PostMessage(hwnd, 0x102/*_WM_CHAR*/, str[i] & 0xFF, 0x0001); 
		}  
	}
	..win.delay(1) 
}
 
sendString = function(str,hwnd){
	//自动发送文本
	hwnd = getFocus(hwnd);

	if(  ..win.isWindow(hwnd) ) {
	    ::SendMessage(hwnd,0xC2/*_EM_REPLACESEL*/,null,str);  
	}
}

click = function( hwnd,cmdid ){
   hwnd := win.getForeground() ;
 	
   if(! cmdid ) {
   		cmdid = ..win.getId(hwnd)
   		if(cmdid){
   			hwnd = ..win.getParent(hwnd) 
   		}
   		else{
      		var hctrl = ..win.findEx(hwnd,0); 
      		if(!hctrl) 
      			return; 
      		
      		cmdid = ..win.getId(hctrl);
      	}
   }
 
   ::PostMessage(hwnd, 0x111/*_WM_COMMAND*/,cmdid ,0);
}

//菜单函数 
GetMenu = ::User32.api("GetMenu","int(int hwnd)")
GetSubMenu = User32.api("GetSubMenu","int(int hMenu,int nPos)");
GetMenuItemID = User32.api("GetMenuItemID","INT(int hMenu,int nPos)"); 
GetMenuItemCount = User32.api("GetMenuItemCount","int(int hMenu)");
GetMenuString = User32.api("GetMenuStringA","int(int hMenu,int wIDItem,string& lpString,int nMaxCount,int wFlag)")

MENU_ITEM_IS_SUBMENU = 0xFFFFFFFF
findMenu = function(target_window,...) { 
	lables = {...}
	
	var hMenu = GetMenu(target_window); 
	if (!hMenu)  
		return 0;

	if( not #lables )
		return hMenu;


	var menuId = MENU_ITEM_IS_SUBMENU;
	var itmeCount = GetMenuItemCount(hMenu); 
	if (itmeCount < 1)  
		return;  
 
    var hMenuFind;
 	onFindSubMenu = function( menu_pos ){ 
 		
		menuId = GetMenuItemID(hMenu, menu_pos); 
	 
		if (menuId == MENU_ITEM_IS_SUBMENU) {
			hMenu = GetSubMenu(hMenu, menu_pos);
			itmeCount = GetMenuItemCount(hMenu); 
			hMenuFind = hMenu;
		}
		else { 
			itmeCount = 0; 
			hMenu = null; 
		}
	}
 
	for(i=1;#lables ) {
		if (!hMenu)  
			return;   
    
		if( type(lables[i])==type.number ){ 
			if ( (lables[i] <= -1) || ( lables[i] >= itmeCount) )  
				return;  
			
			onFindSubMenu(lables[i])

		}
		else if(type(lables[i])==type.string) {
			for(pos=0;itmeCount-1){
				var len, target= GetMenuString(hMenu, pos, 1024, 1024-1, 0x400/*_MF_BYPOSITION*/);
				
				if( not  ..string.find( lables[i],"\&" ) )
 					target = ..string.replace(target,"\&","");
 				
			 	if( ..string.find( target,lables[i] ) ){
			 		onFindSubMenu(pos)
					break;
			 	}
				
				
			}  
			
		}  
		else
			return ;
	}  

	if (menuId == MENU_ITEM_IS_SUBMENU) 
		return hMenuFind;   
	else
		return hMenuFind,menuId 
 

}

/**intellisense(winex)
findMenu(.(窗口句柄,"子菜单标题",或子菜单序号,.....) = 例如：\nhmenu,menuid = winex.findMenu(hwnd ,"文件","另存为"  )
click(.(窗口句柄,命令ID) = 模拟点击命令
sendString("") = 向前台窗口的文本框发送文本
sendString("",__/*输入窗口句柄*/) = 向指定窗口上的文本框发送文本
say("") = 向前台窗口的文本框发送文本
say("",__/*输入窗口句柄*/) = 向指定窗口上的文本框发送文本
say2("") = 向前台窗口的文本框发送文本
say2("",__/*输入窗口句柄*/) = 向指定窗口上的文本框发送文本
sayIme("") = 向前台窗口的文本框发送文本\n使用_WM_IME_CHAR消息
sayIme("",__/*输入窗口句柄*/) = 向指定窗口上的文本框发送文本\n使用_WM_IME_CHAR消息 
end intellisense**/

 