Problem with TSRVPrint when no default printer is assigned

ScaleRichView support and discussion (TRichView add-on for WYSIWYG editing)
Post Reply
JonRobertson
Posts: 164
Joined: Tue Nov 08, 2011 5:11 pm

Problem with TSRVPrint when no default printer is assigned

Post by JonRobertson » Tue Jul 03, 2012 7:04 pm

When TSRVPrint is on a form and the user's profile has no default printer assigned, a couple of nasty things occur.

TSRVPrint.Create creates a TSRVPrtInfo, but always "Gets Printer Data Immediately".

TSRVPrtInfo.UpdateCurPrinterData sets Printer.PrinterIndex := Printer.PrinterIndex, I assume to refresh the current printer. However, when there is not a default printer, this throws the exception "EPrinter: There is no default printer currently selected"

This alone prevents the form from loading. However, it gets uglier. TSRVPrint is automatically destroyed by Delphi since an exception occurred in its constructor. TSRVPrint's destructor assumes SRVPrtInfo was created and always tries to destroy it. In this scenario, that causes an Access Violation, which prevents the original exception from being seen by the developer.

Code: Select all

destructor TSRVPrint.Destroy;
begin
  SRVPrtInfo.Destroy;
  inherited;
end;
That second issue can be fixed by ensuring SRVPrtInfo is assigned before destroying it:

Code: Select all

destructor TSRVPrint.Destroy;
begin
  if Assigned(SRVPrtInfo) then
    SRVPrtInfo.Destroy;
// or simply call Free, which will not free a nil reference:
// SRVPrtInfo.Free;

  inherited;
end;
I can make sure a default printer is available, notify the user if there isn't one and avoid calling TSRVPrint.Print in that scenario. Unfortunately, I can't prevent the exception upon form creation.

I'm going to investigate the impact of changing TSRVPrint.Create from doing

Code: Select all

    SRVPrtInfo := TSRVPrtInfo.Create(True);
to

Code: Select all

    SRVPrtInfo := TSRVPrtInfo.Create(False);
If the component will create without blowing up, I can have it get printer information before printing, and only print if a default printer is available.

I have a function that I created from code in Printers.pas that determines if a default printer is available, if that would be helpful to you.

JonRobertson
Posts: 164
Joined: Tue Nov 08, 2011 5:11 pm

Post by JonRobertson » Tue Jul 03, 2012 7:08 pm

Well, not that simple. :(

TSRVPrint.Loaded calls .UpdatePage calls .calculateSize calls .calculatePictureSize calls .GetPrinterInfo which eventually call TPrinter.GetPrinterIndex and TPrinter.SetToDefaultPrinter and the "There is no default printer currently selected" occurs.

I'm switching to my second approach, which is to create TSRVPrint in code only after I've confirmed the user has a default printer. :(

Sergey Tkachenko
Site Admin
Posts: 14513
Joined: Sat Aug 27, 2005 10:28 am
Contact:

Post by Sergey Tkachenko » Thu Jul 05, 2012 11:16 am

We will make the following changes in SRV code:

1) In SRVPrtInfo.pas, change all occurrences of

Code: Select all

Printer.Printers.Count = 0
to

Code: Select all

Printer.PrinterIndex<0
2) In the same unit, change TSRVPrtInfo.GetOrientation to:

Code: Select all

function TSRVPrtInfo.GetOrientation: TPrinterOrientation;
begin
  if Printer.PrinterIndex>=0 then
    Result := Printer.Orientation
  else
    Result := poPortrait;
end;
3) In SRVPrint.pas, change GetOrientation to SRVPrtInfo.GetOrientation

4) In the same unit, in TSRVPrint.Destroy, change SRVPrtInfo.Destroy to SRVPrtInfo.Free.

JonRobertson
Posts: 164
Joined: Tue Nov 08, 2011 5:11 pm

Post by JonRobertson » Thu Jul 05, 2012 12:43 pm

Thanks. What I have now works (creating the TSRVPrint object at run-time). I'll retest once I have the new version installed.

Post Reply