I need to open the Printer Dialog box when there is no Windows default printer setup. It works fine with the below code when a Windows default printer is set up.
TPrintDialog *dlgPrint = new TPrintDialog(frmDisplayDetail);
if( dlgPrint->Execute()) { //code here }
But if there is no default printer setup in Windows, dlgPrint->Execute()
throws an exception:
There is no default printer selected
To check the default printer index, I used Printer()->PrinterIndex
. This value becomes inaccessible when there is no Windows printer setup:
error E2122 Function call terminated by unhandled exception XXX at address XXX
Am I doing something wrong? Please suggest a solution.
The exception "There is no default printer currently selected" is thrown only by the TPrinter::SetToDefaultPrinter()
method, when either:
the Win32 API EnumPrinters()
function fails with an ERROR_INVALID_NAME
error code when enumerating with the (undocumented) PRINTER_ENUM_DEFAULT
flag.
the TPrinter::Printers
list does not contain the default printer. If EnumPrinters()
above does not report a default device, the default is then queried from the Win32 API GetDefaultPrinter()
function (Delphi/C++Builder 2009 and later), or the device
value of the [windows]
section of %windir%\win.ini
(Delphi/C++Builder 2007 and earlier).
TPrinter::SetToDefaultPrinter()
is called only by:
the TPrinter::PrinterIndex
getter if the FPrinterIndex
member is currently -1.
the TPrinter::PrinterIndex
setter if the FPrinterIndex
member is currently -1, or the property is being set to -1.
TPrintDialog::Execute()
(which uses the Win32 API PrintDlg()
function) first calls TPrinter::GetPrinter()
to get a handle to the DEVMODE
of the currently selected printer, which it then uses to initialize the dialog (via the PRINTDLG::hDevMode
field). TPrinter::GetPrinter()
reads the TPrinter::PrinterIndex
property, so if there is no currently selected printer and no default printer, the exception is thrown.
If there is no default printer configured, you are basically out of luck, as you can't even set the TPrinter::PrinterIndex
property to a value >= 0 since it will first call SetToDefaultPrinter()
before checking if the new value matches the current value.
TPrinter
has a long history of failing/crashing when there is no default printer configured in Windows. To work around this, you should call the Win32 API PrintDlg()
function directly instead. At least then, you can manually call TPrinter::GetPrinter()
(to get the initial DEVMODE
) and wrap it in a try/catch
block to discard any exceptions that it may throw.
If the dialog result indicates success, you can manually call TPrinter::SetPrinter()
to assign the selected printer options to TPrinter
for subsequent printing.
I am checking existence of printers before the dialog usage:
// check printer interface
TPrinter *prn = Printer();
int pxs,pys,i=0;
if (prn==NULL)i=1;
if (prn->Printers==NULL) i=1;
if (prn->Printers->Count<=0) i=1;
if (i) return;
// use dialog
dlg_print->Options.Clear();
dlg_print->Options<<poPrintToFile;
if (!dlg_print->Execute()) return;
// print
prn = Printer();
pxs=prn->PageWidth;
pys=prn->PageHeight;
prn->BeginDoc();
for (i=0;i<dlg_print->Copies;i++)
{
if (i) prn->NewPage();
// here render page into prn->Canvas as image in resolution pxs,pys
}
prn->EndDoc();
where dlg_print
is my dialog placed on the form.