|
Wednesday, April 26, 2006
PowerShell, Change ... is Good ... but Painful
There is a bunch of improvement for console user:
1. Tab completion gets better now.
Did anyone know where is the API mentioned in release note: We added support for parameters and variables tab completion. This was done by having the host call a PowerShell function TabExpansion that takes two parameters – the line being entered and the last token on that line.
2. Some cmdlet Parameters alias changed
For example: *-Process Changed -ProcessName to -Name alias -ProcessName, add alias ProcessId to –Id
This makes me feel much better when working with *-process cmdlet.
Here comes the pain:
1. Change your old script file extention: *.msh - > *.ps1
2. Check into individual script (especially you profile.msh) see if you use any old cmdlet names: for example, combine-path -> join-path
3. Change you %my documents%\msh\profile.msh to %my documents%\PSconfiguration\profile.ps1
If you previously wrote some cmdlets or hosting application, you are going to deal with some mess here:
1. Be careful about name changes:
CmdletAttribute
VerbNounCommand
MshHostRawUserInterface
PSHostRawUserInterface
MshCredential
PSCredential
MshCredentialTypes
PSCredentialTypes
MshCredentialUIOptions
PSCredentialUIOptions
MshInvalidCastException
PSInvalidCastException
Some hidden changes:
FieldDescription.AssemblyFullName
FieldDescription.ParameterAssemblyFullName
FieldDescription.TypeFullName
FieldDescription.ParameterTypeFullName
FieldDescription.TypeName
FieldDescription.ParameterTypeName
BufferCell.Type
BufferCell.BufferCellType
If you are using my “Monad Remoting”
Uninstall “Monad Remoting” before upgrade to “PowerShell”
You’ve been warned!
To use “PowerShell Remoting”, you have to change you client script.
To build your own client, be careful about name change.
Monday, April 24, 2006
Build Your Own Client for Monad Remoting
Uninstall your old Server before upgrade!
My DELL laptop was down again. This time, it was hard drive. I lost two weeks' work. (Ouch! If anyone want to donate a hard drive to me for backup, please contact me. Hehe. ) But I am not giving up. After one month's hard work, I was able to present you the new version of Monad Remoting.
We have a better server: more compatible with ConsoleHost and many bug fixed. But most dramatic changes were made at client side. Now you can build your own cleint application using client class library :
1. To build your own client, you need to initialize an instance of ClientMshHost (Add reference MonadRemoting.ClientMshHost.dll, namespace MonadRemoting), which served as proxy between Monad Remoting server user interface (remote UI) and Local userinterface (Local UI). Although with similar public properties, this "Host" is not derived from MshHost thus can not be used to open a runspace.
2. ClientMshHost has only two public method Start() (to connect server and run) and SetShouldExit(int) (to force client to quit) . Let ClientMshHost do all the hard work: connect to server, authenticate & encrypt, open runspace and run script. Whenever a user interface API was invoked at Server, parameters were sent to ClientMshHost. ClientMshHost will invoke same user interface API at Local UI: display information or get user input, return data back to server. ClientMshHost tries not to throw any exceptions, instead it use Local UI to display all server error and local error.
3. The constructor of ClientMshHost requires a IPEndPoint (to connect Remote UI) and a MshHostUserinterface (to connect LocalUI).
public ClientMshHost(IPEndPoint host, MshHostUserInterface UI){}MshHostRawUserinterface within MshHostUserinterface is NOT mandatory but is supported by ClientMshHost. Minimum requirement is to implement a MshHostUserinterface which overridden
public abstract string ReadLine();Note: To simplify server and client implementation, following API in local UI were ignored (instead normal Read/Write API will be invoked):
public abstract SecureString ReadLineAsSecureString();
public abstract void Write(string value);
public abstract void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value);
public abstract void WriteDebugLine(string message);
public abstract void WriteErrorLine(string value);
public virtual void WriteLine();
public abstract void WriteLine(string value);
public virtual void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value);
public abstract void WriteVerboseLine(string message);
public abstract void WriteWarningLine(string message);
public abstract Dictionary<string, MshObject> Prompt(string caption, string message, Collection<FieldDescription> descriptions);There are great advantages of this design:
public abstract int PromptForChoice(string caption, string message, Collection<ChoiceDescription> choices, int defaultChoice);
public abstract MshCredential PromptForCredential(string caption, string message, string userName, string targetName);
public abstract MshCredential PromptForCredential(string caption, string message, string userName, string targetName, MshCredentialTypes allowedCredentialTypes, MshCredentialUIOptions options);
1. There is no need to implement my own console client. Don't invent wheels! Msh.exe already exposed a great MshHostUserinterface (ConsoleHostUserInterface) as $host.UI variable. So just run a simple script, we are able to "steal" ConsoleHostUserInterface for remoting (Or I should say,unleash the power of $host.UI). I already add following script into my profile.msh.
function start-remotehost {2. We enjoy all the benefits of ConsoleHostUserInterface and whenever Monad was upgraded, our Monad Remoting local UI was automatically upgraded. Actually, because it use 100% naive msh.exe UI, it is difficult to find out you are in local shell or remote shell. You can always invoke
Param ([string] $IPAddress = "127.0.0.1", [int] $Port = 8080)
# Make sure you change path to where you saved MonadRemoting.ClientMshHost.dll
[void][System.Reflection.Assembly]::LoadFile("D:\msh\MonadRemoting.ClientMshHost.dll")
$RemoteIP = [System.Net.IPAddress]::Parse($IPAddress)
$RemoteEP = new-object System.Net.IPEndPoint ($RemoteIP, $Port)
$RemotingClient = new-object
MonadRemoting.ClientMshHost($RemoteEP,$host.ui)
$RemotingClient.Start()
$RemotingClient = $null
}
$hostIf you saw ConsoleHost, you were in local shell
Name : ConsoleHostIf you saw ServerMshHost, you were in remote shell.
Version : 1.0.7487.0
InstanceId : ae801745-7671-4a46-9501-6f120bae8a81
UI : System.Management.Automation.Internal.Host.InternalHostUserI
nterface
...
Name : ServerMshHost
Version : 0.1.0.283
InstanceId : 84574e63-2b39-4438-b8cc-bb13bd7af932
UI : System.Management.Automation.Internal.Host.InternalHostUserI
nterface
...
3. You will never leave you msh.exe console window and start another program for remote access. You can call start-remoteHost to gain remote access within msh.exe. When you are done, type
exitand return to your local shell. You want connect to Remote host again? Just call start-remoteHost again. Actually, I am now using this client as a in-process su command. I was thinking of building this client as a MshSnapin someday.
4. The ClientMshHost dose not care what kind of outter application it is in. It works for console application like msh.exe, also works for Winform base GUI application like Karl Prosser's Msh Analyzer. Hopefully, soon I can use Msh analyzer as my Monad remoting client (Its Rich UI is really cool)
Have Fun
[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]
Tags: msh monad PowerShell
Sunday, April 09, 2006
MshObject, the Magic Therein
How? It is MshObject which does all the tricks. MshObject serve as an adapter to .Net, WMI and COM objects. For more details, read MSH Object Concepts.
Here are members of System.Management.Automation.MshObject:
public class MshObject : IFormattableMshObject is hidden under the surface of Msh. You might not notice its exsitance unless you write a cmdlet which deal with MshObject directly. I prevously working on Serialization of MshObject in my Monad Remoting project. I wrote a small cmdlet to track Mshobject within MSH.exe
{
// Methods
static MshObject();
public MshObject();
public MshObject(object obj);
public static MshObject AsMshObject(object obj);
public virtual MshObject Copy();
public override string ToString();
public string ToString(string format, IFormatProvider formatProvider);
// Properties
public object BaseObject { get; }
public object ImmediateBaseObject { get; }
public MshMemberInfoCollection Members { get; }
public MshMemberInfoCollection Methods { get; }
public MshMemberInfoCollection Properties { get; }
public Collection<string> TypeNames { get; }
}
using System;It is really intersting to see how export-clixml and import-clixml works:
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
namespace MshCx
{
[Cmdlet("get", "mshobject")]
public class GetMshobject: Cmdlet
{
private MshObject[] inputObjects = null;
[Parameter(Mandatory = false,ValueFromPipeline = true)]
public MshObject[] InputObjects
{
get { return inputObjects; }
set { inputObjects = value; }
}
protected override void ProcessRecord()
{
if (inputObjects != null)
{
StringBuilder sb = new StringBuilder();
foreach (MshObject Item in inputObjects)
{
sb.AppendLine();
sb.AppendLine("MshObject: ");
sb.AppendLine("\t" + Item.ToString());
sb.AppendLine("BaseObject: ");
sb.AppendLine("\t"+ Item.BaseObject.ToString() + " (" + Item.BaseObject.GetType().FullName + ")");
sb.AppendLine("ImmediateBaseObject: ");
sb.AppendLine("\t" + Item.ImmediateBaseObject.ToString() + " (" + Item.ImmediateBaseObject.GetType().FullName + ")");
sb.AppendLine("TypeNames: ");
foreach (string Type in Item.TypeNames)
{
sb.AppendLine("\t"+ Type);
}
sb.AppendLine("=============================================================");
}
WriteObject(sb.ToString());
}
}
}
}
> $file= (get-childitem)[0]If you look carefully, you will notice:
> $file
Directory: Microsoft.Management.Automation.Core\FileSystem::D:\msh
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2006-4-3 22:27 350 1.txt
> $file | get-member
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
AppendText Method System.IO.StreamWriter AppendText()
CopyTo Method System.IO.FileInfo CopyTo(String de...
Create Method System.IO.FileStream Create()
CreateObjRef Method System.Runtime.Remoting.ObjRef Crea...
CreateText Method System.IO.StreamWriter CreateText()
Decrypt Method System.Void Decrypt()
Delete Method System.Void Delete()
Encrypt Method System.Void Encrypt()
Equals Method System.Boolean Equals(Object obj)
get_Attributes Method System.IO.FileAttributes get_Attrib...
get_CreationTime Method System.DateTime get_CreationTime()
get_CreationTimeUtc Method System.DateTime get_CreationTimeUtc()
get_Directory Method System.IO.DirectoryInfo get_Directo...
get_DirectoryName Method System.String get_DirectoryName()
get_Exists Method System.Boolean get_Exists()
get_Extension Method System.String get_Extension()
get_FullName Method System.String get_FullName()
get_IsReadOnly Method System.Boolean get_IsReadOnly()
get_LastAccessTime Method System.DateTime get_LastAccessTime()
get_LastAccessTimeUtc Method System.DateTime get_LastAccessTimeU...
get_LastWriteTime Method System.DateTime get_LastWriteTime()
get_LastWriteTimeUtc Method System.DateTime get_LastWriteTimeUtc()
get_Length Method System.Int64 get_Length()
get_Name Method System.String get_Name()
GetAccessControl Method System.Security.AccessControl.FileS...
GetHashCode Method System.Int32 GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetObjectData Method System.Void GetObjectData(Serializa...
GetType Method System.Type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeSer...
MoveTo Method System.Void MoveTo(String destFileN...
Open Method System.IO.FileStream Open(FileMode ...
OpenRead Method System.IO.FileStream OpenRead()
OpenText Method System.IO.StreamReader OpenText()
OpenWrite Method System.IO.FileStream OpenWrite()
Refresh Method System.Void Refresh()
Replace Method System.IO.FileInfo Replace(String d...
set_Attributes Method System.Void set_Attributes(FileAttr...
set_CreationTime Method System.Void set_CreationTime(DateTi...
set_CreationTimeUtc Method System.Void set_CreationTimeUtc(Dat...
set_IsReadOnly Method System.Void set_IsReadOnly(Boolean ...
set_LastAccessTime Method System.Void set_LastAccessTime(Date...
set_LastAccessTimeUtc Method System.Void set_LastAccessTimeUtc(D...
set_LastWriteTime Method System.Void set_LastWriteTime(DateT...
set_LastWriteTimeUtc Method System.Void set_LastWriteTimeUtc(Da...
SetAccessControl Method System.Void SetAccessControl(FileSe...
ToString Method System.String ToString()
MshChildName NoteProperty System.String MshChildName=1.txt
MshDrive NoteProperty System.Management.Automation.DriveI...
MshIsContainer NoteProperty System.Boolean MshIsContainer=False
MshParentPath NoteProperty System.String MshParentPath=Microso...
MshPath NoteProperty System.String MshPath=Microsoft.Man...
MshProvider NoteProperty System.Management.Automation.Provid...
Attributes Property System.IO.FileAttributes Attributes...
CreationTime Property System.DateTime CreationTime {get;s...
CreationTimeUtc Property System.DateTime CreationTimeUtc {ge...
Directory Property System.IO.DirectoryInfo Directory {...
DirectoryName Property System.String DirectoryName {get;}
Exists Property System.Boolean Exists {get;}
Extension Property System.String Extension {get;}
FullName Property System.String FullName {get;}
IsReadOnly Property System.Boolean IsReadOnly {get;set;}
LastAccessTime Property System.DateTime LastAccessTime {get...
LastAccessTimeUtc Property System.DateTime LastAccessTimeUtc {...
LastWriteTime Property System.DateTime LastWriteTime {get;...
LastWriteTimeUtc Property System.DateTime LastWriteTimeUtc {g...
Length Property System.Int64 Length {get;}
Name Property System.String Name {get;}
Mode ScriptProperty System.Object Mode {get=$catr = "";
> $file | get-mshobject
MshObject:
1.txt
BaseObject:
1.txt (System.IO.FileInfo)
ImmediateBaseObject:
1.txt (System.IO.FileInfo)
TypeNames:
System.IO.FileInfo
System.IO.FileSystemInfo
System.MarshalByRefObject
System.Object
=============================================================
> $file |export-clixml abc.xml
> $file = import-clixml abc.xml
> $file
Directory: Microsoft.Management.Automation.Core\FileSystem::D:\msh
Mode LastWriteTime Length Name
---- ------------- ------ ----
2006-4-3 22:27 350 1.txt
> $file | get-member
TypeName: Deserialized.System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
MshChildName NoteProperty System.String MshChildName=1.txt
MshDrive NoteProperty System.Management.Automation.MshObject MshDri...
MshIsContainer NoteProperty System.Boolean MshIsContainer=False
MshParentPath NoteProperty System.String MshParentPath=Microsoft.Managem...
MshPath NoteProperty System.String MshPath=Microsoft.Management.Au...
MshProvider NoteProperty System.Management.Automation.MshObject MshPro...
Attributes Property System.String {get;set;}
CreationTime Property System.DateTime {get;set;}
CreationTimeUtc Property System.DateTime {get;set;}
Directory Property System.String {get;set;}
DirectoryName Property System.String {get;set;}
Exists Property System.Boolean {get;set;}
Extension Property System.String {get;set;}
FullName Property System.String {get;set;}
IsReadOnly Property System.Boolean {get;set;}
LastAccessTime Property System.DateTime {get;set;}
LastAccessTimeUtc Property System.DateTime {get;set;}
LastWriteTime Property System.DateTime {get;set;}
LastWriteTimeUtc Property System.DateTime {get;set;}
Length Property System.Int64 {get;set;}
Name Property System.String {get;set;}
> $file | get-mshobject
MshObject:
@{MshPath=Microsoft.Management.Automation.Core\FileSystem::D:\msh\1.txt;
MshPa
rentPath=Microsoft.Management.Automation.Core\FileSystem::D:\msh; MshChildName=
1.txt; MshDrive=; MshProvider=; MshIsContainer=False; Name=1.txt; Length=350; D
irectoryName=D:\msh; Directory=D:\msh; IsReadOnly=False; Exists=True; FullName=
D:\msh\1.txt; Extension=.txt; CreationTime=2006-4-3 22:27:16; CreationTimeUtc=2
006-4-4 2:27:16; LastAccessTime=2006-4-3 22:27:16; LastAccessTimeUtc=2006-4-4 2
:27:16; LastWriteTime=2006-4-3 22:27:16; LastWriteTimeUtc=2006-4-4 2:27:16; Att
ributes=Archive}
BaseObject:
(System.Management.Automation.MshCustomObject)
ImmediateBaseObject:
(System.Management.Automation.MshCustomObject)
TypeNames:
Deserialized.System.IO.FileInfo
Deserialized.System.IO.FileSystemInfo
Deserialized.System.MarshalByRefObject
Deserialized.System.Object
=============================================================
1. export-clixml write MshObject.Properties into a xml file and import-clixml reconstruct a MshObject.
2. Although they looks the same, something changed after serializtion and deserializtion:
Before: A FileInfo object is wrapped within MshObject
After: A MshCustomObject is wrapped within MshObject. All Methods are lost.
3. Deserialized MshCustomObject is a Hash table.
Have Fun.
[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]
Tags: msh monad PowerShell