Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Look at me! Windows Image Acquisition

  Interfacing a Webcam that supports Windows Image Acquisition (WIA) using .Net
Scott Hanselman's Computer Zen

Difficulty: Intermediate
Time Required: 1-3 hours
Cost: $50-$100
Software: Visual Studio Express Editions, Windows® Image Acquisition Automation Library v2.0 Tool: Image acquisition and manipulation component for VB and scripting
Hardware: Logitech Webcam
Download:

I love Goodwill. If you're not familiar with Goodwill, it's a chain of non-profit stores that takes donations and then resells the items back to the public. I was at Goodwill recently (we stop by at least once a week) and noticed this little Logitech Webcam for US$3.99. Frankly, I thought it was a little overpriced, but I figured I'd go for it regardless.

One of the nice things about these older Logitech Webcams is that they don't require a driver download and they support Windows Image Acquisition (WIA) directly. WIA is an API included in Windows that aims to unify the acquisition of images from all kinds of devices, including scanners and cameras. This is a pretty low-level API and a bit of a hassle to us. However, after Windows XP SP1 the WIA Automation Layer was released with a simpler COM API meant for VB6 and ASP developers. You can download this layer from MSDN and other places as it is free for redistribution. The meat of the layer is the wiaaut.dll that should be copied into the system32 directory and registered via the command "regsvr32 wiaaut.dll."

Getting to WIA from .NET

The wiaaut.dll COM automation library can be added via "Add Reference" from with Visual Studio.NET 2005 and a .NET wrapper will be automatically generated. Only devices whose drivers support WIA will be available via this interface. Most name-brand cameras will work just fine, but some no-name brands won't appear. If your device appears in the Control Panel's "Scanners and Cameras" interface, then this technique, and this program, should be able to see it. My camera is shown in the figure below.

In our code, we can add a namespace qualifying statement like using WIA in C# or imports WIA in VB.NET to name it easier to access these newly imported classes and interfaces. To start, we'll need to get a hold of a DeviceID. WIA thinks about things in terms of Devices, Commands, and Formats. Devices have types like Camera, Scanner or Video. Commands are things like "Take Picture" and Formats are JPEG or BMP, etc.

We'll create an instance of a CommandDialogClass (from the newly imported WIA namespace) and ask it to show us a select dialog so that we might select from any kind of device. You can show only Video devices or only Scanners by changing the WiaDeviceType enumeration that's passed in. We'll only show this select dialog when the user clicks "Configure" in our application, or when the application has been started with invalid configuration data.

Visual C#

CommonDialogClass class1 = new CommonDialogClass();
Device d = class1.ShowSelectDevice(WiaDeviceType.UnspecifiedDeviceType, true,false);
if (d != null)
{
settings.DeviceID = d.DeviceID;
settings.Save();
}

Visual Basic

Dim class1 As CommonDialogClass = New CommonDialogClass
Dim d As Device = class1.ShowSelectDevice(WiaDeviceType.UnspecifiedDeviceType, true, false)
If (Not (d) Is Nothing) Then
settings.DeviceID = d.DeviceID
settings.Save
End If

This select dialog is, fortunately, supplied completely by Windows and returns a Device. Each Device has a DeviceID that we will save into our User-specific settings class. This class was generated automatically by new features in Visual Studio .NET 2005 that make managing settings fantastically easy. I right-clicked on the Project from within the Visual Studio Solution Explorer and selected "Settings." After I indicated the names and data types of the settings I needed to save, Visual Studio 2005 generated a class that exposed strongly-typed properties such as DeviceID. Settings can also be saved more easily in .NET 2.0. The DeviceID for my Webcam happened to be "{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\0003" and is stored in my user's Documents And Settings\Local Settings\Application Data\BlogWebcam directory.

Taking a Picture

Once we've stored away a DeviceID in the configuration file, we'll want to connect to our device and take a picture. We'll need to find the device via it's ID without showing the dialog, connect to it and hold on the Device instance.

Visual C#

DeviceManager manager = new DeviceManagerClass();
Device d = null;
foreach (DeviceInfo info in manager.DeviceInfos)
{
if (info.DeviceID == settings.DeviceID)
{
d = info.Connect();
break;
}
}

Visual Basic

Dim manager As DeviceManager = New DeviceManagerClass
Dim d As Device = Nothing
For Each info As DeviceInfo In manager.DeviceInfos
If (info.DeviceID = settings.DeviceID) Then
d = info.Connect
Exit For
End If
Next

Now we're back where we were before, with a Device instance in the variable d. We'll be connecting to the device each time our timer is started. Each device has a series of commands available to it, and these commands are well-known and identified by GUIDS both in the Registry and in the MSDN Help. We're interested in "Take Picture" which has the GUID string value "AF933CAC-ACAD-11D2-A093-00C04F72DC3C”, but the COM interface also exposes this value in the constant CommandID.wiaCommandTakePicture. When we get a hold of our Device we can spin through its available Commands until we find this one to determine if the device supports it, or we can just call it directly via Device.ExecuteCommand.

Visual C#

Item item = device.ExecuteCommand(CommandID.wiaCommandTakePicture);

Visual Basic


Dim item As Item = device.ExecuteCommand(CommandID.wiaCommandTakePicture)

Once we've called ExecuteCommand on our device an "Item" is returned. This isn't just any ordinary item, it's a WIA Item. Each WIA may include any number of image Formats. These formats are also well-known and appear in the registry. I look them up in the startup of the app just in case they change, rather than hard-coding them.

We spin through the available formats looking for JPEG. I could likely have hard-coded the JPEG GUID and avoided this quick spin, but I also wanted to illustrate how you can find your way around the WIA object model. Once we've found JPEG, we call item.Transfer and an ImageFile is returned that we can save to disk.

Visual C#

Item item = device.ExecuteCommand(CommandID.wiaCommandTakePicture);
foreach (string format in item.Formats)
{
if (format == jpegGuid)
{
WIA.ImageFile imagefile = item.Transfer(format) as WIA.ImageFile;
filename = GetFreeFileName();
if (string.IsNullOrEmpty(filename) == false)
{
imagefile.SaveFile(filename);
}
this.picLastImage.Load(filename);
return filename;
}
}

Visual Basic

Item item = device.Execute
Dim item As Item = device.ExecuteCommand(CommandID.wiaCommandTakePicture)
For Each format As String In item.Formats
If (format = jpegGuid) Then
Dim imagefile As WIA.ImageFile = CType(item.Transfer(format),WIA.ImageFile)
filename = GetFreeFileName
If (String.IsNullOrEmpty(filename) = false) Then
imagefile.SaveFile(filename)
End If
Me.picLastImage.Load(filename)
Return filename
End If
Next

Incidentally, I also load the saved image into a PictureBox on my WinForm and return it from this TakePicture() method.

Uploading the Picture to my WebLog

Now that I've taken a picture, what am I going to do to it? Well, why not upload it to a specific filename on my blog so folks can see me and my workspace; what could be more thrilling? In the past, FTP'ing a file would require a third-party library, but .NET 2.0 has extended the System.Net.WebRequest class with support for FTP.

Here we'll create an FtpWebRequest by passing an ftp:// URL to the WebRequest.Create method. For convenience I'll include the username and password in the URL like this: ftp://username:password@ftp.myurl.com/blog/webcam.jpg. Note that the URL includes the username, password, domain name and destination filename all in one string. We'll load our local file into a byte array and write it out (upload it) to the FtpWebRequest's underlying stream by retrieving it with GetRequestStream() and then Write().

Visual C#

FtpWebRequest request = (FtpWebRequest)WebRequest.Create(settings.FTPServerURL);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.UseBinary = true;
FileInfo fileInfo = new FileInfo(filename);
byte[] fileContents = new byte[fileInfo.Length];
using (FileStream fr = fileInfo.OpenRead())
{
fr.Read(fileContents, 0, Convert.ToInt32(fileInfo.Length));
}
using (Stream writer = request.GetRequestStream())
{
writer.Write(fileContents, 0, fileContents.Length);
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
}

Visual Basic

Dim request As FtpWebRequest = Nothing
request = CType(WebRequest.Create(settings.FTPServerURL),FtpWebRequest)
request.Method = WebRequestMethods.Ftp.UploadFile
request.UseBinary = true
Dim fileInfo As FileInfo = New FileInfo(filename)
Dim fileContents() As Byte = New Byte((fileInfo.Length) - 1) {}
Using fr As FileStream = fileInfo.OpenRead
fr.Read(fileContents, 0, Convert.ToInt32(fileInfo.Length))
End Using
Using writer As Stream = request.GetRequestStream
writer.Write(fileContents, 0, fileContents.Length)
End Using
Dim response As FtpWebResponse = CType(request.GetResponse,FtpWebResponse)

You may notice the use of the "using" statements in the code snippet above. Using "using" with classes that implement IDisposable will automatically cause Dispose() to be called when then using block exits. Some folks don't like the syntax and others believe that using it even if the underlying Dispose() doesn't do anything is syntactic sugar. Personally, I really like the syntax, and in this sample the using statement is closing the FileStream, the Request stream, and the FtpWebResponse.

Conclusion

There are things that could be extended, added, and improved on with this project. Here are some ideas to get you started:

  • Interface with X10 or your doorbell to take a picture and display it on your Media Center PC.
  • Stitch together hundreds of photos, perhaps of your baby, into time-lapse videos.
  • Create a security system that detects motion by diff'ing photos and emails you with an alarm and the attached photo!
  • Upload photos to Flickr or Smugmug with their APIs.

Have fun and have no fear when faced with the words: Some Assembly Required!

Follow the Discussion

  • Anthony GrahamAnthony Graham

    How widely is WIA actually supported? I'm using an Epson PX800FW on Vista 64, and the WIA driver doesn't even support the Auto Document Feeder. I say this because property 3088 is simply not there.

    I'd have thought that to get WHQL certification, all the parts of the hardware would need to work :/ I'm thinking twain is my only hope, sigh!

  • AlexAlex

    Thanks again for the information, so the solution is to find another activex control rather then wiaaut.dll to control my webcam in Vista OS right?

    Do you have any information about it? so i can still using my application in Vista Based environment.

    And I hope it is a freeware like wiaaut.dll

    Thank's

  • omaromar

    Hi

    Anybody try connect digital camera to take a picture?

    How to connect  camera digital in mode printing, computer , etc?

    thanks.

  • Clint RutkasClint I'm a "developer"

    @Gajendra Kumar, an IP camera works different than this code.  

  • Gajendra KumarGajendra Kumar

    Hello Friend,

    Any one hase idea about IP camera?? How it's work??

    Thnak.s

    Gajendra Kumar

  • menframenfra

    Thanks for the sample.

    Can you show how to work it on a Scanner?

    I will be much happy to see that

  • dr_csharpdr_csharp

    hi friends

    it was helpful,but in Vista it cause error :

    Exception from HRESULT: 0x80210015

    whats wrong ?

  • Clint RutkasClint I'm a "developer"

    @dr_csharp based on a quick bing search on that error, sounds like the device you're using isn't WIA compliant.  I'd suggest using check out DirectShow.net or http://wpfmediakit.codeplex.com/

  • Clint RutkasClint I'm a "developer"

    @adnan, there are a bunch of things out there that can do stuff like that but we don't.

    To store an image in a database (most wouldn't recommend this), use a BLOB data type.  I'd suggest storing a file path instead to the image.

    Check out DirectShow.Net or the WPF Media Kit to pull images off webcams.  http://wpfmediakit.codeplex.com/

  • AdnanAdnan

    Hi,

    Nice article Smiley

    I am making a Lab Complaint Management in C# and would like the webcams placed in every lab to record the videos and save in a database or at some place.

    The people using software, to whom given the rights should be able to see the live web cams of any lab, all the labs are networked to each other.

    So two features

    1) Able to checkout live cams of any lab

    2) Record the video at some place

    This need to be done in C# -  Can anyone help? Am a newbie - Thanks a lot friends.

  • kaquikaqui

    Hi, I am trying to interface a webcam to a C# program. I need it to scan the screen to find a red dot.

    What shall I do?

    URGENT PLS.

    Thanks

  • Clint RutkasClint I'm a "developer"

    @Oea Tek, part of the problem here is you need API references into the camera to do this.  It isn't as straight forward as you think.  I've been wanting to do this to my Canon T1i.  Each camera's API may be slightly different and depending on it, the camera may not even have one.  I know Canon mid and high range cameras do, as does Nikon.

    This is something I've been thinking about building an SDK for but it is far harder and has more moving parts than one may think.  Canon, while it has the API, has some restrictions on how you can distribute and how you can even get a hold of their API.  I tried twice and haven't heard back.

  • Oea TekOea Tek

    Are there any solution where one can capture images from a digital Camera to an image control in real time (not a web cam)?

    I have been searching the web for days trying to locate such hardware software solution to no avail. I found plenty of references for web cams though.

  • AmbeenAmbeen

    It great!!

    Any one can try sample c# and vb.net webcam connecting.

    Very easy to use.

    [link removed]

  • Clint RutkasClint I'm a "developer"

    @Ambeen, I removed the link since I did a quick glance at it and the DLL that does the actual webcam processing worries me where you got it from and how you're distributing it.  I'll be more than happy to post a link to your blog once I feel a bit more comfortable about where that webcam code came from.  Please hit the "email us" link so we can clear this up.

  • shoaibshoaib

    i am new in WIA programming. plz guide me how to implement & where write these code for scanner.

  • alinalin

    brilliant post

    Thanks a million!!!

  • Clint RutkasClint I'm a "developer"

    @Paul Chu, I understand why Scott is doing what he is doing but not 100% how to fix the issue.

    Instead of using WIA to do this type of thing, I'd suggest using check out DirectShow.net or http://wpfmediakit.codeplex.com/

  • Paul ChuPaul Chu

    Hi all,

    I'm very new to this type of stuff and

    I wanted to try this out on my Win 7 laptop.

    I downloaded the C# code and I get a registry entry error at runtime

    jpegGuid is null !

           private void Form1_Load(object sender, EventArgs e)

           {

               Microsoft.Win32.RegistryKey jpegKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(@"CLSID\{D2923B86-15F1-46FF-A19A-DE825F919576}\SupportedExtension\.jpg");

               jpegGuid = jpegKey.GetValue("FormatGUID") as string;

    A regedit search shows the key exists:

    D2923B86-15F1-46FF-A19A-DE825F919576

    but none of the \SupportedExtension\.jpg

    Can anyone help me ?

    Thanks, Paul

  • meme

    why it doesn't work in windows7?

    can't find any device

  • Clint RutkasClint I'm a "developer"

    @me a lot depends on the device.  If I need to use a camera, I tend to use either the WPF Media Kit or DirectShow.Net

  • Clint RutkasClint I'm a "developer"

    @Donald did you import the WIA namespace?

  • DonaldDonald

    I get "CommonDialogClass" not define. I am using Windows Vista32 and Visual Basic 2008 express. What am I doing wrong?

  • NoahNoah

    I developed under Win 7 64 bit, and was faced with a user base of XP SP3.

    It is likely that wiaaut.dll is not installed or registered.

    Find the XP WIA SDK (via Bing, I guess), and add it to your install project.  Set the Register property to "vsdrfCOM".

    [Coding4Fun]
    http://www.microsoft.com/downloads/en/details.aspx?familyid=a332a77a-01b8-4de6-91c2-b7ea32537e29&displaylang=en

     

  • EricEric

    Wow, it's simple and shows everything what I need to do to capture using WIA.

    Thannks.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.