PRINTDLG pdlg;
HDC printerdc;
DOCINFO di;
memset ((void *) &pdlg, 0, sizeof (pdlg));
pdlg.lStructSize = sizeof (pdlg);
pdlg.Flags = PD_RETURNDC
| PD_NOPAGENUMS | PD_NOSELECTION | PD_USEDEVMODECOPIESANDCOLLATE;
if (!PrintDlg (&pdlg)) {
... error routine ...
}
printerdc = pdlg.hDC;
if (printerdc == NULL) {
... error routine ...
}
... set up printer abort procedure ...
memset ((void *) &di, 0, sizeof (di));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = "myname";
di.lpszOutput = NULL;
if (StartDoc (printerdc, &di) <= 0) {
... error routine ...
}
This is as far as the code gets; StartDoc() never returns. A message
box comes up stating that the program referenced memory at location
xxx and that said memory could not be read. Sometimes a much more
interesting message box appears:
"The image file C:\Windows\system32\spool\DRIVERS\x64\3\UNIDRVUI.DLL
is valid, but is for a machine type other than the current machine.
Select OK to continue, or CANCEL to fail the DLL load."
The customer's machine is running the 64-bit version of Windows 7,
with an HP LaserJet 4100 printer. My program is 32-bit, but we've
run it in our office on a Win7/64 box with an HP LaserJet 4000, and
it works fine. We've tried re-installing drivers on the customer
machine with no success - although if UNIDRVUI.DLL isn't part of
the printer driver package I suppose that makes sense.
Oddly enough, other programs on the customer's machine (e.g. Notepad)
can print through this printer with no problems - so even if something
isn't quite right with their printer setup, it makes us look bad.
So it comes down to two questions:
1. How do we analyze and, if necessary, correct the customer's printer?
2. Is there a more more robust way to print than the method I'm using?
Any suggestions would be appreciated.
--
/~\ cgi...@kltpzyxm.invalid (Charlie Gibbs)
\ / I'm really at ac.dekanfrus if you read it the right way.
X Top-posted messages will probably be ignored. See RFC1855.
/ \ HTML will DEFINITELY be ignored. Join the ASCII ribbon campaign!
I don't know, but remember that C++ being what it is, the problem could be
anywhere else, in a source file that has nothing to do with printing...
even the compiler has bugs and one could have an effect here. I'd to this:
* Check all code with PC-Lint or similar and correct errors
* Turn off optimizations (leaving assume aliazing across function calls)
* Shuffle the code around a bit to affect stack and what the compiler see
> I have a report program that works at many installations - except one,
> where it consistently crashes. It calls PrintDlg() to get a printer
> device context, then uses this device context in a call to StartDoc():
<code snipped>
I've since discovered that the site in question is running Print Audit 6
on their 64-bit Windows 7 system. My code runs on 32-bit systems with
or without Print Audit 6 running. It also runs on a 64-bit Win7 box
without Print Audit 6 - but as soon as we installed Print Audit 6 on
this box, my program started to crash.
I'm currently working with the nice people at www.printaudit.com
to try to track down the problem. I'll post my findings here.
char devname[256];
HDC printerdc;
PRINTDLG pdlg;
LPDEVNAMES pdn;
DOCINFO di;
char *s;
/* Get the device context for the default printer. */
memset ((void *) &pdlg, 0, sizeof (pdlg));
pdlg.lStructSize = sizeof (pdlg);
pdlg.Flags = PD_RETURNDC | PC_RETURNDEFAULT
| PD_NOPAGENUMS | PD_NOSELECTION | PD_USEDEVMODECOPIESANDCOLLATE;
if (!PrintDlg (&pdlg))
... error routine ...
printerdc = pdlg.hDC;
/* Get the printer name. */
if ((pdn = (LPDEVNAMES) GlobalLock (pdlg.hDevNames)) != NULL) {
s = (char *) pdn + pdn->wDeviceOffset;
strncpy (devname, s, sizeof (devname) - 1;
devname[sizeof(devname)-1] = '\0';
GlobalUnlock (pdlg.hDevNames);
}
/* Start the document. */
memset ((void *) &di, 0, sizeof (di));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = "test";
di.lpszOutput = NULL;
if (startDoc (printerdc, &di) <= 0)
... error routine ...
The code would work on any 32-bit system, whether Print Audit 6
was installed or not. It would also work on a Win7/64 box without
Print Audit 6 installed - but as soon as we installed Print Audit 6
the program would start crashing in the StartDoc API.
I eventually tracked down the problem to a bogus HDC being returned
from PrintDlg(). My initial workaround was to get the default
printer name by calling GetDefaultPrinter(), and using this name
in a call to CreateDC(), thus avoiding the use of PrintDlg()
completely. However, this didn't allow for cases where I needed
to allow the user to choose a printer. But since I was already
getting the printer name from pdlg.hDevNames, I simply unset the
PD_RETURNDC bit in the PrintDlg() call, and instead got the HDC
by again passing the printer name to CreateDC(). End of problem.
So be warned. I've been reading stories of various problems with
PrintDlg() on 64-bit systems, although no one has reported exactly
the problem I was having. It seems that PrintDlg() can no longer
be trusted to return a valid handle to a printer device context.
Fortunately, the workaround I described above seems to allow us
to bypass that particular bug.
Regards,
Friedel
> Thank you, Charlie, for posting your workaround here.
> Looks like this problem is caused by printaudit6 ?
> What does the printaudit support say?
I don't know whether I spurred them to dig too deeply. I did,
however, send them my test program, which has options to get
the HDC either from PrintDlg() or by calling its own CreateDC().
I suspect, though, that there is some sort of negative synergy
involved. It seems that if Print Audit 6 is trying to trace a
program's printer accesses, something causes the HDC returned
by PrintDlg() to become corrupted. (This doesn't happen if
Print Audit 6 is running but has been configured to not track
the program in question.) Print Audit 6 obviously places some
hooks deep in the system - maybe Win7/64 doesn't react too
gracefully to such things. All in all, I can think of several
possible reasons the HDC could get corrupted:
- A bug in Print Audit 6
- Fragility of the hooks
- A bug in PrintDlg()
Whatever's happening, I find it worthwhile to simply assume that -
for whatever reason - PrintDlg() can no longer be trusted to return
a valid HDC. Fortunately, it's easy to get the printer name and
call CreateDC() myself.
I've incorporated my workaround into our production code and
shipped the revised programs off to the customer who discovered
the problem, and all is once again right with the world.