How can I read the verbose output from a Cmdlet in C# using Exchange Powershell
Environment: Exchange 2007 sp3 (2003 sp2 mixed mode)
Visual Studio 2008, .Net 3.5
I'm working with an Exchange powershell move-mailbox cmdlet and have noted when I do so from the Exchange Management shell (using the Verbose switch) there is a ton of real-time information provided.
To provide a little context, I'm attempting to create a UI application that moves mailboxes similarly to the Exchange Management Console but desire to support an input file and specific server/database destinations for each entry (and threading).
Here's roughly what I have at present but I'm not sure if there is an event I need to register for or what... And to be clear, I desire to get this information in real-time so I may update my UI to reflect what's occurring in the move sequence for the appropriate user (pretty much like the native functionality offered in the Management Console). And in case you are wondering, the reason why I'm not content with the Management Console functionality is, I have an algorithm which I'm using to balance us开发者_JS百科ers depending on storage limit, Blackberry use, journaling, exception mailbox size etc which demands user be mapped to specific locations... and I do not desire to create many/several move groups for each common destination or to hunt for lists of users individually through the management console UI.
I can not seem to find any good documentation or examples of how to tie into reading the verbose messages that are provided within the console using C# (I see value in being able to read this kind of information in many different scenarios).
I've explored the Invoke and InvokeAsync methods and the StateChanged & DataReady events but none of these seem to provide the information (verbose comments) that I'm after.
Any direction or examples that can be provided will be very appreciated!
A code sample which is little more than how I would ordinarily call any other powershell command follows:
// config to use ExMgmt shell, create runspace and open it
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
PSSnapInException snapInException = null;
PSSnapInInfo info = rsConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException);
if (snapInException != null) throw snapInException;
Runspace runspace = RunspaceFactory.CreateRunspace(rsConfig);
try
{
runspace.Open();
// create a pipeline and feed script text
Pipeline pipeline = runspace.CreatePipeline();
string targetDatabase = @"myServer\myStorageGroup\myDB";
string mbxOwner = "user@company.com";
Command myMoveMailbox = new Command("Move-Mailbox", false, false);
myMoveMailbox.Parameters.Add("Identity", mbxOwner);
myMoveMailbox.Parameters.Add("TargetDatabase", targetDatabase);
myMoveMailbox.Parameters.Add("Verbose");
myMoveMailbox.Parameters.Add("ValidateOnly");
myMoveMailbox.Parameters.Add("Confirm", false);
pipeline.Commands.Add(myMoveMailbox);
System.Collections.ObjectModel.Collection<PSObject> output = null;
// these next few lines that are commented out are where I've tried
// registering for events and calling asynchronously but this doesn't
// seem to get me anywhere closer
//
//pipeline.StateChanged += new EventHandler<PipelineStateEventArgs>(pipeline_StateChanged);
//pipeline.Output.DataReady += new EventHandler(Output_DataReady);
//pipeline.InvokeAsync();
//pipeline.Input.Close();
//return; tried these variations that are commented out but none seem to be useful
output = pipeline.Invoke();
// Check for errors in the pipeline and throw an exception if necessary
if (pipeline.Error != null && pipeline.Error.Count > 0)
{
StringBuilder pipelineError = new StringBuilder();
pipelineError.AppendFormat("Error calling Test() Cmdlet. ");
foreach (object item in pipeline.Error.ReadToEnd())
pipelineError.AppendFormat("{0}\n", item.ToString());
throw new Exception(pipelineError.ToString());
}
foreach (PSObject psObject in output)
{
// blah, blah, blah
// this is normally where I would read details about a particular PS command
// but really pertains to a command once it finishes and has nothing to do with
// the verbose messages that I'm after... since this part of the methods pertains
// to the after-effects of a command having run, I'm suspecting I need to look to
// the asynch invoke method but am not certain or knowing how.
}
}
finally
{
runspace.Close();
}
Take a look at the PowerShell class. It has a Streams
property that allows you to access the Verbose stream's contents. Note that the PowerShell class was introduced in PowerShell 2.0 but is probably what you want to use when embedded PowerShell within a hosting app. In fact, the docs say:
Provides methods that are used to create a pipeline of commands and invoke those commands either synchronously or asynchronously within a runspace. This class also provides access to the output streams that contain data that is generated when the commands are invoked. This class is primarily intended for host applications that programmatically use Windows PowerShell to perform tasks. This class is introduced in Windows PowerShell 2.0.
I've since created an account on the site so I could interact... I'm the same person who asked this question.
Thanks very much for the information and link provided. As you already indicated, I'm noting that I must upgrade to PS 2.0 to "see" this Powershell class (which is proving a bit of a chore for some reason).
Now that I understand to use the PS class with the streams property, I'm expecting I'll be able to add the Exchange Management snap-in similar to how I was doing previously and I should be on my way.
Thanks for pointing me in the right direction, this appears to be exactly what I needed!
Regards, Keith
精彩评论