Sunday, April 27, 2008

How to make Silverlight be AiR?

Today we’ll speak about three issues

  1. How to make Silverlight application to run as stand alone application and how to insert this application inside your application?
  2. How to escape Silverlight from it’s sand box (how to make it run in full trust mode)
  3. When first two items done, how to make Silverlight to access anyfile in your file system?

Looks scary? Let’s see first reasons for those “hackery” targets. The main reason is to make Silverlight Air (you, probably understand what I’m speaking about :)). Why? When I want to build Silverlight Image Upload control. The one similar to those Yahoo, Facebook and many others have. With live preview, editing (before uploading), drag and drop, etc. Yes, I do not want ugly File Open dialog from Silverlight. I want it sexy, yet functional! To do this, we have to make Silverlight be able to access filesystem. Of cause I want to ask user to authorize me first, then I can get an access.

image

The other reason is to incorporate Silverlight control inside WinForms application. Why? There are some reasons - “light weigh stuff”, maybe :). Maybe banner ads inside desktop application. It’s just cool :). Well, there are some other more serious reasons. So let’s start.

First task – to make it run as stand alone application.

Well, this one is easy. All you have to do is to have WebBrowser control with Silverlight content inside it in your application. So,

WebBrowser wb = new WebBrowser();
wb.Parent = panel1;
wb.Dock = DockStyle.Fill;
wb.Url = new Uri("http://0x15.net/play/SLFindResource/SLFindResource.html");

We done. But we’re in desktop, thus I want it full trust… This is most interesting part of today’s post.

Second task – to make it run in User Full Trust mode.

First try – to incorporate Silverlight’s OCX (ActiveX) control. Add npctrl.dll from [Program Files]\Microsoft Silverlight\[Version] – this is ActiveX and Visual Studio will create wrapper with AxHost. This one is cool, but it wont work. why? As you, probably, know Silverlight connected to it’s web page host DOM when we’re using it as stand alone player it cannot find it’s document, thus initialization failed. So what to do? What can provide me DOM from one side and run in full trust from the other side. Someone remember what HTA is (it is not mobile device, it’s very beginning of RIA era). HTML applications were run by very special host, named mshta.exe it’s in [Windows]\System32 folder and it’s still there. Everything running inside MSHTA will run by default in full trust mode. From one hand it’s regular IE, (do we have DOM), from other hand it’s make us able to run full trust internet application. Let’s use it (from code)

ProcessStartInfo mshta = new ProcessStartInfo("mshta", "http://0x15.net/play/SLFindResource/SLFindResource.html");
Process p = Process.Start(mshta);

Now we have strange window, running our Silverlight application. What’s next? Incorporate it inside our application. What’s the problem p (my process).MainWindowHandle and then SetParent for to the control I want. Well, it does not work. MSHTA has no (publicly) main window. So, we’ll find it and then change it’s parent. His class named “HTML Application Host Window Class”.

LockWindowUpdate(GetDesktopWindow());
ProcessStartInfo mshta = new ProcessStartInfo("mshta", "http://0x15.net/play/SLFindResource/SLFindResource.html");
Process p = Process.Start(mshta);
p.WaitForInputIdle();
ptr = FindWindow("HTML Application Host Window Class", null);

SetParent(ptr, panel1.Handle);
SendMessage(ptr, WM_SYSCOMMAND, SC_MAXIMIZE, 0);

LockWindowUpdate(IntPtr.Zero);

Yu-hoo. We hosted Silverlight page inside our application. It’s full trust so, we can access file system. But wait… Silverlight is not designed to have an access to the file system. The only space it can see is isolated storage, thus it has no classes for listing files anywhere. what to do?

Third task – to make it access user’s file system

We need another ActiveX to run from Javascript (or C# code) that knows to access to file system. Our hosting document can initialize it and then expose relevant methods to Silverlight. What’s such class? Let’s back to gold era of unsafe computing – we have Scripting.FileSystemObject there. This class is very dangerous it can do anything in local file system. Many system administrators using this class to script their evil login scripts (those black quick command line promps, that doing something bad to your system each time you’re logging in in your domain). It know everything about your disks and can be run from full trust environment. So, it’s just exactly what we need. Get all drives in your machine

drivetypes = [ 'Unknown', 'Removable', 'Fixed', 'Network', 'CD-ROM', 'RAM Disk' ],
driveprops = [ 'DriveLetter', 'DriveType', 'ShareName', 'IsReady', 'Path', 'RootFolder', 'FileSystem', 'SerialNumber', 'VolumeName', 'TotalSize', 'AvailableSpace', 'FreeSpace' ];

function getdrives() {
var fso = new ActiveXObject( 'Scripting.FileSystemObject' ),
  e = new Enumerator(fso.Drives),
  add = function(i) {
   i = driveprops[i];
   var prop = f[i];
   if( ( prop || prop===0 || prop===false ) && ( i!=='AvailableSpace' || prop!==free ) ) {
    if( /(Type)$/.test( i ) ) { prop = drivetypes[ prop ]; }
    if( /(Size|Space)$/.test( i ) ) { prop = bykb( prop, true ); }
    s.push( i.toCamelCase() + ':\t' + ( i.length < 8 ? '\t' : '' ) + prop );
   }
  },

Then folders

function getfolder( s ) { s = trim( s ) || 'C:';
var fso = new ActiveXObject( 'Scripting.FileSystemObject' ),
  e, f, i, r = [];
if( fso.FolderExists( s ) ) {
  f = fso.GetFolder( s );
  e = new Enumerator(f.SubFolders);
  for( ; !e.atEnd(); e.moveNext() ) {
   if( ( i = e.item() ) ) { r.push( ' ' + i ); }
  }
  e = new Enumerator(f.files);
  for( ; !e.atEnd(); e.moveNext() ) {
   if( ( i = e.item() ) ) { r.push( '' + i ); }
  }
}
return r;
}

And files at the end

function getfile( form ) {
var fso = new ActiveXObject( 'Scripting.FileSystemObject' ),
  forReading = 1, forWriting = 2, forAppending = 8,
  dd = function( o, s ) {
   try {
    s = f[s] + '';
    o.value = s.replace( /^(\w{3}) (\w+) (\d\d?) ([\d:]+) ([\w+]+) (\d+)$/, '$3 $2 $6 $4' );
   } catch(e) {
    o.value = e.message;
   }
  },

Very cool we have files by using f = fso.GetFile( name ); method, now we can do anything with it. For example get or set attributes f.attributes, or rename f.Name = s, or, even delete it f.Delete(); Isn’t it really evil?

We done. Now you can run Silverlight as full trust desktop application and, even host it wherever you want. Even inside calculator…

ProcessStartInfo calc = new ProcessStartInfo("calc");
using (Process p = Process.Start(calc))
{
    p.WaitForInputIdle();
    SetParent(ptr, p.MainWindowHandle);
    SendMessage(ptr, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
    p.WaitForExit();
}

Happy programming and be good people.

SilverForms.zip [53.7 Kb]

No comments: