提交 13bbdcdc 编写于 作者: L Larry Golding

ProcessWatchdog: Save screenshot on timeout

上级 deb0d74a
......@@ -7,12 +7,18 @@ namespace ProcessWatchdog
{
internal static class ConsoleUtils
{
internal static void ReportError(string messageFormat, params string[] args)
internal static void LogMessage(string format, params object[] args)
{
Console.WriteLine(
string.Format(CultureInfo.CurrentCulture, format, args));
}
internal static void LogError(string messageFormat, params object[] args)
{
string fullMessage = string.Format(
CultureInfo.InvariantCulture,
CultureInfo.CurrentCulture,
Resources.ErrorFormat,
string.Format(CultureInfo.InvariantCulture, messageFormat, args));
string.Format(CultureInfo.CurrentCulture, messageFormat, args));
Console.Error.WriteLine(fullMessage);
}
......
......@@ -28,5 +28,12 @@ internal class Options
"arguments",
HelpText = "Command line arguments to pass to the executable, enclosed in quotes if necessary.")]
public string Arguments { get; set; }
[Option(
'o',
"output-directory",
HelpText = "Directory to which process dumps and screen shots will be written.",
Default = ".")]
public string OutputDirectory { get; set; }
}
}
\ No newline at end of file
......@@ -21,6 +21,8 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xml" />
......@@ -34,6 +36,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="ScreenshotSaver.cs" />
</ItemGroup>
<ItemGroup>
<None Include="project.json" />
......
......@@ -16,8 +16,6 @@ internal sealed class Program
{
private Options _options;
private TimeSpan _timeout;
private bool _exited;
private DateTime _startTime;
public Program(Options options)
{
......@@ -32,7 +30,7 @@ private int Run()
}
catch (Exception ex)
{
ConsoleUtils.ReportError(Resources.ErrorInvalidTimeoutInterval, _options.Timeout, ex.Message);
ConsoleUtils.LogError(Resources.ErrorInvalidTimeoutInterval, _options.Timeout, ex.Message);
return 1;
}
......@@ -46,14 +44,15 @@ private int Run()
{
if (DateTime.Now - process.StartTime > _timeout)
{
ConsoleUtils.ReportError(Resources.ErrorProcessTimedOut, _options.Executable, _options.Timeout);
ConsoleUtils.LogError(Resources.ErrorProcessTimedOut, _options.Executable, _options.Timeout);
ScreenshotSaver.SaveScreen(_options.Executable, _options.OutputDirectory);
return 1;
}
Thread.Sleep(1000);
}
Console.WriteLine(Resources.ProcessExited, _options.Executable, process.ExitTime - process.StartTime);
ConsoleUtils.LogMessage(Resources.ProcessExited, _options.Executable, process.ExitTime - process.StartTime);
}
return 0;
......@@ -81,13 +80,13 @@ private static void Banner()
IEnumerable<Attribute> attributes = entryAssembly.GetCustomAttributes();
string version = entryAssembly.GetName().Version.ToString();
Console.WriteLine(string.Format(CultureInfo.CurrentCulture, Resources.Banner, Resources.ApplicationName, version));
ConsoleUtils.LogMessage(Resources.Banner, Resources.ApplicationName, version);
var copyrightAttribute = attributes.Single(a => a is AssemblyCopyrightAttribute) as AssemblyCopyrightAttribute;
string copyright = copyrightAttribute.Copyright;
Console.WriteLine(copyright);
Console.WriteLine();
ConsoleUtils.LogMessage(copyright);
ConsoleUtils.LogMessage(string.Empty);
}
}
}
......@@ -78,6 +78,24 @@ internal class Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Could not take screenshot for process {0}, because this process does not have a UI..
/// </summary>
internal static string ErrorCannotTakeScreenshotNoConsoleSession {
get {
return ResourceManager.GetString("ErrorCannotTakeScreenshotNoConsoleSession", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Could not take screenshot for process {0}..
/// </summary>
internal static string ErrorCannotTakeScreenshotUnexpectedError {
get {
return ResourceManager.GetString("ErrorCannotTakeScreenshotUnexpectedError", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error: {0}.
/// </summary>
......@@ -105,6 +123,24 @@ internal class Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Created screenshot folder {0}..
/// </summary>
internal static string InfoCreatedScreenshotfolder {
get {
return ResourceManager.GetString("InfoCreatedScreenshotfolder", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Saved screenshot to file {0}..
/// </summary>
internal static string InfoSavedScreenshot {
get {
return ResourceManager.GetString("InfoSavedScreenshot", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The process {0} exited successfully after {1}..
/// </summary>
......
......@@ -123,6 +123,12 @@
<data name="Banner" xml:space="preserve">
<value>{0} version {1}</value>
</data>
<data name="ErrorCannotTakeScreenshotNoConsoleSession" xml:space="preserve">
<value>Could not take screenshot for process {0}, because this process does not have a UI.</value>
</data>
<data name="ErrorCannotTakeScreenshotUnexpectedError" xml:space="preserve">
<value>Could not take screenshot for process {0}.</value>
</data>
<data name="ErrorFormat" xml:space="preserve">
<value>Error: {0}</value>
</data>
......@@ -132,6 +138,12 @@
<data name="ErrorProcessTimedOut" xml:space="preserve">
<value>The process {0} timed out after {1}.</value>
</data>
<data name="InfoCreatedScreenshotfolder" xml:space="preserve">
<value>Created screenshot folder {0}.</value>
</data>
<data name="InfoSavedScreenshot" xml:space="preserve">
<value>Saved screenshot to file {0}.</value>
</data>
<data name="ProcessExited" xml:space="preserve">
<value>The process {0} exited successfully after {1}.</value>
</data>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
namespace ProcessWatchdog
{
public static class ScreenshotSaver
{
public static void SaveScreen(string description, string outputDirectory)
{
try
{
var fileName = GenerateScreenshotFileName(description, outputDirectory);
SaveScreenToFile(fileName);
ConsoleUtils.LogMessage(Resources.InfoSavedScreenshot, fileName);
}
catch (Win32Exception ex)
{
// System.ComponentModel.Win32Exception (0x80004005): The handle is invalid. This
// means we're not running in a console session, hence there's no UI to take a
// screenshot of. This is perfectly normal on the server.
ConsoleUtils.LogError(Resources.ErrorCannotTakeScreenshotNoConsoleSession, ex);
}
catch (Exception ex)
{
// This is something else, we'd better know about this.
ConsoleUtils.LogError(Resources.ErrorCannotTakeScreenshotUnexpectedError, ex);
}
}
private static void SaveScreenToFile(string fileName)
{
int width = SystemInformation.VirtualScreen.Width;
int height = SystemInformation.VirtualScreen.Height;
int screenLeft = SystemInformation.VirtualScreen.Left;
int screenTop = SystemInformation.VirtualScreen.Top;
using (Bitmap screenshot = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
using (Graphics graphics = Graphics.FromImage(screenshot))
{
graphics.CopyFromScreen(screenLeft, screenTop, 0, 0, new Size(width, height));
}
screenshot.Save(fileName, ImageFormat.Png);
}
}
private static string GenerateScreenshotFileName(string description, string outputDirectory)
{
var screenshotsFolder = new DirectoryInfo(outputDirectory);
if (!screenshotsFolder.Exists)
{
screenshotsFolder.Create();
ConsoleUtils.LogMessage(Resources.InfoCreatedScreenshotfolder, screenshotsFolder.FullName);
}
var fileName = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss_ff") + " - " + description + ".png";
fileName = Path.Combine(screenshotsFolder.FullName, fileName);
return fileName;
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册