Sean Blakemore's Blog

Like trying to fit a square peg in a round hole

22. December 2009 22:01
by Sean
13 Comments

Silverlight 4 COM Automation – Taking photos with a DSLR using Windows Image Acquisition

22. December 2009 22:01 by Sean | 13 Comments

Note: This code was written using the PDC09 Beta and may not work without modification on future builds!

Silverlight 4 brings a lot to the table and in my opinion is the most exciting looking release so far. There is a whole slew of new possibilities which have opened up because of a number of important new features, the feature I’m going to be looking at is COM automation.

Huge amounts of functionality built into Windows and installed by third party software is now at your fingertips. So how do we get access to it?

ComAutomationFactory and the ‘dynamic’ keyword

ComAutomationFactory is a new static class in the System.Windows.Interop namespace which exposes three methods and a single property. ‘dynamic’ is a new language keyword available in C# 4.

ComAutomationFactory methods and property

 

ComAutomationFactory.CreateObject("ProgId");
ComAutomationFactory.GetObject("ProgId");

 

CreateObject and GetObject allow us to either activate and spin up a new instance, or get a reference to a currently activated and running instance of a given COM server. So for example, you are able to spawn a new copy of Outlook to add a calendar entry or alternatively you can attach to the copy the user is already running.

These methods require the ProgId for the COM server you want to talk to. Each COM object on a computer is uniquely identified by a CLSID (essentially a Guid) which is stored in the registry. A ProgId is a string which maps to a CLSID in the registry, making COM servers much easier to work with, this is much the same as how a domain name maps to an IP address. For example, Outlook can be addressed as “Outlook.Application” instead of {D5CDD1EA-BB6A-4AAF-A3B0-98E5A3B120F4}!

ComAutomationFactory.GetEvent(comObject, "eventName");

GetEvent returns an instance of ComAutomationEvent and is interesting in that it allows us to subscribe to events raised by the COM server, we will be using this later!

ComAutomationFactory.IsAvailable;

The IsAvailable property returns a boolean as you might expect which lets us know if it is possible to use COM automation in the current context. COM automation in Silverlight requires that the application is running Out-Of-Browser and has been given elevated permissions, it is also available only on Windows systems!

The ‘dynamic’ keyword

dynamic is a new type which bypasses static type checking. Sounds confusing but it comes in really handy for working with objects the compiler doesn’t know the exact shape of at design time, so for example objects retrieved via reflection, from a dynamic language like IronPython or IronRuby or indeed COM objects we’ve grabbed by ProgId!

What all this means is that we can declare a variable to be of type dynamic and the compiler will let us call any method or property without complaining about it not being there. These calls are then resolved at run time.

 

dynamic dyn = "This is actually a string";
//This code builds without complaint from the compiler.
dyn.ThisMethodObviouslyDoesntExistOnAString();
dyn.NorDoesThisProperty = true;

 

And as you can see below, Visual Studio allows me to do this.

dynamic expression

 

Windows Image Acquisition

Windows Image Acquisition is an API provided by Windows and has been around in the OS since Windows ME in one form or another. WIA allows us to program in a uniform way against any scanner or camera device which has a compatible driver. Windows ME introduced WIA 1.0 which was replaced with WIA 2.0 in Windows Vista, this application was written and tested on Windows 7 and WIA 2.0 so this comes under a “works on my machine” disclaimer!

It is possible to download the WIA 2.0 binaries from Microsoft and register them under Windows XP SP1+, I’ve had this working in the past and is fairly painless so I won’t go into it here.

The reference documentation on MSDN is comprehensive and also handily gives the ProgId for all the different objects making use from Silverlight really easy.

The sample code

To have a little play around with these new features in Silverlight and C# I put together a little application which allows my to take pictures with my WIA enabled Nikon DSLR. Taking photos from a Nikon using a browser plug-in, crazy!?!

First of all we need to let the user choose a device to take pictures with:

using (dynamic dialog = ComAutomationFactory.CreateObject("WIA.CommonDialog"))
{
    var device = dialog.ShowSelectDevice(
        (int)WiaDeviceType.CameraDeviceType, true, false);
}

Notice that CreateObject returns IDisposable, so we try to make sure we call Dispose for any reference obtained this way to ensure we release all resources cleanly.

This will pop open a modal dialog which allows us to pick our device.

D40X

Now since we’re able to subscribe to events raised by the COM object, lets see if we can get a notification when a picture is taken. This will allow us to respond to both our application requesting a picture be taken but also let us know when someone presses the shutter release on the camera itself!

ComAutomationEvent evt = ComAutomationFactory.GetEvent(manager, "OnEvent");
evt.EventRaised += OnEvent;

private void OnEvent(object sender, ComAutomationEventArgs e)
{
    if (e.Arguments[0].ToString() != EventId.ItemCreated)
        return;

    //Grab the raw image bytes
    var item = device.Items[device.Items.Count];
    var imagefile = item.Transfer(FormatId.Jpeg);
    var imageData = (byte[]) imagefile.FileData.BinaryData;

    PictureTaken(this, new PictureTakenEventArgs(imageData));
}

public event EventHandler<PictureTakenEventArgs> PictureTaken = delegate { };

Here we get a ComAutomationEvent from the ComAutomationFactory.GetEvent method and subscribe to it’s EventRaised event. In the handler we extract the raw image bytes using the WIA API and then notify any interested parties a picture was taken.

Taking a picture programmatically is even more trivial.

device.ExecuteCommand(CommandId.TakePicture);

The sample application in the download below has a simple UI which allows you, when running Out-Of-Browser on Windows, to take pictures and displays them on screen.

Mmmm Coffee

Conclusion

All in all I’m very excited about Silverlight 4 and Silverlight in general, a lot of missing pieces are falling into place with this new release and we should be able to create some really compelling stuff.

My concern is that since none of this COM business will work on OSX, nor is there any alternative scheme on that platform, we are heading down the wrong track. Yes, we can now create applications that “Light Up” on Windows, but really I’d like to offer the same powerful functionality to all my users. Some are of the opinion that the real exciting thing with Silverlight and Moonlight is the future of a true cross platform development model based on C#, XAML and web based deployment, and for me not having feature parity across even officially supported operating systems is a worry.

Never the less, I’m loving it!

Code Download

Comments (13) -

Very well written and clear. Good explanation of Dynamic.

Good job. Very clear and concise. Do you know of any COM object that would allow us to take snapshots of the screen instead ?

Thanks

GoodWork !

Hello,

First of all thanks for your article.

Then I have a question. I'm programming an out of the browser application to interact with a scanner.
Here ispart of  my class:
        private void OnEvent(object sender, EventArgs e)
        {
              //Do something
        }

        private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            dynamic DeviceManager = AutomationFactory.CreateObject("WIA.DeviceManager");
            AutomationEvent evt = DeviceManager.GetEvent(DeviceManager, "OnEvent");
            using (DeviceManager )
            {
              var deviceInfos = DeviceManager.DeviceInfos;
              for (int i = 1; i <= deviceInfos.Count; i++)
              {
                  var IDevice = deviceInfos.Item(i).Connect();
                  if (IDevice.Properties("Name").Value.Equals(comboBox1.SelectedValue.ToString()))
                        {
                 dynamic item = IDevice.Items[1];
                             dynamic Devices = AutomationFactory.CreateObject("WIA.CommonDialog");
                 Devices.ShowTransfer(DeviceManager.DeviceInfos.Item(1).Connect().Items[1]);
                  }
               }
             }
        }
The code runs well without exception, but the event OnEvent is not triggered. Any Idea pleasE???

Thanks.

Regards Michel Muhar
                        

I forgot to put the line
evt.EventRaised += OnEvent;

after the line AutomationEvent evt = AutomationFactory.GetEvent(DeviceManager, "OnEvent");

but it stills don't work

any help would be appreciated.

Michel

Hi All,

I have created custom COMVISIBLE library in dot net. I tested it at my local machine with silverlight which works fine.

Now I want to host it so that other user can also call that from their silverlight apps. I dont want to register that assembly at all the client machines manually. Is there some way to do installation step automatically from silverlight.

Response will be highly appreciated.

Thanks.

Hi,

As far as I'm aware what you're asking is not possible. The COM features in Silverlight 4 are only meant for interacting with COM objects already installed and registered on the client machine.

Hope that helps!


Here Some possibility given [As far as proof of concept], I am trying to do that. I will share my experience on that.

jmorrill.hjtcentral.com/.../...M-Registration.aspx

Try This, May be it will help us.

jmorrill.hjtcentral.com/.../...M-Registration.aspx

Wow, I hadn't seen that, I guess it is possible then!

I would caution against using that method though because it is a blatant and nasty (but very cool) hack. If you write an application that relies on that functionality you may find that Microsoft patches it and your software no longer works. If you really need this, would you be better off using a WPF application and deploying as XBAP?

Yes. But, If I am not wrong, WPF application require the complete dot net framework at each client machine, while silverlight require only silverlight tini framework. And hosting at live is another case. What do you say about this. Actually, I am just investigating for the case that Registering some COM / Interop assembly in system regitry by using some way from web.


You're correct that WPF requires the desktop .Net framework.

The reason I suggest to just use WPF is if you are writing a ComVisible component with .Net and running it somehow from Silverlight the full .Net framework is required on the machine to run the ComVisible component anyway!

In addition, you should check out the ".Net Client Profile" which is greatly improved in .Net 4 and will give much smaller download sizes than the full framework. Client Profile is now the default and recommended framework for most WPF client applications and will be offered on Windows Update before too long.

My question is how to listen to the shutter of the camera, and take pictures by press it?

@Desheng For me this just worked, the ItemCreated event was fired whether I took a picture from code or from using the shutter release button on the camera.
If this isn't working for you then it is probably due to the way that your camera drivers are implemented and unless you can find another way there probably isn't much you can do about it.

Pingbacks and trackbacks (2)+

Comments are closed