# Monday, July 26, 2010

Write your own PowerShell provider using only script, no C# required. Module definition is provided by a Windows PowerShell 2.0 Module, which may be pure script, binary or a mix of both.

Debugging is as easy as any ordinary ps1 script file:

image

All functions in backing module reflect the same signature as those found on MSDN. This means that you go to MSDN documentation on providers to learn about how to write the corresponding script.

Current Release PSProvider 0.4

Samples and Templates

Roadmap

0.1
  • ContainerCmdletProvider support through "ModuleBoundProvider" provider
  • Demo provider included navigating a Hashtable
  • Can be debugged in the debugger of your choice: console, ISE, PowerGUI.
0.2
  • NavigationCmdletProvider support
  • Providers rename to ContainerScriptProvider and TreeScriptProvider
  • Container Sample & Tree Template modules
  • Supports: Clear-Item, Copy-Item, Get-Item, Invoke-Item, Move-Item, New-Item, Remove-Item, Rename-Item, Set-Item
0.3
  • IContentCmdletProvider support
  • New Commands: New-ContentReader, New-ContentWriter implement IContentReader, IContentWriter
  • Adds support for: Add-Content, Clear-Content, Get-Content, Set-Content
0.4 (Current Release)
  • IPropertyCmdletProvider support
  • Adds support for: Clear-ItemProperty, Copy-ItemProperty, Get-ItemProperty, Move-ItemProperty, New-ItemProperty, Remove-ItemProperty, Rename-ItemProperty, Set-ItemProperty
0.5
  • Dynamic Parameter support
0.6
  • Security Interfaces
  • Adds support for: Get-ACL, Set-ACL

http://psprovider.codeplex.com/

posted on Monday, July 26, 2010 5:33:21 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1] Trackback
# Friday, May 21, 2010

If you want to do this, you won’t really need much of an explanation as to why I’m posting this. As to the rest of you, never mind; stick with your BigEndian Unicode (hint: powershell console and other console applications prefer ASCII) :)

First up, create yourself a Windows ISE Profile script that will be loaded by default when ISE starts (and/or when you open a new “Tab”)

# run this one-liner from within ISE through the interactive window (command pane):
if (-not (test-path $profile)) { md -force (split-path $profile); "" > $profile; psedit $profile }

Now, put this one-liner (well, it could fit on one line) in your $profile:

# watch for changes to the Files collection of the current Tab
register-objectevent $psise.CurrentPowerShellTab.Files collectionchanged -action {
    # iterate ISEFile objects
    $event.sender | % {
         # set private field which holds default encoding to ASCII
         $_.gettype().getfield("encoding","nonpublic,instance").setvalue($_, [text.encoding]::ascii)
    }
}
Every time the tabs "files" collection changes, it will set the default save encoding to ASCII for all files in that tab. As the profile is loaded in each tab, all files in all tabs will default to ASCII when saving. No more "save as" annoyances; just hit save and ASCII will be used for encoding. "Save as" will still let you save as unicode if you wish.

Have fun!

posted on Friday, May 21, 2010 5:58:46 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2] Trackback
# Wednesday, May 05, 2010

This is a lot of fun if you spend a lot of time tinkering around with APIs in PowerShell. This function (included in the upcoming PSCX 2.0, alias: refl) will let you open Lutz Roeder’s Reflector for any Type or Cmdlet. Reflector will automatically load the correct Assembly and will highlight the relevant Type, without you having to do diddley-squat. Examples and help will display with -?

function Invoke-Reflector {
<#
    .SYNOPSIS
        Quickly load Reflector, with the specified Type or Command selected.
    .DESCRIPTION
        Quickly load Reflector, with the specified Type or Command selected. The function will also
        ensure that Reflector has the Type or Command's containing Assembly loaded.
    .EXAMPLE
        # Opens System.String in Reflector. Will load its Assembly into Reflector if required.
        ps> [string] | invoke-reflector
    .EXAMPLE
        # Opens GetChildItemCommand in Reflector. Will load its Assembly into Reflector if required.
        ps> gcm ls | invoke-reflector
    .EXAMPLE
        # Opens GetChildItemCommand in Reflector. Will load its Assembly into Reflector if required.
        ps> invoke-reflector dir
    .PARAMETER CommandName
        Accepts name of command. Does not accept pipeline input.
    .PARAMETER CommandInfo
        Accepts output from Get-Command (gcm). Accepts pipeline input.
    .PARAMETER Type
        Accepts a System.Type (System.RuntimeType). Accepts pipeline input.
    .PARAMETER ReflectorPath
        Optional. Defaults to Reflector.exe's location if it is found in your $ENV:PATH. If not found, you must specify.
    .INPUTS
        [System.Type]
        [System.Management.Automation.CommandInfo]
    .OUTPUTS
        None
#>
     [cmdletbinding(defaultparametersetname="name")]
     param(
         [parameter(
            parametersetname="name",
            position=0,
            mandatory=$true
         )]
         [validatenotnullorempty()]
         [string]$CommandName,

         [parameter(
            parametersetname="command",
            position=0,
            valuefrompipeline=$true,
            mandatory=$true
         )]
         [validatenotnull()]
         [management.automation.commandinfo]$CommandInfo,

         [parameter(
            parametersetname="type",
            position=0,
            valuefrompipeline=$true,
            mandatory=$true
         )]
         [validatenotnull()]
         [type]$Type,

         [parameter(
            position=1
         )]
         [validatenotnullorempty()]
         [string]$ReflectorPath = $((gcm reflector.exe -ea 0).definition)
     )

        # no process block; i only want
        # a single reflector instance

        if ($ReflectorPath -and (test-path $reflectorpath)) {

            $typeName = $null
            $assemblyLocation = $null

            switch ($pscmdlet.parametersetname) {

                 { "name","command" -contains $_ } {

                    if ($CommandName) {
                        $CommandInfo = gcm $CommandName -ea 0
                    } else {
                        $CommandName = $CommandInfo.Name
                    }

                    if ($CommandInfo -is [management.automation.aliasinfo]) {

                        # expand aliases
                        while ($CommandInfo.CommandType -eq "Alias") {
                            $CommandInfo = gcm $CommandInfo.Definition
                        }
                    }

                    # can only reflect cmdlets, obviously.
                    if ($CommandInfo.CommandType -eq "Cmdlet") {

                        $typeName = $commandinfo.implementingtype.fullname
                        $assemblyLocation = $commandinfo.implementingtype.assembly.location

                    } elseif ($CommandInfo) {
                        write-warning "$CommandInfo is not a Cmdlet."
                    } else {
                        write-warning "Cmdlet $CommandName does not exist in current scope. Have you loaded its containing module or snap-in?"
                    }
                }

                "type" {
                    $typeName = $type.fullname
                    $assemblyLocation = $type.assembly.location
                }
            } # end switch


            if ($typeName -and $assemblyLocation) {
                & $reflectorPath /select:$typeName $assemblyLocation
            }

        } else {
            write-warning "Unable to find Reflector.exe. Please specify full path via -ReflectorPath."
        }
}

Have fun!

posted on Wednesday, May 05, 2010 5:05:16 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Monday, May 03, 2010

These days I'm incredibly busy both in my professional and private life, so I’ve not had a lot of time to construct the usual meaty posts I like to write. Instead I figured I could write a series of short – very short – posts centered around the little tricks that you would need to be an efficient developer when targeting PowerShell. Here's the first tip: how to create a Runspace and have one or more Module(s) preloaded. The RunspaceInvoke class is a handy wrapper that will do most of the plumbing for you if you just want to run scripts or commands. If you want to manually construct your own Pipeline instances then you must work with the Runspace class directly.

    InitialSessionState initial = InitialSessionState.CreateDefault();
    initialSession.ImportPSModule(new[] { modulePathOrModuleName1, ... });
    Runspace runspace = RunspaceFactory.CreateRunspace(initial);
    runspace.Open();
    RunspaceInvoke invoker = new RunspaceInvoke(runspace);
    Collection<PSObject> results = invoker.Invoke("...");

Have fun!

posted on Monday, May 03, 2010 12:54:13 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Thursday, April 01, 2010

This is a just a short post to remind myself (and you, frustrated googlers/bingers) about the different ways resources are defined and accessed within SharePoint. While some .NET applications use embedded resources or satellite assemblies, SharePoint has a preference for raw RESX files. These resx files are dumped in one of two places:

12\CONFIG\RESOURCES

- application-level "global" resources
- propagated at site definition instantiation
- later modifications require stsadm -o copyappbincontent
- they live in <approot>\App_GlobalAppResources
- accessed via HttpContext.GetGlobalResourceObject / TemplateControl.GetGlobalResourceObject
- http://msdn.microsoft.com/en-us/library/system.web.httpcontext.getglobalresourceobject.aspx
- declaratively accessible <asp:foo runat="server" text="<%$ Resources: myapp.core, ResKeyName %>" />
   (where myapp.core represents myapp.core.resx)

12\RESOURCES
- farm-level global resources
- available to all applications
- remain in 12\RESOURCEs, not copied anywhere
- typically used programatically via SPUtility.GetLocalizedString  ( Microsoft.SharePoint.Utilities )
- http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.utilities.sputility.getlocalizedstring.aspx

posted on Thursday, April 01, 2010 11:43:37 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback