News

FoxPro Document Display

Building a high-speed document viewer for legacy FoxPro applications without asking the host application to modernize first.

FoxPro Document Display

FoxPro Document Display

Building a high-speed document viewer for legacy FoxPro applications without asking the host application to modernize first.

Platform
Visual FoxPro / ActiveX
Stack
C++, ATL, COM, GDI, WIC, PDFium
Goal
Fast in-form viewing of JPG, PNG and PDF files

Project Overview

This project solves a common legacy-software problem: users need modern document viewing inside an application that was never designed for it. In this case the host environment is Visual FoxPro, which means the solution had to behave like a classic ActiveX control, expose a conservative COM interface, remain strictly 32-bit, and avoid any instability that could bring down the host process.

The result is FoxDocViewer, a native C++ document viewer that can be embedded directly in a FoxPro form. It supports PDFs through PDFium, image formats through Windows Imaging Component, and wraps the whole pipeline in an interface that FoxPro can call reliably using familiar types such as BSTR, LONG, DOUBLE and VARIANT.

How The Implementation Works

The architecture is intentionally split into focused layers so the COM-facing shell stays small and the rendering logic stays extensible.

1. A FoxPro-friendly COM shell

The ActiveX control is built with ATL and exposes a dual interface for late binding and vtable access. In practice that means FoxPro can drive the viewer with simple calls such as loading a document list, moving between pages, changing zoom levels, and reading state such as page count or the active document path.

One subtle part of the implementation is the path ingestion layer. FoxPro can emit arrays in shapes that are not always intuitive, including one-dimensional and two-dimensional variants. The control therefore normalizes incoming SAFEARRAY values, dereferences by-ref variants, coerces values to strings, and safely ignores unusable entries instead of failing hard.

2. A controller that owns navigation, zoom and state

Behind the ActiveX layer sits a dedicated viewer controller. This class knows nothing about Win32 painting or COM, which keeps the logic clean. It owns the current document, current page, zoom mode, zoom percentage, and cache lifecycle. The control forwards commands into this controller and reads prepared frame data back out of it.

That separation matters because it turns a historically messy kind of component into a predictable state machine. Switching documents resets the active page, fit modes recalculate zoom from the client area, and error state is maintained in one place instead of leaking through multiple layers.

3. Format adapters for PDF and images

The viewer uses separate adapters for different file types. JPG and PNG decoding are handled through Windows Imaging Component, which converts images into a normalized 32-bit BGRA format and scales them with WIC's higher-quality interpolation. PDFs are handled through PDFium, loaded dynamically at runtime rather than statically linked into the control.

That dynamic loading strategy is important for deployment. If pdfium.dll is missing, the control still works for images and reports a specific PDF-engine error when a PDF is requested. The host stays alive, and deployment mistakes surface as recoverable runtime feedback instead of a broken registration or startup failure.

4. Fast rendering through caching and preloading

Rendering performance was a core requirement. The implementation uses a bounded least-recently-used page cache keyed by document, page, and zoom bucket. Zoom values are intentionally coarsened into buckets so the cache does not thrash on every tiny zoom change. This is one of those details that has an outsized effect on perceived speed.

The control also arms a lightweight timer that pre-renders adjacent pages when the UI is idle. That means the next or previous page is often already warm in memory by the time the user navigates, which makes page turning feel immediate even though decoding and rasterization are happening in native code behind the scenes.

5. Flicker-free drawing inside a legacy host

The final stage is the render engine, which uses an off-screen back buffer and GDI blitting to paint into the control window without flicker. Scrollbars are updated from the logical canvas size, pages are centered when smaller than the viewport, and the back buffer only grows when necessary to keep resize churn cheap.

This may sound low-level, but it is exactly where user trust is won or lost in desktop software. A viewer that tears, flashes, repaints badly, or stalls on resize immediately feels fragile. This implementation avoids that by treating paint operations as a controlled pipeline rather than a chain of ad hoc Win32 calls.

Key Challenges

Bridging modern rendering with a legacy application model

The host application expects classic COM behavior, not a modern embedded browser or managed runtime. That ruled out many easy solutions and forced the implementation to fit inside the constraints of ActiveX, 32-bit hosting, and the message-driven lifecycle of a traditional Windows control.

Making FoxPro data structures predictable

FoxPro array behavior can be surprisingly awkward when values cross the COM boundary. The implementation had to accept several VARIANT and SAFEARRAY shapes, and the sample integration shows that this was not theoretical. Real-world interoperability work meant accounting for how FoxPro actually emits arrays, not how a pure COM specification says it should.

Balancing speed with memory use

High-resolution PDFs and images can consume memory quickly once rasterized. The page cache therefore uses a bounded capacity with eviction, while zoom bucketing prevents a flood of near-identical cached bitmaps. The goal was not maximum caching at any cost, but stable responsiveness under realistic desktop workloads.

Failing safely inside the host process

A hard crash in the viewer would be a hard crash in FoxPro itself. Because of that, the control was designed to report errors through state and events, keep old content visible if a new document fails to load, and release system resources deterministically. Stability was not a nice-to-have feature; it was part of the product definition.

Outcome

FoxDocViewer gives a legacy business application a modern document-viewing capability without rewriting the host platform. Users can open PDFs and images inside the existing workflow, navigate pages quickly, zoom smoothly, and move between documents with minimal latency.

From an engineering perspective, the project is a good example of pragmatic modernization: keep the interface conservative, isolate format-specific logic, optimize the rendering path where it matters, and treat interoperability and failure handling as first-class design concerns rather than afterthoughts.

Highlights
Native ActiveX control for Visual FoxPro. Dynamic PDFium integration. WIC-backed image decoding. Double-buffered GDI rendering. Error-first COM design that protects the host process.
Back to news
Share:

More

Related Articles

Want to work with us?

Get in touch and let's discuss your project.