The snap call worked on the first afternoon in Visual Basic: add the vendor's ActiveX control, create its image object, snap, attach the image to the viewer control. Move the same logic into a C++ project and the build stops at the snap function — identifier not found. Nothing is broken and nothing is missing from the DLL. The Basic project carried a declaration that your C++ project does not have yet. You will leave able to declare the call correctly, pass the right handle, verify the snap with a four-step test, and size the grab buffer with real arithmetic.
Why the snap call exists in VB but not in C++
In the Basic project you added the vendor's ActiveX control. A type library is a machine-readable declaration set: it declares every DLL function the control wraps, so the Basic compiler resolves the snap call without help, and the control's factory method builds an image object its viewer can attach and paint. C++ sees none of that. It resolves only the symbols you declare or link, and nobody declared the snap function.
The function itself exists. DLLs export plain C functions that take handles and return a status integer, callable from any language. Two routes make it visible to C++, and both use only material from the SDK install: compile against the vendor's C header and import library, or load the DLL at run time and resolve the address yourself. The same declaration gap is what you bridge when calling a camera SDK DLL from LabVIEW — different client, identical fix.
What you need
- The SDK's C header (.h), its import library (.lib) and the DLL itself, from the SDK install
- The exact exported snap name, its signature and the calling-convention macro, from that header
- The return-code constants defined in the same header
- A camera the SDK can open, with its driver installed and the link up
- Your toolchain's export-listing tool, for the run-time route
All of it comes from the SDK's own install or its printed documentation. If the header is missing from the machine that builds — the usual case after a move to a containerized build — fix that first; the SDK header not found in a containerized build walkthrough covers the include-path side.
Step 1: read the signature from the C header
Open the header and write down four things exactly as printed: the exported function name, the calling-convention macro on the declaration, every parameter type in order, and the return type. A camera snap entry point is almost always shaped handle-in, image-handle-in, status-out — but the spelling belongs to the vendor. The ActiveX control's method list is not the source: it shows objects and methods, while the DLL exports plain functions that take handles.
Without a header, list the exports directly: dumpbin /exports CameraSdk.dll prints every exported symbol with its exact decoration. Copy spellings character for character.
Step 2: declare the import — header plus library, or LoadLibrary
Route A, implicit linking: add the SDK's include directory to the project, include the header, and add the import library to the linker inputs. The loader binds the snap function when the process starts. Use this route whenever the .lib is on the machine — the linker then catches signature drift at build time instead of at run time.
Route B, run-time linking: declare a function-pointer type, load the DLL and resolve the address:
// Illustrative names only - use the exact spellings printed in your SDK's C header.
typedef int (__stdcall *SnapFn)(int hCamera, void* hImage);
int hCam = 0; // filled by the SDK's open-camera function
void* hImg = nullptr; // filled by the SDK's create-image function
HMODULE hSdk = LoadLibraryW(L"CameraSdk.dll"); // documented Windows API
SnapFn Snap = (SnapFn)GetProcAddress(hSdk, "CameraSdkSnap");
int rc = Snap(hCam, hImg); // status per the header's constants
HMODULE, LoadLibraryW and GetProcAddress are documented Windows API names; the camera-side spellings above are placeholders, declared with their types. GetProcAddress needs the exported name exactly as the export table stores it — one changed character returns null. The Windows API's run-time dynamic linking reference documents both calls and the DLL search order. Prefer Route A; keep Route B for the cases where the DLL name or version is chosen at run time.
Step 3: match the calling convention
Copy the convention macro from the header declaration verbatim — into your own prototype, and into the function-pointer typedef if you took Route B. On Windows, C-exported SDK functions are most often __stdcall, with __cdecl the second case. The header decides, not habit.
The failure mode explains why this is never a guess: caller and callee pop different stack byte counts, the call can return correct data, and the corruption surfaces as a crash a few statements later.
Decoration follows the same rule as spelling: the export table is the truth, so resolve whatever it actually lists.
Step 4: pass the SDK's image handle
The Basic wrapper accepted its own image object because the wrapper's code converts that object to the SDK's internal handle before the DLL is entered. C++ has no wrapper doing that conversion, so you deal in handles only. Call the SDK's create-image function, keep the handle it returns, and pass that handle to the snap. Your own allocation, a pointer into a private buffer, or a UI object's image member is none of these — the call fails at best and writes through a bad pointer at worst.
The camera handle obeys the same rule: it comes from the SDK's open call. After the snap, the SDK's accessor functions hand you the pixel pointer, width, height and stride for that handle. Only then do you read pixels.
Step 5: show the frame
Attach-and-paint was the wrapper's UI plumbing, and from C++ that step is yours. It is a copy: take the buffer pointer and stride from the SDK handle, build a top-down device-independent bitmap sized to the image, copy row by row — stride is not always width times bytes per pixel — and invalidate the window that displays it. Keep the copy on the thread that owns the window.
The cost is trivial. A 1600 × 1200 8-bit frame is 1.9 MB, and memcpy on a current x86 core moves several GB/s, so the display copy takes a fraction of a millisecond. Many SDKs also ship their own display-window function; if yours does, call it and skip the bitmap work entirely.
Verify the snap before you wire it in
Run this sequence before any display code:
- Snap with the lens capped. The status equals the header's success constant and the buffer statistics sit near zero.
- Uncap and aim at a lamp. The mean pixel value rises far above the capped reading.
- Read width and height back from the SDK handle and compare with the sensor resolution in the camera's specification — both must match exactly.
- Snap twice and compare buffer content — the second snap must reflect the new scene, proving the SDK re-read the sensor rather than handing back the first frame.
A null address in step 1 means the exported name is wrong. A failure inside the call means the camera handle is wrong or the camera never opened. Set exposure through the SDK's own feature-set call before the first snap, so the test image is not black.
Size the buffer and the link before continuous grab
Snap allocates through the SDK; a grab loop allocates on your side. Do the arithmetic first — illustrative sensor, 1600 × 1200 monochrome 8-bit at 30 fps:
A GigE link carries 1 Gbit/s = 125 MB/s at line rate, and a real camera stream lands near 100 MB/s once protocol overhead is paid. One stream at 57.6 MB/s takes about 58 % of that practical budget — it fits with margin. Two streams need 115.2 MB/s, already over; four need 230.4 MB/s. The decision at this frame size and rate: one camera per copper GigE link, and for two or more cameras either add NICs or step the link class up — the 5GBASE-T camera interface guide covers what the faster links buy and their cable limits.
Keep two or three buffers in flight per stream: 3 × 1.9 MB ≈ 5.7 MB per camera. Reuse them across frames instead of allocating per snap; the image buffer reuse walkthrough shows the pattern and what per-frame allocation costs.
Common mistakes
- Declaring the ActiveX control's method names as exports — the DLL exports the C function, not the wrapper's method.
- Typing __cdecl where the header says __stdcall, or leaving the macro out of the function-pointer typedef.
- Handing the SDK a raw buffer where its own image handle is expected.
- Allocating a fresh image every frame and then chasing the growth — the managed-language counterpart is preventing memory leaks when calling vision libraries from C#.
- Blaming the camera when a build error is a header collision — the abs include-order fix is the same two-minute diagnosis.
Hardware this pattern runs on
Any camera whose vendor ships a C-callable SDK with header and import library — the standard case for area scan cameras on GigE and USB3, which the camera finder filters by interface, shutter and resolution. The host needs a NIC the driver trusts and spare cores for the display copy; a vision controller bundles those with camera-oriented LAN ports and light outputs.
The MV-CU020-90GC is a 2.5 MP GigE area scan camera with a global shutter, a 1/1.7" sensor and a C-mount.
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
- Interface
- GigE
- Resolution
- Up to 3 MP
On the host side, the MV-VC2140-128G40-1T VC2100 vision controller pairs an Intel N-series processor with 8 GB of memory, 3-4 LAN ports, 2 USB 3 ports and 4 built-in light channels.
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
If the host is a general-purpose box instead — one of the industrial mini PCs common beside automation lines — the same declaration pattern applies unchanged.
When to skip the direct DLL call
Three cases, three different tools. If one acquisition codebase must serve C++, C# and a scripting layer, a GenICam library with bindings per language removes the per-language declarations; the cross-language GenICam library approach shows that structure. If frames arrive through a frame grabber, the SDK is the board's rather than the camera's, and the frame grabber SDK support guide explains why one viewer works while another fails on the same board. And if the goal is only live exposure and white-balance tweaks from your own process, runtime camera parameter control covers that narrower job end to end.
The next step is hardware, not code: pick the camera and host pair, then write the declaration once. Start with the camera configurator, browse the systems guides for the rest of the integration, or contact us for a second opinion on the design.
Questions
Why does the snap call compile in Visual Basic but not in C++?
The Basic project referenced the vendor's ActiveX control, and a type library is a machine-readable declaration set: every DLL function the control wraps is already declared, so the Basic compiler resolves the snap call on its own. A C++ compiler resolves only declared symbols. Add the declaration yourself — include the SDK's C header and link its import library, or resolve the address at run time with LoadLibrary and GetProcAddress.
How do I find the exact exported name to pass to GetProcAddress?
Read it from the DLL's export table, not from a tutorial: dumpbin /exports from your toolchain prints every exported symbol exactly as stored, decoration included. Copy the spelling character for character. If the header wraps the declaration in an extern C block the export is usually the plain function name; a mangled export hints the vendor did not declare it extern C.
Can I hand the C function the viewer control's image object?
No. The Basic wrapper accepted its image object because the wrapper converts that object to the SDK's internal handle before entering the DLL; C++ has no such conversion layer. Call the SDK's create-image function, keep the returned handle, and pass only handles the SDK issued. When an image is finished, release it with the SDK's own destroy function rather than deleting it yourself.
Which calling convention should a hand-written snap declaration use?
The one printed on the declaration in the SDK's header — on Windows most C-exported SDK functions are __stdcall, but some vendors ship __cdecl, so the header decides. Copy the convention macro verbatim into your function-pointer typedef too. Basic clients effectively assume stdcall, which is why wrapper-built projects never hit the issue; a mismatched C++ declaration corrupts the stack and crashes later, not at the call.
How much memory does one snapped frame need?
Width times height times bytes per pixel, plus row padding. A 1600 × 1200 8-bit monochrome frame needs 1.92 MB; the same sensor at 16 bits per pixel needs 3.84 MB. Query the stride from the SDK handle instead of assuming width times bytes per pixel: many drivers pad rows to 4-, 16- or 32-byte boundaries depending on the transfer format, so the real buffer can exceed the pixel total.
Does the same pattern apply to GigE Vision and USB3 Vision cameras?
Yes. Vendor SDKs for GigE Vision and USB3 Vision cameras ship as C-callable DLLs with headers and import libraries, so both declaration routes work unchanged; the transport below the SDK differs but the snap, the image handle and the status codes look the same from C++. Read the return-code constants from the header before interpreting a status value — success is not universally zero across SDKs.

