“There is no default printer selected” error when

2019-06-14 16:02发布

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.

2条回答
家丑人穷心不美
2楼-- · 2019-06-14 16:32

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.

查看更多
时光不老,我们不散
3楼-- · 2019-06-14 16:41

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.

查看更多
登录 后发表回答