Archives for the Month of February, 2006

Another big Monad blogger splash

Without a doubt, we are blessed here on the Monad team with a community of very talented bloggers.  To add another to the list, we are now joined by Karl Prosser.  His first few blogs are doosies – integrating Monad with SQL server, and hosting Monad in an interface very similar to SQL’s Query Analyzer.

Keep up the great work!

 

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

Introduction to Cryptography

Cryptography is one of the areas about security that many feel is difficult to approach.  Encryption, certificates, signatures, public keys, hashing -- these are terms that most people like to keep at arm's length.
 
That is until you've read a good introduction to the subject. Although the topic of cryptography is large and very deep, a good introduction easily provides the concepts in which to frame your thoughts and discussion on the subject.  At the very least, it can help give you an idea about what that big mess of text is at the bottom of types.mshxml 🙂
 
For this purpose, I've always held Phil Zimmermann's "Introduction to Cryptography" paper in high regard.  You can find it at ftp://ftp.pgpi.org/pub/pgp/6.5/docs/english/IntroToCrypto.pdf.
 
Phil Zimmermann created PGP, a very early suite of encryption software.

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

Pithy and Readable? Do tell!

A long time ago, Raymond Chen wrote about how to interact with the ShellWindows COM object using C++.  It was doable, but clumsy.  He then revisited his post, showing how much easier it was when accessed through a scripting language as its designers had intended.  As can be expected, the comments flowed fast and furious in illustration of the conciseness of various languages that the readers were fond of.

I’ve been meaning to show the Monad equivalent – not just because it is pithy, but because it is pithy and meaningful.  As MOW points out, it is amazing how frequently this happens.

The purpose: list the title and location of all open explorer windows:

[D:\Temp]
MSH:46 > (new-object -com "Shell.Application").Windows() | select LocationName,
LocationUrl

LocationName                            LocationURL
------------                            -----------
Smash Dance Hits                       
file:///H:/lee/My%20Music/Dance/Vari...
scriptomatic                           
file:///C:/temp/scriptomatic
Administrative Tools                    file:///C:/Documents%20and%20Setting...
The Old New Thing : Using script to ... http://blogs.msdn.com/oldnewthing/ar
...

Vivek makes a similar point in his latest post: It’s a one line world.  You don’t need to be a scripting guru to unleash the power of Monad:

When we started designing the Exchange cmdline and scripting interface, we made absolutely sure that 80% of our customers, who normally have little or no scripting experience, can still use Monad/Exchange cmdline to auotmate or perform their tasks.
(…)
You can write lots of complicated scripts, but the majority of time, you simply have to construct a one or two line pipeline.

 

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

MSN Search, without the carpal tunnel syndrome

As their blog  has so far failed to point out, it looks like MSN Search pushed a new release live today.  Unfortunately, I can’t find any mention anywhere on the internet about what’s new. 

I very much like the new colour scheme, though.  It’s light.  It’s fresh.  The old blue box made me feel constrained, but the impact goes deeper than that.

When I worked on Encarta, there was a MSN-wide effort to move to the white theme.  One of the major factors was perception: people feel that sites with minimalistic palettes load faster.  Even if the only change is to the background colour in a stylesheet.

When I implemented this change for Encarta, that was the majority of the work.  Here, try for yourself:

[Before]: http://web.archive.org/web/20050331084437/encarta.msn.com/
[After]: http://encarta.msn.com/

Oh, and Robert?  Rounded corners are so 2003 🙂

Anyways, that’s a digression.

The topic often comes up at work (and online) about how much effort it takes to type search.msn.com.  It’s true – no matter which keyboard layout you use, it just feels awkward.

So, here is my contribution to the solution:

http://ouhn.net (for all Dvorak lovers out there – represent!) and
http://sfjl.net (for all of you mainstream types.)

All on the home row, and much quicker to type than Google.

MSH Logo – allowing users to extend its functionality

In the last article, we continued to develop our version of MSH Logo.  In typical software-geek fashion, we designed a train-wreck for a user interface.  Rather than provide our users with the power required to make fancy graphics, we shackled them with a handful of ineffective commands.  But it was all we thought the users needed!

Many situations aren’t even this clear-cut.  For example, the Microsoft Office System is a phenomenal suite of applications.  It has more features than you could ever want to count.  But that still isn’t the whole story.  The scripting support in Microsoft Office turns it into an infinitely extensible, bona fide, application development platform.

Roy Osherove goes over some great approaches to adding scripting support to an application, and we’ll take a similar approach by hosting the Monad engine.

To do this, we’ll tack on another page to our tab strip, and place a textbox in it.  We’ll also include a button that users can press to execute the MSH script inside.  We will expose a single extension point to the user – our Turtle object – which they can control with much greater ease than our ridiculous hyperlinks on the other tab.

The code that sits behind the “Run” button is quite simple.  After adding references to the required Monad assemblies, we:

  • Create and open a Runspace
  • Expose our Turtle object to scripts in the Runspace
  • Create a Pipeline inside the Runspace, populated with the user’s script
  • Invoke the Pipeline
  • Clean up.

In 6 lines of code, our application is now scripting-enabled.

private void run_Click(object sender, EventArgs e)
{
    drawingSurface.DrawImage(savedImage, 00);

    // Create and open a Monad runspace.  A runspace is a container 
    // that holds Monad pipelines, and provides access to the state
    // of the Runspace session (aka SessionState.)
    Runspace runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();

    // Create a variable, called "$turtle" in the Runspace, and populate
    // it with a reference to the turtle object in our host.  Pipeline
    // commands can interact with this object like any other .Net object.
    runspace.SessionStateProxy.SetVariable("turtle", turtle);

    // Create a pipeline, and populate it with the script given in the
    // edit box of the form.
    Pipeline pipeline = runspace.CreatePipeline(scriptText.Text);

    // Invoke (execute) the pipeline, and close the runspace.
    pipeline.Invoke();
    runspace.Close();
    
    savedImage = new Bitmap(this.pictureBox.Image);
    turtle.Draw();
    this.pictureBox.Refresh();
}

 

You can download the completed application here: [monad_hosting_2.zip].  It includes a text file chock full of MSH Logo examples.  That includes one that I may well get fired for implementing: using the output of get-process to graph the working set of system processes.

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

It doesn't matter if search engines lie

Robert Scoble recently asked the question, "Why Do Search Engines Lie?"  He calls out that the engines are usually off by a few results - with 692 results instead of a claimed 699, 100 results instead of a claimed 101, etc.  The numbers are worse when you count only "unique hits."  That gives numbers like 62 of 713, or 44 of 368.  I'm sure that developers of the respective search engines could give great technical answers to his question, but it's probably best answered rhetorically: "Who cares?"

In search engines, numbers are pointless:

  • Index size?  Pointless.  If I don't get appropriate results for my query, another search engine gets my business.
  • Number of results for your query?  Pointless.  For all intents and purposes, it might as well be capped at 30.  If you don't find your results in the first few pages, the solution is to fix your search term - not to keep on clicking.  If that doesn't work, another search engine gets my business.

In fact, I wrote a search engine at work that indexes source code.  The user interface gives no indication of either index size, or the number of results for a query.  The only buttons are "Search", and "Next."  
 

The URL maintains your current page number, of course, but all of those missing numbers?  Who needs them!

Caching credentials for administrative tasks

Tony has been working on a great series of posts to explore some of Monad's security features.

He provides a method to start programs using the Administrator account, without having to always type in the Administrator's password.  To do this, he uses the export-secureString cmdlet to export the password to disk, and the import-secureString cmdlet to re-import it when required.

The export-secureString cmdlet, when not given an encryption key, uses Windows' Data Protection API, known more commonly as DPAPI.  The Data Protection API is the standard Windows mechanism by which programs protect sensitive data, such as passwords and private keys.  Internally, Windows protects the data by encrypting it with a password it creates from your logon credentials - making the data unavailable to other users.

By holding the Administrator's password on disk (even encrypted,) though, somebody using your account can still technically use this method to launch any application as Administrator.  That's very unlikely, and not necessarily worth being concerned about.  However, as security freaks, or job is to be concerned about trivial things like that.  For example, a snippet from Bruce Schneier's piece about the strong (and playing card-based!) "Solitaire" encryption algorithm:

5. For maximum security, try to do everything in your hands and head. If the secret police starts breaking down your door, just calmly shuffle the deck. (Don't throw it up in the air; you'd be surprised how much of the deck ordering is maintained during a game of 52-Pickup.) Remember to shuffle the backup deck, if you have one.
6. Be careful about worksheets, if you have to write things down. They will have sensitive information on them.
Burning is probably the best method of data destruction available, but think about the paper. Ungummed, rice cigarette papers seem ideally suited to this role. A colleague did some tests with Club Cabaret Width papers, and they burn completely.
(…)
And good cigarette papers are made to burn cleanly and completely. The Club papers burned best when allowed to burn in the free air. That is, lit and released at about chest level. These papers also have the advantage of having very low volume and could be easily eaten if required.
(…)
8. Most card games do not include jokers, so carrying a deck around with them may be suspicious. Be prepared with a story.

[The scary thing is that I'm not sure how much of this is in jest!]

Anyways, back to the real world.  One idea to increase the security of this approach is to use credential caching.  We ask for the Administrator's password if we don't already have it, and reuse the cached password if we do.

Here is a slightly modified script that uses this approach:

##############################################################################
## Defrag-Disk.msh
##
## Start Disk Defragmenter using cached credentials
## From http://mshforfun.blogspot.com/
##############################################################################

## Get the cached credential.  This prompts for the credential if not

## yet entered.
${GLOBAL:lee.holmes.credential.administrator} = 
   get-credential ${GLOBAL:lee.holmes.credential.administrator}

$Windir=$env:Windir
$StartInfo = new-object System.Diagnostics.ProcessStartInfo
$StartInfo.UserName = ${GLOBAL:lee.holmes.credential.administrator}.Username
$StartInfo.Password = ${GLOBAL:lee.holmes.credential.administrator}.Password
$StartInfo.FileName = $Windir + "\system32\mmc.exe"
$StartInfo.Arguments = $Windir + "\system32\dfrg.msc"
$StartInfo.WorkingDirectory = $Windir + "\system32"
$StartInfo.LoadUserProfile = $true
$StartInfo.UseShellExecute = $false
[System.Diagnostics.Process]::Start($StartInfo)

To clear this cached credential, exit your shell, or run the command:

[D:\Lee\MSH]
MSH:56 > ${GLOBAL:lee.holmes.credential.administrator} = $null

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

MSH Logo – A GUI Disaster

Ok, so now that we’ve talked about our grand design for MSH Logo, our next task is to simply integrate this into a GUI.  You can download the Visual Studio 2005 project from here.

The most interesting class, by far, is our Turtle class:

using System;
using System.Collections.Generic;
using System.Text;

using System.Drawing;

namespace Monad_Hosting
{
    /// <summary>
    /// A turtle class that implements some of the logo primitives.
    /// It stores a reference to the canvas upon which it draws, and
    /// is responsible for drawing on that canvas.
    /// </summary>
    class Turtle
    {
        Graphics canvas;
        Pen drawingPen = new Pen(Color.LightGreen);

        // Although the canvas can only represent integer
        // positions, we store our state in double precision.
        // Otherwise, most interesting graphics (that tend to
        // involve recursion and small numbers) look terribly
        // broken.
        double currentX, currentY;
        bool penDown = true;
        bool showTurtle = true;
        int direction = 90;

        public Turtle(Graphics canvas)
        {
            this.canvas = canvas;

            Initialize();
        }

        public void PenUp()
        {
            penDown = false;
        }

        public void PenDown()
        {
            penDown = true;
        }

        public void Forward(double steps)
        {
            int oldX = (int) currentX;
            int oldY = (int) currentY;

            // In essense, the turtle draws the hypotenuse
            // of a triangle as it moves.  Since the user provides
            // the length of the hypotenuse, we use standard
            // trigonometry to determine the X and Y components
            // of the movement independently.
            currentX += steps * Math.Cos(DegToRad(direction));
            currentY -= steps * Math.Sin(DegToRad(direction));

            if(penDown)
            {
                canvas.DrawLine(drawingPen, oldX, oldY, 
                    (int) currentX, (int) currentY);
            }
        }

        public void Backward(double steps)
        {
            Forward(-1 * steps);
        }

        public void Left(int degrees)
        {
            direction = (direction + degrees) % 360;
        }

        public void Right(int degrees)
        {
            direction = (direction - degrees + 360) % 360;
        }

 &
nbsp;      public void Hide()
        {
            showTurtle = false;
        }

        public void Show()
        {
            showTurtle = true;
        }

        public void Draw()
        {
            if (showTurtle)
            {
                // We leverage the 2d transformations of the .Net
                // Graphics class here to save us from doing the 
                // math for rotation contortions ourselves.
                // Rather than draw a rotated turtle, we instead rotate
                // (and reposition) the canvas, then draw a straight
                // turtle.  When we restore the canvas again, the
                // turtle now appears rotated.

                System.Drawing.Drawing2D.GraphicsState canvasState = 
                    canvas.Save();
                canvas.TranslateTransform((float) currentX, (float) currentY);
                canvas.RotateTransform(90 - direction);

                canvas.DrawLine(drawingPen, -440, -8);
                canvas.DrawLine(drawingPen, 0, -844);
                canvas.DrawLine(drawingPen, -4444);

                canvas.Restore(canvasState);
            }
        }

        public void Reset()
        {
            Initialize();
            canvas.Clear(Color.DarkGreen);
        }

        private void Initialize()
        {
            currentX = canvas.VisibleClipBounds.Width / 2.0;
            currentY = canvas.VisibleClipBounds.Height / 2.0;

            penDown = true;
            showTurtle = true;
            direction = 90;
        }

        // The user specifies their angles in degrees, but
        // the .Net math classes prefer radians.
        private double DegToRad(int degrees)
        {
            return (Math.PI * (double) degrees / 180.0);
        }
    }
}

Our GUI application mainly interacts with the Turtle object:

public partial class MonadHost : Form 
{
        // The drawing surface is the canvas on which the
        // turtle draws.
        private Graphics drawingSurface = null;
        private Turtle turtle = null;

        // We save the image of the turtle's tracks just
        // before we draw the turtle icon.  That way, when
        // the turtle moves, we don't have to worry about erasing
        // the icon.
        Image savedImage = null;

        public MonadHost()
        {
            InitializeComponent();
            InitializeCustom();
        }

        private void InitializeCustom()
        {
            InitializeCanvas();

            this.tabControl.Focus();
        }

        // Make our form look a little more presentable when
        // we resize it.
        private void MonadHost_ResizeEnd(object sender, EventArgs e)
        {
            InitializeCanvas();
        }

        // This brings our application back to a clean state.
        // We create a fresh new canvas the same size as the current
        // form, create a new turtle to reference that canvas,
        // and draw the turtle.
        private void InitializeCanvas()
        {
            this.pictureBox.Image = 
                new Bitmap(pictureBox.Width, pictureBox.Height);
            drawingSurface = Graphics.FromImage(this.pictureBox.Image);
            drawingSurface.SmoothingMode = 
                System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            turtle = new Turtle(drawingSurface);
            turtle.Reset();
            
            savedImage = new Bitmap(this.pictureBox.Image);
            turtle.Draw();
        }

        // The following methods are pretty similar.  We
        // first draw our saved image to the canvas (the one without
        // the turtle icon,) have the turtle draw (or do) whatever
        // it was told to do, save the resulting image, draw
        // the turtle icon, and finally refresh the view of
        // the canvas.
        private void penUp_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            drawingSurface.DrawImage(savedImage, 00);

            turtle.PenUp();

            savedImage = new Bitmap(this.pictureBox.Image);
            turtle.Draw();
            this.pictureBox.Refresh();
        }

        (...)

        private void backward10_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            drawingSurface.DrawImage(savedImage, 00);

            turtle.Backward(10);

            savedImage = new Bitmap(this.pictureBox.Image);
            turtle.Draw();
            this.pictureBox.Refresh();
        }
    }
}

When you run the program, its user interface may feel a little barbaric.  Or (more probably,) a lot barbaric.  The feature set is closed, and offers no extensibility.  In fact, you may already be contemplating a lawsuit against me for an acute case of carpal tunnel syndrome.

Next time, we’ll look at a way to resolve this issue.

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]