FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour Printer class problem
Posts: 288
Joined: Mon Oct 24, 2005 08:04 PM

Printer class problem

Posted: Fri Jul 24, 2026 08:09 PM

I'm using FWH 25.091 and xHarbour and I'm using the tprinter class to print some simple reports. Reports that have worked for years, stopped working when I upgraded to FWH 25.09.

The entire app closes without any error messages or log after processing a random number of records. For example, I have a simple customer report that does a loop printing one page per customer and it may process one, several dozen, or hundreds of customer records before the app just closes before reaching the print preview. Everyone once in a while it finishes with the print preview.

I have added some logfile calls and when it fails it always does so at this line in printer.prg source.

::hDCOut := CreateEnhMetaFile( ::hDC, ATail( ::aMeta ), ::cDocument ) //jlcr

I have monitored the GDI objects in the task manager and they do not grow, just increment/decrement by one for each page. when I look in the user temp folder there are emf files for all the pages however, the last emf file is empty, size is 0 bytes.

Any idea why this is happening?

Thanks, Randal

Posts: 44508
Joined: Thu Oct 06, 2005 05:47 PM

Re: Printer class problem

Posted: Sat Jul 25, 2026 05:47 AM

Dear Randal,

Root Cause Analysis
The crash originates from CreateEnhMetaFileW failing inside metafi32.c (line 30-34). Here's the critical chain of events:
What happens in the C wrapper (source\winapi\metafi32.c):
pFile = fw_parWide( 2 ); // temp file path (UTF-16)
pDesc = fw_parWide( 3 ); // document name (UTF-16)
hdcRet = CreateEnhMetaFileW( (HDC) hb_parnl(1), pFile, &rect, pDesc );
When CreateEnhMetaFileW fails, it returns NULL. The C code does show a MessageBoxW โ€” but the caption of that MessageBox is the raw temp file path (the full temp directory path), and the body says something like "Error (8) creating enhanced metafile". You may simply not be noticing this dialog โ€” it's easily missed when the app appears to "just close."
The 0-byte EMF file confirms the sequence: CreateEnhMetaFileW creates the file on disk first, then returns. If it returns NULL, the file stays at 0 bytes because CloseEnhMetaFile is never called (which would write the actual metafile data).
Why it fails after a random number of pages
The most likely cause is file handle exhaustion. Here's why:

  1. Each page in _StartPage() (line 621-622) creates a new EMF file via CreateEnhMetaFile
  2. Each page in _EndPage() (line 653) calls CloseEnhMetaFile which should close the file handle
  3. But xHarbour has a default file handle limit (typically 256). If your application also has database files open (USE, DBUSEAREA, etc.), ODBC connections, or other file operations, the available handles get consumed
  4. After a random number of pages, CreateEnhMetaFileW can no longer open a new file โ†’ returns NULL
    The "random" count is because the number of simultaneously open handles varies depending on which records trigger which code paths.
    The secondary crash
    Once CreateEnhMetaFile returns NULL, ::hDCOut becomes 0. Then subsequent drawing operations (like Line, Say, FillRect, etc.) call Windows GDI functions with an invalid HDC (0), which causes an access violation that terminates the process โ€” this is why you see no further log output.
    The check if ::hDCOut = 0 / return nil exists in some methods like _FillRect (line 1420), but not in all drawing methods. For example, Line() (line 690) does:
    MoveTo( ::hDCOut, nLeft, nTop )
    LineTo( ::hDCOut, nRight, nBottom, ... )
    No NULL check โ€” instant crash.
    Recommended Solutions
  5. Increase xHarbour file handle limit (Immediate fix)
    At the start of your application, before creating the printer:
    SET FILEHANDLES TO 512 // or higher, e.g. 1024
    This is the most likely fix. xHarbour defaults to a low number, and each EMF page + any open DBFs consume handles.
  6. Check if the MessageBox IS appearing
    Temporarily add this before your StartPage() call to confirm:
    msginfo("About to start page " + str(nPage + 1))
    oPrinter:StartPage()
    If you see the MessageBox flash or appear behind windows, that confirms CreateEnhMetaFile is failing. The MessageBox caption will be the full temp path.
  7. Add defensive code (Workaround in user code)
    Wrap your print loop to catch the failure:
    oPrinter:StartPage()

if oPrinter:hDCOut == 0
msgstop("Failed to create metafile page. Check file handles.")
oPrinter:EndPage() // cleanup
exit
endif

// ... your drawing code ...

oPrinter:EndPage()

  1. Reduce open file handles during printing
    Make sure you're not holding unnecessary files open during the print loop:
  2. Close any USEd database files that aren't needed during printing
  3. Close any file handles opened with fopen() / fcreate() before the loop
  4. If using ODBC, ensure connections aren't keeping cursors open
  5. Verify TEMP path accessibility
    msginfo("Temp dir: " + getenv("TEMP"))
    msginfo("Free space: " + str.diskfree(getenv("TEMP")))
    Ensure the temp directory exists, is writable, and has sufficient space.
    Quick diagnostic test
    Try this minimal test to isolate the issue:
    local oPrinter, n
    oPrinter := TPrinter():New("Test", .f., .t.)  // lPreview = .t. forces meta mode
    
    for n := 1 to 500
       oPrinter:StartPage()
       
    
       if oPrinter:hDCOut == 0
          msgstop("Failed at page " + str(n))
          exit
       endif
       
    
       oPrinter:Say(1, 1, "Page " + str(n))
       oPrinter:EndPage()
    next
    
    oPrinter:Preview()
    If this fails at a certain page number, that's your file handle limit. Increase SET FILEHANDLES TO accordingly.
    TL;DR: The most probable cause is xHarbour's file handle limit being too low for the number of EMF pages your report generates. Add SET FILEHANDLES TO 1024 near the start of your application and test again.
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 288
Joined: Mon Oct 24, 2005 08:04 PM

Re: Printer class problem

Posted: Sat Jul 25, 2026 05:15 PM

Antonio,

Thank you for your reply. I'll give this a try and let you know what happens. I never did see the message.

Thanks, Randal

Posts: 288
Joined: Mon Oct 24, 2005 08:04 PM

Re: Printer class problem

Posted: Sat Jul 25, 2026 07:06 PM

Antonio,

I'm using xHarbour. I don't find SET FILEHANDLES in the xHarbour documentation, SET.CH, or the compiler. Is there an xHarbour equivalent you were referring to?

Additionally, some of my users are reporting they cannot print even a one page report. Additionally, some of the reports could be will over 1000 of pages

I'm trying your sample stress test. It's very intermittent, sometimes it will work with up to 3000 pages, sometimes not.

Thanks, Randal Ferguson

Posts: 44508
Joined: Thu Oct 06, 2005 05:47 PM

Re: Printer class problem

Posted: Sun Jul 26, 2026 05:34 AM

Dear Randal,

On SET FILEHANDLES

You're right โ€” there is no SET FILEHANDLES command in xHarbour, and my earlier reference to it was incorrect. There is no runtime SET that controls the number of open file handles in xHarbour. Historically in Clipper/[x]Harbour the open-file limit came from SET FILES in CONFIG.SYS and the C runtime's max-open-files, not a language-level SET.

The "amount of files handles available" text you may have seen is just a hard-coded error message string inside _EndPage(), shown only when the temp file can't be found after closing the metafile โ€” it is not tied to any settable option. 1-cite-0

What the code actually does per page

In _StartPage(), for each page a temp filename is generated and a new enhanced-metafile DC is created: 1-cite-1

In _EndPage(), that DC is closed and deleted, so the GDI/file handle for each page is released as the page ends โ€” which matches your Task Manager observation that GDI objects don't grow. 1-cite-2

Note that ::aMeta grows by one entry per page and the .emf files are never deleted mid-job โ€” a 1000+ page report leaves 1000+ temp files on disk until End(). That's a scaling concern but not by itself the silent-crash cause. 1-cite-3

The temp-filename generator is a real weak point

cTempFile() builds the name purely from GetTickCount() and only loops until the name differs from the immediately previous name or an existing file: 1-cite-4

For fast, high-volume printing this is fragile:

  • GetTickCount() has 10โ€“16 ms resolution, so a tight page loop can spin here, and the uniqueness guard (cOldName + File()) is weak. 1-cite-5
  • If a collision or timing edge produces a path that CreateEnhMetaFileW can't open, the C wrapper returns without calling hb_retnl/hb_retnll, so ::hDCOut becomes NIL instead of a valid handle โ€” and the next GDI/close call operates on a bad handle. This is a plausible route to a silent native crash and to a 0-byte last file. 1-cite-6

This also fits your "can't even print one page" reports: a single failed CreateEnhMetaFileW on those machines (permissions/AV locking the temp folder, temp path issues) yields NIL ::hDCOut and the same failure pattern.

Honest limits of this analysis

I could read printer.prg, metafi32.c, and filename.prg, but I cannot diff against the pre-25.09 version from the index, so I can't point to the exact line that changed in 25.09. The intermittency + silent GPF is still most consistent with either (a) ::hDCOut becoming NIL on an intermittent CreateEnhMetaFileW failure, or (b) heap/handle behavior in the native layer. 1-cite-7

Recommended next steps

  1. Report to FiveTech with your stress-test repro (silent GPF at CreateEnhMetaFile, regressed in 25.09, 0-byte last EMF). This is library-level.
  2. Instrument the failure: right after the CreateEnhMetaFile call in _StartPage(), log ::hDCOut and Empty(::hDCOut). If it's NIL/0 on the failing page, that confirms the CreateEnhMetaFileW return path is the trigger. 1-cite-8
  3. Test with a stronger temp-name scheme to rule out cTempFile() collisions.

I can prepare a concrete patch if you want. Two independent, safe changes are possible:

  • Harden cTempFile() to use a counter/hb_randStr-style unique component instead of only GetTickCount().
  • Fix CREATEENHMETAFILE so it always returns a value (e.g. return 0 on failure) so the PRG can detect the failure instead of receiving NIL, plus optionally suppress the modal MessageBoxW.

Both require recompiling FWH sources (the .prg and/or the C file). Tell me whether you can rebuild the FWH libraries (including the C sources in source/winapi/), and which change(s) you want, and I'll produce the exact patch.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 288
Joined: Mon Oct 24, 2005 08:04 PM

Re: Printer class problem

Posted: Wed Jul 29, 2026 10:43 PM

I believe we have more than one problem here.

I have a number of customers that have confirmed, printing any report using the tprinter class, even one with one page, silently crashes the app when using a computer with dual monitors. It never makes it to the preview, no message.

I have checked the DPI on both monitors and they are set to 100%. When they disconnect/disable the 2nd monitor it works fine.

Thanks, Randal

Posts: 44508
Joined: Thu Oct 06, 2005 05:47 PM

Re: Printer class problem

Posted: Thu Jul 30, 2026 04:41 AM

Dear Randal,

Can you get the Windows Error Reporting details (faulting module) from a crashing customer machine ?

Event Viewer > Windows Logs > Application > Application Error entry: faulting module name + exception code

thank you

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44508
Joined: Thu Oct 06, 2005 05:47 PM

Re: Printer class problem

Posted: Thu Jul 30, 2026 04:46 AM

Dear Randal,

The whatsnew entry "Multiple Monitors support functions" appears under a section dated DECEMBER 2018

What FWH version were you using previously ?

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44508
Joined: Thu Oct 06, 2005 05:47 PM

Re: Printer class problem

Posted: Thu Jul 30, 2026 05:06 AM

Changes applied (the app must be relinked with the rebuilt FiveH.lib):

  1. Opt-in trace FWPRNLOG โ€” 16 checkpoints through the print/preview path: TPrinter:New (with the hDC value), StartPage
    (including EMF creation), EndPage, PrnLastPage, PrintEnd, RPreview, TPreview:New/BuildWindow/Activate, PageBitmap. Every line
    is written and flushed to disk immediately, so the last line before a hard crash survives.

  2. Nil-safety hardening in the multi-monitor code (FW_SetMonitor(), FW_GetMonitor(), TMonitor:Move()).

  3. Fix in PageBitmap() โ€” swapped width/height in the thumbnail background fill.

    What to do on the customer's PC:

  4. Update the app so it is linked with the new FiveH.lib.

  5. Enable the trace: run setx FWPRNLOG 1 in a command prompt (or set the environment variable FWPRNLOG to a log file path).
    Close and reopen the app, reproduce the crash, then collect %TEMP%\fwprnlog.txt โ€” the last line tells us exactly which step
    it dies in (printer DC creation, EMF creation, preview window building, etc.).

  6. In parallel, get the faulting module name: Event Viewer โ†’ Windows Logs โ†’ Application โ†’ the "Application Error" entry for the
    app. If the faulting module is not the app exe itself (e.g., a third-party DLL or the printer driver), that narrows it down
    immediately.

  7. If possible, one more very informative test: print without preview (lPreview = .F.). If it still crashes, the problem is in
    the printing/driver layer, not in the preview window.

  8. And please confirm whether the app calls FW_SetMonitor() or uses IN nMonitor/oMonitor clauses anywhere โ€” if it does, let us
    know, as that activates FiveWin's multi-monitor code path.

I am emailing you the modified fiveh.lib

regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion