Monday, June 28, 2004

I have been wondering about the cmdlet lifecycle. 

 

How do you add a new cmdlet?

How are the cmdlets loaded by MSH?

 

You use registercommand.exe <Path/dllname> and the new dll is registered. Now what happens when msh is started again? Does it load all the dlls in memory? Or does it load it when a cmdlet in the dll is explicitly invoked? Loading all cmdlet dlls in memory seems like too much memory consumption and overhead. The converse argument could be that practically the number of cmdlets wouldn’t be a very huge number; so doing this at start would give a performance hit.

 

Then again, when do we design stuff for pracrtical cases alone, we usually think of the worst cases!

 

Well, so how do you really find out how MSH is handling commandlet life cycle? There is no cordbg for .Net 2.0 yet and no CLR profilers in the picture.

 

There still is filemon.exe. Thanks to filemon.exe, these are my observations.

 

  1. All the cmdlet dlls are not loaded at msh start up.
  2. get-command doesn’t use reflection on all dlls’ to get all cmdlet names.
  3. Typing a specific cmdlet name loads the corresponding dll, processes and closes it

 These observations lead to the following conclusions:

 

  1. At start-up, msh does not load all the cmdlets in memory
  2. On typing get-command, it does a look-up [may be something like a file lookup], finds all the cmdlet name entries and outputs them
  3. On typing a specific cmdlet at the msh prompt, the corresponding dll is loaded by msh, the processing done, results obtained and dll unloaded [I think]
  4. Due to caching, all cmdlets take time during their first invocation and are faster thereafter.

 

How is a cmdlet executed?

 

First the corresponding dll is found and loaded. Then the cmdlet class is instantiated.

StartProcessing() if overrid is executed. If there are any input parameters, then they are filled with the values by msh and passed to ProcessRecord() if one exists. If pipelined input is enabled then the input parameter is filled with the current pipeline object and ProcessRecord() is exceuted for each of the input records. [So, this kind of looping is not the cmdlet’s responsibility, msh handles it and pumps in the value each time into the input parameter variable]. Finally if EndProcessing() is overrid, then it is executed. The object is then disposed off. Msh handles the output display/formatting/piping/redirection.

 

DISCLAIMER: These are my views only. This is not documented stuff, so I am just taking a guess as to how things may be working under the cover. I could be wrong!

Monday, June 28, 2004 1:15:42 PM (US Eastern Standard Time, UTC-05:00)  #    Comments [0]Trackback
 Friday, June 25, 2004

I took a session on Monad at the Bangalore .NET User Group meeting yesterday. Here is a brief write-up about the session for those of you who missed the session. People starting off on Monad may be interested in reading this.

 

Introduction

Unlike conventional shells that work on text streams and expect the end user to be an expert in text parsing, MSH introduces the idea of using structured object pipelines where objects instead of text flow between commands.

 

At the msh prompt, if we type

MSH C:/ > dir

we see a list of files [the same way the dos dir worked]. We see the same output for ls.

 

In Monad commands are called cmdlet [Commandlets]. Each cmdlet is a combination of a verb-noun pair. For example the cmdlet get-children or get/children gets all the children [files and directories when invoked in a FS]. dir and ls are aliases of get-children. By default the verb ‘get’ is assumed for a command given without a verb. So typing children gives the result of get-children and command gives the result of get-command. Alias is an exception. Typing alias gives the result of set-alias and typing aliases give the result of get-alias.

 

To see all the commands type command. To see all the aliases type aliases.

 

Some example Commands

 

MSH C:/> get-process

 

By using reflection, any object can list the methods and properties that it supports. To get a list of methods supported by a get-process object [which is a System.Diagnostics.Process object] try the command below.

 

MSH C:/> get-process | get-member -methods

 

MSH C:/> $a = get-process

 

MSH C:/> $a[0]

 

MSH C:/> $a[0].ToString()

 

Here $a is an array of Process Objects. We can invoke any function or property from the process class on object $a[0] and see the results like the ToString() example above.

 

MSH C:/> get-process | get-member -property

 

MSH C:/> $a[0].handlecount

 

MSH C:/> gps

 

MSH C:/> gps | tail

 

MSH C:/> gps | sort id | tail

 

MSH C:/> gps | where “processname –like cmd*”

 

MSH C:/> gps | reduce-expression {$_.handlecount -ge 500}

 

get-process or gps returns an array of objects. Reduce-expression works like a lambda in functional programming languages [similar to anonymous method]

For example in scheme (lambda((a b)(+ a b))) acts as a block of code.

In Monad the block of code after reduce-expression in braces is the script that will be carried for each pipeline object. $_ is set to the current pipeline object. So for each object in the object array returned by gps, $_ is filled with an object (the current pipeline object) for which the block of code is executed.

 

MSH C:/> gps | sort handlecount | tail | out-excel

 

MSH C:/> get-process | out-chart processname,handlecount -Title "Processes" -Filename Processes.html

 

MSH C:/> gps | sort handlecount | tail | out-grid processname,handlecount

 

MSH C:/> get-command out-*

 

MSH C:/> foreach ($p in $a | where "handlecount -ge 500"  | sort handlecount) { "{0,-15} has {1,6} handles" % $p.processname,$p.handlecount }

 

Notice the .NET style for formatting strings above.

 

MSH C:/> function n{notepad}

 

MSH C:/> n

 

MSH C:/> n;n;n

 

MSH C:/> gps [a-c]*,[t-z]* -exclude *[p-t] | stop-process –whatif

 

MSH C:/> gps | where “processname –like notepad*” | stop-ps1 –confirm

 

For invalid process ids, an error message is displaying when trying to stop them.

 

MSH C:/> stop-process 123,345,100000,200000,300000

 

MSH C:/> stop-process 123,345,100000,200000,300000 -errorpolicy notifystop

 

MSH C:/> stop-process 100000,200000,300000 -errorpolicy inquire

[Error stop-process]: (No process found for given ID :  : 100000) 100000"

 

[Error stop-process]: (No process found for given ID :  : 100000) 100000"

Continue :[y/yes/ n/no t/yestoall l/notoall s/suspend] s

MSH C:/> exit

 

[Error stop-process]: (No process found for given ID :  : 100000) 100000"

Continue :

[y/yes/ n/no t/yestoall l/notoall s/suspend] l

Stopped (Cmdlet:stop-process): User requested stop

 

MSH C:/>

 

Cmdlet

Each cmdlet is a managed class and not a separate executable. A simple cmdlet code is given below.

 

using System;

using System.Diagnostics;

using System.Management.Automation;

 

namespace SampleCmdlet

{

#region GetPs1

       [CmdletDeclaration("get", "ps1")]

       public class GetPs1: Cmdlet

       {

              public override void ProcessRecord()

              {

                     WriteObjects(Process.GetProcesses());

              }

       }

#endregion

}

 

The class that is to be exposed as a cmdlet is annotated with a CmdletDeclaration property specifying the verb and noun that is to be used to access this cmdlet. Also your class must inherit from the Cmdlet class and override atleast one of the three functions StartProcessing, ProcessRecord and EndProcessing.

 

A cmdlet that receives some input parameters and does exception-handling is given below

 

using System;

using System.Diagnostics;

using System.Management.Automation;

 

namespace SampleCmdlet

{

#region StopPs1

       [CmdletDeclaration("stop", "ps1")]

       public class StopPs1: Cmdlet

       {

              [ParsingParameterMapping(0)]

              [ParsingAllowPipelineInput]

              [ParsingMandatoryParameter]

              [ParsingPromptString( "Input the id: "  )]

              public int [] Id;

      

              public override void ProcessRecord()

              {

                     Process p = null;

                     foreach ( int i in Id )

                     {      if (ShouldProcess(i.ToString()))

                           {

try

                                  {

p = Process.GetProcessById(i);

                                         p.Kill();

                                  }

                                  catch (System.ComponentModel.Win32Exception e)

                                  { WriteErrorObject("No adequate permissions",e); }

 

                                  catch (System.ArgumentException e)

                                  { WriteErrorObject("No such process exists",e); }

                           }

                     }

              }

       }

#endregion

      

}

 

The code declares the input parameter, annotates it with attributes and assumes that the input values have been filled in. It doesn’t care if the input is coming form the pipeline, file, console or any other means. All the parsing and filling up the value is taken care by the shell. The same goes for output. The code calls WriteObject() function to do any output and the shell takes care of the formatting, displaying and routing of output as per the context.

 

The code above is for Monad, March 2004 build. Sample code for July, 2004 build is available at

http://msdn.microsoft.com/theshow/Episode043/Transcript.html

 

Cmdlet Providers

Cmdlet Providers provide the user with a basic set of cmdlets like pushd, move, copy, cd, dir etc that make the provider as navigable as a file store. Two standard providers come with the msh installation, one for registry browsing and the other for active directory.

 

[ProviderDeclaration("REG", "Microsoft", "Windows", "6.0", "MSH", "Registry", "1.0", ProviderCapabilityFlags.None)]

public class RegistryProvider :  NavigationCmdletBase

{

    protected override void GetItem(string path)

    {

        RegistryKey key = GetRegkeyForPath(path, false);

 

        if (key == null)

        {  WriteErrorObject(path, new ArgumentException("does not exist"));

        }

        WriteObject(key);

    }

    ....

}

 

For installing registry provider use:

MSH C:/> new-provider -Assembly "${MSHHOME}\System.Management.Automation.Core.dll" -Provider REG

 

 

 

 

 

Registry Example

To add a new item to the context menu of directories, some values need to be added to the registry. Here is an example that adds a “Launch MSH” option in the context menu of a directory such that on right clicking a directory and selecting “Launch MSH”, Monad is launched and a changedir [or cd] to the corresponding directory is done.

 

For this we create new items, “Launch msh” and “Command: in the registry using msh commands to give HKLM:/Software/Classes/Directory/Shell/Launch MSH/Command. Note: The escape character in MSH is ` => the back quote character.

 

MSH C:/> $val = format-string "`"{0}`" `"{1}`" `"{2}`" "  "${MSHHOME}\msh.exe" "-command" "cd  '%1' "

 

MSH C:/> $val

 

MSH C:/> new-item -path "HKLM:/SOFTWARE/Classes/Directory/shell" -name "Launch MSH"

 

MSH C:/> new-item -path "HKLM:/SOFTWARE/Classes/Directory/shell/Launch MSH" -name "Command"

 

MSH C:/> set-item -path "HKLM:/SOFTWARE/Classes/Directory/shell/Launch MSH/Command" –value $val

 

Now on right clicking any directory an option “Launch MSH” should appear. On selecting that option, MSH is launched with the current directory as the directory that was right clicked. Neat?

 

MSH C:/> Get-drive

 

MSH C:/> cd HKLM:/

 

MSH HKLM:/> dir

 

MSH HKLM:/> cd Software

 

Yeah, the registry provider lets you move through the registry just the way you move through a file system!

 

Download and Installation

Monad Beta Release 1.0

  1. Go to beta.microsoft.com
  2. Sign in with your passport id
  3. Enter Guest login as mshPDC
  4. Fill up the form with your details and submit
  5. After 24-36 hours you should be able to login to the system. Click the downloads link on left pane
  6. Click the Microsoft Command Shell link.
  7. Download .NET framework 2.0. Install this [22.16MB] [It does not interfere with your earlier .NET versions]
  8. Download msh – Microsoft Command Shell Preview [1.88 MB]

 

System requirements

Windows XP or Windows Server 2003.

[If you are on another windows version read 1 and 2]

 

Reference Material

http://msdn.microsoft.com/theshow/Episode043/default.asp

 

The best way to learn monad is to download the shell and read the documentation that comes with the shell. As of now, there are no technical resources about the shell except for the video by Jeffrey Snover – Architect, Monad and Jim Truher – Program Manager, Monad[link above]

 

Take a look at profile.msh file that is installed along with Monad in the user’s home directory.

There is a lot of code using aliases, environment variables, functions, commandlets etc – a good place to pick up some tips from.

 

Do write to me your feedback about Monad – good and bad.

Friday, June 25, 2004 6:22:04 AM (US Eastern Standard Time, UTC-05:00)  #    Comments [14]Trackback
 Tuesday, June 15, 2004

Monad works on .NET framework V2.0. After a comment on one of my earlier entries, I decided to find out if there were any framework 2.0 dependencies in Monad. If not, then we could get it to run on an earlier version of the framework with a bit of work. Else, it will be good to know what features of the new framework are being used by Monad. [Optimistic, ain’t I? ;)]

 

I modified the version numbers in the IL generated by ildasm [.NET framework V1.0 and V1.1] on msh.exe and tried assembling the modified IL using ilasm. Worked smoothly. Next step was to take the dependent assemblies and generate each of them for .NET framework 1.1. On disassembling System.Management.Automation.Core.dll [one of the assemblies referenced by monad], changing the version info in the IL and assembling it back an error message with failure popped up. Looking through the IL generated I noticed generics being used throughout the code.

 

Okay, so monad is using generics. Well, that ensures that Monad will not work on earlier version of .NET framework. Generics is one of the new additions in the new .NET framework. So I have decided not to get too ambitious as to try and get this working on the earlier version(s) of .NET framework.

 

So much for installing Monad J

 

I noticed that .NET framework 2.0 doesn’t come with ildasm.exe. ilasm.exe is there though.

 

[If you are not able to install monad do read my previous post]

Tuesday, June 15, 2004 2:53:58 AM (US Eastern Standard Time, UTC-05:00)  #    Comments [11]Trackback

Recently, an installation exe refused to run on my OS with an error message saying that the adequate OS was not present. All I wanted to do was extract the files from the package. If I could just get past the OS check within the installation exe, then I could get the files. Changing the OS string and environment variable etc didn’t help in this case.

 

Fortunately, I found an easier way to do this.

 

How do you extract files from an install shield exe?

 

If it is based on install shield wizard you may want to take a look at i6comp tool.

If it is based on msi package, then you first need to get the msi out of the exe.

 

From Brad’s e-mail:

Quote

To Hack the MSI file you have to first get to the .MSI file. Begin the install process by double clicking on the .EXE file. When you get the error message DO NOT click ok. Browse to your /local settings/temp folder in /Documents and Settings/. In the temp  folder look for a new folder with current date and time. Inside the folder you will see  file called, "Microsoft command shell Preview.msi" This file can be edited with a program called ORCA.

Unquote

 

Once you have the msi file, you need to edit the target OS version in the file using orca. [Refer this KB article]. Save the msi file after making the changes. Run the msi and you should be able to install the package.

 

Credits: Thanks to Brad Hite for helping out with install shield exe/msi packages.

Tuesday, June 15, 2004 1:32:45 AM (US Eastern Standard Time, UTC-05:00)  #    Comments [9]Trackback
 Thursday, June 10, 2004

Microsoft Command Shell [codename Monad] is Microsoft’s upcoming shell that is planned to ship with Longhorn. I read up a bit about the shell and decided that I have to try things for myself to believe that it was actually true. There is not much material about Monad on the internet now, except a lot of blogs, most people blogging about it are PDC attendees.

 

I downloaded the monad beta bits, its release notes claim that monad will work on Windows XP and Windows Server 2003 only. Monad requires .NET framework 2.0. I installed the framework on my Win2k Professional machine and tried installing monad. The monad installer started off and displayed a message box with the message “The operating system is not adequate for running Microsoft Command Shell Preview”. I did the same things on another machine with win2k3 server. Monad installed and I could work on the shell. Monad is a .NET application and I can’t think of reasons why this shouldn’t work on my win2k machine. Good time to use the deployment feature of .NET apps that I keep talking about. I copied the extracted files [dll,exe and configs] from the server machine to my machine. Launched cmd.exe, moved to the monad folder in my machine [where I copied the monad files from server] and started msh.exe. Bingo, after a minute the shell prompt appeared. Something was different though. I did not see the regular message “Microsoft Command Shell (msh)” that is displayed when you start msh. I took the profile.msh file from my home directory on the server machine and copied to my home directory in my machine. And presto, I have msh.exe working on my win2k machine now. I haven’t found anything missing until now, the behavior of the shell seems to be the same on my machine as on the server machine.


Monad is cool :), absolutely!

Thursday, June 10, 2004 6:54:34 AM (US Eastern Standard Time, UTC-05:00)  #    Comments [17]Trackback
 Tuesday, May 18, 2004

-James Gosling is talking at Hyderabad, Too bad it’s not at Bangalore.

 

-Canadian Differentrepreneurs: http://www.microsoft.com/canada/mac/default.mspx#

 

-How easy it is today to get pirated stuff? http://www.the-cool-book-shop.com/ [Scroll to the bottom and read].

 

-From “The FountainHead” by Ayn Rand:

"Look," said Roark evenly, and pointed at the window. "Can you see the campus and the town? Do you see how many men are walking and living down there? Well, I don't give a damn what any or all of them think about architecture -- or about anything else, for that matter. Why should I consider what their grandfathers thought of it?"

"That is our sacred tradition."

"Why?"

"For heaven's sake, can't you stop being so naive about it?"

"But I don't understand. Why do you want me to think that this is great architecture?" He pointed to the picture of the Parthenon.

"That," said the Dean, "is the Parthenon."

"So it is."

"I haven't the time to waste on silly questions."

"All right, then." Roark got up, he took a long ruler from the desk, he walked to the picture. "Shall I tell you what's rotten about it?"

"It's the Parthenon!" said the Dean.

"Yes, God damn it, the Parthenon!"

The ruler struck the glass over the picture.

"Look," said Roark. "The famous flutings on the famous columns -- what are they there for? To hide the joints in wood -- when columns were made of wood, only these aren't, they're marble. The triglyphs, what are they? Wood. Wooden beams, the way they had to be laid when people began to build wooden shacks. Your Greeks took marble and they made copies of their wooden structures out of it, because others had done it that way. Then your masters of the Renaissance came along and made copies in plaster of copies in marble of copies in wood. Now here we are, making copies in steel and concrete of copies in plaster of copies in marble of copies in wood. Why?"

The Dean sat watching him curiously. Something puzzled him, not in the words, but in Roark's manner of saying them.

"Rules?" said Roark. "Here are my rules: what can be done with one substance must never be done with another. No two materials are alike. No two sites on earth are alike. No two buildings have the same purpose. The purpose, the site, the material determine the shape. Nothing can be reasonable or beautiful unless it's made by one central idea, and the idea sets every detail. A building is alive, like a man. Its integrity is to follow its own truth, its one single theme, and to serve its own single purpose. A man doesn't borrow pieces of his body. A building doesn't borrow hunks of its soul. Its maker gives it the soul and every wall, window and stairway to express it."

"But all the proper forms of expression have been discovered long ago."

"Expression -- of what? The Parthenon did not serve the same purpose as its wooden ancestor. An airline terminal does not serve the same purpose as the Parthenon. Every form has its own meaning. Every man creates his meaning and form and goal. Why is it so important -- what others have done? Why does it become sacred by the mere fact of not being your own? Why is anyone and everyone right -- so long as it's not yourself? Why does the number of those others take the place of truth? Why is truth made a mere matter of arithmetic -- and only of addition at that? Why is everything twisted out of all sense to fit everything else? There must be some reason. I don't know. I've never known it. I'd like to understand."

"For heaven's sake," said the Dean. "Sit down....That's better....Would you mind very much putting that ruler down?...Thank you....Now listen to me. No one has ever denied the importance of modern technique to an architect. We must learn to adapt the beauty of the past to the needs of the present. The voice of the past is the voice of the people. Nothing has ever been invented by one man in architecture. The proper creative process is a slow, gradual, anonymous, collective one, in which each man collaborates with all the others and subordinates himself to the standards of the majority."

"But you see," said Roark quietly, "I have, let's say, sixty years to live. Most of that time will be spent working. I've chosen the work I want to do. If I find no joy in it, then I'm only condemning myself to sixty years of torture. And I can find the joy only if I do my work in the best way possible to me. But the best is a matter of standards -- and I set my own standards. I inherit nothing. I stand at the end of no tradition. I may, perhaps, stand at the beginning of one."

-And fans of “Friends” may find this interesting:

http://shlomif.il.eu.org/humour/TOWTF/TOW_Fountainhead_1.html

Tuesday, May 18, 2004 2:18:00 AM (US Eastern Standard Time, UTC-05:00)  #    Comments [16]Trackback
 Monday, May 17, 2004

I am enjoying the sleek UI that gmail provides you. For once I seem to be happy emailing without outlook express or Microsoft Outlook.

 

If you have a gmail id then do check out http://www.bladam.com/archives/0404202120.htm, some useful tips in there. I am quite happy about the dot tip [My email id is my Firstname.Lastname@gmail.com]. I wonder what will happen when somebody actually chooses my FirstnameLastname@gmail.com.

 

To check for name availability try

http://www.google.com/accounts/CheckAvailability?service=mail&continue=http://www.google.com&Email=YOURNAMEHERE

 

Too bad I can’t have pooja@gmail.com :(

Nonetheless, gmail did help while I was on vacation.

Monday, May 17, 2004 3:22:46 AM (US Eastern Standard Time, UTC-05:00)  #    Comments [19]Trackback
 Tuesday, May 04, 2004

I am leaving for a vacation trip today. Its after 8+ years  that I am going to be traveling to Rajasthan, am quite excited. I am back on 17th May. I am going to Baroda [Gujarat], from there to Udaipur. On the way back will be visiting Nathdwara and Bombay.

 

Will have lots to say when I am back. I wonder how life is going to be without a computer for 2 weeks at a stretch :)). I wish I had a digital cam, that is one of the things I am going to get soon after I am back.

 

Adios!

 

Tuesday, May 04, 2004 12:10:43 AM (US Eastern Standard Time, UTC-05:00)  #    Comments [0]Trackback