# Wednesday, October 24, 2007

Someone on the newsgroup ran into an issue whereby a instance of a non CLS Compliant type was fed to PowerShell's default formatter. The type in question had two properties with the same name, but in different casing. I'm pretty much just going to carbon-copy the qestion and answer here as I feel this is worth preserving in the annals of blogdom.

> I am in the process of writing a script to document SharePoint
> installations.  When I try to get information about the content database
> I get this error.

> "out-lineoutput : The field/property: "Id" for type:
> "Microsoft.SharePoint.Administration.SPContentDatabase" differs only in
> case from the field/property: "ID". Failed to use non CLS compliant type."

> Here is some of the PowerShell Script that I am using

> [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
> $site = New-Object Microsoft.SharePoint.SPSite("http://localhost")
> $site.ContentDatabase

> It is the final line that is giving me the error.  I can get other
> properties without error.  Can someone enlighten me on this error
> message and let me know if there is a way to work around it.

> Thanks
> Jerry

Hi Jerry,

This is a tricky one for people very new to powershell without any real experience of the .NET framework. One of the rules of a so-called "CLS compliant type" (cls = common lanaguage specification) is that you should not expose any public members that only differ in casing. The SPContentDatabase type has two properties, "Id" and "ID." This is perfectly legal in C# since case in important in that language and the compiler sees them as different as chalk and cheese. However, in VB.NET, case is not important and as such it [vb] has no native mechanism to differentiate between the two. The same goes for PowerShell's grammar. So, how do we get around this?

First, you grab a handle to the method directly by asking for it from the Type itself:

PS> $idMethod1 = [Microsoft.SharePoint.Administration.SPContentDatabase].getmethod("get_ID")

and for the second version,

PS> $idMethod2 = [Microsoft.SharePoint.Administration.SPContentDatabase].getmethod("get_Id")

Note the differing case for get_Id and get_ID. Btw, Properties like ID in .NET are actually served by special accessor methods prefixed with get_ and set_, hence the prefixes. Next, you need a way to invoke those methods on the instance itself: this is done by using the Invoke method on the MethodInfo object representing the method itself. You pass the instance to the Invoke method telling it that the method is "instance" (e.g. not static/shared) and "public" and it will call that method for you on the instance, returning the value, if any.

PS> $result = $idMethod1.Invoke($site.ContentDatabase, "instance,public", $null, $null, $null)

I know from the SharePoint documenation, that this property returns a guid. Also, due to another oversight, Posh does not know how to format Guids, so you must call ToString() on the result to get the value back or you'll be greeted by a blank line from powershell's formatter.

PS> $result.ToString()
1ade149d-2049-4668-b555-4b7be9374c65

Bizarrely, these two properties return the same guid, but hey, I'll let someone from the sharepoint team answer that one. Anyway, you should have enough information here to extract the properties that you need.

So, that's the interesting portion out of the way. Another way to do this might be to create a custom view for the SPContentDatabase type omitting the duplicated property and use the Update-FormatData cmdlet to activate it. This may not work though, as the non-cls compliancy issue may rear it's ugly head before posh's formatter gets this far.

Hope this helps,

- Oisin / x0n.

posted on Wednesday, October 24, 2007 4:18:50 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Thursday, October 11, 2007

A question came up on the PowerShell newsgroup concerning how to use enums, particularly the shortcut form whereby posh will coerce strings. Roman Kuzmin of PowerShell/FarNet fame offered up a quick answer on how to provide multiple values to be "or'd" together for [flags] decorated enums:

$srv.ReplicationServer.Script("Creation,SomeOtherValue")

To which the original poster (moff) replied:

Out of interest, can this syntax be used for xor'ing values together too? 
At the moment my statement looks like this: 


$pub_svr.ReplicationServer.Script(([Microsoft.SqlServer.Replication.scripto­­ptions]::Creation `
    -bor [Microsoft.SqlServer.Replication.scriptoptions]::IncludeAll ` 
    -bxor [Microsoft.SqlServer.Replication.scriptoptions]::IncludeReplicationJobs ))

To which I explained that sure, one way has you casting the operands (using system.attributetargets as the enum example):

$targets = ([attributetargets]"all" -bxor [attributetargets]"event,field")

And if the type name is long, another handy trick is to assign the enum type to a variable, e.g.

$enum = [Microsoft.SqlServer.Replication.ScriptO­ptions] 
$options = ($enum::creation -bor $enum::IncludeAll) -bxor $enum::includereplicationjobs

...and finally, if you want to cast multiple flags using a variable shortcut, use the -as operator:

$options = $enum::all -bxor ("includeall,includereplicationjobs" -as $enum)

because [$enum]"creation,includeall" (or other guessed-at variants) won't work.

posted on Thursday, October 11, 2007 10:30:27 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Wednesday, October 03, 2007

Holy poop! This is huge!

I know Scott was leading the charge in a general source code cleanup which started some times ago, but it looks like they're nearly done! This is amazing - from within VS 2008 (or your favourite source code editor), you'll be able to seemlessly single-step debug from your own code in that of the .NET framework itself.

Bravo Microsoft! This is an incredibly symbolic step!

http://weblogs.asp.net/scottgu/archive/2007/10/03/releasing-the-source-code-for-the-net-framework-libraries.aspx

posted on Wednesday, October 03, 2007 5:02:52 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Saturday, September 22, 2007

Marco Shaw recently asked on the powershell newsgroup:

Anyone have a working example of using "CodeMethod" to define methods
for a custom object?

And funnily enough, not even Microsoft themselves seem to have a decent working example. So, spelunking in the packaged Types.ps1xml that comes with a PowerShell install I found only three examples. Let's look at the ETS definition for one of them (XmlNode.ToString) in Types.ps1xml:

<Type>
  <
Name>System.Xml.XmlNode</Name>
    <
Members>
      <
CodeMethod>
        <
Name>ToString</Name>
        <
CodeReference>
          <
TypeName>Microsoft.PowerShell.ToStringCodeMethods</TypeName>
          <
MethodName>XmlNode</MethodName>
        </
CodeReference>
      </
CodeMethod>
   </
Members>
</
Type>

Using Reflector, I took a look at the managed code providing this service - btw, ETS CodeMethods are always provisioned by static methods on a managed type:

public static class ToStringCodeMethods
{
    ...
    public static string XmlNode(PSObject instance);
    ...
}


The C# 3.0 users among you will see a remarkable similarity to Extension Methods here, except here ETS CodeMethods can also replace a native method if you so desire. To define a new CodeMethod, you need to provide the full type name, the name of the static method and the ETS method name for the extension method on the target type. In this particular example, all instances of XmlNode exposed in PowerShell will now have their native ToString method replaced with this new one. When you invoke ToString on the XmlNode instance, the static method ToStringCodeMethods.XmlNode will be invoked and the instance parameter will be passed the PSObject wrapped XmlNode. Taking the only three examples in Types.ps1xml at face value, one might assume that CodeMethods can only be parameterless, but it appears not to be the case...

ETS Code Methods with additional parameters

A little experimentation verified that to me that we're not stuck with parameterless codemethods; any additional parameters you define on your static method will be bound to parameters provided in a script invocation of the ETS CodeMethod. The first argument is mandatory for any statics providing CodeMethod services: it will always be passed the ETS extended instance. Here are three examples of some multi-argument signatures and the backing managed code to provide ETS with the implementation:

<Types>
 <
Type>
  <
Name>System.String</Name>
  <
Members>
  <
CodeMethod>
   <
Name>Test1</Name>
   <
CodeReference>
    <
TypeName>Nivot.CodeMethods.TestCodeMethods</TypeName>
    <
MethodName>TestNoArguments</MethodName>
   </
CodeReference>
  </
CodeMethod>
  <
CodeMethod>
   <
Name>Test2</Name>
   <
CodeReference>
    <
TypeName>Nivot.CodeMethods.TestCodeMethods</TypeName>
    <
MethodName>TestOneArgument</MethodName>
   </
CodeReference>
  </
CodeMethod>
  <
CodeMethod>
   <
Name>Test3</Name>
   <
CodeReference>
    <
TypeName>Nivot.CodeMethods.TestCodeMethods</TypeName>
    <
MethodName>TestVariableArguments</MethodName>
   </
CodeReference>
  </
CodeMethod>
 </
Members>
 </
Type>
</
Types>

And here is the corresponding backing managed code signatures:

public static class TestCodeMethods
{
    // Methods
    public static string TestNoArguments(PSObject instance) { ... }
    public static string TestOneArgument(PSObject instance, PSObject arg1) { ... }
    public static string TestVariableArguments(PSObject instance, params PSObject[] args) { ... }
}
Have fun!

posted on Saturday, September 22, 2007 4:42:05 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Friday, August 24, 2007

While I absolutely love MoW's PowerTab, there's been one little niggling thing that's been missing since day one; while you can navigate Types using the opening square bracket syntax, e.g. [<tab> , when using the new-object command this method is not totally syntactically compatible. (update: MoW explains in a comment below that there is a special trick for navigating without the square brackets, oops! me wrong!) You have to navigate to your type, then edit out the square brackets which is a little bit annoying. Also, when using this syntax you typically know exactly which type you need but can't remember the namespace. Or perhaps you know the type name, but don't want to have to type it all out (or navigate there).

So, I figured I'd crack open PowerTab and see how easy it was to implement. To cut a long story short, in about 30 minutes it was done. The thing that made it so easy was that MoW already has a DataSet in memory of all types and their namespaces, so the grunt work is done by a simple DataTable.Select call. I modified the function TabExpansion in TabExpansion.ps1 (version 0.96 b12), and put the following snippet just after the switch on $lastWord :

switch -regex ($lastWord) {

    # Handle inline type search, e.g. new-object .identityreference<tab> or .identityre<tab>
    '^\.(\w+)$' {
       
$typeName = $matches[1]
      
$types = $dsTabexpansion.tables["Types"]
       
$rowFilter = "name like '%.${typeName}%'"
       $types.select($rowFilter) | % {$_["name"] } | Invoke-TabItemSelector $lastWord -Select $SelectionHandler
       break;
    }

The type search is initiated by prefixing the full or partial type name with a period (.) and then hitting tab. There you have it, inline type search. Have fun!

posted on Friday, August 24, 2007 3:28:45 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2] Trackback
# Monday, August 20, 2007

Another answer I posted to the NG, and not all that hard once you know the right classes to use from the BCL. But if you didn't know where to look, I can imagine it being a royal pain in the ass.

--- begin ConvertTo-Sid.ps1 ---

param ($account = $(throw "need account in form domain\username or
[ntaccount] object"))

if ($account -is [security.principal.ntaccount]) { 
    $ntaccount = $account

} else {
   
$ntaccount = new-object security.principal.ntaccount $account
}

$ntaccount.translate( [security.principal.securityidentifier] )
-- end ConvertTo-Sid.ps1 ---

and the reverse:

--- begin ConvertTo-NTAccount.ps1 ---

param ($sid = $(throw "need sid string or [securityidentifier] object"))

if ($sid -is [security.principal.securityidentifier]) {
    $securityidentifier  = $sid

} else { 
    $securityidentifier  = new-object security.principal.securityidentifier $sid
}

$securityidentifier.translate( [security.principal.ntaccount] )

--- end ConvertTo-NTAccount.ps1 ---

You can pass strings as args, or their respective native objects. They both output objects. The output of one can be used as the input of the other.

 

posted on Monday, August 20, 2007 5:50:36 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback