Webcam makes pictures -- that's essential all there is to say when it comes to the idea. I want to use my webcam to take a picture. Unfortunately Windows 7 (which my webcam is attached to) does not feature a simple program to satisfy this essential need. Which is why it had to be created in the most painful way there is.
Nowerdays, a webcam is usually accessed by the means of DirectX (on Microsoft Windows systems, that is). But historically, there's also the good old-fashioned way of using
avicap32.dll -- that is, by using Windows core concepts such as child-windows and window messages.
Looking at the requirements, the list is pretty short:
- Select webcam to use in case more than one is available.
- Be friendly yet determined in case no webcam is found.
- Show a live preview.
- Allow snapshots to be written to a file.
- Leave no fingerprints when sneaking cookies from the jar.
To access a camera using
avicap32.dll, one needs essentially one function call and a number of window-messages; insight to the list of available cameras is provided using yet another function call -- and that's it.
For shortness sake, these are the relevant parts of the code which may be helpful for someone else.
Function call definitions:
function capGetDriverDescriptionA(wDriverIndex: UINT; lpszName: LPSTR; cbName: integer; lpszVer: LPSTR; cbVer: integer): BOOL; stdcall; external 'AVICAP32.DLL'; function capCreateCaptureWindowA(lpszWindowName: PChar; dwStyle: dword; x, y, nWidth, nHeight: word; ParentWin: dword; nId: word): dword; stdcall external 'AVICAP32.DLL';
Constants (which I have been googling myself so maybe the definitions are somewhat diverging from what it should be defined as (i.e. being relative to WM_USER and not to WM_CAP_START)):
const WM_CAP_START = WM_USER; WM_CAP_SET_PREVIEW = WM_USER + 50; WM_CAP_DRIVER_CONNECT = WM_USER + 10; WM_CAP_DRIVER_DISCONNECT = WM_USER + 11; WM_CAP_SAVEDIB = WM_USER + 25; WM_CAP_GRAB_FRAME = WM_USER + 60; WM_CAP_SET_VIDEOFORMAT = WM_USER + 45; WM_CAP_DLG_VIDEOSOURCE = WM_USER + 42; WM_CAP_EDIT_COPY = WM_USER + 30; WM_CAP_SET_OVERLAY = WM_USER + 51; WM_CAP_SET_PREVIEWRATE = WM_USER + 52; WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START+ 41;
Updating the list of available devices in case their number just changed and activating the first device in case there is only one device:
procedure TForm1.RefreshDeviceList; var n, v: array[0..MAX_PATH] of char; sl : TStringList; i: integer; begin sl := tstringList.Create; i := 0; while true do begin if not capGetDriverDescriptionA(i, @n, sizeof(n), @v, sizeof(v)) then break; sl.Add(n + ' (' + v + ')'); i := i + 1; end; if (sl.Count <> self.ComboBox1.Items.Count) then begin self.ComboBox1.Items.assign(sl); if sl.Count = 1 then begin self.ComboBox1.ItemIndex := 0; self.ComboBox1Change(nil); end; end; sl.Free; end;
Connecting to a specific device and activating live (overlay) preview by the means of a visible child window:
CaptureWindow := capCreateCaptureWindowA('CaptureWindow', WS_CHILD or WS_VISIBLE, 0, 0, panel2.ClientWidth, panel2.ClientHeight, self.panel2.handle, self.ComboBox1.ItemIndex); SendMessage(CaptureWindow, WM_CAP_DLG_VIDEOSOURCE, self.ComboBox1.ItemIndex, 0); SendMessage(CaptureWindow, WM_CAP_DRIVER_CONNECT, self.ComboBox1.ItemIndex, 0); SendMessage(CaptureWindow, WM_CAP_SET_PREVIEWRATE, 60, 0); sendMessage(CaptureWindow, WM_CAP_SET_OVERLAY, 1, 0); sendMessage(CaptureWindow, WM_CAP_SET_PREVIEW, 1, 0); showwindow(CaptureWindow, SW_SHOW);
Taking a snapshot and saving it to a file, then returning to live preview:
SendMessage(CaptureWindow, WM_CAP_GRAB_FRAME, 0, 0); if savedialog1.Execute then SendMessage(CaptureWindow, WM_CAP_SAVEDIB, 0, longint(PChar(savedialog1.FileName))); sendMessage(CaptureWindow, WM_CAP_SET_PREVIEW, 1, 0);
Finally: Closing the device.
if captureWindow <> 0 then sendmessage(captureWindow, WM_CAP_DRIVER_DISCONNECT, 0, 0);
You find the sources at down/wcp-src.zip and an executeable at down/wcp-app.zip. Be reminded not to run binaries you downloaded from untrusted sites such as this site. Instead read, understand, and then (!) compile the source. Enjoy responsibly. Things are released under BSD license.
MD5 (wcp-app.zip) = 0cef52aef120c590d1679dfad051fba9 MD5 (wcp-src.zip) = 52ec4be2dd846ae516405f70c9febe34 SHA256 (wcp-app.zip) = 8e61387b4efdd5b6c4e95d5997bb56d374b880d8ecdb9138d6e9c5a4454065f4 SHA256 (wcp-src.zip) = a0e3ea2ee4dac1d014661f91dcda42cd8592396cd150ab8242b2f398d52d043f