Cutting Edge Silverlight4 COM+ Features

Justin Angel picture

Justin
Angel

Hi Folks,

The new Silverlight 4 COM+ Automation feature can be used to perform a myriad of previously unavailable tasks in Silverlight.
In this blog post I’ll review some of these tasks, what COM+ Automation is, some related issues and helpful introduce developer tools.

 

The code we’ve created in this blog post is available @ http://Justinangel.net/Storage/SL4Com/SL4Com.zip
And a Live demo is available @ http://Justinangel.net/Storage/SL4Com/testpage.html 

 

What is Silverlight 4 COM+ Automation support?

Silverlight 4 Beta 1 offers the ability to initialize and use COM+ classes from Silverlight.
It should be noted that these features are only available in elevated privileges Out-Of-Browser mode and are not available in-browser.  
In Silverlight 4 Beta1 Com+ Automation only works on Windows machines (but more on that later).

The majority of work around Silverlight 4 COM+ support is centered around the ComAutomationFactory and ComAutomationEvent classes.

 

Nooo! COM+ killed my inner child back in the 90s!

Man freaking out having been told he must go back to using COM+

Get over it.

Silverlight 4 COM+ Automation is not about authoring, deploying or versioning COM+ components, that is a worst practice for this feature.  
The best practice for Silverlight 4 COM+ is to only use common operating system COM+ classes, and not ship your own.

Given that best practice, you are just the consumer of some time tested Windows APIs.
Using COM+ Windows APIs is no different then consuming the .Net framework, 3rd party frameworks or p/Invoke features. 

 

Stop yapping and show me how to setup a Silverlight 4 OOB project!

We’ll start by creating a new Silverlight 4 OOB (Out Of Browser) project with Elevated Privileges in Visual Studio 2010. 

New Silverlight project

For our test project we don’t need a Server-Side project, and we need to make sure we’re using Silverlight 4.

Selecting Silverlight 4 project

Right-Click on the SL4Com project and choose “Properties”.
Check the “Enable Running Application out of browser”.

Enable Running Application out of browser checkbox

Click “Out of Browser Settings” and set “Require Elevated Trust when running outside the browser”.

Require Elevated Trust when running outside the browser checkbox

Next add a Button to MainPage that’ll install our Out-Of-Browser application.

<Button x:Name="btnInstall" Click="btnInstall_Click" Content="Install Out-Of-Browser App" />

private void btnInstall_Click(object sender, RoutedEventArgs e)

{

    if (Application.Current.InstallState == InstallState.NotInstalled)

        Application.Current.Install();

}

Note: This article is about COM+, not best practices for wiring up events.
If this was a real application, we should have used Prism Commands or Blend Behaviours to hook up these events.

We’ll run our app and install it.

Install OOB button

Security Warning when installing OOB app

Next, we’ll need to tell Visual Studio 2010 to run the Out-Of-Browser app when debugging.

Go to Project properties –> Debug –> Installed out of browser application –> SL4Com.

Select debugging OOB SL4 Apps

One last step we have to do is add a reference to Microsoft.CSharp.dll so we could use the new Silverlight 4 dynamic keyword.

Adding a reference to Microsoft.CSharp.dll

(on my machine the DLL is at c:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\Microsoft.CSharp.dll)

 

Feature #1: Write files anywhere on the local machine

Using the FileSystemObject we have virtually unlimited access to the user’s file storage.

<Button x:Name="btnWriteFile" Content="Write file to C:\test.txt" Click="btnWriteFile_Click" />

private void btnWriteFile_Click(object sender, RoutedEventArgs e)

{

    using(dynamic fsoCom = ComAutomationFactory.CreateObject("Scripting.FileSystemObject"))

    {

        dynamic file = fsoCom.CreateTextFile(@"c:\test.txt", true);

        file.WriteLine("Bloody Hell!");

        file.WriteLine("Silverlight is writing to C:\\");

        file.Close();

    }

}

When we run our app and click the button we can see that a new file has been created on C:\test.txt.

Clcking the Write File button adds a new file to C:\Test.txt

 

Feature #2: Reading any file from the user’s machine

Using FileSystemObject.ReadAll method we can read any file off the user’s machine.

<Button x:Name="btnReadFile" Content="Read file C:\test.txt" Click="btnReadFile_Click" />

private void btnReadFile_Click(object sender, RoutedEventArgs e)

{

    using (dynamic fsoCom = ComAutomationFactory.CreateObject("Scripting.FileSystemObject"))

    {

        dynamic file = fsoCom.OpenTextFile(@"c:\test.txt", 1, true);

        MessageBox.Show(file.ReadAll());

        file.Close();

    }

}

Running the app and clicking the button shows us the file contents:

Clicking the read file button shows the file contents with a MessageBox

 

What else can FileSystemObject do for us?

The FSO object let’s us essentially own the user machine.
It provides a way to read, write, delete or modify any file or directory on the user’s file system.

Here’s a list of Methods you can explore independently:

 

Feature #3: Execute/Run any command or file

Using the WShell Run Method we can execute any command.

<Button x:Name="btnExecuteCmdPing" Content="Cmd Ping 127.0.0.1" Click="btnExecuteCmdPing_Click" />

private void btnExecuteCmdPing_Click(object sender, RoutedEventArgs e)

{

    using(dynamic shell = ComAutomationFactory.CreateObject("WScript.Shell"))

    {

        shell.Run(@"cmd /k ping 127.0.0.1");

    }

}

Clicking the button opens up the CMD and runs the specified command:

Clicking the CMD button executes the ping command

 

Feature #4: Emulate user input

Using the WShell SendKeys method we can send input to the currently focused application.
This feature is extremely useful for integration testing.

<Button x:Name="btnExecuteNotepadAndSendKeys" Content="Run Notepad and write something" Click="btnExecuteNotepadAndSendKeys_Click" />

private void btnExecuteNotepadAndSendKeys_Click(object sender, RoutedEventArgs e)

{

    using (dynamic shell = ComAutomationFactory.CreateObject("WScript.Shell"))

    {

        shell.Run(@"c:\windows\notepad.exe");

        shell.SendKeys("Justin Angel{Enter}Rocks!");

    }

}

Looking at the SendKeys syntax it’s easy to see there are special keys (like Enter, Tab, escape, ctrl, etc) that can be specified using a special notation.

Notepad with the ShowKeys text

 

Feature #5: Pin files to the Windows 7 Taskbar

Using The Shell.Application class we can iterate over all applications, check their Verbs and execute those verbs accordingly.
Here are the Verbs for the Calculator application:

image

However, this method allows us to Pin Calculator to the Win7 taskbar even when the Calculator is closed.
It does that by checking the Windows verbs on the LNK (Link) file to Calculator.

<Button x:Name="btnPinCalculator" Content="Pin Calcuator to the Win7 Taskbar" Click="btnPinCalculator_Click" />

private void btnPinCalculator_Click(object sender, RoutedEventArgs e)

{

    using (dynamic ShellApplication = ComAutomationFactory.CreateObject("Shell.Application"))

    {

        dynamic commonPrograms = ShellApplication.NameSpace(23);

        string allUsersPath = commonPrograms.Self.Path;

 

        dynamic directory = ShellApplication.NameSpace(allUsersPath + @"\Accessories");

        dynamic link = directory.ParseName("Calculator.lnk");

 

        dynamic verbs = link.Verbs();

        for (int i = 0; i < verbs.Count(); i++)

        {

            dynamic verb = verbs.Item(i);

            if (verb.Name.Replace(@"&", string.Empty).ToLower() == "pin to taskbar")

            {

                verb.DoIt();

            }

        }

    }

}

Essentially, what this method does is:
1. Gets the path for all the users start menu links.
2. Gets the specific link for Calculator.
3. Checks what Verbs are available for that link.
4. Execute the “Pin to Taskbar” verb.

And when running this application we can see Calculator is now pinned to the user’s taskbar:

Calculator Pinned to the Win7 Taskbar

 

Feature #6: Read any Registry values

Using the Shell.Application RegRed method you can read values from the machine’s registry.

At the "HKLM\Software\Microsoft\Windows\CurrentVersion\CommonFilesDir" registry key we can see the location of the machine’s Common Files:

RegEdit for HKLM\Software\Microsoft\Windows\CurrentVersion\CommonFilesDir

We can easily access that value using RegRead.

<Button x:Name="btnReadRegistry" Content="Read HKLM registry" Click="btnReadRegistry_Click" />

private void btnReadRegistry_Click(object sender, RoutedEventArgs e)

{

    using (dynamic WShell = ComAutomationFactory.CreateObject("WScript.Shell"))

    {

        string reg = WShell.RegRead(@"HKLM\Software\Microsoft\Windows\CurrentVersion\CommonFilesDir");

        MessageBox.Show("The Program files on this machine are at: " + reg);

    }

}

Clicking the button shows the value of the registry key.

Clicking the read registry button shows the location of the Program Files directory

Feature #7: Add OOB App to Windows’ Startup

Using a combination of the LNK technique we’ve seen earlier and Shell.Application RegWrite method we can add our OOB app to the Windows’ startup applications.
Every program listed under “HKLM\Software\Microsoft\Windows\CurrentVersion\Run\” will be executed when the machine first starts up.
So if we can get our OOB link and add it to that Registry location, we can add our OOB application to Windows’ Startup.

HKLM\Software\Microsoft\Windows\CurrentVersion\Run\

 

<Button x:Name="btnAddOOBAppToStartup" Content="Add out-of-browser application to Startup" Click="btnAddOOBAppToStartup_Click" />

private void btnAddOOBAppToStartup_Click(object sender, RoutedEventArgs e)

{

    using (dynamic ShellApplication = ComAutomationFactory.CreateObject("Shell.Application"))

    {

        dynamic commonPrograms = ShellApplication.NameSpace(11);

        string allUsersPath = commonPrograms.Self.Path;

 

        dynamic directory = ShellApplication.NameSpace(allUsersPath + @"\Programs");

        dynamic link = directory.ParseName(Deployment.Current.OutOfBrowserSettings.ShortName + ".lnk");

        string OOBLink = link.Path;

 

        using (dynamic WShell = ComAutomationFactory.CreateObject("WScript.Shell"))

        {

           WShell.RegWrite(@"HKLM\Software\Microsoft\Windows\CurrentVersion\Run\"

                                        + Deployment.Current.OutOfBrowserSettings.ShortName,

                                        OOBLink);

            MessageBox.Show("Please restart your machine and this application will load on startup.");

        }

    }

}

This code snippet isn’t all that complicated.
First, we get the LNK to the current out-of-browser application by checking the start menu links directory.
Next, we add that LNK file Uri to the registry node we mentioned earlier on.

And after running clicking the button we can see the registry value was added:

SLCOM application Startup registry entry

 

Task #8: Pinning the OOB Application to Windows’ 7 Taskbar

We’ve previously seen how to get the LNK for the OOB application and we’ve seen how to pin those LNKs to the Taskbar.
It’s fairly easy to combine those two techniques to pin the current OOB app to the taskbar.

<Button x:Name="btnPinOOBApp" Content="Pin Out-of-Browser application to Taskbar" Click="btnPinOOBApp_Click" />

private void btnPinOOBApp_Click(object sender, RoutedEventArgs e)

{

    using (dynamic ShellApplication = ComAutomationFactory.CreateObject("Shell.Application"))

    {

        dynamic commonPrograms = ShellApplication.NameSpace(11);

        string allUsersPath = commonPrograms.Self.Path;

 

        dynamic directory = ShellApplication.NameSpace(allUsersPath + @"\Programs");

        dynamic link = directory.ParseName(Deployment.Current.OutOfBrowserSettings.ShortName + ".lnk");

 

        dynamic verbs = link.Verbs();

        for (int i = 0; i < verbs.Count(); i++)

        {

            dynamic verb = verbs.Item(i);

            if (verb.Name.Replace(@"&", string.Empty).ToLower() == "pin to taskbar")

            {

                verb.DoIt();

                MessageBox.Show("Close this application and start it up again from Win7 Taskbar");

            }

        }

    }

}

And we can see that even when our OOB application is closed, it’s still showing up on the Win7 taskbar.

SL4 Com application is closed but still found on the Win7 Taskbar

 

Feature #9: Text to Speech

Using Windows’ built-in SAPI engine we can easily do Text to Speech and even Speech to Text.

We’ll demo using the SAPI.SpVoice class for Text2Speech. 
Feel free to explore the SAPI namespace since it’s full of other useful speech related features.

<Button x:Name="btnText2Speech" Content="Use Text to Speech" Click="btnText2Speech_Click" />

private void btnText2Speech_Click(object sender, RoutedEventArgs e)

{

    using (dynamic ISpeechVoice = ComAutomationFactory.CreateObject("SAPI.SpVoice"))

    {

        ISpeechVoice.Volume = 100;

        ISpeechVoice.Speak("<rate speed=\"0\"><pitch middle=\"0\">Hello World! My HoverCraft is full of eels.");

    }

}

Clicking the button does indeed cause Windows to start speaking.
For a relatively acceptable use of this feature, have a look at my Molecular Biology DNA Visualizer.

It is also possible to do limited speech recognition using SAPI using the SAPI.SpInprocRecognizer class.

 

Feature #10: Execute SQL with Local Databases

Using ODBC features available in Windows, we can easily connect to local databases and execute SQL.

Here’s a sample of executing a simple SQL Insert with the local Northwind database:

<Button x:Name="btnExecuteSQL" Content="Execute SQL Insert statement" Click="btnExecuteSQL_Click" />

using (dynamic IDbConnection = ComAutomationFactory.CreateObject("ADODB.Connection"))

using (dynamic IDbCommand = ComAutomationFactory.CreateObject("ADODB.Command"))

{

    IDbConnection.ConnectionString = "driver={SQL Server};" +

                                    "server=.\\;uid=sa;pwd=password;database=Northwind";

 

    IDbConnection.Open();

    IDbCommand.ActiveConnection = IDbConnection;

 

    IDbCommand.CommandText =

        @"INSERT INTO [Northwind].[dbo].[Region]

                        ([RegionID], [RegionDescription]) VALUES (10, 'JustinLand')";

 

    IDbCommand.Execute();

}

Note: this code snippet does not show best practices for using ODBC for Silverlight. For one thing, Commands should use parameters and not strings.

 

If you were to execute this code sample you’d probably get the following error:

SQL connection exception

Since you don’t have the Northwind Database installed or the username & password are wrong you’d see some exception or another.
Allow me to assure you “It works on my machine”!

The JustinLand record inserted into a local Database

It is important to discuss best practices at this point.
It is not my intent to advocate executing local SQL statements in conjunction with a local database.

However, It is possible to convert any 3rd party OR/M or Persistence layer to run on ODBC.
Which would make for a very comfortable EF/Linq2SQl/NH like syntax. In that case, it would be much more acceptable to use ODBC.

Another issue with this method is the end-user might not have any local database available.
And you’ll end up resorting using Access or the likes.

Personally I believe that for local Silverlight storage we should use local OODBs, and not use ODBC.

 

Feature #11: Automate Scanners and Cameras

Using the WIA (Windows Image Acquisition) infrastructure we can automate external Scanners, Cameras and Video cameras.
Many very unique samples (converting file formats, getting scanners to scan, taking pictures, etc) are available at the WIA Samples page.

One possible task is to iterate over all Cameras and Scanners currently connected to the local machine and their commands:

<Button x:Name="btnIterateWIA" Content="Iterate over external Cameras and Scanners" Click="btnIterateWIA_Click" />

private void btnIterateWIA_Click(object sender, RoutedEventArgs e)

{

    StringBuilder sb = new StringBuilder();

    sb.AppendLine("List of available external devices and commands:");

    using (dynamic DeviceManager = ComAutomationFactory.CreateObject("WIA.DeviceManager"))

    {

        var deviceInfos = DeviceManager.DeviceInfos;

        for (int i = 1; i <= deviceInfos.Count; i++)

        {

            var IDevice = deviceInfos.Item(i).Connect();

            var DeviceID = IDevice.DeviceID;

            var DeviceName = IDevice.Properties("Name").Value;

            var Commands = IDevice.Commands;

            for (int j = 1; j <= Commands.Count; j++)

            {

                var IDeviceCommand = Commands.Item(j);

                var CommandName = IDeviceCommand.Name;

                var CommandDescription = IDeviceCommand.Description;

                sb.AppendLine("    " + DeviceName + " (" + DeviceID + "), " + CommandName + ": " + CommandDescription);

                // Execute with: IDevice.ExecuteCommand(IDeviceCommand.CommandID);

            }

        }

    }

    MessageBox.Show(sb.ToString());

}

The above code is actually pretty simple.
1. We initialize a DeviceManager.
2. We ask for all the DeviceInfos.
3. We then iterate over all devices, getting their commands.
4. We display the command name and device name.

On my dev machine with an External Scanner and External Camera, this is what we see:

List of device commands

Another thing we can do is select Images from external Image devices.

<Button x:Name="btnGetExternalImage" Content="Acquire External Image from Scanner/Camera" Click="btnGetExternalImage_Click" />

private void btnGetExternalImage_Click(object sender, RoutedEventArgs e)

{

    using (dynamic CommonDialog = ComAutomationFactory.CreateObject("WIA.CommonDialog"))

    {

        dynamic imageFile = CommonDialog.ShowAcquireImage();

        if (imageFile != null)

        {

            imageFile.SaveFile(@"c:\myImage.jpg");

            MessageBox.Show("Saved c:\\myImage.jpg");

        }

    }

}

When clicking the button on my local machine we’ll see the following dialogs:

Device Selection Dialog

Select Image Dialog

And we can see c:\myImage.jpg is indeed that same photo:

c:\myImage.jpg print screen 

 

 

Feature #13: Use the Windows 7 Location API

As part of Windows 7 the Location API was added to provide developers with the current machine GPS location.
If the proper hardware is connect, Using the Location API we can get the current Latitude and Longitude of the current Machine.

<Button x:Name="btnWin7LocationAPI" Content="Activate Windows 7 Location API" Click="btnWin7LocationAPI_Click" />

private void btnWin7LocationAPI_Click(object sender, RoutedEventArgs e)

{

    using (dynamic LatLongReportFactory = ComAutomationFactory.CreateObject("LocationDisp.LatLongReportFactory"))

    {

        uint curStatus = LatLongReportFactory.Status;

        if (curStatus == (uint)4)

        {

            using (dynamic LatLongReport = LatLongReportFactory.LatLongReport)

            {

                MessageBox.Show("Latitude: " + LatLongReportFactory.Latitude + "\r\n Longitude: " + LatLongReportFactory.Longitude);

            }

        }

 

        LatLongReportFactory.ListenForReports(5000);

        ComAutomationEvent NewLatLongReportEvent = ComAutomationFactory.GetEvent(LatLongReportFactory, "NewLatLongReport");

        NewLatLongReportEvent.EventRaised += (s, args) =>

        {

            using (dynamic LatLongReport = LatLongReportFactory.LatLongReport)

            {

                MessageBox.Show("Latitude: " + LatLongReportFactory.Latitude + "\r\n Longitude: " + LatLongReportFactory.Longitude);

            }

            LatLongReportFactory.StopListeningForReports();

        };

    }

}

The above code will display the current Latitude and longitude and then listen in for new reports every 5 seconds.

 

Feature #14: Use Classes from the Full .Net framework

While it is not recommended, you can consume some classes from the full .Net framework.
The reason it is not recommended, is because all of your users would need to have the full .Net framework installed.

Here’s an example of initializing the ReaderWriterLock class used for multithreading multiple readers or a single writer.

<Button x:Name="btnUseFullDotNet" Content="Use a class from the full .Net frameowrk" Click="btnUseFullDotNet_Click"/>

private static int foo = 0;

private void btnUseFullDotNet_Click(object sender, RoutedEventArgs e)

{

    using (dynamic ReaderWriterLock = ComAutomationFactory.CreateObject("System.Threading.ReaderWriterLock"))

    {

        ReaderWriterLock.AcquireWriterLock(1);

        MessageBox.Show("System.Threading.ReaderWriterLock.AcquireWriterLock() - acquired lock");

        foo++;

        ReaderWriterLock.ReleaseWriterLock();

        MessageBox.Show("System.Threading.ReaderWriterLock.ReleaseWriterLock() - released lock");

        ReaderWriterLock.AcquireReaderLock(1);

        MessageBox.Show("While in reader lock - Foo value is " + foo);

        ReaderWriterLock.ReleaseReaderLock();

 

    }

}

Executing this code we’ll see the expected messages.

The interesting part here is that we’re using classes from the Full desktop .Net framework which are not available in Silverlight.

 

Feature #15: Use WMI to build FileSystemWatcher

Using the WbemScripting.SWbemLocator class we can execute WMI queries in Silverlight.
WMI allows querying for many operating system related information, like: Performance (CPU & Memory), Security, Networking, Storage, Kernel, Active Directory, etc.

In this sample we’ll rebuild the .Net FileSystemWatcher using WMI Events

<Button x:Name="btnActivateFileSystemWatcher" Content="Active FileSystemWatcher on C:\" Click="btnActivateFileSystemWatcher_Click" />

 

private void btnActivateFileSystemWatcher_Click(object sender, RoutedEventArgs e)

{

    new Thread(() =>

    {

        using (dynamic SWbemLocator = ComAutomationFactory.CreateObject("WbemScripting.SWbemLocator"))

        {

            SWbemLocator.Security_.ImpersonationLevel = 3;

            SWbemLocator.Security_.AuthenticationLevel = 4;

            dynamic IService = SWbemLocator.ConnectServer(".", @"root\cimv2");

 

            string fileSystemWatcherQuery =

                @"SELECT * FROM __InstanceOperationEvent WITHIN 3 WHERE Targetinstance ISA 'CIM_DirectoryContainsFile' and TargetInstance.GroupComponent= 'Win32_Directory.Name=""c:\\\\""'";

            dynamic monitor = IService.ExecNotificationQuery(fileSystemWatcherQuery);

 

            Dispatcher.BeginInvoke(() => MessageBox.Show(@"Now listening to file changes on c:\"));

 

            while (true)

            {

                dynamic EventObject = monitor.NextEvent();

                string eventType = EventObject.Path_.Class;

                string path = EventObject.TargetInstance.PartComponent;

                Dispatcher.BeginInvoke(() =>  MessageBox.Show(eventType + ": " + path));

            }

        }

    }).Start();

}

This code might look complex, but it really isn’t.
1. We’ve initialized a WbemScripting.SWbemLocator and connected to the local Machine with proper security.
2. We’ve executed a WMI Query for all storage changes on C:\.
3. When that event fired we’ve shown a messagebox.

Since Wbem doesn’t directly support events, we have to constantly query it. So we’re executing the whole thing in a background thread.

Executing this code and adding c:\foo.txt file will cause the following messagebox to occur:

__InstanceCreationEvent: \\JITDEV\root\cimv2:CIM_DataFile.Name="c:\\foo.txt"

 

Feature #16: Iterate over valid ProgIDs

This one is just a shout-out to the COM infrastructure.
If you’ve been paying attention you’ve noticed some magic strings when calling ComAutomationFactory.CreateObject.

Those magic strings are the ProgIDs of registered COM+ components.
We can get a list of all valid ProgIDs using WMI.

<Button x:Name="btnIterateOverProgIDs" Content="Iterate over valid COM+ ProgIDs" Click="btnIterateOverProgIDs_Click" />

private void btnIterateOverProgIDs_Click(object sender, RoutedEventArgs e)

{

    StringBuilder sb = new StringBuilder();

    sb.AppendLine("The first 25 valid ProgIDs: ");

    using (dynamic SWbemLocator = ComAutomationFactory.CreateObject("WbemScripting.SWbemLocator"))

    {

        SWbemLocator.Security_.ImpersonationLevel = 3;

        SWbemLocator.Security_.AuthenticationLevel = 4;

        dynamic IService = SWbemLocator.ConnectServer(".", @"root\cimv2");

        dynamic QueryResults = IService.ExecQuery(

            @"SELECT Caption, Description, InprocServer32, ProgID FROM Win32_ClassicCOMClassSetting where progid is not null");

        for (int i = 1; i < 25 /*QueryResults.Count*/; i++)

        {

            string progID = QueryResults.ItemIndex(i).ProgID;

            sb.AppendLine("   " + progID);

        }

    }

    MessageBox.Show(sb.ToString());

}

After clicking the button we can see this list of the first 25 valid Classes for Com+ Automation:

image

 

Feature #17: Automate Microsoft Office

Office exposes a set of COM+ automation classes allowing to automate PowerPoint, word, excel, outlook, etc.
There’s been quite a lot written on this one so It’ll suffice to provide links to what’s already been said by folks more knowledgeable than myself:

Silverlight 4's New COM Automation Support by Jeff Prosise (Outlook)

Silverlight 4 Rough Notes: Trusted Applications by Mike Taulty (Excel)

Silverlight 4: New features overview (Part 3) – Elevated Out-of-browser applications by Alex Golesh (Word)

Silverlight TV Episode 1: The Joys of Trusted Out-of-Browser Applications by Keith Smith & John Papa (PowerPoint)


Tools for Silverlight 4 COM+ Development

When developing Silverlight COM+ applications these 4 tools will prove to be invaluable source of information.

 

Tool #1: WMI Explorer

WMI Explorer is a general purpose tool used to visualize WMI queries. (Which we’ve seen earlier)
Using WMI Explorer and the following query we can see a list of all available COM+ Components on the relevant machine:

SELECT Caption, Description, InprocServer32, ProgID FROM Win32_ClassicCOMClassSetting where progid is not null

WMI Explorer Print Screen 

This list is extremely helpful when examining which ProgIDs are available for COM Automation.

 

Tool #2: DLL Export Viewer

When working with dynamic types there’s no intellisense available.
But fear not, using DLL Export Viewer we essentially get the same information reflector would have provided us on an assembly.

First, find the COM+ DLL file address from WMI Explorer and load it into DLL Export Viewer:

Load DLL Dialog

Make sure “Scan COM Type Libraries” is checked.

And now we can see an exported list of members from the ODBC DLL:

DLL Export View for ADO ODBC

 

Tool #3: Resharper

Though development with dynamic types doesn’t offer intellisense, Resharper 5 makes this a little better by offering intellisense on members that have been previously used.

Resharper Intellisense for Run and SendKeys after being used

Notice the intellisense for the “Run” and “SendKeys” methods.

 

Tool #4: MSDN and Search Engines

Most core Windows’ COM+ components have adequate documentation up on MSDN when searching for the type name on google:

Google Search results for MSDN Search on WScript.Shell

In case the documentation on MSDN is missing or just irrelevant (and that’s quite common) search “CreateObject <ProgID>” and the VBScript results are easily portable to C#.

Bing Search Results for: CreateObject WScript.Shell

 

But What about Macs?

Mac

COM+ Automation isn’t supported for Macs in Silverlight 4 Beta 1.
For a good reason, Mac doesn’t have COM+.

However, Microsoft have stated they are looking into getting COM+ support working in Silverlight 4 RTM on a Mac.

Macs have similar programmatic access to COM known as AppleScript.

image

It’s not that hard to read once you remove the added spaces and add some indents.
Once you get past the syntax, it’s easy to see those are the same classes and members on a Mac as are available for Windows.

The point here is that if Microsoft wants to enable Com Automation-like features on a Mac there are 2 options:

1) Enable executing AppleScripts.
This option will let us have the same amount of control on a mac machine as we do on a windows machine.

2) Add an overload to ComAutomationFactory.CreateObject() that calls the “Tell Application” command under the scenes and gets a AppleScript object.
This option would work extremely well for Office automation. For any other operating system feature, you’ll have to code OS access twice. 

 

If Microsoft chooses to not go ahead with Mac support in Silverlight 4 RTM, well, it’s not because they couldn’t.

 

What about Linux?

Moonlight Logo

Moonlight has had the superior security model since it first shipped.
Mono supports hosting Moonlight in a GtkWidget that has full-trust capabilities.
So while Silverlight has an elevated privileges model, Mono has a full trust model.

Read more about this model over at Miguel de Icaza’s blog @ Standalone Silverlight Applications.

 

Can I author my own COM+ Components?

image

It is possible yet unadvisable.
Authoring COM+ Components has a huge cost of learning COM+, and deploying and versioning COM+ Components.

COM+ was such a successful technology most of us left as soon as we could.

 

Can I author .Net components and then deploy them as COM+ components?

Woman Tangled in Wires - analogous to messing about with COM+

Jeremiah Morril has a blog post on how to go about doing that.

It is possible yet tremendously cumbersome.
Consider that you’ve just added a requirement for the full desktop CLR when running in Silverlight.
Some of your users might not have .Net (or the right .Net version) installed.
Additionally, you’ll still back into COM+ DLL versioning hell.

If you’re going to take that level of dependency it might be best if you moved over to WPF.

Or, if you insist on taking a .Net dependency you shouldn’t use COM+ for communication.
It’d be best to use WCF for that.
Michael Wolf has a good blog post demoing how to create that connection.

 

Fin

In this blog post we’ve reviewed many new features enabled by Silverlight 4 COM+ support.
We’ve also covered how to best use this new feature including tools, best practices and the possible future of this feature.

 

Leave A comment

I’d love to hear what you think of Silverlight 4 COM+ automation?
Better yet, if there’s some novel use of Silverlight 4 COM+ I haven’t shown here, feel free to link to it.

 

Sincerely,

-- Justin Angel



Comments