System designHow-to

Display a FireWire camera image in a C++ viewer, not a VB control

Why CWIMAQViewer and imaq1394SnapCW stay VB-only, and how to display a FireWire camera image in C++: snap into a plain buffer and draw it with Win32.

LumaSpec Vision10 min read Share on LinkedIn

On this page 10 sections
  1. 01Why imaq1394SnapCW has no C++ binding
  2. 02What you need
  3. 03Open a session and snap into a plain image
  4. 04Draw the buffer yourself with StretchDIBits
  5. 05Worked example: buffer size and link load for a 640 × 480 mono8 camera at 30 fps
  6. 06Make it live: grab, buffers and the paint timer
  7. 07Verify the display, layer by layer
  8. 08Common mistakes
  9. 09When to stop patching and migrate to a current camera
  10. 10Questions

The two Visual Basic lines that snap a FireWire camera image and attach it to a viewer control have no C++ equivalent, and they never will. The viewer control and its VB-only snap call ship for the Visual Basic environment alone, which is why a Visual C++ project cannot find the function. This guide is for engineers keeping an IEEE 1394 camera alive in C++: it shows the session-and-snap calls to make, the Win32 draw code that puts pixels on screen, a worked example that sizes the buffer and the link load, and the tests that prove each layer before you trust it.

Why imaq1394SnapCW has no C++ binding

In Visual Basic, CWIMAQImage, imaq1394SnapCW and CWIMAQViewer come as one package: a control library built for that environment. The C environments were never given a binding for it, so the missing function in VC++ is not a link-path problem you can fix with a pragma. No header will ever declare imaq1394SnapCW, because the C import library does not export it.

The supported C path ships with the software: the C documentation and the text-based C examples install alongside the VB ones. If your installation shows only VB examples, the C support option was not selected. The vendor's knowledge base covers how to rerun the installer and add the missing C, C++ and Visual Basic examples; the installer's component list is the place to check.

So the decision is fixed before you write code: open a session, snap into a plain image, and draw the pixels in a window you own. Each VB line maps to a C++ counterpart:

Visual Basic line What it does C++ counterpart
CWIMAQImage object image owned by the control library plain image type created through the C API
imaq1394SnapCW(sid, image) one frame into the control's image the plain C snap from the installed examples, same session id
CWIMAQViewer.Attach the control draws the image StretchDIBits in your own paint handler
IMAQ1394ConfigureTrigger arms the hardware trigger the same call, documented for CVI, Visual Basic and C

What you need

  • The 1394 camera, its 1394 host card seated and its acquisition driver installed, with the camera visible in the vendor's configuration utility.
  • The vision software installed with the C documentation and text-based examples selected, per the note above.
  • A C++ project; a plain Win32 dialog is enough, because the draw code needs only a device context.
  • A client window or picture control you own, and roughly 300 KiB of buffer memory for a VGA mono8 frame, sized in the worked example below.

Run one shipped C example end to end before writing your own code. If it snaps, the camera, card and driver layers are proven, and everything left is yours.

Open a session and snap into a plain image

The same snap that Visual Basic performs becomes four steps in C++:

  1. Add the include and library paths from a shipped C example project, copied rather than retyped.
  2. Open a session for the 1394 camera; the call returns the session id the VB sample passed to its snap.
  3. Create the plain image type the C examples use. It is a buffer with a header, not a CWIMAQImage.
  4. Call the C snap with that session id and image. It performs the operation imaq1394SnapCW performed in Visual Basic and fills your buffer directly.
// Acquisition: use the exact signatures in the installed C examples and
// headers. The library that exports IMAQ1394ConfigureTrigger exports the
// plain snap alongside it. Variable names below are illustrative.
int sid = 0;                      // session id, filled by the C open call
unsigned char* frame = NULL;      // top-down mono8 pixels, width x height
int width  = 640;                 // confirm against the session's image info
int height = 480;
frame = new unsigned char[width * height];  // 307,200 bytes in this example

// Open the session for the 1394 camera into sid, create the plain image
// type the C examples show, call the C snap on that session and image,
// then copy the image's pixels into frame before drawing it.

For hardware triggering, IMAQ1394ConfigureTrigger sends the isochronous trigger command to the camera; the vendor's FireWire acquisition documentation covers it for CVI, Visual Basic and C alike.

Draw the buffer yourself with StretchDIBits

A buffer becomes a displayed image with one documented Win32 call. Fill a BITMAPINFOHEADER for the format you snapped, then blit it in the paint handler of the window you own. This is the owner-draw pattern from drawing detections in a WinForms PictureBox yourself, one layer lower. The StretchDIBits reference defines every field used here.

static BITMAPINFOHEADER bh;       // statics start zeroed; fill once per format
bh.biSize        = sizeof(BITMAPINFOHEADER);
bh.biWidth       = 640;           // pixels, from the session's image info
bh.biHeight      = -480;          // negative: rows arrive top-down
bh.biPlanes      = 1;
bh.biBitCount    = 8;             // mono8, one byte per pixel
bh.biCompression = BI_RGB;        // uncompressed

// In your WM_PAINT handler, with hdc from BeginPaint:
StretchDIBits(hdc, 0, 0, 640, 480,          // destination rectangle
              0, 0, 640, 480,               // source rectangle
              frame, (BITMAPINFO*)&bh, DIB_RGB_COLORS, SRCCOPY);

Stride is the classic trap. GDI pads each row to a 4-byte boundary, and a mono8 row is width bytes, so 640-byte rows need no padding because 640 = 4 × 160. A 641-pixel camera would need each row padded to 644 bytes, or a row-by-row copy into a padded buffer.

If you unpack 10- or 12-bit camera data for a higher-fidelity copy, the display path still wants 8 bits per channel; the 16-bit buffer that reports half the expected pixels is the same family of pixel-layout trap.

Cameras of this generation typically shipped as VGA mono8 at 30 frames per second, so take that as the case: width 640 px, height 480 px, 1 byte per pixel, 30 fps. Substitute your camera's numbers; the arithmetic is identical.

The rules in words: one frame's buffer is width times height times bytes per pixel. The link load is frame bytes times frame rate times 8 bits per byte, divided by the link's rate.

frame_bytes = width_px × height_px × bytes_per_pixel link_mbit_s = frame_bytes × fps × 8 / 1,000,000 load_pct = link_mbit_s / link_rate_mbit_s × 100

The arithmetic, step by step:

  • frame_bytes = 640 × 480 × 1 = 307,200 B, exactly 300 KiB; a 16-bit copy doubles it to 614,400 B.
  • Stream rate = 307,200 B × 30 fps = 9,216,000 B/s; × 8 = 73,728,000 bit/s, about 73.7 Mbit/s.
  • FireWire 400 link (IEEE 1394a, 400 Mbit/s): 73.7 / 400 × 100 = 18.4% of the link.
  • One Gigabit Ethernet port (1,000 Mbit/s line rate): 73.7 / 1,000 × 100 = 7.4% of the port.

Decision: a 73.7 Mbit/s stream sat far inside FireWire 400 and lands at 7.4% of one GigE port. If you budget steady load at no more than half a port's 1,000 Mbit/s line rate, six such VGA mono8 streams (6 × 73.7 = 442 Mbit/s) still fit under the 500 Mbit/s ceiling. Bandwidth is never why this display drops frames; buffer handling is, and the next section closes it.

Make it live: grab, buffers and the paint timer

One snap proves the pipeline; a live display needs continuous grab. Put the library's grab mode over a small ring of buffers and draw the newest completed buffer in the paint path. The ring-and-newest shape is the same one the LabVIEW camera SDK walkthrough wires up for a different library.

Two failure modes to design out:

Pace the loop from the frame period: at 30 fps a new frame arrives every 33 ms, while the 0.3 MB blit runs in well under a millisecond on a current PC. Acquisition and buffer hand-off set the pace, never the draw. Snap and grab divide the same way on other interfaces, as the snap-versus-grab black-image case shows.

Verify the display, layer by layer

Prove each layer separately, so a failure points at one place:

  1. Camera and driver: run a shipped C example end to end. If it snaps, the layers below your code are good.
  2. Buffer contents: write the 307,200-byte mono8 buffer to a headerless file and open it as raw 640 × 480 8-bit gray. Diagonal shearing means the stride or the biHeight sign is wrong; black means the snap never filled the buffer.
  3. Draw path: wrap the StretchDIBits call with QueryPerformanceCounter and log the elapsed time. A 0.3 MB blit should read well under 1 ms; a paint that takes the 33 ms frame period means the draw drops frames, not the camera.
  4. Live check: wave a hand in front of the lens. Smooth, current motion means the ring is handing over fresh buffers; stalls mean stale queued frames, so revisit the buffer handling above.

Common mistakes

  • Retyping the VB call and hoping a C header declares it: the export does not exist in the C library, so no path or pragma fixes it.
  • Drawing 10- or 12-bit data as if it were mono8, which gives a banded or washed-out image; unpack and scale first.
  • Ignoring the 4-byte stride on widths that are not multiples of 4.
  • Taking argument order from a VB sample; the C header and its example project are the only source.
  • Spending days on a 1394 driver a current OS refuses to load. That is a migration signal, not a debugging problem.

More display-path and buffer-handling guides sit on the systems topic page.

When to stop patching and migrate to a current camera

The 1394 path has a hard ceiling: host cards are legacy parts, their drivers targeted older Windows builds, and a current OS can refuse the stack outright. When the driver, not your code, is the blocker, apply the same staged move as the legacy frame grabber on a new OS migration: swap the acquisition hardware, keep the architecture. The replacement search starts at area scan cameras, which the camera finder filters by interface, shutter and resolution, with capture cards under frame grabbers and their accessories finder.

The camera side first: the MV-CU020-90GC is a compact global-shutter GigE camera that covers a VGA-class field with margin.

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
Interface
GigE
Resolution
Up to 3 MP
$309.00View partBuild a system with it

The card side is the same move that once meant a 1394 PCI card: one PoE port per camera. The LS-FGB-0004 puts four PoE Gigabit ports on a PCIe x4 card.

Frame grabbersPCIe x4 4-port PoE Gigabit Ethernet card, Intel i210AT (LS-FGB-0004)LS-FGB-0004
Interface
GigE
$359.00View partBuild a system with it

If the PC has spare USB3 ports instead of free slots, the LS-CMR-0166 needs no capture card at all: 12 MP at 30 fps over USB3.

Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
Resolution
12 MP
Shutter
Rolling
$560.00View partBuild a system with it

Both routes end in the same place: a session, a buffer, and your own draw call, now on drivers that install. To pick the camera and card together, start at the camera configurator, or ask the applications team with your camera model and the images it must produce.

Questions

Why does my VC++ build report imaq1394SnapCW as an undeclared identifier?

The function is exported only for the Visual Basic control library, so no C header can declare it and no import library resolves it. Add the include and library paths used by the installed C example projects, and call the plain C snap from those examples instead. If the paths are right but the C examples are missing, the vision software was installed without its C support option, and rerunning the installer to add it fixes that.

How do I set a hardware trigger on a 1394 camera from C++?

Call IMAQ1394ConfigureTrigger after the session opens. The vendor's FireWire acquisition guide documents this function for CVI, Visual Basic and C alike: it sends the isochronous command that arms the camera's trigger input. Wire your sensor or encoder pulse to that input, call the function once, and every later snap or grab then waits for the pulse.

Will a FireWire card still work in a current PC?

The card often fits a PCIe slot, but the driver decides: legacy 1394 acquisition drivers targeted older Windows builds, and a current OS can refuse to load them. If the vendor's driver installs and the camera snaps, your C++ display code works unchanged. If it does not, treat it as a migration signal and move the acquisition to a current camera rather than patching the driver stack.

Which current camera replaces a VGA FireWire camera one for one?

Match the measurement, not the interface: pick resolution for the smallest feature you must resolve, then confirm frame rate and lens mount. A compact 2.5 MP GigE camera with a 1/1.7 inch global-shutter sensor covers a VGA field with margin, and one PoE port per camera removes the bandwidth question entirely, as the 73.7 Mbit/s worked example above shows.

Does snap or grab suit a live display better?

Snap takes one frame on demand and returns, which suits the first proof and single-shot captures. Grab streams continuously into buffers and is the right mode for a live display, provided you always draw the newest completed buffer. Grabbing into a UI queue that accumulates faster than the paint loop drains it is what makes a belt-mounted display show stale positions.