Tuesday, September 25, 2007

Howto: Invoking cmdlets within a cmdlet using C# (part III: ...using csc.exe)

In the introduction to this howto series, I mentioned part III would show how one invokes cmdlets within a cmdlet using csc.exe.  In this particular example, I'm going to show an example of when the cmdlet we are going to invoke does not derive from the cmdlet class.

As I mentioned in part II, there's a quick PowerShell one-liner to determine if a cmdlet derives from the cmdlet or pscmdlet class.

In our case, our example is from PowerGadgets where we will use their invoke-webserice cmdlet again.  This cmdlet derive from the cmdlet class so my example from part II will also work, but I'm going to use this cmdlet again to show how one can invoke a cmdlet that derives from the pscmdlet class.

In other words, this method I will use works for both types of cmdlets, so it could be seen as the better solution where one doesn't have to determine what class the cmdlet derives from before using it.

Here's the non-commented "guts" of the C# code (I've stripped the regular stuff like the pssnapin stuff), but the full C# code is downloadable here:

   1: string[] args = new string[] { "http://www.webservicex.net/WeatherForecast.asmx?WSDL","GetWeatherByZipCode","80526" };
   2: string command = "invoke-webservice -wsdl $args[0] -method $args[1] $args[2]";
   3: if (PostProcess)
   4: {
   5:     Collection<PSObject> results =
   6:     this.InvokeCommand.InvokeScript(command,false, PipelineResultTypes.None, null, args);
   7:     foreach (PSObject p in results)
   8:     {
   9:          WriteObject(">>> " + LanguagePrimitives.ConvertTo(p, typeof(string)));
  10:     }
  11: }
  12: else
  13: {
  14:     this.InvokeCommand.InvokeScript(command,false, PipelineResultTypes.Output, null, args);
  15: }

We setup our environment in the same way I explained in part II:

   1: PSH> $framework=$([System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory())
   2: PSH> set-alias csc "$($framework)csc.exe"
   3: PSH> set-alias installutil "$($framework)installutil.exe"
   4: PSH> $ref=[psobject].assembly.location
   5: PSH> $pg="c:\program files\powergadgets\"
   6: PSH> $pg1="$($pg)powergadgets.commands.dll"
   7: PSH> $pg2="$($pg)powergadgets.data.dll"


(NOTE: Again from part II, this is to get you going quickly, most do not recommend you use DLLs from the GAC as I do above with how I create the "$ref" variable. Problem is, you need to go and download a huge SDK otherwise, which you should do if you do regular PowerShell development.)

Now, we are going to compile the cmdlet, load it, then run it. I provided the link for the full C# source code above which you will need to go and get:

PSH> csc /t:library /r:$ref /r:$pg1 /r:$pg2 invokewebservice2.cs
PSH> installutil invokewebservice2.dll
PSH> Add-PSSnapin InvokeWebService2
PSH> invoke-webservice2
Latitude         : 40.54729
Longitude        : 105.1076
AllocationFactor : 0.008857
FipsCode         : 08
PlaceName        : FORT COLLINS
StateCode        : CO
Status           :
Details          : {WeatherData, WeatherData, WeatherData, WeatherData...}


Now, if you're cycling through all of my examples, I've named the resulting cmdlet invoke-webservice2, so you're going to get a name conflict if you don't unregister the .DLL you might have created from my other examples or rename the namespace and the cmdlet name in the .cs file before you compile.

Again, the same warning applies here about where you decide to put your .cs file and resulting DLLs.  Please read part II again, if you're not sure what I'm referring to.


I've just shown one way to invoke a cmdlet from within another cmdlet.   You can use this method when the cmdlet you are invoking derives from the cmdlet or the pscmdlet class. In one of my next postings, I'll show you how to do some of this from with Visual Studio 2005 C# Express.