Oct
30
Written by:
cadams
10/30/2007 7:17 PM
In my previous blog entry I discussed doing some MD5sums. I also mentioned that I'll be contributing a Cmdlet to the ScriptVault. In my blog details I mention I'm a QA guy. It's my job to break things. I like to think that it's my job to make sure thing do what they are supposed too. Or more appropriately or subtely put; that software doesn't do what it shouldn't. Often times when working on a bug you need evidence. Sometimes all the detailed steps in the world will not help some developer understand the problem. So snapping a picture of what happens when the bug is encountered is important. Anytime there is UI being involved having a screenshot is needed. Doubly so if your doing any kind of localization testing for multi-language applications.
So I wrote the following Cmdlet. I have called it Get-Screenshot. I hope that others find it valuable and that it gets some use by people. It is pretty generic and I expect people outside of QA will probably find it pretty handy.
I do want to put a few disclaimers however. I want to say that I haven't checked it for all possible bugs. There probably are areas that contain faulty logic. So I'm not responsible for it eating your HDD. You'll see the source so no need to fear. I haven't bundled the zipfile together for uploading to the ScriptVault yet. I will probably not be including the entire Visual Studio Solution/Project. I will also probably not be including the raw binary dll of the PSSnapIn. I'll work on including the PowerShell Help file for the Cmdlet should you wish to have a browsable help file or CHM. I actually had some difficulties with the help file and getting it fully operational. Curse you Vista!! Curse you and your damned User Access Control functionality!!! *shakes-fist*
If you find areas which could be improved feel free to ping me or write a reply I'm open to ideas.
This Cmdlet is leaning towards the simple side. There are some philosophies in writing Cmdlets that I've come to see as truisms. You need to consider the purpose of the Cmdlet your writing. You also must consider the audience that you are writing your Cmdlet too. It needs to have the simplicity and functionality that are inherant in the default Cmdlets that we've all come to know and love. One day I'll write my grand thesis on the considerations you need to take when writing a Cmdlet and how to go about it. In short I wanted my Cmdlet to:
- Just Work - It should be simple and not complicated. Complexity kills. Keeping things simple makes it easy for me to write, maintain, etc. It also leads to fewer bugs. I've shot myself in the foot more often than not when I'm attempting to be clever. Complexity increases the likelihood of errors. Simplicity leads to robustness.
- Do One Thing - Keeping things simple also means I can afford to specialize. I can ensure that my Cmdlet does one thing and does it well. I cut my teeth on Unix systems, so this mantra for CLI programs rings especially true to me.
- Simplicity breeds Modularity - If it's simple it is likely to be general enough to function in more than one need despite being specific enough to accomplish one thing.
This Cmdlet takes one and only one operand. It takes a type [string] operand. It is the filename for your desired output file. This includes the path for that file. It is up to you to make sure that the location you specify to the Cmdlet is writable by the user which is invoking the Cmdlet.
Sample usage:
PS Desktop > Get-Screenshot foo.bmp // will save to current working directory
PS Desktop > Get-Screenshot C:\Temp\foo.jpg // will save to the temp folder of the system
Astute readers will notice that the example commands use two different image formats. The Cmdlet is intelligent enough to determine the image format from the three character extension of the filename. Supported output image formats are: bmp, jpg, png, gif, tif. It will default to jpg format if no file extension is specified for the file.
Looking over the source code it looks like it certainly could use some refactoring. I'm particularly embaressed about the FileInfo nonsense. I am doing that to determine if the user has supplied a path that equals the current working directory or not. If only the filename is supplied it will default to writing the file in the current working directory.
I hope that when people look over the code that they notice how strikingly simple it is to write a Cmdlet. If you have a "pet" class or can think of a useful series of boilerplate .NET objects and methods. They probably can be easily have their functionality rolled into a Cmdlet with minimal effort. This ease also makes it easy to sift out that functionality and with some tinkering roll it into a purely PowerShell function without the need of a C# Cmdlet. Be imaginative.
First off I know I need to include a '-?' operand using the SwitchParameter functionality. I even blogged about doing so in a previous blog entry. I have no excuse really. That'll be coming later. That I haven't spent the time to do it right now is because I've had 5 cups of coffee and I'm a particularly jittery fool. I don't have the focus necessary to devote to involved issues. Next I need to improve that unsightly FileInfo if-else check. It makes my skin crawl. I'm sorry you all are witness to my foolishness. This is the Amateur Corner, but even I have standards. I also want to break the RunInstaller area into it's own self-contained class in a separate file; if for no other reason as it lends itself to giving Visual Studio's parser fits due to scoping issues. I do this for all my other Cmdlets. The fact that it's in-lined is legacy cruft and not a practice I perform regularly I assure you. I also should eventually include functionality that will adapt multi-monitor systems.
I'll make the changes after I detox from these 5 cups of coffee I've had today. I'll devote some time to cleaning things up tomorrow. Or perhaps this evening after my hands and fingers stop twitching. These coffee machines at work are awesome by the way. I credit my babbling energy to those 5 cups as well. It's why everyone is getting two fantabulous blog posts in one day! You lucky dogs you!
I'll be tinkering with submitting this code to either the ScriptVault or the Library. It would probably be better served in the Library but we'll be figuring that out. So without further delay I present to you the Get-ScreenShot Cmdlet!
/*
* Name: Get-ScreenShot
* Version: 1.0.0
* Date: 10/30/2007
*
* Details: This is the source code for producing a PowerShell Cmdlet by the name of Get-ScreenShot. It will take a screenshot
* of the viewable real estate on the primary system monitor. This Cmdlet will recognize the extension given by the user for
* the filename and save it in the requested image format. It will default to an image type of jpg if no extension is given.
* Supported types for image formats are: png, jpg, gif, bmp, tif.
*
* Example Usage:
* Get-ScreenShot foo.bmp // will save to the current working directory
* Get-ScreenShot C:\Temp\foo.png // save a file by the name of foo.png with an image type of png to the temp folder.
*
* Copyright 2007. All rights reserved. No warranty is given either expressly or implied.
*
* */
using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Configuration.Install;
using System.Windows.Forms;
// Windows PowerShell Namespace
using System.Management;
using System.Management.Automation;
using System.ComponentModel;
namespace CmdletScreenShot
{
#region GetScreenShotCommand
[Cmdlet ( VerbsCommon.Get, "ScreenShot",
DefaultParameterSetName = "FileName" )]
public class GetScreenShotCommand : PSCmdlet
{
#region Parameters
///
/// The name with which to save the screenshot being taken
///
[Parameter (
Position = 0,
Mandatory = true,
ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true,
HelpMessage = "A filename must be specified." )]
[ValidateNotNullOrEmpty]
[Alias ( "FileName" )]
public string Name
{
get { return fileName; }
set { fileName = value; }
}
private string fileName;
#endregion Parameters
// Begin our entry point. God this entire bit of code needs some further work.
protected override void ProcessRecord ( )
{
ProcessByFileName ( );
}
private void ProcessByFileName ( )
{
PathIntrinsics pi = SessionState.Path;
string cwd = pi.CurrentFileSystemLocation.Path; // as an option it might be nice to output results in ImageFormat.foo or as an object even
Control c = new Control ( );
Graphics g = c.CreateGraphics ( );
Image img = new Bitmap ( Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, g );
Graphics graph = Graphics.FromImage ( img );
graph.CopyFromScreen ( Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size );
FileInfo fiOld = new FileInfo ( fileName );
FileInfo fiNew;
if ( fiOld.Name == this.Name )
{
fiNew = new FileInfo ( ( cwd + "\\" + fileName ) );
}
else
{
fiNew = fiOld; // this is admittedly retarded. I feel genuinely shamed at doing this silliness.
}
// Get the last three characters of the filename. From that determine the graphical/image-type to export our screenshot.
string type = fileName.Substring ( fileName.IndexOf ( "." ) + 1 );
type = type.ToLower ( );
switch ( type )
{
case "png":
img.Save ( ( fiNew.DirectoryName + "\\" + fileName ), ImageFormat.Png );
break;
case "jpg":
img.Save ( ( fiNew.DirectoryName + "\\" + fileName ), ImageFormat.Jpeg );
break;
case "bmp":
img.Save ( ( fiNew.DirectoryName + "\\" + fileName ), ImageFormat.Bmp );
break;
case "gif":
img.Save ( ( fiNew.DirectoryName + "\\" + fileName ), ImageFormat.Gif );
break;
case "tif":
img.Save ( ( fiNew.DirectoryName + "\\" + fileName ), ImageFormat.Tiff );
break;
default:
img.Save ( ( fiNew.DirectoryName + "\\" + fileName ), ImageFormat.Jpeg );
break;
}
}
#endregion GetScreenShotCommand
#region PowerShell snap-in
[RunInstaller ( true )]
public class GetScreenshotPSSnapIn : PSSnapIn
{
public GetScreenshotPSSnapIn ( )
: base ( )
{
}
public override string Name
{
get
{
return "GetScreenShotPSSnapIn";
}
}
// Vendor Info
public override string Vendor
{
get
{
return "The vendor for this Cmdlet goes here.";
}
}
public override string VendorResource
{
get
{
return "GetScreenShotPSSnapIn01";
}
}
public override string Description
{
get
{
return "This cmdlet allows one to take a screenshot of the primary monitor.";
}
}
}
#endregion PowerShell snap-in
}
}
4 comment(s) so far...
Re: A Cmdlet I like to call: Get-ScreenShot.
I am getting an error
An expression was expected after "<"
line 63 char 49
Preotected override void processrecord < > <<<<<
By Neel on
11/6/2007 11:01 AM
|
Re: A Cmdlet I like to call: Get-ScreenShot.
What references are you using for your project? Do you have System.Management.Automation included as a reference for your project? You probably need that. If that isn't the case then perhaps there is an invisible character that is present? Are you using csc.exe just raw? Or are you using Visual Studio? If you have VS; does Visual Studio and Intellisense show everything in order?
By cadams on
11/7/2007 12:17 PM
|
Re: A Cmdlet I like to call: Get-ScreenShot.
prada which is also considered a world designer brand famous for the prada handbags. It is said that the prada bags that sold in the prada outletthat come as well as with other designer brands. We are also a great prada salethat come with the excellent products. For the new design for the winter which seems to be extrem popular in the prada line. Do you have owned the pradaproducts before or not. For these who have used the products all speak highly of them. So you should also own one the prada handbags for yourself in the prada outlet.
By bb on
2/20/2011 6:28 PM
|
Re: A Cmdlet I like to call: Get-ScreenShot.
Nothing is impossible, I’m your everything possible to get rid of the mbt sabili! mbt sabili half leans forward MBT Bomoa Boots, his face wearing a touch of cynicism, I am here today is trying to figure out a few things, but please cooperation. Liu Fanghai the most is the hot temper, mbt sabili look so young, wild, could not help but cursed little , uncle soon if I call on you to leave home What Liu If not finished, a light will be a fleeting close Liu Fanghai cheeks, then came slamming behind ng Xiang. Liu Fanghai hand touch Women MBT Shoes, his face full of blood, behind the wall, stuck a knife the most common knife, still trembling slightly. I can guarantee that before you yell, the Flying shooting into your mouth MBT Sapatu Sandals! mbt sabili still a smiling appearance.
By yinhu on
3/16/2011 11:39 PM
|