private void Unlocked() { LockEndTime = DateTime.Now; _locked = false; _lastWinState = WinEvntState.Unknown; TimeSpan lockedInterval = LockEndTime - LockStartTime; #if DEBUG Console.WriteLine($" ** UnLocked Writing Time: {LockEndTime} AppName: {Globals.LastWindowEvent.AppName} Title: {Globals.LastWindowEvent.WindowTitle} Project: {Globals.LastWindowEvent.DevProjectName}"); #endif var hlpr = new DHWindowEvents(AppWrapper.AppWrapper.DevTrkrConnectionString); lock (Globals.SyncLockObject) { Globals.LastWindowEvent.EndTime = LockEndTime; var row = hlpr.InsertWindowEvent(Globals.LastWindowEvent); //next, create a new LastWindowEvent // 2) create a locked window event in database string displayName; try { displayName = UserPrincipal.Current.DisplayName; } catch (Exception ex) { displayName = Environment.UserName; } var item = new WindowEvent { ID = Guid.NewGuid().ToString(), StartTime = LockStartTime, WindowTitle = AppWrapper.AppWrapper.AppName, AppName = AppWrapper.AppWrapper.AppName, ModuleName = AppWrapper.AppWrapper.AppName, //EndTime = LockEndTime, DevProjectName = AppWrapper.AppWrapper.AppName, ITProjectID = string.Empty, UserName = Environment.UserName, MachineName = Environment.MachineName, UserDisplayName = displayName }; Globals.LastWindowEvent = item; //var rows = hlpr.InsertWindowEvent(item); } WindowPolling.ResumeWindowPolling(); }
private void UpdateUnknownProjectNameForIDEMatch(string devProjectName, string appName, string unknownKey, string machineName, string userName) { var hlpr = new DHWindowEvents(AppWrapper.AppWrapper.DevTrkrConnectionString); hlpr.UpdateUnknownProjectNameForIDEMatch(devProjectName, appName, unknownKey, machineName, userName); }
/// <summary> /// If log is null do not create a listvieew item, just log to database /// </summary> /// <param name="p"></param> /// <param name="log"></param> public WindowEvents(Process p, ListView log) { try { var now = DateTime.Now; // time window changed Log = log; var title = GetActiveWindowTitle(); #if DEBUG Debug.WriteLine("**** " + title); #endif if (title == null) { return; } var accessDenied = false; var moduleName = string.Empty; try { moduleName = p.MainModule.ModuleName; } catch (Win32Exception ex) { // process access denied b/c it is running as admin moduleName = "Process-Access Denied"; accessDenied = true; } string displayName; try { displayName = UserPrincipal.Current.DisplayName; } catch (Exception ex) { displayName = Environment.UserName; } if (Globals.LastWindowEvent == null) { Globals.StartTime = now; } TimeSpan elapsedTime = now - Globals.StartTime; Globals.StartTime = now; // current window change time var devPrjName = string.Empty; // We set some globals so the file watcher knows who is running try { _currentApp = Globals.CurrentApp = !accessDenied ? p.ProcessName : AccessDenied; } catch (Exception ex) { Debug.WriteLine(ex.Message); Globals.CurrentApp = "Unknown"; } bool writeDB = false; /* make project extraction generic with regex */ // new code ***** NOTE: THE ideMATCHES TABLE SHOULD ONLY HAVE IDES AND SSMS **** // **** WE ARE DOING THIS FOR SSMS THO NOT AN IDE B/C SO MUCH TIME IS SPENT THERE // **** AND EVEN GROUPING BY A SERVER.DBNAME MAY GIVE A CLUE TO THE DEV PROJECT IDEMatch ideMatchObject = null; //new evidence for mulltiple IDEMatch objects for the same IDE i.e., devenv // title = "Add New Item - BusinessObjects" // title = "Add Existing Item - BusinessObjects" // title = "Reference Manager - DevTrkrReports" // ssms failing to update project name here and the reason is that ide.DBUnknown is // likely not set b/c ide switches when window changes, .^. some switch must reside in globals // that will persist between window changes.. and cache updates // i think that is what the index in the match table was // about and then I forgot its purpose and removed it //NOTE: simplifying problem by updating below for whichever app when we find a new match //bool foundIDE = false; foreach (var ide in Globals.IDEMatches) { if (!accessDenied && _currentApp.ToLower() == ide.AppName) { var pat = ide.Regex; var m = Regex.Match(title, pat, RegexOptions.IgnoreCase); devPrjName = string.Empty; if (m.Success && m.Groups[ide.RegexGroupName] != null && !string.IsNullOrWhiteSpace(m.Groups[ide.RegexGroupName].Value)) { #if DEBUG if (ide.AppName == "ssms") { Debug.WriteLine(title); } #endif // we found the ide match we are looking for ideMatchObject = ide; // if we are concatinating two fields in ssms to get server.dbname if (!string.IsNullOrWhiteSpace(ide.ProjNameConcat)) { string[] concats = ide.ProjNameConcat.Split('|'); for (var i = 0; i < concats.Length; i++) { devPrjName += (i > 0 ? ide.ConcatChar : string.Empty) + m.Groups[concats[i]].Value; } } else { devPrjName = m.Groups[ide.RegexGroupName].Value; } if (!string.IsNullOrWhiteSpace(ide.ProjNameReplaces)) { string[] replaces = ide.ProjNameReplaces.Split('|'); foreach (string s in replaces) { devPrjName = devPrjName.Replace(s, string.Empty).Trim(); } } // NOTE: new logic for IDEMatch objects that have AlternateProjName // if it is not null, replace devPrjName with it b/c altho we found a project name // it is not one we want, so make it what we want (probably the same as the unknown value) // so that it will be updated when we find the projname we want... // e.g., ssms has master set so DBname = Server.master in table but new logic will be correctable // e.g. ssms "not connected" can now have its own match object and will get set to unknow until // user connects to a database which they will have to do in order to do anything in ssms if (ideMatchObject.AlternateProjName != null) { devPrjName = ideMatchObject.AlternateProjName; } // update project name in windowevents that were written with projname unknown UpdateUnknownProjectNameForIDEMatch(devPrjName, ide.AppName, ide.UnknownValue, Environment.MachineName, Environment.UserName); // since we have found and processed the ide that we wanted, get out writeDB = true; goto EndOfGenericCode; } else /// if (m.Success && string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) { // ide has no project open yet set as unknown devPrjName = ide.UnknownValue;; //ide.DBUnknown = true; writeDB = true; continue; // loop to see if another IDEMatch row will get the projectname } // *** removing so we loop to see if another IDEMatch will get the PrjName ...goto EndOfGenericCode; } } // if at this point we did not find an idematch just an unknown window EndOfGenericCode: // end new code #region old code /* strt of old cod * if (!accessDenied && Globals.CurrentApp == AppWrapper.AppWrapper.devenv) * { * var patt = "(?<PrjName>.*?)(?<spacer> - )*Microsoft Visual Studio|Microsoft Visual Studio"; * var m = Regex.Match(title, patt); * devPrjName = string.Empty; * if (m.Success && m.Groups["PrjName"] != null && !string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) * { * devPrjName = m.Groups["PrjName"].Value.Replace("(Running)", string.Empty).Replace("(Debugging)", string.Empty).Trim(); * if (Globals.VSUnknowns) * { * Globals.VSUnknowns = false; * UpdateUnknownProjectNameinVSCode(devPrjName, AppWrapper.AppWrapper.devenv, AppWrapper.AppWrapper.VSUnknown); * } * } * else // regex failed to get prjname * { * /* this is one place where project name can be set to devenv * and should not be * //devPrjName = p.ProcessName; * devPrjName = AppWrapper.AppWrapper.VSUnknown; // "devenvUnKnown"; * Globals.VSUnknowns = true; * } * } * else if (!accessDenied && Globals.CurrentApp == AppWrapper.AppWrapper.VSCode) * { * // we are in VSCode * var pattVSCode = "^(?<FileName>.*?) - (?<PrjName>.*?) - Visual Studio Code|Welcome - Visual Studio Code|Open Folder"; * var m = Regex.Match(title, pattVSCode); * devPrjName = string.Empty; * if (m.Success && m.Groups["PrjName"] != null && !string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) * { * devPrjName = m.Groups["PrjName"].Value; * if (Globals.VSCodeUnknowns) * { * Globals.VSCodeUnknowns = false; * UpdateUnknownProjectNameinVSCode(devPrjName, Globals.CurrentApp, AppWrapper.AppWrapper.VSCodeUnknown); * } * } * else if (m.Success && string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) * { * // in vscode the * // developer has not opened a folder yet so mark * // the project name unknown until VSCode opens a folder and then * // we can update the database records * devPrjName = AppWrapper.AppWrapper.VSCodeUnknown; * Globals.VSCodeUnknowns = true; * } * else * { * devPrjName = AppWrapper.AppWrapper.VSCodeUnknown; * Globals.VSCodeUnknowns = true; * } * } * else * { * // current app is not VS or VSCode we will only be able to determine * // the project name if this app saves a file to a known project path * devPrjName = !accessDenied ? p.ProcessName : AccessDenied; * if (devPrjName == "devenv") * { * devPrjName = AppWrapper.AppWrapper.VSUnknown; * Globals.VSUnknowns = true; * } * } * end of generic changes */ //appears to be so FileWatcher can pick it out to use in creating FileAnalyzer // could be set in Globals.LastWindowEvent object not in two places // below is wrong b/c it is updating the last window that we are no longer in //Globals.LastWindowEvent.DevProjectName = devPrjName; // in practice, if you do not record all events, you could later want // to see some that would not be recorded //if (title == "Window Change Log" || title == "Project Description" || p.ProcessName == "DevTracker" || p.ProcessName == "explorer") // return; #endregion //var currApp = !accessDenied ? p.ProcessName : AccessDenied; // see if we are interested in recording this window // if writeDB set, then we already know to write this window b/c of ideMatch found if (!writeDB) { var appConfig = Globals.ConfigOptions.Find(x => x.Name == "RECORDAPPS"); if (appConfig != null) { switch (appConfig.Value) { case "A": writeDB = true; break; case "S": var interestingApp = Globals.NotableApplications.Find(o => o.AppName.ToLower() == _currentApp.ToLower()); writeDB = (interestingApp != null); break; } } } if (_currentApp.ToLower() == "explorer") { writeDB = false; // forget } // if we are writing this window, and devProjectName not set yet // see if a known project name is being worked on by a non IDE //TODO in a large shop this may be time consuming if (writeDB && string.IsNullOrWhiteSpace(devPrjName)) { devPrjName = IsProjectInNonIDETitle(title); } WindowEvent item; /* there are other threads, FileWatcher, changing this object * and we want no conflict */ if (Globals.LastWindowEvent != null) { lock (Globals.LastWindowEvent) { item = Globals.LastWindowEvent; item.EndTime = now; Globals.StartTime = now; if (writeDB) { var hlpr = new DHWindowEvents(AppWrapper.AppWrapper.DevTrkrConnectionString); if (item.DevProjectName.Equals("devenv")) { Debug.WriteLine(item.DevProjectName); } if (ideMatchObject != null && !ideMatchObject.IsIde && !devPrjName.ToLower().Contains(".sql")) { // if next line true if ((!string.IsNullOrWhiteSpace(ideMatchObject.AlternateProjName) && devPrjName == ideMatchObject.AlternateProjName) || devPrjName == ".") { Debug.WriteLine(devPrjName); } else { CheckForInsertingNewProjectPath(devPrjName, ideMatchObject.UnknownValue, Environment.UserName, Environment.MachineName, ideMatchObject.AppName); } } int rows; if (_currentApp.ToLower() == "explorer") { Debug.WriteLine("Dont write explorer"); } rows = hlpr.InsertWindowEvent(item); } } } var appNName = !accessDenied ? p.ProcessName : AccessDenied; item = new WindowEvent { ID = Guid.NewGuid().ToString(), StartTime = now, WindowTitle = title, AppName = appNName, ModuleName = moduleName, EndTime = DateTime.MinValue, DevProjectName = devPrjName, ITProjectID = string.Empty, UserName = Environment.UserName, MachineName = Environment.MachineName, UserDisplayName = displayName }; Globals.LastWindowEvent = item; const string comma = ","; #if DEBUG Debug.Write("*******" + title + comma + _startTime.ToString("HH:mm:ss") + comma + item.EndTime.ToString("HH:mm:ss") + comma + item.AppName + comma + item.ModuleName + comma + devPrjName + Environment.NewLine); #endif } catch (Exception ex) { Debug.Print(ex.Message); } }
//NOTE: new constructor public WindowEvents(/* blank constructor to process the queue WinEventProcesss p, ListView log*/) { try { WinEventProcesss wep; TopOfCode: // get a queue item if it exists while (true) { lock (Globals.WinEventQueue) { if (Globals.WinEventQueue.Count.Equals(0)) { Globals.WindowEventThreadRunning = false; return; } wep = Globals.WinEventQueue.Peek(); Globals.WinEventQueue.Dequeue(); break; } } var now = wep.Starttime; // time window changed var title = wep.MyWindowEvent.WindowTitle; #if DEBUG Debug.WriteLine("**** " + title); #endif if (title == null) { return; } var accessDenied = false; var moduleName = wep.MyWindowEvent.ModuleName; string displayName = wep.MyWindowEvent.UserDisplayName; var devPrjName = string.Empty; // We set some globals so the file watcher knows who is running _currentApp = wep.MyWindowEvent.AppName; bool writeDB = false; /* make project extraction generic with regex */ // new code ***** NOTE: THE ideMATCHES TABLE SHOULD ONLY HAVE IDES AND SSMS **** // **** WE ARE DOING THIS FOR SSMS THO NOT AN IDE B/C SO MUCH TIME IS SPENT THERE // **** AND EVEN GROUPING BY A SERVER.DBNAME MAY GIVE A CLUE TO THE DEV PROJECT IDEMatch ideMatchObject = null; //new evidence for mulltiple IDEMatch objects for the same IDE i.e., devenv // title = "Add New Item - BusinessObjects" // title = "Add Existing Item - BusinessObjects" // title = "Reference Manager - DevTrkrReports" // ssms failing to update project name here and the reason is that ide.DBUnknown is // likely not set b/c ide switches when window changes, .^. some switch must reside in globals // that will persist between window changes.. and cache updates // i think that is what the index in the match table was // about and then I forgot its purpose and removed it //NOTE: simplifying problem by updating below for whichever app when we find a new match //bool foundIDE = false; foreach (var ide in Globals.IDEMatches) { if (!accessDenied && _currentApp.ToLower() == ide.AppName) { var pat = ide.Regex; var m = Regex.Match(title, pat, RegexOptions.IgnoreCase); devPrjName = string.Empty; if (m.Success && m.Groups[ide.RegexGroupName] != null && !string.IsNullOrWhiteSpace(m.Groups[ide.RegexGroupName].Value)) { #if DEBUG if (ide.AppName == "ssms") { Debug.WriteLine(title); } #endif // we found the ide match we are looking for ideMatchObject = ide; // if we are concatinating two fields in ssms to get server.dbname if (!string.IsNullOrWhiteSpace(ide.ProjNameConcat)) { string[] concats = ide.ProjNameConcat.Split('|'); for (var i = 0; i < concats.Length; i++) { devPrjName += (i > 0 ? ide.ConcatChar : string.Empty) + m.Groups[concats[i]].Value; } } else { devPrjName = m.Groups[ide.RegexGroupName].Value; } if (!string.IsNullOrWhiteSpace(ide.ProjNameReplaces)) { string[] replaces = ide.ProjNameReplaces.Split('|'); foreach (string s in replaces) { devPrjName = devPrjName.Replace(s, string.Empty).Trim(); } } // NOTE: new logic for IDEMatch objects that have AlternateProjName // if it is not null, replace devPrjName with it b/c altho we found a project name // it is not one we want, so make it what we want (probably the same as the unknown value) // so that it will be updated when we find the projname we want... // e.g., ssms has master set so DBname = Server.master in table but new logic will be correctable // e.g. ssms "not connected" can now have its own match object and will get set to unknow until // user connects to a database which they will have to do in order to do anything in ssms if (ideMatchObject.AlternateProjName != null) { devPrjName = ideMatchObject.AlternateProjName; } // update project name in windowevents that were written with projname unknown UpdateUnknownProjectNameForIDEMatch(devPrjName, ide.AppName, ide.UnknownValue, Environment.MachineName, Environment.UserName); // since we have found and processed the ide that we wanted, get out writeDB = true; goto EndOfGenericCode; } else /// if (m.Success && string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) { // ide has no project open yet set as unknown devPrjName = ide.UnknownValue;; //ide.DBUnknown = true; writeDB = true; continue; // loop to see if another IDEMatch row will get the projectname } // *** removing so we loop to see if another IDEMatch will get the PrjName ...goto EndOfGenericCode; } } // if at this point we did not find an idematch just an unknown window EndOfGenericCode: // end new code // see if we are interested in recording this window // NOTE: we may always want to write the window to DB b/c the company may want to know every app being used // especially if we want to run the Developer(user) Detail Report // if writeDB set, then we already know to write this window b/c of ideMatch found if (!writeDB) { var appConfig = Globals.ConfigOptions.Find(x => x.Name == "RECORDAPPS"); if (appConfig != null) { switch (appConfig.Value) { case "A": writeDB = true; break; case "S": var interestingApp = Globals.NotableApplications.Find(o => o.AppName.ToLower() == _currentApp.ToLower()); writeDB = (interestingApp != null); break; } } } if (_currentApp.ToLower() == "explorer") { writeDB = false; // forget } // if we are writing this window, and devProjectName not set yet // see if a known project name is being worked on by a non IDE //TODO in a large shop this may be time consuming if (writeDB && string.IsNullOrWhiteSpace(devPrjName)) { // check to see if the window title contains a known project name devPrjName = IsProjectInNonIDETitle(title); } else { goto TopOfCode; } WindowEvent item; /* Here, we are going to record the window in db * there are other threads, FileWatcher, changing this object * and we want no conflict */ if (Globals.LastWindowEvent != null) { lock (Globals.LastWindowEvent) { item = Globals.LastWindowEvent; item.EndTime = now; Globals.StartTime = now; if (writeDB) { var hlpr = new DHWindowEvents(AppWrapper.AppWrapper.DevTrkrConnectionString); if (item.DevProjectName.Equals("devenv")) { Debug.WriteLine(item.DevProjectName); } if (ideMatchObject != null && !ideMatchObject.IsIde && !devPrjName.ToLower().Contains(".sql")) { // if next line true if ((!string.IsNullOrWhiteSpace(ideMatchObject.AlternateProjName) && devPrjName == ideMatchObject.AlternateProjName) || devPrjName == ".") { Debug.WriteLine(devPrjName); } else { if ("Connect.to_Repor._DevTrack.".Contains(devPrjName) || devPrjName.EndsWith(".")) { Debug.WriteLine("bad project name"); } else { CheckForInsertingNewProjectPath(devPrjName, ideMatchObject.UnknownValue, Environment.UserName, Environment.MachineName, ideMatchObject.AppName); } } } int rows; if (_currentApp.ToLower() == "explorer") { Debug.WriteLine("Dont write explorer"); } rows = hlpr.InsertWindowEvent(item); } } } const string comma = ","; #if DEBUG Debug.Write("******* " + title + comma + _startTime.ToString("HH:mm:ss") + comma + wep.MyWindowEvent.EndTime.ToString("HH:mm:ss") + comma + wep.MyWindowEvent.AppName + comma + wep.MyWindowEvent.ModuleName + comma + devPrjName + Environment.NewLine); #endif goto TopOfCode; // check for more } catch (Exception ex) { Debug.Print(ex.Message); } }
/// <summary> /// NOTE: You can put breakpoints in here, but if you do, then writing becomes iffy in spite of /// all that I have done to queue and thread, there are still issues in debugging /// b/c stopping in here changes the window event /// </summary> public WindowEvents() { WinEventProcesss wep; TopOfCode: try { // get a queue item if it exists while (true) { lock (Globals.SyncLockObject) { if (Globals.WinEventQueue.Count.Equals(0)) { Globals.WindowEventThreadRunning = false; return; } wep = Globals.WinEventQueue.Peek(); Globals.WinEventQueue.Dequeue(); break; } } var now = wep.Starttime; // time window changed var title = wep.MyWindowEvent.WindowTitle; if (string.IsNullOrWhiteSpace(title)) { return; } var accessDenied = false; var moduleName = wep.MyWindowEvent.ModuleName; string displayName = wep.MyWindowEvent.UserDisplayName; var devPrjName = string.Empty; var syncId = string.Empty; // We set some properties so the file watcher knows who is running _currentApp = wep.MyWindowEvent.AppName.ToLower(); bool writeDB = false; /* make project extraction generic with regex */ // new code ***** NOTE: THE ideMATCHES TABLE SHOULD ONLY HAVE IDES AND SSMS (DBMGRs) **** // **** WE ARE DOING THIS FOR SSMS THO NOT AN IDE B/C SO MUCH TIME IS SPENT THERE // **** AND EVEN GROUPING BY A SERVER.DBNAME MAY GIVE A CLUE TO THE DEV PROJECT IDEMatch ideMatchObject = null; // ssms failing to update project name here and the reason is that ide.DBUnknown is // likely not set b/c ide switches when window changes, .^. some switch must reside in globals // that will persist between window changes.. and cache updates // i think that is what the index in the match table was // about and then I forgot its purpose and removed it //NOTE: cfp.GetProjectName also sets writeDB base on multiple checks // including the config option RECORDAPPS so the decision whether // to record this window is made there var cfp = new CheckForProjectName(); Tuple <string, IDEMatch, bool, string> cfpObject = cfp.GetProjectName(title, accessDenied, _currentApp, writeDB); devPrjName = cfpObject.Item1; writeDB = cfpObject.Item3; ideMatchObject = cfpObject.Item2; wep.MyWindowEvent.SyncID = cfpObject.Item4; // if we are writing this window, and devProjectName not set yet // see if a known project name is being worked on by a non IDE if (writeDB) { // check to see if the window title contains a known project name if (string.IsNullOrWhiteSpace(devPrjName)) { Tuple <string, string> prjObject = cfp.IsProjectInNonIDETitle(title); if (prjObject != null) { devPrjName = prjObject.Item1; wep.MyWindowEvent.SyncID = prjObject.Item2; } } } else { // one or more goto TopOfCode; } // try to get syncId from DevProjects var hlpr = new DHWindowEvents(); if (/*ideMatchObject != null && !ideMatchObject.IsDBEngine && */ !string.IsNullOrWhiteSpace(devPrjName)) { MaintainProject mp = new MaintainProject(); // bypass next line until we get fw debugged if (string.IsNullOrWhiteSpace(wep.MyWindowEvent.SyncID)) { wep.MyWindowEvent.SyncID = mp.GetProjectSyncIDForProjectName(devPrjName); //, _currentApp); } // here we should update any WindowEvents that have been created by this appname // and for this project w/o a syncid if (!string.IsNullOrWhiteSpace(wep.MyWindowEvent.SyncID)) { hlpr.UpdateWindowEventsWithSyncID(devPrjName, _currentApp, wep.MyWindowEvent.SyncID); } } /* Here, we are going to record the window in db * there are other threads, FileWatcher, changing this object * and we want no conflict */ WindowEvent item = wep.MyWindowEvent; if (item.AppName == "ApplicationFrameHost" && item.WindowTitle.Contains("Solitaire")) { item.AppName = item.WindowTitle; } if (!string.IsNullOrWhiteSpace(item.DevProjectName) && item.DevProjectName.Equals("devenv")) { _ = new LogError($"WindowEvent, Bad Project Name of 'devenv' from Title: {item.WindowTitle}", false, "WindowEvents.ctor"); } if (ideMatchObject != null && devPrjName == "DevTracker" && ideMatchObject.AppName == "ssms") { _ = new LogError($"WindowEvents, 'Devtracker' should not be the project name for ssms, Title: {item.WindowTitle}", false, "WindowEvent.ctor"); } //NOTE: 4 / 27 / 2020 discontinued this doing anything...except writing bad data notes // Window events does not have devpath and therefore is not qualified to insert a project if (ideMatchObject != null && !ideMatchObject.IsIde && !devPrjName.ToLower().Contains(".sql")) { // if next line true if (/*(!string.IsNullOrWhiteSpace(ideMatchObject.AlternateProjName) && devPrjName == ideMatchObject.AlternateProjName) || */ devPrjName == ".") { _ = new LogError($"WindowEvents, Bad Project Name: {devPrjName} from Title: {item.WindowTitle}", false, "WindowEvents.ctor"); } else { // this is a check for convoluted name going to DevProjectName if ("Connect.to_Repor._DevTrack.".Contains(devPrjName) || devPrjName.EndsWith(".")) { _ = new LogError($"WindowEvents bad project name = {devPrjName} from Title: {item.WindowTitle}", false, "WindowEvents.ctor"); } //NOTE: we should not set an unknown value in the devproject table // the way the sproc is written, this call will insert a new project with unknown path // if the project does not exist, and if it does exist, this call will not update // the path b/c this call is passing xxUnknown as the the path, // if this is the way a project gets created in DevProjects, it will only get the correct path // from the save of a project file, which only get done when a new project is created else if (string.IsNullOrWhiteSpace(devPrjName) || string.IsNullOrWhiteSpace(ideMatchObject.UnknownValue)) { _ = new LogError($"WindowEvents, Missing Data, Project: {devPrjName} Path: {ideMatchObject.UnknownValue} from Title: {item.WindowTitle}", false, "WindowEvents.ctor"); } else { // we create projects for database servers here for two reasons // 1) they don't have a path // 2) they will get no files saved with any relation to a path so FileAnalyzer won't create the project // when development of DevTracker was begun, windowevents // was the only way we had of possibly getting the project // name, but it did not, nor does it now have a way to get // the path. FileAnalyzer on the other hand has an exact way of // deriving the path to the projectFile .xxproj so Les has made // the decision of stopping what is at best doing half the job // except for database projects //Debug.WriteLine($"**** Would have checked for writing {devPrjName} to DevProjects"); if (ideMatchObject != null && ideMatchObject.IsDBEngine && devPrjName != ideMatchObject.AlternateProjName) { var mp = new MaintainProject(); DevProjPath dpp = new DevProjPath { DevProjectName = devPrjName, DevProjectPath = ideMatchObject.UnknownValue, IDEAppName = item.AppName, DatabaseProject = ideMatchObject.IsDBEngine, CountLines = false, ProjFileExt = "sql", DevSLNPath = string.Empty, GitURL = devPrjName, Machine = Environment.MachineName, UserName = Environment.UserName, CreatedDate = DateTime.Now }; item.SyncID = mp.CheckForInsertingNewProjectPath(dpp); } } } } if (item.AppName == "ssms" && item.DevProjectName == "Microsoft") { _ = new LogError($"WindowEvents, Bad Project name 'Microsoft' from Title: {item.WindowTitle}", false, "WindowEvents.ctor"); } item.DevProjectName = devPrjName; hlpr = new DHWindowEvents(); int rows = hlpr.InsertWindowEvent(item); goto TopOfCode; // check for more queue entries } catch (Exception ex) { _ = new LogError(ex, false, "WindowEvents.ctor"); } goto TopOfCode; }
public FileAnalyzer() { try { FileChange fc; // get instance of Project/File handler MaintainProject mp = new MaintainProject(); TopOfCode: // get a queue item while (true) { // this lock is local to this machine, and protects the Queue // b/c we are multithreaded and multiple cores (processing) lock (Globals.SyncLockObject) { if (Globals.FileChangeQueue.Count.Equals(0)) { Globals.FileAnalyzerThreadRunning = false; return; } fc = Globals.FileChangeQueue.Peek(); Globals.FileChangeQueue.Dequeue(); break; } } //NOTE: the original reason for the FileWatcher is not to record files being manipulated, // Rather it is to help maintain the DevProjects Table with the best information posssible // so that the WindowEvents class can charge the project and to report back to the windowevents // LastWindowEvent the project being worked on by the currentapp string syncID = null; var ext = Path.GetExtension(fc.FullPath).Replace(".", string.Empty).ToLower(); var devPath = string.Empty; var devProject = string.Empty; var relativeFileName = string.Empty; // filename past the pathname // but we should check to see if the file being saved is a .xxproj file, // we check for "CheckForInsertingNewProjectPath" var projFileObject = Globals.NotableFiles.Find(x => x.Extension == ext); var ideMatch = Globals.IDEMatches.Find(x => x.AppName == fc.CurrentApp); // HERE, app is ide and we are Manupulating the Project File (.xxproj) if (ideMatch != null && ideMatch.IsIde && projFileObject != null && !string.IsNullOrWhiteSpace(projFileObject.IDEProjectExtension) && projFileObject.IDEProjectExtension.Equals(ext)) { // yes, we are manipulating a development language project or sln file // therefore the path is where the project file is saved and // by definition, the filename is the name of the project fc.ProjectName = Path.GetFileNameWithoutExtension(fc.FullPath); devPath = Path.GetDirectoryName(fc.FullPath); // if devPath not in DevProjects path insert it, else update the pathname DevProjPath dpp = new DevProjPath { DevProjectName = fc.ProjectName, DevProjectPath = devPath, IDEAppName = fc.CurrentApp, DatabaseProject = ideMatch.IsDBEngine, CountLines = projFileObject.CountLines, ProjFileExt = projFileObject.IDEProjectExtension }; dpp.DevSLNPath = mp.FindSLNFileFromProjectPath(dpp); dpp.SyncID = syncID = mp.CheckForInsertingNewProjectPath(dpp, fc.FullPath); // the .sln may or may not be in the project table at the time the // project is created, in fact it may never be there for a number of reasons, // e.g., DLL project, etc., but a project may get a .sln at later time // here we do not know whether the code above wrote to DevProjects if (ext.Equals("sln")) { mp.UpdateSLNPathInProject(dpp); } // since project files (.cs,.vb, etc.) could be created before // the project file (.xxproj), now that we know for sure the // name and path, update all the files that may be missing them //NOTE: also, other project files will be saved after this but // we will not update them here until all files are are saved in the // project and the project file .xxproj get saved again which is // unless we get a method to update all the files in a project .xxproj file // which I just did so instead of the call below, we need to // 1. Get a list of all files from the project file. // 2. Loop thru the list of files from step 1. // 3. Call InsertUpdateFileActivity(with fa.Filename = each file from step 2) // We can do this without being concerned about time b/c this class runs on separate thread // from the FileWatcher and is processing files in sequence as they are queued // by FileWatcher class } else if (ideMatch != null && ideMatch.IsIde && projFileObject != null && !string.IsNullOrWhiteSpace(projFileObject.IDEProjectExtension)) { // this is an ide, but not saving a .xxproj file, but we know what the extension of the project file is // here we are addressing the issue of existing projects when this application is installed // look at FileAnalyzer saving code files for a project that has xxUnknown for a path // it could try to find the project name and path by enhancing GetProjectPath() to look for the // project file in the path somewhere // NOTE: changing logic here to something more reliable 04/17/2020 DevProjPath pp = mp.IsFileInADevProjectPath(fc.FullPath); // if pp not null, we have a known project // **** and the following lines is not doing anything... // b/c the project is already in devprojects and ChkforInsert..will do nothing if (pp != null) { fc.ProjectName = pp.DevProjectName; devPath = pp.DevProjectPath; syncID = mp.CheckForInsertingNewProjectPath(new DevProjPath { DevProjectName = fc.ProjectName, DevProjectPath = devPath, IDEAppName = fc.CurrentApp, DatabaseProject = ideMatch.IsDBEngine, CountLines = projFileObject.CountLines, ProjFileExt = projFileObject.IDEProjectExtension }, fc.FullPath); } else { // NOTE: if GetProjectPath returns project name and path it // found the .xxproj file so we are sure that we can check for inserting // a new project in DevProjects table Tuple <string, string, string> tuple = mp.GetProjectFromDevFileSave(fc.FullPath, Globals.NotableFiles, ext); if (tuple == null || string.IsNullOrWhiteSpace(tuple.Item1)) { goto TopOfCode; } else { //NOTE: if devenv is installing something don't let it fool // us into creating a project if ((fc.CurrentApp == "devenv" && tuple.Item2.ToLower().Contains("program files")) || tuple.Item1.ToLower().Contains("install")) { goto TopOfCode; } fc.ProjectName = tuple.Item1; devPath = tuple.Item2; syncID = mp.CheckForInsertingNewProjectPath( new DevProjPath { DevProjectName = tuple.Item1, DevProjectPath = tuple.Item2, IDEAppName = fc.CurrentApp, DatabaseProject = ideMatch.IsDBEngine, CountLines = projFileObject.CountLines, ProjFileExt = projFileObject.IDEProjectExtension }, fc.FullPath); } } } else if (fc.FullPath.ToLower().EndsWith(@"\.git\config")) { // we can try to get the project file name from git config file // the config file will not get a URL until the local repo is pushed to the server var url = mp.GetGitURLFromConfigFile(fc.FullPath); if (string.IsNullOrWhiteSpace(url)) { goto TopOfCode; } // get the repo name from the url, should be a projectname, may not be var m = Regex.Match(url, @".*/(?<PrjName>.*?)\.git"); var trialProjName = m.Success && !string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value) ? m.Groups["PrjName"].Value : string.Empty; // if the url does not contain a projectname quit if (string.IsNullOrWhiteSpace(trialProjName)) { goto TopOfCode; } var exten = Globals.NotableFiles.Find(x => x.Extension == "config"); if (exten == null) { goto TopOfCode; } Tuple <string, string, string> projObj = mp.GetProjectFromGitConfigSaved(fc.FullPath, Globals.NotableFiles); if (projObj == null) { goto TopOfCode; } fc.ProjectName = projObj.Item1; devPath = projObj.Item2; syncID = mp.CheckForInsertingNewProjectPath( new DevProjPath { DevProjectName = projObj.Item1, DevProjectPath = projObj.Item2, IDEAppName = fc.CurrentApp, DatabaseProject = false, // above code found vs project extension CountLines = true, ProjFileExt = projObj.Item3 }, fc.FullPath); } else { // current app is not an IDE, or we don't have a projectFileExt type, // so see if file is saved in // a known current devprojectpath for this machine and user DevProjPath pp = mp.IsFileInADevProjectPath(fc.FullPath); if (pp == null) { goto TopOfCode; } // if this is a .sln file, check for updating the sln path in pp if (ext == "sln" && string.IsNullOrWhiteSpace(pp.DevSLNPath)) { pp.DevSLNPath = Path.GetDirectoryName(fc.FullPath); mp.UpdateSLNPathInProject(pp); } // yes, this file is project file of a known DevProjects project fc.ProjectName = pp.DevProjectName; devPath = pp.DevProjectPath; syncID = pp.SyncID; pp.CountLines = projFileObject.CountLines; // we update files only here b/c we are not calling mp.CheckForInsertingProject // as we do when we have an IDE above... mp.UpdateProjectFiles(pp, fc.FullPath, new DHMisc()); } // NOTE: if we get here we have a file that is being updated // in a known development path, else we would have ignored the file before getting here // if devProject is not empty, update the windowevent so that the app that caused this // FW event can be charged to the project if (!string.IsNullOrWhiteSpace(devPath)) { lock (Globals.LastWindowEvent) { var hlpr2 = new DHWindowEvents(); if (Globals.LastWindowEvent.ID == fc.CurrentWindowID && string.IsNullOrWhiteSpace(Globals.LastWindowEvent.DevProjectName)) { Globals.LastWindowEvent.DevProjectName = fc.ProjectName; Globals.LastWindowEvent.SyncID = string.IsNullOrWhiteSpace(syncID) ? string.Empty : syncID; } else { // since the ID has changed in the last window event // that means the current window changed before we could get // here (somewhat unlikely), so the window event that saved the // current file has already been written to database and the // projName is incorrect, it should be the last row // but not necessarily WindowEvent we = hlpr2.GetLastWindowEventWritten(fc.CurrentWindowID); if (we != null && string.IsNullOrWhiteSpace(we.DevProjectName)) { // update the window event that was current when this file was modified // with the correct proj name so work on this file gets charged hlpr2.UpadateProjAndPathInWindowEvent(fc.CurrentWindowID, fc.ProjectName, string.IsNullOrWhiteSpace(syncID) ? string.Empty : syncID); } } //update syncid in window events if syncid is known if (!string.IsNullOrWhiteSpace(syncID)) { hlpr2.UpdateWindowEventsWithSyncID(fc.ProjectName, fc.CurrentApp, syncID); } } } goto TopOfCode; } catch (Exception ex) { _ = new LogError(ex, false, "FileAnalyzer.ctor"); Globals.FileAnalyzerThreadRunning = false; } }
private string CreateSQL(List <ProjectNameAndSync> projects, List <DeveloperNames> developers, DateTime?startTime, DateTime?endTime) { var hlpr = new DHWindowEvents(string.Empty); string userName = developers[0].UserName; string userDisplayName = hlpr.GetUserDisplayName(userName, Environment.MachineName); //DevProjectName, Hours, Minutes, UserDisplayName, Activity string sql = string.Empty; // **** Project related code **** sql += "SELECT DevProjectName, sum(TotalSeconds) / 3600 as Hours, " + "(sum(TotalSeconds) % 3600) / 60 as Minutes, sum(TotalSeconds) % 60 as Seconds, UserDisplayName , 'Development' as Activity " + "from " + "(select DevProjectName, DateDiff(second, StartTime, EndTime) as TotalSeconds " + //", Username, UserDisplayName " + ", UserDisplayName " + "from DevTrkr..WindowEvents w " + "where 1 = 1 " + GetListSQL(developers, "username") + GetDateSQL(StartTime, EndTime, true) + GetListSQL(projects) + "and DevProjectName in (Select DevProjectName from DevTrkr..DevProjects with (nolock) where isnull(DatabaseProject,0) != 1)" + ") as x " + "Group by UserDisplayName, DevProjectName " + "Union " + // *** Meetings **** "Select Subject as DevProjectName, sum(TotalSeconds) / 3600 as Hours, sum(TotalSeconds) % 3600 / 60 as Minutes, sum(TotalSeconds) % 60 as Seconds, " + "UserDisplayName, 'Meeting' as Activity " + //"Case Recurring = 1 then 'Meetings (Recurring)' Else 'Meetings (Scheduled)' End as Activity " + "from " + "(select [Subject] + ' (Organizer: ' + Organizer + ' Recurs: ' + Case when Recurring = 1 then 'Yes' else 'No' end + ')' as Subject, DATEDIFF(second, StartTime, EndTime) as TotalSeconds, " + "UserDisplayName " + "from DevTrkr..Meetings m with(nolock) " + "where 1=1 " + GetDateSQL(StartTime, EndTime, true) + GetListSQL(developers, "username") + //"and username = '******'" + " ) as x " + "Group by UserDisplayName, Subject " + "Union " + // **** Time spent in Email/Outlook Only *** "Select DevProjectName, sum(TotalSeconds)/ 3600 as Hours, sum(TotalSeconds) % 3600 / 60 as Minutes, sum(TotalSeconds) % 60 as Seconds, " + //"'" + userDisplayName + "' as UserDisplayName , 'EMail' as Activity " + "UserDisplayName , 'EMail' as Activity " + "from " + "(select DevProjectName, DATEDIFF(second, StartTime, EndTime) as TotalSeconds, " + "'" + userDisplayName + "' as UserDisplayName " + "from DevTrkr..WindowEvents w with(nolock) Where 1=1 " + GetListSQL(developers, "username") + GetDateSQL(StartTime, EndTime, true) + //"and username = '******' " + "and AppName = 'outlook' " + ") as x " + "Group by UserDisplayName, DevProjectName " + "Union " + // time spent in database work "Select DevProjectName, sum(TotalSeconds)/ 3600 as Hours, sum(TotalSeconds) % 3600 / 60 as Minutes, sum(TotalSeconds) % 60 as Seconds, " + //"'" + userDisplayName + "' as UserDisplayName, 'Database' as Activity " + "UserDisplayName, 'Database' as Activity " + "from " + "(select DevProjectName, DATEDIFF(second, StartTime, EndTime) as TotalSeconds, " + //"'" + userDisplayName + "' as UserDisplayName " + "UserDisplayName " + "from DevTrkr..WindowEvents w with(nolock) " + "where 1=1 " + GetListSQL(developers, "username") + GetDateSQL(StartTime, EndTime, true) + "and DevProjectName in (Select DevProjectName from DevTrkr..DevProjects with (nolock) where DatabaseProject = 1)" + ") as x " + "Group by UserDisplayName, DevProjectName " + "Union " + //TODO **** Documentation **** need generic code rather than hard coded appnames below "Select DevProjectName, sum(TotalSeconds)/ 3600 as Hours, sum(TotalSeconds) % 3600 / 60 as Minutes, sum(TotalSeconds) % 60 as Seconds, " + //"'" + userDisplayName + "' as UserDisplayName, 'Documentation' as Activity " + "UserDisplayName, 'Documentation' as Activity " + "from " + "(select DevProjectName, DATEDIFF(second, StartTime, EndTime) as TotalSeconds, " + //"'" + userDisplayName + "' as UserDisplayName " + "UserDisplayName " + "from DevTrkr..WindowEvents w with(nolock) " + "where 1=1 " + GetListSQL(developers, "username") + //"and username = '******' " + "and AppName in ('winword', 'notepad++', 'notepad', 'wordpad') " + ") as x " + "Group by UserDisplayName, DevProjectName " + "Union " + // **** Computer Locked ****** "SELECT DevProjectName, sum(TotalSeconds) / 3600 as Hours, " + "(sum(TotalSeconds) % 3600) / 60 as Minutes, sum(TotalSeconds) % 60 as Seconds, UserDisplayName , 'Development' as Activity " + "from " + "(select DevProjectName, DateDiff(second, StartTime, EndTime) as TotalSeconds " + ", Username, UserDisplayName " + "from DevTrkr..WindowEvents w " + "where 1 = 1 " + "and DevProjectName = 'ComputerLocked' " + GetListSQL(developers, "username") + GetDateSQL(StartTime, EndTime, true) + ") as x " + "Group by UserDisplayName, DevProjectName " + "Order by UserDisplayName, Activity, DevProjectName "; return(sql); }
/// <summary> /// If log is null do not create a listvieew item, just log to database /// </summary> /// <param name="p"></param> /// <param name="log"></param> public WindowEvents(Process p, ListView log) { try { var now = DateTime.Now; // time window changed Log = log; var title = GetActiveWindowTitle(); #if DEBUG Debug.WriteLine("**** " + title); #endif if (title == null) { return; } var accessDenied = false; var moduleName = string.Empty; try { moduleName = p.MainModule.ModuleName; } catch (Win32Exception ex) { // process access denied b/c it is running as admin moduleName = "Process-Access Denied"; accessDenied = true; } string displayName; try { displayName = UserPrincipal.Current.DisplayName; } catch (Exception ex) { displayName = Environment.UserName; } if (Globals.LastWindowEvent == null) { Globals.StartTime = now; } TimeSpan elapsedTime = now - Globals.StartTime; Globals.StartTime = now; // current window change time var devPrjName = string.Empty; // We set some globals so the file watcher knows who is running try { Globals.CurrentApp = !accessDenied ? p.ProcessName : AccessDenied; } catch (Exception ex) { Debug.WriteLine(ex.Message); Globals.CurrentApp = "Unknown"; } if (!accessDenied && Globals.CurrentApp == AppWrapper.AppWrapper.devenv) { var patt = "(?<PrjName>.*?)(?<spacer> - )*Microsoft Visual Studio|Microsoft Visual Studio"; var m = Regex.Match(title, patt); devPrjName = string.Empty; if (m.Success && m.Groups["PrjName"] != null && !string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) { devPrjName = m.Groups["PrjName"].Value.Replace("(Running)", string.Empty).Replace("(Debugging)", string.Empty).Trim(); if (Globals.VSUnknowns) { Globals.VSUnknowns = false; UpdateUnknownProjectNameinVSCode(devPrjName, AppWrapper.AppWrapper.devenv, AppWrapper.AppWrapper.VSUnknown); } } else /* regex failed to get prjname */ { /* this is one place where project name can be set to devenv * and should not be */ //devPrjName = p.ProcessName; devPrjName = AppWrapper.AppWrapper.VSUnknown; // "devenvUnKnown"; } } else if (!accessDenied && Globals.CurrentApp == AppWrapper.AppWrapper.VSCode) { // we are in VSCode var pattVSCode = "^(?<FileName>.*?) - (?<PrjName>.*?) - Visual Studio Code|Welcome - Visual Studio Code|Open Folder"; var m = Regex.Match(title, pattVSCode); devPrjName = string.Empty; if (m.Success && m.Groups["PrjName"] != null && !string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) { devPrjName = m.Groups["PrjName"].Value; if (Globals.VSCodeUnknowns) { Globals.VSCodeUnknowns = false; UpdateUnknownProjectNameinVSCode(devPrjName, Globals.CurrentApp, AppWrapper.AppWrapper.VSCodeUnknown); } } else if (m.Success && string.IsNullOrWhiteSpace(m.Groups["PrjName"].Value)) { // in vscode the // developer has not opened a folder yet so mark // the project name unknown until VSCode opens a folder and then // we can update the database records devPrjName = "UnKnown"; Globals.VSCodeUnknowns = true; } else { devPrjName = AppWrapper.AppWrapper.VSCodeUnknown; Globals.VSCodeUnknowns = true; } } else { // current app is not VS or VSCode we will only be able to determine // the project name if this app saves a file to a known project path devPrjName = !accessDenied ? p.ProcessName : AccessDenied; if (devPrjName == "devenv") { devPrjName = AppWrapper.AppWrapper.VSUnknown; Globals.VSUnknowns = true; } } //appears to be so FileWatcher can pick it out to use in creating FileAnalyzer // could be set in Globals.LastWindowEvent object not in two places // below is wrong b/c it is updating the last window that we are no longer in //Globals.LastWindowEvent.DevProjectName = devPrjName; // in practice, if you do not record all events, you could later want // to see some that would not be recorded //if (title == "Window Change Log" || title == "Project Description" || p.ProcessName == "WindowChangeTracker" || p.ProcessName == "explorer") // return; var currApp = !accessDenied ? p.ProcessName : AccessDenied; // see if we are interested in recording this window var weCare = Globals.NotableApplications.Find(o => o.AppName.ToLower() == currApp.ToLower()); var writeDB = (weCare != null); if (currApp.ToLower() == "explorer") // && !title.StartsWith("File Explorer")) { writeDB = false; // forget } WindowEvent item; /* there are other threads, FileWatcher, changing this object * and we want no conflict */ if (Globals.LastWindowEvent != null) { lock (Globals.LastWindowEvent) { item = Globals.LastWindowEvent; item.EndTime = now; Globals.StartTime = now; if (writeDB) { var hlpr = new DHWindowEvents(AppWrapper.AppWrapper.DevTrkrConnectionString); if (item.DevProjectName.Equals("devenv")) { Debug.WriteLine(item.DevProjectName); } var rows = hlpr.InsertWindowEvent(item); } } } var appNName = !accessDenied ? p.ProcessName : AccessDenied; item = new WindowEvent { ID = Guid.NewGuid().ToString(), StartTime = now, WindowTitle = title, AppName = appNName, ModuleName = moduleName, EndTime = DateTime.MinValue, DevProjectName = devPrjName, ITProjectID = string.Empty, UserName = Environment.UserName, MachineName = Environment.MachineName, UserDisplayName = displayName }; Globals.LastWindowEvent = item; const string comma = ","; #if DEBUG Debug.Write("*******" + title + comma + _startTime.ToString("HH:mm:ss") + comma + item.EndTime.ToString("HH:mm:ss") + comma + item.AppName + comma + item.ModuleName + comma + devPrjName + Environment.NewLine); #endif if (Log != null && writeDB) { ListViewItem lvi = new ListViewItem(Globals.StartTime.ToString("MM/dd/yyyy HH:mm:ss")); // starttime lvi.SubItems.Add(title); // WindowTitle lvi.SubItems.Add(item.AppName); // AppName lvi.SubItems.Add(moduleName); // ModuleName lvi.SubItems.Add(TimeSpan.FromTicks(elapsedTime.Ticks).ToString()); lvi.SubItems.Add(devPrjName); lvi.SubItems.Add(""); //ID lvi.SubItems.Add(""); // desc lvi.SubItems.Add(p.StartInfo.WorkingDirectory); lvi.SubItems.Add(displayName); log.Items.Add(lvi); } } catch (Exception ex) { Debug.Print(ex.Message); } }
private void UpdateUnknownProjectNameinVSCode(string devProjectName, string appName, string unknownKey) { var hlpr = new DHWindowEvents(AppWrapper.AppWrapper.DevTrkrConnectionString); hlpr.UpdateUnknownProjectNameinVSCode(devProjectName, appName, unknownKey); }
private void Locked() { LockStartTime = DateTime.Now; var accessDenied = false; var _currentApp = Globals.LastWindowEvent.AppName; IDEMatch ideMatchObject = null; bool writeDB = false; //_locked = true; // turn off polling while locked, so we will not see any window change while locked // therefore LastWindowEvent should be the one created below when we detect unlock WindowPolling.SuspendWindowPolling(); // Try to get the project name for the Globals.LastWindowEvent var cfp = new CheckForProjectName(); Tuple <string, IDEMatch, bool, string> cfpObject = cfp.GetProjectName(Globals.LastWindowEvent.WindowTitle, accessDenied, Globals.LastWindowEvent.AppName, writeDB); string devProjectName = cfpObject.Item1; ideMatchObject = cfpObject.Item2; writeDB = cfpObject.Item3; if (string.IsNullOrWhiteSpace(Globals.LastWindowEvent.DevProjectName)) { Globals.LastWindowEvent.DevProjectName = devProjectName; } var hlpr = new DHWindowEvents(AppWrapper.AppWrapper.DevTrkrConnectionString); lock (Globals.SyncLockObject) { // now, make it look like the current window when the lock occurs is being moved away from // by writing it to database Globals.LastWindowEvent.EndTime = LockStartTime; hlpr.InsertWindowEvent(Globals.LastWindowEvent); // next, start a new LastWindowEvent called ComputerLocked // and put it in Globals.LastWindowEvent string displayName; try { displayName = UserPrincipal.Current.DisplayName; } catch (Exception ex) { displayName = Environment.UserName; } var item = new WindowEvent { ID = Guid.NewGuid().ToString(), StartTime = LockStartTime, WindowTitle = locked, AppName = locked, ModuleName = locked, EndTime = LockEndTime, DevProjectName = locked, ITProjectID = string.Empty, UserName = Environment.UserName, MachineName = Environment.MachineName, UserDisplayName = displayName }; Globals.LastWindowEvent = item; } }