Using a webcam in a WPF application
Join the DZone community and get the full member experience.
Join For FreeThere are a lot of developers who try to integrate web cam interaction capabilities in their applications. During my work as an administrator on an online developer community, I encountered many question regarding this topic. The variety of implementations is huge – some people recommend using SDKs provided by the camera manufacturer, some advice using the WIA (Windows Image Acquisition) layer. It can work in specific situations, however, these are in no way universal solutions. From my own experience, I can say that not every camera manufacturer provides a SDK. Also, many of the new cameras (especially those integrated in laptops) aren’t WIA-compatible, so this is also not a choice.
From this point I will admit that the developer who is going to use the code below has the proper camera drivers installed. This means, that the camera is successfully detected by Windows and is able to capture an image.
To start, create a new WPF application. In this specific article, I won’t focus on XAML code, but much rather on the code-behind functionality. I will be heavily relying on Win32 API for this, on the avicap32 library, to be specific.
Create a standard C# WPF application. You can switch to the code view right when you start, since I won’t be touching the UI at all for now. First of all, a declaration for the System.Runtime.InteropServices is needed – I will be using some DllImport to access the functions provided by avicap32.
Now, some additional declarations are needed. Here is what I put inside my class:
IntPtr deviceHandle;public const uint WM_CAP = 0x400;public const uint WM_CAP_DRIVER_CONNECT = 0x40a;public const uint WM_CAP_DRIVER_DISCONNECT = 0x40b;public const uint WM_CAP_EDIT_COPY = 0x41e;public const uint WM_CAP_SET_PREVIEW = 0x432;public const uint WM_CAP_SET_OVERLAY = 0x433;public const uint WM_CAP_SET_PREVIEWRATE = 0x434;public const uint WM_CAP_SET_SCALE = 0x435;public const uint WS_CHILD = 0x40000000;public const uint WS_VISIBLE = 0x10000000;
IntPtr represents a pointer – the integer size depending on the system (be it 32-bit or 64-bit). It is used to store the device handle. The next set of constants is used to set the camera parameters for the capture. You can use the values directly, but I would strongly recommend keeping the constants.
Now, there are a few function declarations that need to be introduced. Through these functions, the application will interact with WinAPI to obtain the image through the system layer. The declarations are the following:
[DllImport("avicap32.dll")]public extern static IntPtr capGetDriverDescription(ushort index, StringBuilder name, int nameCapacity, StringBuilder description, int descriptionCapacity);[DllImport("avicap32.dll")]public extern static IntPtr capCreateCaptureWindow(string title, uint style, int x, int y, int width, int height, IntPtr window, int id);[DllImport("user32.dll")]public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);[DllImport("user32.dll")]public static extern IntPtr SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
As you see, there is also user32.dll involved. SendMessage will send the capture message to the capture frame (also referred as window). I will be using SetWindowPos to establish the margins, where the image will be shown.
I also need to create an Attach() function, that will get the image from the connected device. The function looks like this:
public void Attach(){ deviceHandle = capCreateCaptureWindow(string.Empty, WS_VISIBLE | WS_CHILD, 0, 0, (int)this.ActualWidth -150, (int)this.ActualHeight, new WindowInteropHelper(this).Handle, 0); if (SendMessage(deviceHandle, WM_CAP_DRIVER_CONNECT, (IntPtr)0, (IntPtr)0).ToInt32() > 0) { SendMessage(deviceHandle, WM_CAP_SET_SCALE, (IntPtr)(-1), (IntPtr)0); SendMessage(deviceHandle, WM_CAP_SET_PREVIEWRATE, (IntPtr)0x42, (IntPtr)0); SendMessage(deviceHandle, WM_CAP_SET_PREVIEW, (IntPtr)(-1), (IntPtr)0); SetWindowPos(deviceHandle, new IntPtr(0), 0, 0, (int)this.ActualWidth-150, (int)this.ActualHeight, 6); }}
Note that I am using WindowInteropHelper(this).Handle to get the current window handle. WPF assigns a handle only to the window, so trying to get this to work with a specific control will be way too complicated at this point. Therefore, I am just going to use the current window.
The function above sets all the needed capture parameters and you can see that I am using the constants declared prior to the actual function.
And this is it. Now, in the Window_Loaded event handler, just write Attach(); then build and run the application. You will see the webcam image shown on screen.
As you see, the margins are set by (int)this.ActualWidth -150 and (int)this.ActualHeight when calling capCreateCaptureWindow. You can adjust the margins as you need, but note that the image will lose the proportions, so you will also have to adjust the scaling settings and consider those when resizing. Setting proper margins also allows the placement of additional controls on the window, so the capture won’t cover the entire working area.
Opinions expressed by DZone contributors are their own.
Comments