经过近三周的研究,终于实现了windows桌面锁定后的自动登录。可以说实现这个功能还是挺复杂的,用到的主要技术包括windows服务启动程序,桌面的自动化技术、还有管道通信等等
代码主要用到三个模块,windows服务启动程序模块,自动登录程序模块,和主程序模块,主程序模块用于监视windows桌面的锁定状态,然后通过管道通信通知windows服务去启动自动登录程序,自动登录程序获取到windows登录desktop对象,然后利用uiautomation技术实现自动登录
这里面为什么要用windows服务去启动登录程序?主程序不直接启动登录程序?因为使用windows服务去启动进程,进程可以获取到system权限,system是最大的权限,进程只有获取到system权限才可以获取windows登录桌面对象。
现在贴上部分核心代码仅供参考
windows服务
protected override void OnStart(string[] args)
{
thread = new Thread(() =>
{
string currentUserName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
while (true)
{
using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("autologonpipe", PipeDirection.InOut))
{
pipeServer.WaitForConnection();//等待连接,程序会阻塞在此处,直到有一个连接到达
try
{
using (StreamReader sr = new StreamReader(pipeServer))
{
var codeExec = sr.ReadLine();
Helper.RespawnInActiveTerminalSession(codeExec);
}
}
catch (Exception ex)
{
using (StreamWriter writer = new StreamWriter(logTxt, true))
{
writer.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + ex.Message);
}
}
}
}
});
thread.IsBackground = true;
thread.Start();
}
windows服务将通过管道监听命令,收到命令后启动自动登录程序。
public static void RespawnInActiveTerminalSession(string exepath)
{
IntPtr token = IntPtr.Zero;
IntPtr newToken = IntPtr.Zero;
try
{
UInt32 sessionId = GetActiveTerminalSessionId();
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, out token))
{
throw new WinApiException("OpenProcessToken");
}
if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, out newToken))
{
throw new WinApiException("DuplicateTokenEx");
}
if (!SetTokenInformation(newToken, TOKEN_INFORMATION_CLASS.TokenSessionId, ref sessionId, (UInt32)Marshal.SizeOf(sessionId)))
{
throw new WinApiException("SetTokenInformation");
}
STARTUPINFO startupInfo = new STARTUPINFO();
startupInfo.cb = Marshal.SizeOf(startupInfo);
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_SHOW;
PROCESS_INFORMATION processInformation;
if (!CreateProcessAsUser(
newToken,
exepath??System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
IntPtr.Zero,
ref startupInfo,
out processInformation
))
{
throw new WinApiException("CreateProcessAsUser");
}
}
finally
{
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (newToken != IntPtr.Zero)
{
CloseHandle(newToken);
}
}
}
ui自动化 实现自动登录
AutomationElement rootElement = AutomationElement.RootElement;
AutomationElementCollection subChildren = rootElement.FindAll(System.Windows.Automation.TreeScope.Subtree, System.Windows.Automation.Condition.TrueCondition);
foreach (AutomationElement element in subChildren)
{
using (StreamWriter writer = new StreamWriter(logTxt, true))
{
writer.WriteLine(element.Current.Name + " " + element.Current.LocalizedControlType);
}
if (element.Current.IsPassword)
{
object parttern;
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out parttern))
{
ValuePattern valuePattern = parttern as ValuePattern;
valuePattern.SetValue("123456");
using (StreamWriter writer = new StreamWriter(logTxt, true))
{
writer.WriteLine("set password");
}
}
}
if (!string.IsNullOrEmpty(element.Current.Name) && element.Current.Name.Contains("提交"))
{
if (element.TryGetCurrentPattern(InvokePattern.Pattern, out object pattern))
{
try
{
InvokePattern invokePattern = pattern as InvokePattern;
invokePattern.Invoke();
using (StreamWriter writer = new StreamWriter(logTxt, true))
{
writer.WriteLine("执行操作 登录 提交");
}
}
catch (Exception exp)
{
}
}
break;
}
if (!string.IsNullOrEmpty(element.Current.Name) && element.Current.Name.Contains("确定"))
{
if (element.TryGetCurrentPattern(InvokePattern.Pattern, out object pattern))
{
try
{
InvokePattern invokePattern = pattern as InvokePattern;
invokePattern.Invoke();
using (StreamWriter writer = new StreamWriter(logTxt, true))
{
writer.WriteLine("登录 提交");
}
}
catch (Exception exp)
{
}
}
break;
}
}