The purpose of this post, and the next few posts, is to introduce some Cmdlets I have written. I won't spend time on how to write a Cmdlet, as this topic has already been covered by other authors. A couple of the Cmdlets I have written have equivalents in PowerShell Community Extensions, so users of that snap-in probably won't find them useful, but I am going to talk about them anyway.
I'll start with the first Cmdlets I wrote, which are also the simplest: Get-Clipboard and Set-Clipboard:
using System;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Management.Automation;
using System.Collections.Specialized;
using System.IO;
namespace CustomCmdlets
{
public class ClipboardCmdlet : Cmdlet
{
protected StringBuilder clipboardStringBuilder = new StringBuilder();
protected StringCollection clipboardFileList = new StringCollection();
#region Parameters
protected bool files;
[Parameter]
public SwitchParameter Files
{
get
{
return files;
}
set
{
files = value;
}
}
#endregion
internal void GetClipboard()
{
Thread thread = new Thread( new ThreadStart( delegate
{
if ( !this.files && Clipboard.ContainsText() )
{
clipboardStringBuilder = new StringBuilder( Clipboard.GetText() );
}
else if ( Clipboard.ContainsFileDropList() )
{
clipboardFileList = Clipboard.GetFileDropList();
}
} ) );
thread.SetApartmentState( ApartmentState.STA );
thread.Start();
thread.Join();
}
internal void SetClipboard()
{
Thread thread = new Thread( new ThreadStart( delegate
{
if ( this.files )
{
if ( this.clipboardFileList.Count > 0 )
{
Clipboard.SetFileDropList( clipboardFileList );
}
}
else
{
if ( !string.IsNullOrEmpty( clipboardStringBuilder.ToString() ) )
{
Clipboard.SetText( clipboardStringBuilder.ToString() );
}
}
} ) );
thread.SetApartmentState( ApartmentState.STA );
thread.Start();
thread.Join();
}
}
[Cmdlet( VerbsCommon.Get, "Clipboard" )]
public class GetClipboard : ClipboardCmdlet
{
protected override void BeginProcessing()
{
GetClipboard();
if ( this.files )
{
for ( int i = 0; i < this.clipboardFileList.Count; i++ )
{
WriteVerbose( this.clipboardFileList[ i ] );
WriteObject( new FileInfo( this.clipboardFileList[ i ] ) );
}
}
else
{
WriteVerbose( this.clipboardStringBuilder.ToString() );
WriteObject( this.clipboardStringBuilder.ToString() );
}
}
}
[Cmdlet( VerbsCommon.Set, "Clipboard", SupportsShouldProcess = true )]
public class SetClipboard : ClipboardCmdlet
{
#region Parameters
private object[] dataArray;
[Parameter( Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true )]
public object[] Data
{
get
{
return dataArray;
}
set
{
dataArray = value;
}
}
private bool append;
[Parameter]
public SwitchParameter Append
{
get
{
return append;
}
set
{
append = value;
}
}
private bool trimWhiteSpace;
[Parameter]
public SwitchParameter TrimWhiteSpace
{
get
{
return trimWhiteSpace;
}
set
{
trimWhiteSpace = value;
}
}
private bool passThru;
[Parameter]
public SwitchParameter PassThru
{
get
{
return passThru;
}
set
{
passThru = value;
}
}
#endregion
protected override void BeginProcessing()
{
if ( append )
{
GetClipboard();
}
}
protected override void ProcessRecord()
{
if ( dataArray != null )
{
for ( int i = 0; i < dataArray.Length; i++ )
{
if ( this.Files )
{
if ( ShouldProcess( ( (FileInfo)dataArray[ i ] ).FullName ) )
{
if ( this.passThru )
{
WriteObject( ( (FileInfo)dataArray[ i ] ).FullName );
}
clipboardFileList.Add( ( (FileInfo)dataArray[ i ] ).FullName );
}
}
else
{
if ( ShouldProcess( dataArray[ i ].ToString() ) )
{
if ( this.passThru )
{
WriteObject( dataArray[ i ].ToString() );
}
clipboardStringBuilder.AppendLine( dataArray[ i ].ToString() );
}
}
}
}
}
protected override void EndProcessing()
{
if ( trimWhiteSpace )
{
clipboardStringBuilder = new StringBuilder( clipboardStringBuilder.ToString().Trim() );
}
SetClipboard();
}
}
}
I think the source is pretty straightforward, so I won't bore you by talking about it. Get-Clipboard really doesn't do anything special; it just dumps the contents of the clipboard into the pipeline. If the Files SwitchParameter is set, and the file drop list isn't empty, each path is converted to a FileInfo object before it is written to the pipeline.
The Set-Clipboard Cmdlet is slightly more interesting. It has SwitchParameters that let you specify if you want to append to the current contents of the clipboard, and if you want to trim whitespace from the text that is being added to the clipboard. A PassThru parameter is also provided if the objects should be written to the pipeline as well as to the clipboard.
Thats about it for the Clipboard Cmdlets. Not too exciting, but I do tend to use them often. I have created the aliases "gcb" and "scb" in my profile, to allow for terseness at the command line.
The next Cmdlet is a little more original, but I haven't found it as useful. It is the Get-ScreenShot Cmdlet:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Management.Automation;
namespace CustomCmdlets
{
public enum ImageFormat
{
Bmp,
Emf,
Gif,
Jpg,
Png,
Tif,
Wmf
}
[Cmdlet( VerbsCommon.Get, "ScreenShot" )]
public class GetScreenShot : PSCmdlet
{
#region Parameters
private string path;
[Parameter( Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true )]
public string Path
{
get
{
if ( string.IsNullOrEmpty( path ) )
{
path = System.IO.Path.Combine( SessionState.Path.CurrentFileSystemLocation.Path,
string.Format( "ScreenShot.{0}", this.ImageFormat.ToString().ToLower() ) );
}
else if ( !System.IO.Path.IsPathRooted( path ) )
{
// the specified path is relative; make it relative to the current working directory
path = System.IO.Path.Combine( SessionState.Path.CurrentFileSystemLocation.Path, path );
}
return path;
}
set
{
path = value;
}
}
private bool noClobber;
[Parameter]
public SwitchParameter NoClobber
{
get
{
return noClobber;
}
set
{
noClobber = value;
}
}
private bool all;
[Parameter]
public SwitchParameter All
{
get
{
return all;
}
set
{
all = value;
}
}
private int? screenIndex;
[Parameter]
[ValidateRange( 0, Int32.MaxValue )]
public int ScreenIndex
{
get
{
if ( !screenIndex.HasValue )
{
if ( Screen.AllScreens.Length > 1 )
{
// get the primary monitor
for ( int i = 0; !screenIndex.HasValue && i < Screen.AllScreens.Length; i++ )
{
if ( Screen.AllScreens[ i ].Primary )
{
screenIndex = i;
}
}
}
else
{
screenIndex = 0;
}
}
return screenIndex.Value;
}
set
{
screenIndex = value;
}
}
private ImageFormat? imageFormat = null;
[Parameter]
public ImageFormat ImageFormat
{
get
{
if ( !imageFormat.HasValue && this.path != null )
{
imageFormat = (ImageFormat)Enum.Parse( typeof( ImageFormat ),
( new FileInfo( this.path ) ).Extension.Substring( 1 ), true );
}
return imageFormat.GetValueOrDefault( ImageFormat.Jpg );
}
set
{
imageFormat = value;
}
}
private bool passThru;
[Parameter]
public SwitchParameter PassThru
{
get
{
return passThru;
}
set
{
passThru = value;
}
}
#endregion
protected override void BeginProcessing()
{
// make sure our file format and file extension match
// use the Path property this time to be sure the path is initialized properly
if ( this.ImageFormat.ToString().ToLower() != ( new FileInfo( this.Path ) ).Extension.Substring( 1 ).ToLower() )
{
throw new Exception( "The specified file format and the extention of the specified path do not match." );
}
}
protected override void EndProcessing()
{
Bitmap screenBitmap;
int sourceX = Int32.MaxValue;
int sourceY = Int32.MaxValue;
int width = 0;
int bottom = 0;
if ( this.all )
{
for ( int i = 0; i < Screen.AllScreens.Length; i++ )
{
Screen screen = Screen.AllScreens[ i ];
sourceX = Math.Min( sourceX, screen.Bounds.X );
sourceY = Math.Min( sourceY, screen.Bounds.Y );
width += screen.Bounds.Width;
bottom = Math.Max( bottom, screen.Bounds.Bottom );
}
}
else
{
// the ScreenIndex property will be initialized with the appropriate value if it has not been specified.
Screen screen = Screen.AllScreens[ this.ScreenIndex ];
sourceX = screen.Bounds.X;
sourceY = screen.Bounds.Y;
width = screen.Bounds.Width;
bottom = screen.Bounds.Bottom;
}
screenBitmap = new Bitmap( width, bottom - sourceY, PixelFormat.Format32bppArgb );
using ( Graphics screenGraphics = Graphics.FromImage( screenBitmap ) )
{
screenGraphics.CopyFromScreen( sourceX, sourceY, 0, 0, new Size( width, bottom - sourceY ), CopyPixelOperation.SourceCopy );
}
if ( this.noClobber )
{
if ( File.Exists( this.path ) )
{
int fileNumber = 1;
FileInfo fileInfo = new FileInfo( this.path );
string name = System.IO.Path.GetFileNameWithoutExtension( this.path );
do
{
this.path = System.IO.Path.Combine( fileInfo.DirectoryName,
string.Format( "{0}{1}{2}", name, fileNumber++, fileInfo.Extension ) );
}
while ( File.Exists( this.path ) );
}
}
screenBitmap.Save( this.path, ImageFormatEnumToImageFormat( this.ImageFormat ) );
if ( passThru )
{
WriteObject( new FileInfo( this.path ) );
}
}
private System.Drawing.Imaging.ImageFormat ImageFormatEnumToImageFormat( ImageFormat imageFileFormat )
{
System.Drawing.Imaging.ImageFormat imageFormat = System.Drawing.Imaging.ImageFormat.Jpeg;
switch ( imageFileFormat )
{
case ImageFormat.Bmp:
imageFormat = System.Drawing.Imaging.ImageFormat.Bmp;
break;
case ImageFormat.Emf:
imageFormat = System.Drawing.Imaging.ImageFormat.Emf;
break;
case ImageFormat.Gif:
imageFormat = System.Drawing.Imaging.ImageFormat.Gif;
break;
case ImageFormat.Jpg:
imageFormat = System.Drawing.Imaging.ImageFormat.Jpeg;
break;
case ImageFormat.Png:
imageFormat = System.Drawing.Imaging.ImageFormat.Png;
break;
case ImageFormat.Tif:
imageFormat = System.Drawing.Imaging.ImageFormat.Tiff;
break;
case ImageFormat.Wmf:
imageFormat = System.Drawing.Imaging.ImageFormat.Wmf;
break;
}
return imageFormat;
}
}
}
This Cmdlet provides several parameters that allow you to customize how a screenshot is taken. A relative or absolute path to an image file may be specified; if no path is specified, the file is saved in the current working directory. The NoClobber SwitchParameter is used to specify that existing image files will not be overwritten; each new screenshot file is given a unique number, e.g. ScreenShot1.jpg, ScreenShot2.jpg, etc. With a multiple monitor setup, the ScreenIndex parameter can be used to specify the monitor to use for the screenshot. The primary monitor is used by default, and the All parameter can be used to capture all monitors at once. The ImageFormat parameter is used to specify the type of image file that will be created; Jpg is the default. The PassThru parameter indicates if the path to the image file will be written to the pipeline as a FileInfo object.
Well that's it for today. I have enough Cmdlets for two more posts like this one. To finish up, the following is the XML help for these three Cmdlets.
<?xml version="1.0" encoding="utf-8" ?>
<helpItems xmlns="http://msh" schema="maml">
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>Get-Clipboard</command:name>
<maml:description>
<maml:para>The Get-Clipboard Cmdlet returns the contents of the system clipboard.</maml:para>
</maml:description>
<maml:copyright>
<maml:para></maml:para>
</maml:copyright>
<command:verb>Get</command:verb>
<command:noun>Clipboard</command:noun>
<dev:version></dev:version>
</command:details>
<maml:description>
<maml:para>The Get-Clipboard Cmdlet returns the contents of the system clipboard. By default, only text is returned. If file drop list data is available, and the Files parameter is specified, the files in the list are returned.</maml:para>
</maml:description>
<command:syntax>
<command:syntaxItem>
<maml:name>Get-Clipboard</maml:name>
<command:parameter required="false" position="named">
<maml:name>files</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>Files</maml:name>
<maml:description>
<maml:para>Retrieves FileInfo objects from the file drop list on the clipboard.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
</command:parameters>
<command:inputTypes>
<command:inputType>
<dev:type>
<maml:name>None</maml:name>
<maml:uri/>
<maml:description />
</dev:type>
<maml:description></maml:description>
</command:inputType>
</command:inputTypes>
<command:returnValues>
<command:returnValue>
<dev:type>
<maml:name>object []</maml:name>
<maml:uri />
<maml:description>
<maml:para>
The contents of the system clipboard.
</maml:para>
</maml:description>
</dev:type>
<maml:description></maml:description>
</command:returnValue>
</command:returnValues>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>Set-Clipboard</command:name>
<maml:description>
<maml:para>The Set-Clipboard Cmdlet puts its input on the system clipboard.</maml:para>
</maml:description>
<maml:copyright>
<maml:para></maml:para>
</maml:copyright>
<command:verb>Set</command:verb>
<command:noun>Clipboard</command:noun>
<dev:version></dev:version>
</command:details>
<maml:description>
<maml:para>The Set-Clipboard Cmdlet puts its input on the system clipboard. By default, only text is accepted. FileInfo objects can be added the file drop list on the clipboard by specifying the Files parameter.</maml:para>
</maml:description>
<command:syntax>
<command:syntaxItem>
<maml:name>Set-Clipboard</maml:name>
<command:parameter required="true" position="1">
<maml:name>data</maml:name>
<command:parameterValue required="true" variableLength="false">object []</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>files</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>append</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>trimWhiteSpace</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>passThru</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="true" position="1" globbing="false" pipelineinput="true">
<maml:name>Data</maml:name>
<maml:description>
<maml:para>The data to be added to the clipboard. Strings and FileInfo objects are accepted. The Files parameter must be specified when working with FileInfo objects.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">string</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>Files</maml:name>
<maml:description>
<maml:para>Adds incoming FileInfo objects to the file drop list on the clipboard.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>Append</maml:name>
<maml:description>
<maml:para>Appends incoming data to the end of the current contents of the clipboard. Text will be concatenated, and FileInfo objects will be added to the end of the file drop list.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>TrimWhiteSpace</maml:name>
<maml:description>
<maml:para>Trims trailing whitespace from text data. This parameter does not apply when working with FileInfo objects.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>PassThru</maml:name>
<maml:description>
<maml:para>Passes the objects added to the clipboard through the pipeline. By default, this Cmdlet does not pass any objects through the pipeline.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
</command:parameters>
<command:inputTypes>
<command:inputType>
<dev:type>
<maml:name>object []</maml:name>
<maml:uri/>
<maml:description>
<maml:para>
Data to be added to the clipboard.
</maml:para>
</maml:description>
</dev:type>
<maml:description></maml:description>
</command:inputType>
</command:inputTypes>
</command:command>
<command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
<command:details>
<command:name>Get-ScreenShot</command:name>
<maml:description>
<maml:para>The Get-ScreenShot Cmdlet creates a file containing a screen shot.</maml:para>
</maml:description>
<maml:copyright>
<maml:para></maml:para>
</maml:copyright>
<command:verb>Get</command:verb>
<command:noun>ScreenShot</command:noun>
<dev:version></dev:version>
</command:details>
<maml:description>
<maml:para>The Get-ScreenShot Cmdlet creates a file containing a screen shot. By default, the screen shot is of the primary monitor. For multiple-monitor setups, the All and ScreenIndex parmeters can be used to customize the result of the command.</maml:para>
</maml:description>
<command:syntax>
<command:syntaxItem>
<maml:name>Get-ScreenShot</maml:name>
<command:parameter required="false" position="1" pipelineinput="true">
<maml:name>path</maml:name>
<command:parameterValue required="false" variableLength="false">string</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>noClobber</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>all</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>screenIndex</maml:name>
<command:parameterValue required="true" variableLength="false">int</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>imageFormat</maml:name>
<command:parameterValue required="true" variableLength="false">ImageFormat</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>passThru</maml:name>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
</command:syntaxItem>
</command:syntax>
<command:parameters>
<command:parameter required="false" position="named" globbing="false" pipelineinput="true">
<maml:name>Path</maml:name>
<maml:description>
<maml:para>The path to the image file that will be created. If not specified, a default name of "ScreenShot" will be used.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">string</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named">
<maml:name>NoClobber</maml:name>
<maml:description>
<maml:para>Ensures that the cmdlet cannot overwrite an existing file. If the NoClobber parameter is not specified, Get-ScreenShot overwrites the file at the specified path, or the at the default path. If the NoClobber parameter is specified, a unique filename will be created by sequentially adding numbers to the end of the filename until a unique name is found.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>All</maml:name>
<maml:description>
<maml:para>Creates a screenshot of all of the monitors on a multiple-monitor system.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>ScreenIndex</maml:name>
<maml:description>
<maml:para>Used to specify the index of a specific monitor in a multiple-monitor system. If the ScreenIndex parameter is not specifed with a multiple-monitor system, the primary monitor will be used.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>ImageFormat</maml:name>
<maml:description>
<maml:para>Sets the format of the image file created by Get-ScreenShot. This parameter is not necessary if the Path parameter is specified; the format of the image will be determined based on the extension of the filename.</maml:para>
<maml:para>The following lists the acceptable values for this parameter:</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">ImageFormat</command:parameterValue>
<dev:type>
<maml:name>ImageFormat</maml:name>
<maml:uri/>
</dev:type>
<dev:defaultValue>Jpg</dev:defaultValue>
<dev:possibleValues>
<dev:possibleValue>
<dev:value>Bmp</dev:value>
<maml:description>
<maml:para></maml:para>
</maml:description>
</dev:possibleValue>
<dev:possibleValue>
<dev:value>Emf</dev:value>
<maml:description>
<maml:para></maml:para>
</maml:description>
</dev:possibleValue>
<dev:possibleValue>
<dev:value>Gif</dev:value>
<maml:description>
<maml:para></maml:para>
</maml:description>
</dev:possibleValue>
<dev:possibleValue>
<dev:value>Jpg</dev:value>
<maml:description>
<maml:para></maml:para>
</maml:description>
</dev:possibleValue>
<dev:possibleValue>
<dev:value>Png</dev:value>
<maml:description>
<maml:para></maml:para>
</maml:description>
</dev:possibleValue>
<dev:possibleValue>
<dev:value>Tif</dev:value>
<maml:description>
<maml:para></maml:para>
</maml:description>
</dev:possibleValue>
<dev:possibleValue>
<dev:value>Wmf</dev:value>
<maml:description>
<maml:para></maml:para>
</maml:description>
</dev:possibleValue>
</dev:possibleValues>
</command:parameter>
<command:parameter required="false" position="named" globbing="false" pipelineinput="false">
<maml:name>PassThru</maml:name>
<maml:description>
<maml:para>Passes the image file created by Get-ScreenShot through the pipeline. By default, this Cmdlet does not pass any objects through the pipeline.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">SwitchParameter</command:parameterValue>
</command:parameter>
</command:parameters>
</command:command>
</helpItems>
No comments:
Post a Comment