import win.reg;
import process.popen;
import thread.token;

namespace win.ts;

::Wtsapi32 := ..raw.loadDll("Wtsapi32.dll");

class session{
	ctor( sessionId=-1,serverHandle ){
		this.sessionId = sessionId;
		this.serverHandle = serverHandle;
	};
	queryInfo  = function(infoClass,info){
		var buffer = {ptr v}
		var bufferSize = {INT v}
		if(::Wtsapi32.WTSQuerySessionInformation(this.serverHandle,this.sessionId,infoClass,buffer,bufferSize) ){
			
			if(info===0) info = ..raw.convert(buffer.v,{INT v}).v; 
			elseif(!info) info = ..raw.str(buffer.v,true); 
			else ..raw.convert(buffer.v,info); 
			
			::Wtsapi32.WTSFreeMemory(buffer.v);
			return info;
		} 
	};
	id = function(){
		return this.queryInfo(4/*WTSSessionId*/,0)
	};
	userName = function(){
		return this.queryInfo(5/*WTSUserName*/)
	};
	stationName = function(){
		return this.queryInfo(6/*WTSWinStationName*/)
	};
	domainName = function(){
		return this.queryInfo(5/*WTSDomainName*/)
	};
	clientName = function(){
		return this.queryInfo(10/*WTSClientName*/)
	};
	clientDirectory = function(){
		return this.queryInfo(11/*WTSClientDirectory*/)
	};
	connectState = function(){
		var state = this.queryInfo(8/*WTSConnectState*/,0) 
		if(state!==null){
			return self.connectState[state+1]
		}
	};
	connect = function(targetId,logonPassword,wait){
    	if(targetId===null)targetId = activeConsoleId();
    	if(#logonPassword){
    		return ::Wtsapi32.WTSConnectSessionB(this.sessionId,targetId,logonPassword,wait);
    	}
    	
    	return ..thread.token.impersonate("winlogon.exe",function(){
 			return ::Wtsapi32.WTSConnectSessionB(this.sessionId,targetId,"",wait);
		})
	}
	disconnect = function(wait){
		return ::Wtsapi32.WTSDisconnectSessionB(this.serverHandle,this.sessionId,wait) 
	};
	isRemote = function(){
		return !!this.queryInfo(29/*WTSIsRemoteSession*/)
	};
}

session.isRemote = lambda() ::User32.GetSystemMetricsB(0x1000/*_SM_REMOTESESSION*/);

session.connect = function(logonId,targetName,logonPassword){
	var prcs = ..process.popen("tscon.exe "+( logonId:session.currentId() )+" /dest:" + (targetName:"console") + " " +(logonPassword?("/password:"+pw):""))
	prcs.writeClose();
	return rget(2,prcs.wait());
}

session.currentId = function(){
	var _,id = ::Kernel32.ProcessIdToSessionId( ::Kernel32.GetCurrentProcessId(),{int v})
	return id.v;
}

session.activeConsoleId = function(){
	return ::Kernel32.WTSGetActiveConsoleSessionId();
}

session.connectState = {"active","connected","connectQuery","shadow","disconnected","idle","listen","reset","down"}
session.enum = function(proc,serverHandle){
	var infos = {ptr p}
	var count = {INT v}
	if (::Wtsapi32.WTSEnumerateSessions(serverHandle,,1,infos,count)){
		var ret = ..raw.convertArray(infos.p,count.v,{ 
				INT sessionId;
				ustring stationName;
				int state; 
		})
		
		::Wtsapi32.WTSFreeMemory(infos.p);
		if(!proc) return ret;
		
		for(i=1;#ret;1){
			var v = ret[i];
			v.state = session.connectState[v.state+1] 
			var r = proc( v.sessionId,v.stationName,v.state );
			if(r!==null) return r;
		} 
	}	
} 

suppressWhenMinimized = function(mode){
	var reg = ..win.reg("HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client");
	reg.setDwValue("RemoteDesktop_SuppressWhenMinimized",mode:0);
	reg.close();
}

/**intellisense()
win.ts = 远程桌面（即 Windows Terminal Server ）接口,\n更多会话函数请参考标准库 service
win.ts.session(.(sessionId,serverHandle) = 创建会话对象，所有参数可选
win.ts.session() = !wtsSession.
win.ts.suppressWhenMinimized(mode) = 在客户端机器上设置远程桌面客户端最小化后是否保持桌面活动状态\n参数设为 0 为自动设置,1 为禁止活动状态,2 为保持活动状态
end intellisense**/

/**intellisense(win.ts.session)
enum = @.enum(\n	function(sessionId,stationName,connectState){\n		console.log(sessionId,stationName,connectState,win.ts.session(sessionId).userName())__/*sessionId 会话ID\nstationName 会话名称，本机控制会话名为"console",\nconnectState 连接状态\n回调函数返回任何非 null 值可终止枚举,\n并且回调函数的返回值将传回为枚举函数的返回值*/\n	}\n)
isRemote() =  当前是否通过远程桌面登录
currentId() = 获取当前会话 ID
activeConsoleId() = 获取活动桌面会话 ID
connect( = 连接并切换会话,此函数需要管理权限,\n这个函数可用于源登录会话重新连接到其他会话拥有的激活的控制桌面,\n例如将当前远程桌面会话重新连接到本机控制桌面,\n以避免远程桌面关闭而退出会话\n\n此函数需要调用 tscon.exe,但家庭版缺少 tscon.exe,\n建议改用不依赖 tscon.exe 的 win.ts.session().connect() 函数
connect(.(logonId,targetName,logonPassword) = 所有参数都是可选参数,\n@logonId 指定源会话ID，@logonId 默认为当前会话,\n@logonId指定源会话的登录用户必须拥有连接目标会话的权限,\n连接到 @targetName 指定目标会话名的桌面,@targetName 默认为本机控制会话（"console"）,\n@logonPassword 指定 @logonId 参数指定会话的密码,\n返回值1为标准输出,返回值2为错误输出,返回值3为退出代码\n\n如果切换远程桌面与本地桌面会话,\n@targetId 指定的控制会话桌面将被@logonId 指定的会话接管,\n@targetId指定的会话断开,\n\n如果用于切换本地登录用户,\n@targetId指定的控制会话桌面断开,@logonId 指定的会话将被激活
end intellisense**/

/**intellisense(!wtsSession)
isRemote() = 是否通过远程桌面登录
queryInfo(.(infoClass,info) = 查询信息，用法参考源码
id() = 会话ID
userName() = 用户名
stationName() = 会话名
domainName() = 域名
clientName() = 客户端名
clientDirectory() = 客户端目录
connectState() = 连接状态
connect( = 连接并切换会话,此函数需要管理权限,\n这个函数可用于源登录会话重新连接到其他会话拥有的激活的控制桌面,\n例如将当前远程桌面会话重新连接到本机控制桌面,\n以避免远程桌面关闭而退出会话
connect(.(targetId,logonPassword,wait) = 当前源会话登录用户必须拥有连接到目标会话的权限,\n连接到 @targetId 指定目标会话ID,@targetId默认为本机控制会话（即"console"）,\n可选参数  @logonPassword 指定当前源会话的登录密码,注意不是目标会话的密码,\n可选参数 @wait 指定是否等待操作完成,\n函数执行成功返回true,失败返回false\n\n如果切换远程桌面与本地桌面会话,\n@targetId指定的控制会话桌面将被源会话接管,@targetId指定的会话断开,\n\n如果用于切换本地登录用户,\n@targetId指定的控制会话桌面断开,源会话将被激活
disconnect(wait) = 断开会话连接，但不关闭会话, \n后续可重新连接到该会话
end intellisense**/

/**details(创建远程桌面客户端)

import com.lite;
var rdpClient = com.lite("MsTscAx.dll").createEmbed(winform,"{7cacbd7b-0d99-468f-ac33-22e495c0afe5}")
var msTscAx = rdpClient._object;//获取控件对象 https://docs.microsoft.com/en-us/windows/win32/termserv/mstscax
 
完整范例： 
"~\codes\范例程序\F) COM 组件\1) 嵌入 ActiveX 控件\lite.aardio"

end details**/