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!

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.

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

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

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

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.


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.

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.

(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.

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:

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:
Add Method (Folders)
BuildPath Method
Close Method (FileSystemObject ...
Copy Method (FileSystemObject)
CopyFile Method
CopyFolder Method
CreateFolder Method
CreateTextFile Method
Delete Method
DeleteFile Method
DeleteFolder Method
DriveExists Method
FileExists Method
FolderExists Method
GetAbsolutePathName Method
GetBaseName Method
GetDrive Method
GetDriveName Method
GetExtensionName Method
GetFile Method
GetFileName Method
GetFileVersion Method
GetFolder Method
GetParentFolderName Method
GetSpecialFolder Method
GetStandardStream Method
GetTempName Method
Move Method
MoveFile Method
MoveFolder Method
OpenAsTextStream Method
OpenTextFile Method
Read Method
ReadAll Method
ReadLine Method
Skip Method
SkipLine Method
Write Method (FileSystemObject ...
WriteBlankLines Method
WriteLine Method (FileSystemOb ...
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:

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.

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:

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:

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:

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.

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.

<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:

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.

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:

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”!

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:

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:


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

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:

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:

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

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:

Make sure “Scan COM Type Libraries” is checked.
And now we can see an exported list of members from the ODBC DLL:

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.

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:

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#.

But What about Macs?

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.

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 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?

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?

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.