After much research in a completely different direction (API hooking) I came up with this:
var
sLastUsedPrinter: String;
threadvar
ghHook: Integer;
...
//set frxPrintDialog hook
ghHook := SetWindowsHookEx(WH_CBT, @PrintDialogHookProc, 0, GetCurrentThreadId);
//show prepared report
frxReport.ShowPreparedReport;
//unhook frxPrintDialog hook
UnhookWindowsHookEx(ghHook);
...
function PrintDialogHookProc(uMsg, wParam, lParam: Integer): Integer; stdcall;
var
//15 chars in 'TfrxPrintDialog' + 1 for string terminator
sClassName: array [0..15] of Char;
frxPrintDialog: TForm;
PrintersCB: TComboBox;
begin
//when a windows gets activated
if uMsg = HCBT_ACTIVATE then
begin
//get window class name
GetClassName(wParam, sClassName, 16);
//window class name is Fast Report's Print Dialog
if String(sClassName) = 'TfrxPrintDialog' then
begin
frxPrintDialog := FindControl(wParam) as TForm;
PrintersCB := frxPrintDialog.FindComponent('PrintersCB') as TComboBox;
//remember currently selected printer
sLastUsedPrinter := PrintersCB.Text;
//OnChange event handler for the printer selection ComboBox
PrintersCB.OnChange := PrintersCBChange;
end;
end;
Result := CallNextHookEx(ghHook, uMsg, wParam, lParam);
end;
procedure PrintersCBChange(Sender: TObject);
begin
//remember last user selected printer
sLastUsedPrinter := (Sender as TComboBox).Text;
end;
In real code sLastUsedPrinter and PrintersCBChange are actually class members but I changed them to keep things short(er).