PSCX Developer's Guide

PowerShell Integration

The goal is to provide the PSCX user with a snapin that looks and feels like PowerShell cmdlets. We also want to avoid polluting the user's environment with unnecessary temp variables.

Variables

Try to limit the number of global scope variables that you create. In fact, the only global variable created by PowerShell should be $Pscx. Any exceptions should be posted for comment in the discussions group. Under $Pscx there are two hashtables:
  1. Session - for global session variables
  2. Preferences - for preference variables
Preferences are for CMDLETs that pull default values from a Preferernce variable in the absence of a parameter. Session global variables are for all the little global state variable that you need to persist between invocations of your command e.g.

$Pscx.Session.Cd#ForwardStack

Note the naming convention for session objects. To help identify which command (script, function, cmdlet) the session variable is associated with, please prefix the property name with this information. For instance, ForwardStack is need by the CD function that PSCX installs. So the session variable name would be "CD#ForwardStack".

Periodically run PowerShell and examine the global variable space after running your functions and/or scripts. Make sure that your functions and scripts do not pollute the gloal scope. When this is necessary please conform to the following naming style:

Variables that don't follow naming convention

Any other "approved" global variables created from PSCX that don't follow the PSCX naming convention should be created using the Set-Variable cmdlet as shown in the following example in order to associate the variable with PSCX:

Set-Variable Shell (new-object -com Shell.Application) -Scope global -Option ReadOnly -Description "PSCX variable"

Aliases

In order to find all aliases created by PSCX, we give each alias a description that starts with "PSCX" e.g.:

Set-Alias cvxml Convert-Xml -Option AllScope -Description "PSCX cmdlet alias"

This makes it very easy to find all the PSCX created aliases by executing:

galpscx

How to be a nice PSCX cmdlet

We have a few base classes which handle most of the repeating common tasks a cmdlet can perform. The most basic class, which all commands should derive from, is called Pscx.Commands.PscxCmdlet. From this, we have derived some higher level Cmdlets you may find useful:
  • PscxPathCommandBase - base for commands that act upon paths, that may or may not contain wildcard characters. Contains Path and LiteralPath parameters.
  • PscxEncodingCommandBase - derived from PscxPathCommandBase, adds an Encoding parameter. We recommend to derive from PscxInputObjectPathCommandBase, which provides much more functionality, if that makes sense for your command.
  • PscxInputObjectPathCommandBase - base class for commands that can work both on files and objects. Your implementation should only register its methods using RegisterInputType<T> method, and possibly override InputSettings property.
PscxCmdlet has some useful features for working with Paths and Providers built-in. We have a specific class called PscxPathInfo which is similiar to System.Management.Automation.PathInfo except that it can represent all sorts of paths: literal, resolvable and even invalid paths. We use two attributes ProviderConstraintAttribute and PscxPathAttribute in conjunction with each other to constrain our cmdlets to particular providers (or particular interfaces that a provider might implement, like IContentProvider), and to automatically create PscxPathInfo instances representing path strings passed to cmdlet parameters. You can view how this works by examing this sample PscxCmdlet: Export-Registry Cmdlet.

Cmdlet Naming

You should try to use the built-in PowerShell verbs, defined in VerbsCommon, VerbsCommunications, VerbsData, VerbsDiagnostic, VerbsLifecycle, VerbsOther and VerbsSecurity. If you are not sure, create a work item or discussion thread, so we all can choose the best name.

If the non-standard verb is the only one that makes sense in that particular domain, place it in Pscx.PscxVerbs class. Similarily, any noun you use should be a constant in the Pscx.PscxNouns class.

File Access

If you are going to read or write from/to a file, you should call one of the FileHandler.ProcessRead, FileHandler.ProcessWrite or FileHandler.ProcessText
methods, and provide them with a delegate that will perform the task. These methods will ensure correct error handling.

Text Encoding

We have a nice little structure EncodingParameter which should be used when you want an Encoding parameter and can't derive from PscxEncodingCommandBase. See its usage in PscxEncodingCommandBase.

Error Handling

If you need to handle a non-terminating error, use one of the ErrorHandler.WriteXXXError methods. If there is no method that would suit your error condition, and there is the slightest chance your ErrorRecord might be reused, add a method to the IPscxErrorHandler that will create the error record and call either WriteError or ThrowTerminatingError.

If a rare situation, you might want to create the error record somewhere else than in the write method. In that case, put it in the PscxErrorRecord class, and call that method from IPscxErrorHandler.

You should not throw any exceptions (using the throw keyword), unless the error is caused by a bug. User input errors, external error conditions, etc. should be handled using the ErrorHandler.WriteXXX and ErrorHandler.ThrowXXX only.

Preference Variables

There are a number of preference variables that are used by various PSCX cmdlets. If you need to create one of these please conform to the following naming convention:

$Pscx<CmdletNameorAbbrev><DescriptiveName

For example:

$PscxSmtpHostPreference

To retrieve the parameter / preference variable, use PscxCmdlet.GetPreferenceVariable.

Cmdlet Unit Testing

NUnit Installation and Setup

To get started with being able to run the Pscx_UnitTests follow these steps:
  1. Install NUnit from $/PowerShellCX/Trunk/Tools/NUnit
  2. Set the Pscx_UnitTests project to be the startup project (right-click on project node in the Solution Explorer and select "Set as StartUp Project".
  3. Open Pscx_UnitTests Properties (right-click on project node in the Solution Explorer and select Properties)
  4. Select the Debug tab in the Pscx_UnitTest Properties window.
  5. Set the Start Action to be "Start exteral program:" and use the path to the nunit-gui.exe which is typically something like: C:\Program Files\NUnit-Net-2.0 2.2.9\bin\nunit-gui.exe
  6. In Start Options set the "Command line arguments to "Pscx_UnitTests.dll".

Creating new unit tests

Most new tests should derive from PscxCmdletTest which takes care of the Runspace setup (adds the snapin) and creates a pipeline for use in your tests. Right now the runspace gets created and destroyed after every test. We'll try it this way and see how the performance is. It seemed like a good way to start to minimize interaction between tests.

Project structure

The project is organized roughly like so:
  • $/PowerShellCX
    • /Branches - Release and private developer branches go here
    • /Trunk - Most development should be on the trunk
      • /Imports - External dependencies go here
      • /Src
        • /PscxCore - Shared code that isn't Pscx specific.
        • /PscxSnapin - The PSCX snapin project (duh)
          • /Commands - Cmdlet source goes here
            • /Clipboard - Cmdlets that target the clipboard go here
            • /DirectoryServices - Cmdlets related to active directory go here
            • /IO - Cmdlets that target dirs and files go here
            • /Net - Network-related cmdlets go here
            • /Text - Put related cmdlets together in a subdir
            • /Xml - Cmdlets that operate on Xml go here
          • /Providers - Provider source goes here
          • /Profile- Profile/environment related scripts go here
        • /PscxHelp - Project used to generate help files
        • /PscxSetup - The intaller project

Namespaces

The directory structure reflects the namespace structure. Always put the object types your commands
produce into the same namespace as the command. The root namespace is reserved for classes
shared by all code.

Coding Style

File Comment Headers

//---------------------------------------------------------------------
// Authors: <your name>
//
// Description: <short description>
//
// Creation Date: <creation date>
// Modified Date: <modified date>: <your name>: <your changes>
//---------------------------------------------------------------------

Source code line length - 120

Please do not bother wrapping your source code until you hit column 120. Most of us have wide monitors these days so this shouldn't result in horizontal scrolling to see code.

using directives

Place using directives outside of the namespace, and sort them alphabetically

Fields

Place all fields at the top of the class declaration for easy reference. Always specify explicitly their accessibility e.g. private. For all non-public fields, prepend their name with an underscore for easier identification of fields. Follow the underscore with a camelCase name describing the intent of the field e.g. _paths. Avoid using public and protected fields. Use properties instead.

P/Invoke

All safe PInvoke functions and structures should be placed in the NativeMethods.cs file. Those which require pointer manipulation should be placed in the UnsafeNativeMethods.cs. However the preferred way is to use IntPtrs, GCHandles and Marshal API when possible.

#region usage

If you only have a few properties and fields then avoid using regions for fields and/or parameters. Most cmdlets are simple enough that having these areas of code in a region doesn't add much value but use your judgment. If you have a large amount of properties and/or fields then using a region can add value to the readability of the code.

Attributes

Please put each custom attribute in its own brackets for easy copying and pasting.

CMDLET Documentation

Pscx.dll-help.xml

The cmdlet help is generated using the Update-PscxHelp script in PscxHelp\Scripts directory, which merges xml files in Pscx\Help with the information obtained from assembly metadata. The following attributes are used:
  • Pscx.AcceptsWildcardsAttribute
  • Pscx.RelatedLinksAttribute
Here's how the help is generated in versions 1.1 and later:
cd $PscxRoot\PscxHelp
.\Scripts\Update-PscxHelp

The script generates the MAML file and the about_Pscx.help.txt file. You need to checkout the files first before running the script.

How to obtain and use the TFS client

If you just want to obtain our source code, grab it from the Source Code tab. However, if you are a new member of the development team, you'll need some bits and pieces:

Obtaining the Team Explorer client

There is some great info for VSTS Source Control (e.g. Team Foundation) at the VSTSGuidance project

Last edited Jul 27, 2013 at 4:00 AM by r_keith_hill, version 37

Comments

r_keith_hill Feb 8, 2007 at 12:37 AM 
You can download the code via the Source Code tab.

dlpowell Jan 8, 2007 at 7:52 PM 
Am I missing something? Where do I go to get the code? Do I have to be a developer on the project first?

vermorel Dec 31, 2006 at 10:05 AM 
Could you provide more detailed explanation about the system that you are using to generate the MAML documentation? I am currently working on another PowerShell project (see [url:http://www.codeplex.com/lokad]), and I would be very interested in duplicating your solution rather than reinventing a new one.

Thanks in advance,
Joannès
[url:http://www.lokad.com/]