private void WatchWindow() { IntPtr handle; try { handle = process.MainWindowHandle; } catch (Exception e) { Util.Logging.Log(e); return; } Windows.FindWindow.TextComparer classCallback = new Windows.FindWindow.TextComparer( delegate(string name, StringBuilder sb) { if (sb.Length == EDIT_CLASSNAME.Length) { return(sb.ToString().Equals(EDIT_CLASSNAME)); } return(false); }); Windows.FindWindow.TextComparer textCallback = new Windows.FindWindow.TextComparer( delegate(string name, StringBuilder sb) { if (name != null && name.Equals(EDIT_CLASSNAME)) { return(sb.Length > 500 && sb[0] == '*'); } return(true); }); StringBuilder buffer = new StringBuilder(DX_WINDOW_CLASSNAME.Length); int length = 0; var wce = new WindowChangedEventArgs(); var _handle = IntPtr.Zero; //watched for windowed mode process exit with no handle -- likely an invisible crash due to outdated Local.dat do { try { process.Refresh(); handle = process.MainWindowHandle; } catch (Exception e) { Util.Logging.Log(e); } try { if (this.process.HasExited) { if (handle == IntPtr.Zero && (IsAutomaticLogin(this.Account.Settings) || Util.Args.Contains(this.Args, "nopatchui"))) { var elapsed = this.process.ExitTime.Subtract(this.process.StartTime).TotalSeconds; if (elapsed < 60 && WindowCrashed != null) { WindowCrashed(this, CrashReason.NoPatchUI); } } break; } } catch (Exception e) { Util.Logging.Log(e); return; } if (handle != IntPtr.Zero) { if (_handle != handle) { _handle = handle; wce.Handle = handle; if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.HandleChanged; WindowChanged(this, wce); } } buffer.Length = 0; NativeMethods.GetClassName(handle, buffer, buffer.Capacity + 1); int l; if ((l = buffer.Length) != length) { length = l; if (l > 0) { string className = buffer.ToString(); if (className.Equals(DX_WINDOW_CLASSNAME)) { if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.DxWindowCreated; WindowChanged(this, wce); } try { var t = DateTime.UtcNow.AddSeconds(30); do { buffer.Length = 0; if (NativeMethods.GetWindowText(handle, buffer, 2) > 0 && buffer[0] != 'U') //window text is initially Untitled { if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.TitleChanged; WindowChanged(this, wce); } break; } }while (DateTime.UtcNow < t && !process.WaitForExit(100)); } catch (Exception e) { Util.Logging.Log(e); } using (var volumeControl = new Windows.Volume.VolumeControl(process.Id)) { //already loaded if audio is initialized (only checks default playback device) var hasVolume = volumeControl.Query(); if (hasVolume) { if (WindowChanged != null && !this.process.HasExited) { wce.Type = WindowChangedEventArgs.EventType.DxWindowReady; WindowChanged(this, wce); } return; } var nextCheck_Seconds = 3; var memoryUsage = process.PeakWorkingSet64; var limit = DateTime.UtcNow.AddMinutes(3); var nextCheck = DateTime.UtcNow.AddSeconds(nextCheck_Seconds); var memoryChecks = 0; //var verified = false; do { //the volume will be initialized first, then a child process (CoherentUI) will spawn if (hasVolume = volumeControl.Query() || HasChildProcess(process.Id)) { break; } //watching for changes in memory usage to check if it's still doing something //bypassing the launching can cause it to get stuck on authentication if either the credentials are wrong or the network isn't authorized if (watchAutologin && DateTime.UtcNow > nextCheck) { process.Refresh(); var memoryChange = process.PeakWorkingSet64 - memoryUsage; memoryUsage += memoryChange; if (memoryChange < 1000000) { if (++memoryChecks == 3) { //this account may be stuck trying to authenticate var account = this.Account.Settings; if (account.HasCredentials && account.NetworkAuthorizationState != Settings.NetworkAuthorizationState.Disabled && Settings.NetworkAuthorization.HasValue) { if (account.NetworkAuthorizationState == Settings.NetworkAuthorizationState.Unknown) { if (AuthenticationRequired != null) { AuthenticationRequired(this, null); } return; } else { Tools.ArenaAccount session; switch (NetworkAuthorization.Verify(account, true, null, out session)) { case NetworkAuthorization.VerifyResult.Completed: case NetworkAuthorization.VerifyResult.Required: if (AuthenticationRequired != null) { AuthenticationRequired(this, session); } return; case NetworkAuthorization.VerifyResult.OK: //authentication was ok - assuming it's a slow load nextCheck_Seconds = 10; //verified = true; break; case NetworkAuthorization.VerifyResult.None: default: //authentication isn't being tracked - assuming it's stuck nextCheck_Seconds = 10; break; } } } else { //authentication isn't enabled nextCheck_Seconds = 10; } } else if (memoryChecks > 6) { if (AuthenticationRequired != null) { AuthenticationRequired(this, null); } return; } } else { memoryChecks = 0; } nextCheck = DateTime.UtcNow.AddSeconds(nextCheck_Seconds); } }while (DateTime.UtcNow < limit && !this.process.WaitForExit(500)); if (WindowChanged != null && !this.process.HasExited) { wce.Type = WindowChangedEventArgs.EventType.DxWindowReady; WindowChanged(this, wce); } return; } } else if (className[0] == '#') { var result = Find(handle, classCallback, textCallback); if (result != null) { CrashReason reason = CrashReason.Unknown; string text = result.Text; int i = text.IndexOf("Assertion:", 0, 100); if (i != -1) { int j = text.IndexOf('\n', i); if (text.IndexOf("Is your archive up to date?", i, j - i, StringComparison.OrdinalIgnoreCase) != -1 || text.IndexOf("Client needs to be patched", i, j - i, StringComparison.OrdinalIgnoreCase) != -1) { reason = CrashReason.PatchRequired; } } if (WindowCrashed != null) { WindowCrashed(this, reason); } } return; } else if (className.Equals(LAUNCHER_WINDOW_CLASSNAME)) { if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.TitleChanged; WindowChanged(this, wce); } } } } } }while (!process.WaitForExit(500) && this.watcher != null); }
private void WatchWindow() { IntPtr handle; try { handle = process.MainWindowHandle; } catch (Exception e) { Util.Logging.Log(e); return; } Windows.FindWindow.TextComparer classCallback = new Windows.FindWindow.TextComparer( delegate(string name, StringBuilder sb) { if (sb.Length == EDIT_CLASSNAME.Length) { return(sb.ToString().Equals(EDIT_CLASSNAME)); } return(false); }); Windows.FindWindow.TextComparer textCallback = new Windows.FindWindow.TextComparer( delegate(string name, StringBuilder sb) { if (name != null && name.Equals(EDIT_CLASSNAME)) { return(sb.Length > 500 && sb[0] == '*'); } return(true); }); StringBuilder buffer = new StringBuilder(DX_WINDOW_CLASSNAME.Length); int length = 0; var wce = new WindowChangedEventArgs(); var _handle = IntPtr.Zero; do { try { do { process.Refresh(); handle = process.MainWindowHandle; if (handle == IntPtr.Zero) { if (!process.WaitForExit(10)) { process.WaitForInputIdle(); } } else { break; } }while (!process.HasExited); } catch (Exception e) { Util.Logging.Log(e); } try { if (this.process.HasExited) { //no longer applicable - nopatchui exiting without creating a window likely meant the client wasn't up to date //if (handle == IntPtr.Zero && (IsAutomaticLogin(this.Account.Settings) || Util.Args.Contains(this.Args, "nopatchui"))) //{ // var elapsed = this.process.ExitTime.Subtract(this.process.StartTime).TotalSeconds; // if (elapsed < 60 && WindowCrashed != null) // WindowCrashed(this, CrashReason.NoPatchUI); //} break; } } catch (Exception e) { Util.Logging.Log(e); return; } if (handle != IntPtr.Zero) { if (_handle != handle) { _handle = handle; wce.Handle = handle; if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.HandleChanged; WindowChanged(this, wce); } } buffer.Length = 0; NativeMethods.GetClassName(handle, buffer, buffer.Capacity + 1); int l; if ((l = buffer.Length) != length) { length = l; if (l > 0) { string className = buffer.ToString(); if (className.Equals(DX_WINDOW_CLASSNAME)) { if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.DxWindowCreated; WindowChanged(this, wce); } //window initialization order: //title change > volume (window is ready) > coherentui int pidChild = 0; bool hasVolume = false; DateTime limit; using (var volumeControl = new Windows.Volume.VolumeControl(process.Id)) { if (processWasAlreadyStarted && ((hasVolume = volumeControl.Query()) || (pidChild = GetChildProcess(process.Id)) > 0)) { //window is already loaded if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.TitleChanged; WindowChanged(this, wce); } limit = DateTime.UtcNow.AddMinutes(1); } else { try { limit = DateTime.UtcNow.AddSeconds(30); var ws1 = IntPtr.Zero; do { //window style changes when loading into fullscreen mode var ws2 = NativeMethods.GetWindowLongPtr(handle, (int)GWL.GWL_STYLE); if (ws2 != ws1) { if (ws1 != IntPtr.Zero && WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.DxWindowStyleChanged; WindowChanged(this, wce); } ws1 = ws2; } buffer.Length = 0; if (NativeMethods.GetWindowText(handle, buffer, 2) > 0 && buffer[0] != 'U') //window text is initially Untitled { if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.TitleChanged; WindowChanged(this, wce); } break; } }while (DateTime.UtcNow < limit && !process.WaitForExit(100)); } catch (Exception e) { Util.Logging.Log(e); } var nextCheck_Seconds = 3; var memoryUsage = process.PeakWorkingSet64; var nextCheck = DateTime.UtcNow.AddSeconds(nextCheck_Seconds); var memoryChecks = 0; //var verified = false; limit = DateTime.UtcNow.AddMinutes(3); do { if ((hasVolume = volumeControl.Query()) || (pidChild = GetChildProcess(process.Id)) > 0) { break; } #region -nopatchui activity check (obsolete) //watching for changes in memory usage to check if it's still doing something //bypassing the launcher (-nopatchui) can cause it to get stuck on authentication if either the credentials are wrong or the network isn't authorized if (!hasVolume && DateTime.UtcNow > nextCheck) { process.Refresh(); var memoryChange = process.PeakWorkingSet64 - memoryUsage; memoryUsage += memoryChange; if (memoryChange < 1000000) { if (++memoryChecks == 3) { //this account may be stuck trying to authenticate var account = this.Account.Settings; if (account.HasCredentials && account.NetworkAuthorizationState != Settings.NetworkAuthorizationState.Disabled && Settings.NetworkAuthorization.HasValue) { if (account.NetworkAuthorizationState == Settings.NetworkAuthorizationState.Unknown) { if (AuthenticationRequired != null) { AuthenticationRequired(this, null); } return; } else { Tools.ArenaAccount session; switch (NetworkAuthorization.Verify(account, true, null, out session)) { case NetworkAuthorization.VerifyResult.Completed: case NetworkAuthorization.VerifyResult.Required: if (AuthenticationRequired != null) { AuthenticationRequired(this, session); } return; case NetworkAuthorization.VerifyResult.OK: //authentication was ok - assuming it's a slow load nextCheck_Seconds = 10; //verified = true; break; case NetworkAuthorization.VerifyResult.None: default: //authentication isn't being tracked - assuming it's stuck nextCheck_Seconds = 10; break; } } } else { //authentication isn't enabled nextCheck_Seconds = 10; } } else if (memoryChecks > 6) { if (AuthenticationRequired != null) { AuthenticationRequired(this, null); } return; } } else { memoryChecks = 0; } nextCheck = DateTime.UtcNow.AddSeconds(nextCheck_Seconds); } #endregion }while (DateTime.UtcNow < limit && !this.process.WaitForExit(500)); } } if (WindowChanged != null && !this.process.HasExited) { wce.Type = WindowChangedEventArgs.EventType.DxWindowReady; WindowChanged(this, wce); } #region Find CoherentUI if (pidChild == 0) { do { if ((pidChild = GetChildProcess(process.Id)) > 0) { break; } }while (DateTime.UtcNow < limit && !this.process.WaitForExit(500)); } if (pidChild > 0) { try { using (var child = Process.GetProcessById(pidChild)) { limit = DateTime.UtcNow.AddSeconds(10); var pid = pidChild; do { if ((pidChild = GetChildProcess(pid)) > 0) { break; } }while (DateTime.UtcNow < limit && !child.WaitForExit(500)); } if (pidChild > 0) { using (var child = Process.GetProcessById(pidChild)) { long mem = 0; byte counter = 0; limit = DateTime.UtcNow.AddSeconds(60); do { if (mem > 0) { child.Refresh(); } var _mem = child.PeakWorkingSet64; if (_mem > mem) { mem = _mem; counter = 0; } else if (++counter > 2) { break; } }while (DateTime.UtcNow < limit && !child.WaitForExit(100)); } } } catch (Exception ex) { Util.Logging.Log(ex); } } #endregion if (WindowChanged != null && !this.process.HasExited) { wce.Type = WindowChangedEventArgs.EventType.InGameCoherentUIReady; WindowChanged(this, wce); } return; } else if (className[0] == '#') { var result = Find(handle, classCallback, textCallback); if (result != null) { CrashReason reason = CrashReason.Unknown; string text = result.Text; int i = text.IndexOf("Assertion:", 0, 100); if (i != -1) { int j = text.IndexOf('\n', i); if (text.IndexOf("Is your archive up to date?", i, j - i, StringComparison.OrdinalIgnoreCase) != -1 || text.IndexOf("Client needs to be patched", i, j - i, StringComparison.OrdinalIgnoreCase) != -1) { reason = CrashReason.PatchRequired; } } if (WindowCrashed != null) { WindowCrashed(this, reason); } } return; } else if (className.Equals(LAUNCHER_WINDOW_CLASSNAME)) { if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.TitleChanged; WindowChanged(this, wce); } #region Find CoherentUI var pidChild = 0; var limit = DateTime.UtcNow.AddSeconds(30); do { if ((pidChild = GetChildProcess(process.Id)) > 0) { break; } this.process.Refresh(); if (_handle != this.process.MainWindowHandle) { break; } }while (DateTime.UtcNow < limit && !this.process.WaitForExit(500)); if (pidChild > 0) { try { using (var child = Process.GetProcessById(pidChild)) { limit = DateTime.UtcNow.AddSeconds(10); var pid = pidChild; do { if ((pidChild = GetChildProcess(pid)) > 0) { break; } this.process.Refresh(); if (_handle != this.process.MainWindowHandle) { break; } }while (DateTime.UtcNow < limit && !child.WaitForExit(500)); } if (pidChild > 0) { using (var child = Process.GetProcessById(pidChild)) { long mem = 0; byte counter = 0; limit = DateTime.UtcNow.AddSeconds(60); do { if (mem > 0) { child.Refresh(); } var _mem = child.PeakWorkingSet64; if (_mem > mem) { mem = _mem; counter = 0; } else if (++counter > 2) { break; } }while (DateTime.UtcNow < limit && !child.WaitForExit(100)); if (WindowChanged != null) { wce.Type = WindowChangedEventArgs.EventType.LauncherCoherentUIReady; WindowChanged(this, wce); } } } } catch (Exception ex) { Util.Logging.Log(ex); } } #endregion } } } } }while (!process.WaitForExit(500) && this.watcher != null); }