The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

marco.shaw (24) [Avatar] Offline
#1
Bruce,

You gotta have the answer... smilie

I'm playing around trying to create a cmdlet that will use a PowerGadgets cmdlets. Specifically their invoke-webservice.

Read my comments below:

protected override void ProcessRecord()
{
//Here I've just copied code that I was able to use to
create a cmdlet
//using get-process and it works.
//GetProcessCommand g = new GetProcessCommand();
//foreach (Process p in g.Invoke<Process>())
//{
//WriteObject(p);
//}

//Here's my attempt to use invoke-webservice from PowerGadgets.
InvokeWebServiceCommand iws = new InvokeWebServiceCommand();

iws.WSDL = new string[] {
"http://www.webservicex.net/WeatherForecast.asmx?WSDL" }();
iws.Method = new string[] { "GetWeatherByZipCode(80526)" }();

//I made this up, I'm a bit lost from here on.
foreach (WeatherForecasts wf in iws.Invoke<WeatherForecasts>())
{
WriteObject(wf);
}

}


Can you help me figure out how to invoke cmdlets from another?

I'm trying to duplicate this blog post with the info I'm passing:
http://keithhill.spaces.live.com/blog/cns!5A8D2641E0963A97!512.entry

I guess I could also do this in a runspace, but I want the cmdlet I create to still be able to participate in a pipe, so I guess I must use a cmdlet...

Message was edited by:
marco.shaw
brucepay (155) [Avatar] Offline
#2
Re: Invoking cmdlets from other cmdlets
You can only directly instantiate and invoke cmdlets that derive from Cmdlet. If they derive from PSCmdlet, then you have to run them from within a PowerShell pipeline. We'll be making this all much easier in V2, but for now, you can craft a small script in the cmdlet to do the work. Here's an example that shows how to do it. (I've substituted Write-Object for Invoke-WebService but that should be a simple substitution...) Hopefully the forum stuff won't mangle the code too badly. A number of people have asked this question so if this works out, would you mind blogging the result? (I know I should do it but I just don't have time these days...)

-bruce

----------------------------------cut here-----------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

[Cmdlet("test", "weather")]
public class TestWeather : PSCmdlet
{
// When specified, postprocess the result objects
// for the cmdlet call - see below...
[Parameter]
public SwitchParameter PostProcess;

protected override void ProcessRecord()
{
string[] args = new string[] {
"http://www.webservicex.net/WeatherForecast.asmx?WSDL",
"GetWeatherByZipCode(80526)"
};

// Now define the command script - I don't have the Invoke-WebService
// so I'm faking it with Write-Output
string command = "Write-Output -wsdl $args[0] -method $args[1]";

if (PostProcess)
{
// Invoke the command and return the results for
// additional processing...
Collection<PSObject> results =
this.InvokeCommand.InvokeScript(command,
false, PipelineResultTypes.None, null, args);

// Now process each result object
foreach (PSObject p in results)
{
// Use the PowerShell converter to convert the
// object to the desired type. This is available
// to programmers through the LanguagePrimitives
// class.
WriteObject(">>> " +
LanguagePrimitives.ConvertTo(p, typeof(string)));
}
}
else
{
// Invoke it, sending the results directly to the
// calling cmdlet's output pipe
this.InvokeCommand.InvokeScript(command,
false, PipelineResultTypes.Output, null, args);
}
}
}
marco.shaw (24) [Avatar] Offline
#3
Re: Invoking cmdlets from other cmdlets
Oisin Grehan posted this in the PSH newsgroup (careful of the formatting):
get-command | % { $_.name + " is pscmdlet: " +
[management.automation.pscmdlet].IsAssignableFrom($_.implementingtype) }

If the result is false, the current cmdlet is derived from the cmdlet class. invoke-webservice appears to be OK (and I had actually read the SDK docs to confirm this beforehand).

I will check your code out, and be happy to blog about it!
marco.shaw (24) [Avatar] Offline
#4
Re: Invoking cmdlets from other cmdlets
Using write-ouput works great. Using PowerGadgets' invoke-webservice seems to choke on the method though. I've contacted their dev guy. I'd think this should work, but doesn't:

PSH> invoke-webservice -wsdl
http://www.webservicex.net/WeatherForecast.asmx?WSDL -method
GetWeatherByZipCode(80526)


Latitude : 40.54729
Longitude : 105.1076
AllocationFactor : 0.008857
FipsCode : 08
PlaceName : FORT COLLINS
StateCode : CO
Status :
Details :



PSH> [string]$zip="GetWeatherByZipCode(80526)"
PSH> [string]$url="http://www.webservicex.net/WeatherForecast.asmx?WSDL"
PSH> invoke-webservice -wsdl $url -method $zip
Invoke-WebService : Method GetWeatherByZipCode(80526) Not Found
At line:1 char:18
+ invoke-webservice <<<< -wsdl $url -method $zip
brucepay (155) [Avatar] Offline
#5
Re: Invoking cmdlets from other cmdlets
The command

invoke-webservice -wsdl
http://www.webservicex.net/WeatherForecast.asmx?WSDL -method
GetWeatherByZipCode(80526)

will parse as 3 arguments, not 2 as in

PS (57) > write-output `
>> -wsdl http://www.webservicex.net/WeatherForecast.asmx?WSDL `
>> -method GetWeatherByZipCode(80526)
>>
-wsdl
http://www.webservicex.net/WeatherForecast.asmx?WSDL
-method
GetWeatherByZipCode
80526

It's equivalent to:

invoke-webservice -wsdl
http://www.webservicex.net/WeatherForecast.asmx?WSDL -method
GetWeatherByZipCode 80526

So try modifying the example as follows

---snip----

string[] args = new string[] {
"http://www.webservicex.net/WeatherForecast.asmx?WSDL",
"GetWeatherByZipCode",
"80526"
};

// Now define the command script - I don't have the Invoke-WebService
// so I'm faking it with Write-Output
string command = "Write-Output -wsdl $args[0] -method $args[1] $args[2]";

---snip---

Also, if you do Get-Command on Invoke-Webservice, you should be able to see the name of this third parameter....

-bruce
brucepay (155) [Avatar] Offline
#6
Re: Invoking cmdlets from other cmdlets
Oh yeah - and the 3 parameter thing may also be the answer to the original problem where the direct invocation was failing. You probably have to set 3 properties on the object instead of two.

-bruce
marco.shaw (24) [Avatar] Offline
#7
Re: Invoking cmdlets from other cmdlets
This works as you indicated:

PSH> [string]$url="http://www.webservicex.net/WeatherForecast.asmx?WSDL"
PSH> [string]$meth="GetWeatherByZipCode"
PSH> [string]$zip="80526"
PSH> invoke-webservice -wsdl $url -method $meth $zip

This is likely not the solution to my original problem where I'm trying to use invoke to run the cmdlet directly.

The SDK says this:
"System.Management.Automation.Cmdlet.Invoke: This variant executes the cmdlet object and returns a collection of “T” type objects – Ienumerable<T>(in our case Process)."

With this example:
"foreach (Process p in g.Invoke<Process>())
{
Console.WriteLine(p.ToString());
}"

I don't understand how to translate the above to use with my example... I understand to change to WriteObject, and that I can likely drop the foreach loop, but I don't understand how "Process" applies in my case.

But you provided a solution for now. I don't like not being able to figure something out once I have started...
brucepay (155) [Avatar] Offline
#8
Re: Invoking cmdlets from other cmdlets
Here's a version that works with direct invocation. Because the WSDL is being compiled at runtime, there is no DLL for the compiler to reference when resolving types. To work around this, you have to use <object> as the target type for the generic invoke method. This works, but if you want to do anything with the objects being returned, you'll need to wrap them in PSObjects and use the PSObject members to access the properties and methods. (Or you can use VB.Net with option strict turned off.)

-bruce

---------------------Get-Weather cmdlet using direct invocation-----------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using PowerGadgets.Commands;

[Cmdlet("get", "weather")]
public class TestWeather : PSCmdlet
{
protected override void ProcessRecord()
{
InvokeWebServiceCommand iws = new InvokeWebServiceCommand();
iws.WSDL = "http://www.webservicex.net/WeatherForecast.asmx?WSDL";
iws.Method = "GetWeatherByZipCode";
iws.Parameters = new object[] { "98052" };
foreach (object o in iws.Invoke<object>())
{
WriteObject(o);
}
}
}