/// <summary> /// Loads the screenshots. /// </summary> public void Load(ImageFormatCollection imageFormatCollection, ScreenCollection screenCollection, RegionCollection regionCollection) { try { _mutexWriteFile.WaitOne(); Log.Write("Loading screenshots from \"" + FileSystem.ScreenshotsFile + "\" (if this is a large file then it might take a while)"); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); if (_screenshotList == null) { _screenshotList = new List <Screenshot>(); Log.Write("Initialized screenshot list"); } if (_slideList == null) { _slideList = new List <Slide>(); Log.Write("Initialized slide list"); } if (_slideNameList == null) { _slideNameList = new List <string>(); Log.Write("Initialized slide name list"); } if (_screenshotList != null && !File.Exists(FileSystem.ScreenshotsFile)) { Log.Write("Could not find \"" + FileSystem.ScreenshotsFile + "\" so creating default version"); XmlWriterSettings xSettings = new XmlWriterSettings { Indent = true, CloseOutput = true, CheckCharacters = true, Encoding = Encoding.UTF8, NewLineChars = Environment.NewLine, IndentChars = XML_FILE_INDENT_CHARS, NewLineHandling = NewLineHandling.Entitize, ConformanceLevel = ConformanceLevel.Document }; using (XmlWriter xWriter = XmlWriter.Create(FileSystem.ScreenshotsFile, xSettings)) { xWriter.WriteStartDocument(); xWriter.WriteStartElement(XML_FILE_ROOT_NODE); xWriter.WriteAttributeString("app", "version", XML_FILE_ROOT_NODE, Settings.ApplicationVersion); xWriter.WriteAttributeString("app", "codename", XML_FILE_ROOT_NODE, Settings.ApplicationCodename); xWriter.WriteStartElement(XML_FILE_SCREENSHOTS_NODE); xWriter.WriteEndElement(); xWriter.WriteEndElement(); xWriter.WriteEndDocument(); xWriter.Flush(); xWriter.Close(); } Log.Write("Created \"" + FileSystem.ScreenshotsFile + "\""); } if (_screenshotList != null && File.Exists(FileSystem.ScreenshotsFile)) { xDoc = new XmlDocument(); lock (xDoc) { xDoc.Load(FileSystem.ScreenshotsFile); AppVersion = xDoc.SelectSingleNode("/autoscreen").Attributes["app:version"]?.Value; AppCodename = xDoc.SelectSingleNode("/autoscreen").Attributes["app:codename"]?.Value; Log.Write("Getting screenshots from \"" + FileSystem.ScreenshotsFile + "\" using XPath query \"" + SCREENSHOT_XPATH + "\""); XmlNodeList xScreeshots = xDoc.SelectNodes(SCREENSHOT_XPATH); if (xScreeshots != null) { Log.Write("Loading " + xScreeshots.Count + " screenshots from \"" + FileSystem.ScreenshotsFile + "\" ..."); foreach (XmlNode xScreenshot in xScreeshots) { Screenshot screenshot = new Screenshot(); screenshot.Slide = new Slide(); XmlNodeReader xReader = new XmlNodeReader(xScreenshot); while (xReader.Read()) { if (xReader.IsStartElement()) { switch (xReader.Name) { case SCREENSHOT_VIEWID: xReader.Read(); screenshot.ViewId = Guid.Parse(xReader.Value); if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] View Id = " + screenshot.ViewId); } break; case SCREENSHOT_DATE: xReader.Read(); screenshot.Date = xReader.Value; screenshot.Slide.Date = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Date = " + screenshot.Date); } break; case SCREENSHOT_TIME: xReader.Read(); screenshot.Time = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Time = " + screenshot.Time); } break; case SCREENSHOT_PATH: xReader.Read(); screenshot.Path = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Path = " + screenshot.Path); } break; case SCREENSHOT_FORMAT: xReader.Read(); screenshot.Format = imageFormatCollection.GetByName(xReader.Value); if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Format = " + screenshot.Format); } break; // 2.1 used "screen" for its definition of each display/monitor whereas 2.2 uses "component". // Active Window is now represented by 0 rather than 5. case SCREENSHOT_SCREEN: if (Settings.VersionManager.IsOldAppVersion(AppCodename, AppVersion) && Settings.VersionManager.Versions.Get("Clara", "2.1.8.2") != null && string.IsNullOrEmpty(AppCodename) && string.IsNullOrEmpty(AppVersion)) { xReader.Read(); screenshot.Screen = Convert.ToInt32(xReader.Value); screenshot.Component = screenshot.Screen == 5 ? 0 : screenshot.Screen; } if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Component = " + screenshot.Component); } break; // We still want to support "component" since this was introduced in version 2.2 as the new representation for "screen". case SCREENSHOT_COMPONENT: xReader.Read(); screenshot.Component = Convert.ToInt32(xReader.Value); if (screenshot.Component == -1) { screenshot.ScreenshotType = ScreenshotType.Region; } else if (screenshot.Component == 0) { screenshot.ScreenshotType = ScreenshotType.ActiveWindow; } else { screenshot.ScreenshotType = ScreenshotType.Screen; } if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Component = " + screenshot.Component); } break; case SCREENSHOT_SLIDENAME: xReader.Read(); screenshot.Slide.Name = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Slide Name = " + screenshot.Slide.Name); } break; case SCREENSHOT_SLIDEVALUE: xReader.Read(); screenshot.Slide.Value = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Slide Value = " + screenshot.Slide.Value); } break; case SCREENSHOT_WINDOW_TITLE: xReader.Read(); screenshot.WindowTitle = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Window Title = " + screenshot.WindowTitle); } break; case SCREENSHOT_PROCESS_NAME: xReader.Read(); screenshot.ProcessName = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Process Name = " + screenshot.ProcessName); } break; case SCREENSHOT_LABEL: xReader.Read(); screenshot.Label = xReader.Value; if (Log.DebugMode) { Log.Write("[" + screenshot.ViewId + "] Label = " + screenshot.Label); } break; } } } xReader.Close(); if (Settings.VersionManager.IsOldAppVersion(AppCodename, AppVersion)) { if (Settings.VersionManager.Versions.Get("Clara", "2.1.8.2") != null && string.IsNullOrEmpty(AppCodename) && string.IsNullOrEmpty(AppVersion)) { // We need to associate the screenshot's view ID with the component's view ID // because this special ID value is used for figuring out what screenshot image to display. screenshot.ViewId = screenCollection.GetByComponent(screenshot.Component).ViewId; string windowTitle = "*Screenshot imported from an old version of Auto Screen Capture*"; Regex rgxOldSlidename = new Regex(@"^(?<Date>\d{4}-\d{2}-\d{2}) (?<Time>(?<Hour>\d{2})-(?<Minute>\d{2})-(?<Second>\d{2})-(?<Millisecond>\d{3}))"); string hour = rgxOldSlidename.Match(screenshot.Slide.Name).Groups["Hour"].Value; string minute = rgxOldSlidename.Match(screenshot.Slide.Name).Groups["Minute"].Value; string second = rgxOldSlidename.Match(screenshot.Slide.Name).Groups["Second"].Value; string millisecond = rgxOldSlidename.Match(screenshot.Slide.Name).Groups["Millisecond"].Value; screenshot.Date = rgxOldSlidename.Match(screenshot.Slide.Name).Groups["Date"].Value; screenshot.Time = hour + ":" + minute + ":" + second + "." + millisecond; screenshot.Slide.Name = "{date=" + screenshot.Date + "}{time=" + screenshot.Time + "}"; screenshot.Slide.Value = screenshot.Time + " [" + windowTitle + "]"; screenshot.WindowTitle = windowTitle; } // Remove all the existing XML child nodes from the old XML screenshot. xScreenshot.RemoveAll(); // Prepare the new XML child nodes for the old XML screenshot ... XmlElement xViewId = xDoc.CreateElement(SCREENSHOT_VIEWID); xViewId.InnerText = screenshot.ViewId.ToString(); XmlElement xDate = xDoc.CreateElement(SCREENSHOT_DATE); xDate.InnerText = screenshot.Date; XmlElement xTime = xDoc.CreateElement(SCREENSHOT_TIME); xTime.InnerText = screenshot.Time; XmlElement xPath = xDoc.CreateElement(SCREENSHOT_PATH); xPath.InnerText = screenshot.Path; XmlElement xFormat = xDoc.CreateElement(SCREENSHOT_FORMAT); xFormat.InnerText = screenshot.Format.Name; XmlElement xComponent = xDoc.CreateElement(SCREENSHOT_COMPONENT); xComponent.InnerText = screenshot.Component.ToString(); XmlElement xSlidename = xDoc.CreateElement(SCREENSHOT_SLIDENAME); xSlidename.InnerText = screenshot.Slide.Name; XmlElement xSlidevalue = xDoc.CreateElement(SCREENSHOT_SLIDEVALUE); xSlidevalue.InnerText = screenshot.Slide.Value; XmlElement xWindowTitle = xDoc.CreateElement(SCREENSHOT_WINDOW_TITLE); xWindowTitle.InnerText = screenshot.WindowTitle; XmlElement xProcessName = xDoc.CreateElement(SCREENSHOT_PROCESS_NAME); xProcessName.InnerText = screenshot.ProcessName; XmlElement xLabel = xDoc.CreateElement(SCREENSHOT_LABEL); xLabel.InnerText = screenshot.Label; // Create the new XML child nodes for the old XML screenshot so that it's now in the format of the new XML screenshot. xScreenshot.AppendChild(xViewId); xScreenshot.AppendChild(xDate); xScreenshot.AppendChild(xTime); xScreenshot.AppendChild(xPath); xScreenshot.AppendChild(xFormat); xScreenshot.AppendChild(xComponent); xScreenshot.AppendChild(xSlidename); xScreenshot.AppendChild(xSlidevalue); xScreenshot.AppendChild(xWindowTitle); xScreenshot.AppendChild(xProcessName); xScreenshot.AppendChild(xLabel); } if (!string.IsNullOrEmpty(screenshot.Date) && !string.IsNullOrEmpty(screenshot.Time) && !string.IsNullOrEmpty(screenshot.Path) && screenshot.Format != null && !string.IsNullOrEmpty(screenshot.Slide.Name) && !string.IsNullOrEmpty(screenshot.Slide.Value) && !string.IsNullOrEmpty(screenshot.WindowTitle)) { screenshot.Saved = true; Add(screenshot); } } } else { Log.Write("WARNING: Unable to load screenshots from \"" + FileSystem.ScreenshotsFile + "\""); } if (Settings.VersionManager.IsOldAppVersion(AppCodename, AppVersion)) { Log.Write("Old application version discovered when loading \"" + FileSystem.ScreenshotsFile + "\""); // We'll have to create the app:version and app:codename attributes for screenshots.xml if we're upgrading from "Clara". if (Settings.VersionManager.Versions.Get("Clara", "2.1.8.2") != null && string.IsNullOrEmpty(AppCodename) && string.IsNullOrEmpty(AppVersion)) { XmlAttribute attributeVersion = xDoc.CreateAttribute("app", "version", "autoscreen"); XmlAttribute attributeCodename = xDoc.CreateAttribute("app", "codename", "autoscreen"); attributeVersion.Value = Settings.ApplicationVersion; attributeCodename.Value = Settings.ApplicationCodename; xDoc.SelectSingleNode("/" + XML_FILE_ROOT_NODE).Attributes.Append(attributeVersion); xDoc.SelectSingleNode("/" + XML_FILE_ROOT_NODE).Attributes.Append(attributeCodename); } // Apply the latest version and codename if the version of Auto Screen Capture is older than this version. xDoc.SelectSingleNode("/" + XML_FILE_ROOT_NODE).Attributes["app:version"].Value = Settings.ApplicationVersion; xDoc.SelectSingleNode("/" + XML_FILE_ROOT_NODE).Attributes["app:codename"].Value = Settings.ApplicationCodename; xDoc.Save(FileSystem.ScreenshotsFile); Log.Write("Upgraded \"" + FileSystem.ScreenshotsFile + "\""); } } } stopwatch.Stop(); Log.Write("It took " + stopwatch.ElapsedMilliseconds + " milliseconds to load " + _screenshotList.Count + " screenshots"); } catch (Exception ex) { Log.Write("ScreenshotCollection::Load", ex); } finally { _mutexWriteFile.ReleaseMutex(); } }
/// <summary> /// /// </summary> /// <param name="keepScreenshotsForDays"></param> public void Save(int keepScreenshotsForDays) { try { _mutexWriteFile.WaitOne(); lock (_screenshotList) { if (_screenshotList != null && _screenshotList.Count > 0 && keepScreenshotsForDays > 0) { List <Screenshot> screenshotsToDelete = _screenshotList.Where(x => !string.IsNullOrEmpty(x.Date) && Convert.ToDateTime(x.Date) <= DateTime.Now.Date.AddDays(-keepScreenshotsForDays)).ToList(); if (screenshotsToDelete != null && screenshotsToDelete.Count > 0) { foreach (Screenshot screenshot in screenshotsToDelete) { XmlNodeList nodesToDelete = xDoc.SelectNodes(SCREENSHOT_XPATH + "[" + SCREENSHOT_DATE + "='" + screenshot.Date + "']"); foreach (XmlNode node in nodesToDelete) { node.ParentNode.RemoveChild(node); } if (File.Exists(screenshot.Path)) { File.Delete(screenshot.Path); } _screenshotList.Remove(screenshot); } } } for (int i = 0; i < _screenshotList.Count; i++) { Screenshot screenshot = _screenshotList[i]; if (!screenshot.Saved && xDoc != null && screenshot?.Format != null && !string.IsNullOrEmpty(screenshot.Format.Name)) { XmlElement xScreenshot = xDoc.CreateElement(XML_FILE_SCREENSHOT_NODE); XmlElement xViedId = xDoc.CreateElement(SCREENSHOT_VIEWID); xViedId.InnerText = screenshot.ViewId.ToString(); XmlElement xDate = xDoc.CreateElement(SCREENSHOT_DATE); xDate.InnerText = screenshot.Date; XmlElement xTime = xDoc.CreateElement(SCREENSHOT_TIME); xTime.InnerText = screenshot.Time; XmlElement xPath = xDoc.CreateElement(SCREENSHOT_PATH); xPath.InnerText = screenshot.Path; XmlElement xFormat = xDoc.CreateElement(SCREENSHOT_FORMAT); xFormat.InnerText = screenshot.Format.Name; XmlElement xComponent = xDoc.CreateElement(SCREENSHOT_COMPONENT); xComponent.InnerText = screenshot.Component.ToString(); XmlElement xSlidename = xDoc.CreateElement(SCREENSHOT_SLIDENAME); xSlidename.InnerText = screenshot.Slide.Name; XmlElement xSlidevalue = xDoc.CreateElement(SCREENSHOT_SLIDEVALUE); xSlidevalue.InnerText = screenshot.Slide.Value; XmlElement xWindowTitle = xDoc.CreateElement(SCREENSHOT_WINDOW_TITLE); xWindowTitle.InnerText = screenshot.WindowTitle; XmlElement xProcessName = xDoc.CreateElement(SCREENSHOT_PROCESS_NAME); xProcessName.InnerText = screenshot.ProcessName; XmlElement xLabel = xDoc.CreateElement(SCREENSHOT_LABEL); xLabel.InnerText = screenshot.Label; xScreenshot.AppendChild(xViedId); xScreenshot.AppendChild(xDate); xScreenshot.AppendChild(xTime); xScreenshot.AppendChild(xPath); xScreenshot.AppendChild(xFormat); xScreenshot.AppendChild(xComponent); xScreenshot.AppendChild(xSlidename); xScreenshot.AppendChild(xSlidevalue); xScreenshot.AppendChild(xWindowTitle); xScreenshot.AppendChild(xProcessName); xScreenshot.AppendChild(xLabel); XmlNode xScreenshots = xDoc.SelectSingleNode(SCREENSHOTS_XPATH); if (xScreenshots != null) { if (xScreenshots.HasChildNodes) { xScreenshots.InsertAfter(xScreenshot, xScreenshots.LastChild); } else { xScreenshots.AppendChild(xScreenshot); } screenshot.Saved = true; _screenshotList[i] = screenshot; } } } if (xDoc != null) { lock (xDoc) { xDoc.Save(FileSystem.ScreenshotsFile); } } } } finally { _mutexWriteFile.ReleaseMutex(); } }
/// <summary> /// Saves the captured bitmap image as a screenshot to an image file. /// </summary> /// <param name="security">The security class.</param> /// <param name="jpegQuality">The JPEG quality setting for JPEG images being saved.</param> /// <param name="screenshot">The screenshot to save.</param> /// <param name="screenshotCollection">A collection of screenshot objects.</param> /// <returns>A boolean to determine if we successfully saved the screenshot.</returns> public int SaveScreenshot(Security security, int jpegQuality, Screenshot screenshot, ScreenshotCollection screenshotCollection) { int returnFlag = 0; try { int filepathLengthLimit = Convert.ToInt32(_config.Settings.Application.GetByKey("FilepathLengthLimit", _config.Settings.DefaultSettings.FilepathLengthLimit).Value); if (!string.IsNullOrEmpty(screenshot.FilePath)) { if (screenshot.FilePath.Length > filepathLengthLimit) { _log.WriteMessage($"File path length exceeds the configured length of {filepathLengthLimit} characters so value was truncated. Correct the value for the FilepathLengthLimit application setting to prevent truncation"); screenshot.FilePath = screenshot.FilePath.Substring(0, filepathLengthLimit); } // This is a normal path used in Windows (such as "C:\screenshots\"). if (!screenshot.FilePath.StartsWith(_fileSystem.PathDelimiter)) { if (_fileSystem.DriveReady(screenshot.FilePath)) { // The low disk mode we want to use (either check by percentage (0) or check the number of available bytes (1)). int lowDiskMode = Convert.ToInt32(_config.Settings.Application.GetByKey("LowDiskMode", _config.Settings.DefaultSettings.LowDiskMode).Value); // Low disk space threshold by percentage. int lowDiskPercentageThreshold = Convert.ToInt32(_config.Settings.Application.GetByKey("LowDiskPercentageThreshold", _config.Settings.DefaultSettings.LowDiskPercentageThreshold).Value); double freeDiskSpacePercentage = _fileSystem.FreeDiskSpacePercentage(screenshot.FilePath); // Low disk space threshold in bytes. long lowDiskBytesThreshold = Convert.ToInt64(_config.Settings.Application.GetByKey("LowDiskBytesThreshold", _config.Settings.DefaultSettings.LowDiskBytesThreshold).Value); long freeDiskSpaceBytes = _fileSystem.FreeDiskSpace(screenshot.FilePath); _log.WriteDebugMessage("Percentage of free disk space on drive for \"" + screenshot.FilePath + "\" is " + (int)freeDiskSpacePercentage + "% and low disk percentage threshold is set to " + lowDiskPercentageThreshold + "%"); _log.WriteDebugMessage("Bytes of free disk space on drive for \"" + screenshot.FilePath + "\" is " + freeDiskSpaceBytes + " and low disk bytes threshold is set to " + lowDiskBytesThreshold); // Check low disk space by percentage. if (lowDiskMode == 0) { if (freeDiskSpacePercentage < lowDiskPercentageThreshold) { return(NotEnoughDiskSpace(screenshot, returnFlag, freeDiskSpacePercentage, lowDiskPercentageThreshold, freeDiskSpaceBytes, lowDiskBytesThreshold)); } } // Check low disk space in bytes. if (lowDiskMode == 1) { if (freeDiskSpaceBytes < lowDiskBytesThreshold) { return(NotEnoughDiskSpace(screenshot, returnFlag, freeDiskSpacePercentage, lowDiskPercentageThreshold, freeDiskSpaceBytes, lowDiskBytesThreshold)); } } return(SaveToFile(security, jpegQuality, screenshot, screenshotCollection)); } else { // Drive isn't ready so log an error message. _log.WriteErrorMessage($"Unable to save screenshot for \"{screenshot.FilePath}\" because the drive is not found or not ready"); return(returnFlag | (int)ScreenSavingErrorLevels.DriveNotReady); } } else { // This is a UNC network share path (such as "\\SERVER\screenshots\"). return(SaveToFile(security, jpegQuality, screenshot, screenshotCollection)); } } return(returnFlag & (int)ScreenSavingErrorLevels.None); } catch (PathTooLongException ex) { _log.WriteErrorMessage($"The path is too long. I see the path is \"{screenshot.FilePath}\" but the length exceeds what Windows can handle so the file could not be saved. There is probably an exception error from Windows explaining why"); _log.WriteExceptionMessage("ScreenCapture::SaveScreenshot", ex); // This shouldn't be an error that should stop a screen capture session. return(returnFlag | (int)ScreenSavingErrorLevels.PathLengthExceeded); } catch (Exception ex) { _log.WriteExceptionMessage("ScreenCapture::SaveScreenshot", ex); return(returnFlag | (int)ScreenSavingErrorLevels.ExceptionCaught); } }