StillCam – My first experimental WIA still image capture program

I’m working on a program called StillCam, which uses the (horrible) Windows Image Acquisition API to capture a still image from a USB webcam. Unfortunately, it doesn’t work yet. It looks for available devices and opens one if it finds any. However, the bit where it tries to actually capture an image from the device isn’t working yet.

Hopefully I’ll get it working soon and then I’ll tidy it up. In the meantime, here’s the current non-working code:

// StillCam.cpp - A still image capture program
// written by Ted Burke - last updated 21-5-2012
// This is my first experiment in using the
// Windows Image Aquisition (WIA) API.
// Website:

#include <windows.h>
#include <stdio.h>
#include <wia.h>

IWiaDevMgr *pWiaDevMgr = NULL;
IEnumWIA_DEV_INFO *pWiaEnumDevInfo = NULL;
IWiaPropertyStorage *pWiaPropertyStorage = NULL;
IWiaItem *pWiaDevice = NULL;
PROPSPEC PropSpec[3] = {0};
PROPVARIANT PropVar[3] = {0};
IWiaDataTransfer *pWiaDataTransfer = NULL;
STGMEDIUM stgMedium = {0};

void exit_program(const char* exit_message)
	// Clean up WIA / COM stuff
	if (pWiaDataTransfer) pWiaDataTransfer->Release();
	FreePropVariantArray(3, PropVar);
	if (pWiaDevice) pWiaDevice->Release();
	if (pWiaPropertyStorage) pWiaPropertyStorage->Release();
	if (pWiaEnumDevInfo) pWiaEnumDevInfo->Release();
	if (pWiaDevMgr) pWiaDevMgr->Release();

	// If an error message was supplied print it
	if (strlen(exit_message) > 0)
		fprintf(stderr, exit_message);
		fprintf(stderr, "\n");

	// Exit the program normally

int main()
	int devnum = 1;

	if (FAILED(hr)) exit_program("Could not initialise COM");

	hr = CoCreateInstance(
			IID_IWiaDevMgr, (void**)&pWiaDevMgr);
	if (FAILED(hr)) exit_program("CoCreateInstance failed");

	hr = pWiaDevMgr->EnumDeviceInfo(WIA_DEVINFO_ENUM_LOCAL, &pWiaEnumDevInfo);
	if (FAILED(hr)) exit_program("EnumDeviceInfo failed");

	fprintf(stderr, "Scanning WIA devices...\n");

	int dev_count = 0;

	while (hr == S_OK)
		// Try to get IWiaPropertyStorage for next device
		hr = pWiaEnumDevInfo->Next(1, &pWiaPropertyStorage, NULL);

		// If there are no more devices, stop scanning
		if (hr != S_OK) break;

		// Found another device
		fprintf(stderr, "Found device %d\n", dev_count);

		// If this is the requested device, break out of loop
		if (dev_count == devnum) break;

		// Release the current device's IWiaPropertyStorage
		pWiaPropertyStorage = NULL;

	PropSpec[0].ulKind = PRSPEC_PROPID;
	PropSpec[0].propid = WIA_DIP_DEV_ID;
	PropSpec[1].ulKind = PRSPEC_PROPID;
	PropSpec[1].propid = WIA_DIP_DEV_NAME;
	PropSpec[2].ulKind = PRSPEC_PROPID;
	PropSpec[2].propid = WIA_DIP_DEV_DESC;
	hr = pWiaPropertyStorage->ReadMultiple(3, PropSpec, PropVar);
	if (FAILED(hr)) exit_program("ReadMultiple failed");
	if (VT_BSTR == PropVar[0].vt) fprintf(stderr, TEXT("WIA_DIP_DEV_ID: %ws\n"), PropVar[0].bstrVal );
	if (VT_BSTR == PropVar[1].vt) fprintf(stderr, TEXT("WIA_DIP_DEV_NAME: %ws\n"), PropVar[1].bstrVal );
	if (VT_BSTR == PropVar[2].vt) fprintf(stderr, TEXT("WIA_DIP_DEV_DESC: %ws\n"), PropVar[2].bstrVal );

	hr = pWiaDevMgr->CreateDevice(PropVar[0].bstrVal, &pWiaDevice);
	if (FAILED(hr)) exit_program("CreateDevice failed");

	FreePropVariantArray(3, PropVar);

	// Configure storage medium
	GUID guidOutputFormat = WiaImgFmt_BMP;
	PropSpec[0].ulKind = PRSPEC_PROPID;
	PropSpec[0].propid = WIA_IPA_FORMAT;
	PropSpec[1].ulKind = PRSPEC_PROPID;
	PropSpec[1].propid = WIA_IPA_TYMED;
	PropVar[0].vt = VT_CLSID;
	PropVar[0].puuid = &guidOutputFormat;
	PropVar[1].vt = VT_I4;
	PropVar[1].lVal = TYMED_FILE;
	pWiaPropertyStorage->WriteMultiple(2, PropSpec, PropVar, WIA_IPA_FIRST);
	if (FAILED(hr)) exit_program("WriteMultiple failed");
	hr = pWiaDevice->QueryInterface(IID_IWiaDataTransfer, (void**)&pWiaDataTransfer);
	if (FAILED(hr)) exit_program("QueryInterface IID_IWiaDataTransfer failed");

	// Set filename (NOT WORKING!)
	stgMedium.tymed = TYMED_FILE;
	wchar_t* filename = L"temp.bmp";
	stgMedium.lpszFileName = filename;

	// idtGetData is currently returning S_FALSE
	hr = pWiaDataTransfer->idtGetData(&stgMedium, 0);
	if (hr == E_INVALIDARG) fprintf(stderr, "E_INVALIDARG\n");
	if (hr == E_OUTOFMEMORY) fprintf(stderr, "E_OUTOFMEMORY\n");
	if (hr == E_UNEXPECTED) fprintf(stderr, "E_UNEXPECTED\n");
	if (hr == S_FALSE) fprintf(stderr, "S_FALSE\n");
	if (hr == S_OK) fprintf(stderr, "S_OK\n");
	if (hr == STG_E_MEDIUMFULL) fprintf(stderr, "STG_E_MEDIUMFULL\n");
	if (hr == WIA_S_NO_DEVICE_AVAILABLE) fprintf(stderr, "WIA_S_NO_DEVICE_AVAILABLE\n");
	//if (FAILED(hr)) exit_program("idtGetData failed");
	if (hr != S_OK) exit_program("idtGetData failed");

	fprintf(stderr, TEXT("Filename: %ws\n"), stgMedium.lpszFileName);

	// Not working!!
	system("copy temp.bmp other.bmp");

	// Release COM objects and exit

This is the makefile:

# StillCam makefile
# Written by Ted Burke - last modified 21-5-2012
# Website:

StillCam.exe: StillCam.cpp
	cl StillCam.cpp /MD -link ole32.lib wiaguid.lib
This entry was posted in Uncategorized. Bookmark the permalink.

10 Responses to StillCam – My first experimental WIA still image capture program

  1. Serge says:

    LPWSTR pwszFileName = (LPWSTR) ::CoTaskMemAlloc (MAX_PATH * sizeof (WCHAR));
    #ifdef UNICODE
    ::lstrcpy (pwszFileName, “”temp.bmp””);
    ::MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, “”temp.bmp””, -1,
    pwszFileName, MAX_PATH);
    stgMedium.tymed = TYMED_FILE;
    stgMedium.lpszFileName = pwszFileName;

  2. David says:

    Hey Ted!
    Did you managed to get images using WIA?
    I’ve had a look at your blog and it seems that there are 3 possible solutions: VFW, WIA and DirectShow. I’d like to take snaps from a cam using a simple and windows-less solution. I’ve had a look at WIA but fails and I do not understand why. MSDN examples are kind of bloated with lots of stupid code.
    By th way the solution should work with mingw. Any ideas?

    Thank you! Your blog is just great!

    • batchloaf says:

      Hi David,

      Thanks for your generous comment!
      Unfortunately, I never got time to come back to this program and get it working. I’ve been up to my eyes working on other things. However, I have a couple of other similar (but working) programs that may do just what you’re looking for:

      Both of those use the DirectShow API because the (much simpler) VFW API doesn’t allow selecting between cameras on some computers (it’s a long story). Anyway, unfortunately I have never succeeded in compiling a DirectShow program using MinGW, so I was forced to use cl.exe (the Microsoft compiler from Visual C++) to compile CommandCam and RobotEyez. So, if MinGW compatibility is a very high priority, you might want to look at these older VFW programs that I posted a good while ago instead:

      I think I compiled snapz and multisnapz with MinGW. Perhaps one or other of these will do the trick?


      • David says:

        Hello Ted!

        Thank you very much for your quick reply!
        I’ve managed to compile snapz with mingw, but as you said it uses an old wrapper which is basically a s**t. I tried with CommandCam but I can’t find DexterLib. Could you explain me a little more about that?
        I just did a quick look at RobotEyez but it seems more difficult to use in mingw because it lacks the streams.h and lots of includes, which basically are part of Wine😦
        So by now I think I can manage to work CommandCam if I find the way to deal with DexterLib or implement a replacement.

        Thank you very much!

      • batchloaf says:

        Hi David,

        Yes, it looks like you’re running into some of the same problems that tripped me up when I tried to compile DirectShow programs with MinGW – I became mired in mysterious dependencies (e.g. dexterlib???) that I did not understand. In the end, I just gave up and used cl.exe as the compiler instead. If you manage to compile CommandCam with MinGW, I’ll be really interested to hear how.

        As you can read in my comments at the top of the CommandCam source code, I used a couple of tricks to resolve some (possibly bogus) dependency issues relating to qedit.h, qedit.dll, strmiids.h, DexterLib, etc. It’s possible that by resolving those issues a different way, MinGW compilation would be feasible. I’ve done a search for any files with the word dexter in the name and there’s not much showing up – no DLLs at all, and only three files in the folder of an unrelated Windows SDK sample (“C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\web\xml\xmllite”). Basically, I don’t know if DexterLib is a real dependency at all, or just a relic from something I’ve included from the Base Classes in the DirectShow samples. It’s all very confusing!!


  3. David says:

    Hi again Ted,
    Long time no talk. I finally managed to get it cross compiled under Linux with mingw32.
    Are you interested in the sources? I can mail you them so you can put them in you blog.
    Thank you!

    • batchloaf says:

      Hi David,
      Sorry for the delay in replying.
      So, you compiled it under Linux, but to run in Windows? Is that right?
      If so, I’m impressed that you managed to get it working! I’ve never tried doing that, but it sounds like it would be complicated.

      • David says:

        Hi Ted,
        I did some hacking around your code. I googled a lot and found many references from other video-related projects which are cross-platform and compile under mingw. So in the end I just edited your code a little bit but added many stuff to solve the dependencies.
        The important thing is that I used dshow headers from the mingw project, which are opensource. In fact I continued the work after seeing some headers in OpenCV (
        I’ll clean up the sources so that it’s possible to compile them properly and I’ll mail them to you.

      • batchloaf says:

        Hi David,

        Sounds good. Once you have the code tidied up, perhaps the best thing would be to put the code up on github so that other people can hack away on it too? I can write a short post on it too, but I’m sure other people will be interested in seeing / forking an open-source-friendly implementation of this functionality.


  4. David says:

    Here it is, screw github hehe
    As you can see it’s a matter of GUIDS and shitty stuff. Also you need dshow.h & co headers from mingw-w64, otherwise it won’t compile. Right now works out of the box in fc17 as long as you have the mingw-w32api package. I’m trying to install the compiler and the stuff in a fc16 box. Also it’s important to have gcc 4.7, because gives some errors on 4.6 due to some extended functionality missing (__uuidof) but seems it can be emulated somehow with this patch:

    Just try to compile it if you have the time (it generates a DLL with to exports) and if you feel up to, add the uuidof emulation.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s