Боролся тут с непечатью из нашего продукта на неумолчательный принтер.
Ох ну там и "макароны".
Вот:
Масса "вкусностей".
Особенно "доставляет" вот это: "< - this rebuilds the FPrinters list"
Т.е. парни что-то "закостыляли" и оставили себе "напоминалку".
Что Borland, что MicroSoft - "на высоте".
"Вкус" программирования тут по моему скромному мнению - отсутствует напрочь.
А я привык "учиться у других".
Но конкретно тут - "сложно учиться".
Ну и Embarcadero к сожалению код так и не переработала, только щедро добавили IfDef:
В чём была проблема? А в том, что тут:
Driver был от ОДНОГО (неумолчательного) принтера, а DevMode от ПРЕДЫДУЩЕГО (умолчательного).
И некоторые драйвера принтеров "плюют" на это, а некоторые (видимо более дотошные) - возвращают код ошибки.
Как так получилось - пока до конца не понял. Но - "залечил".
Перечитыванием настроек из принтера.
Но тут - "ПЯТЁРКА с ПЛЮСОМ" Майкрософту.
Вообще не очень понятно - "зачем плодить и дублировать сущности".
Да и к классу TPrinter - у меня много вопросов. С SRP - там явно проблемы.
А уж публикация "кишочков" через GetPrinter/SetPrinter - это отдельная песня.
Ну и DocumentProperties - вот уж точно - НЕ SRP. И швец и жнец и на дуде игрец.
И читает свойства, и пишет, да ещё и диалог показывает.
Молодцы короче - ВСЕ. И Borland, и Microsoft, и Embarcadero, да и я с коллегами. Тоже...
Вот так - не работает:
А вот так - работает:
Комментарий - "Если что-то с принтером, то лучше упадем уже здесь..." - тоже - "доставляет".
Но "с волками жить"...
Ох ну там и "макароны".
Вот:
procedure TPrinter.SetPrinter(ADevice, ADriver, APort: PChar; ADeviceMode: THandle);
var
I, J: Integer;
StubDevMode: TDeviceMode;
BufSize: Longint;
begin
CheckPrinting(False);
if ADeviceMode <> DeviceMode then
begin // free the devmode block we have, and take the one we're given
if DeviceMode <> 0 then
begin
GlobalUnlock(DeviceMode);
GlobalFree(DeviceMode);
end;
DeviceMode := ADeviceMode;
end;
if DeviceMode <> 0 then
begin
DevMode := GlobalLock(DeviceMode);
SetPrinterCapabilities(DevMode.dmFields);
end;
FreeFonts;
if FPrinterHandle <> 0 then
begin
ClosePrinter(FPrinterHandle);
FPrinterHandle := 0;
end;
SetState(psNoHandle);
J := -1;
with Printers do // < - this rebuilds the FPrinters list
for I := 0 to Count - 1 do
begin
if TPrinterDevice(Objects[I]).IsEqual(ADriver, ADevice, APort) then
begin
TPrinterDevice(Objects[I]).Port := APort;
J := I;
Break;
end;
end;
if J = -1 then
begin
J := FPrinters.Count;
FPrinters.AddObject(Format(SDeviceOnPort, [ADevice, APort]),
TPrinterDevice.Create(ADriver, ADevice, APort));
end;
FPrinterIndex := J;
if OpenPrinter(ADevice, FPrinterHandle, nil) then
begin
if DeviceMode = 0 then // alloc new device mode block if one was not passed in
begin
BufSize := DocumentProperties(0, FPrinterHandle, ADevice, StubDevMode, StubDevMode, 0);
if BufSize > 0 then {V}
DeviceMode := GlobalAlloc(GHND, BufSize);
if DeviceMode <> 0 then
begin
DevMode := GlobalLock(DeviceMode);
if DocumentProperties(0, FPrinterHandle, ADevice, DevMode^,
DevMode^, DM_OUT_BUFFER) < 0 then
begin
GlobalUnlock(DeviceMode);
GlobalFree(DeviceMode);
DeviceMode := 0;
end
end;
end;
if DeviceMode <> 0 then
SetPrinterCapabilities(DevMode^.dmFields);
end;
end;
...
procedure TPrinter.SetToDefaultPrinter;
var
I: Integer;
ByteCnt, StructCnt: DWORD;
DefaultPrinter: array[0..1023] of Char;
Cur, Device: PChar;
PrinterInfo: PPrinterInfo5;
begin
ByteCnt := 0;
StructCnt := 0;
if not EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, nil, 0, ByteCnt,
StructCnt) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
begin
// With no printers installed, Win95/98 fails above with "Invalid filename".
// NT succeeds and returns a StructCnt of zero.
if GetLastError = ERROR_INVALID_NAME then
RaiseError(SNoDefaultPrinter)
else
RaiseLastOSError;
end;
PrinterInfo := AllocMem(ByteCnt);
try
EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, PrinterInfo, ByteCnt, ByteCnt,
StructCnt);
if StructCnt > 0 then
Device := PrinterInfo.pPrinterName
else begin
GetProfileString('windows', 'device', '', DefaultPrinter,
SizeOf(DefaultPrinter) - 1);
Cur := DefaultPrinter;
Device := FetchStr(Cur);
end;
with Printers do
for I := 0 to Count-1 do
begin
if AnsiSameText(TPrinterDevice(Objects[I]).Device, Device) then
begin
with TPrinterDevice(Objects[I]) do
SetPrinter(PChar(Device), PChar(Driver), PChar(Port), 0);
Exit;
end;
end;
finally
FreeMem(PrinterInfo);
end;
RaiseError(SNoDefaultPrinter);
end;
DevMode, DeviceMode, TPrinterDevice, with, StringList.Масса "вкусностей".
Особенно "доставляет" вот это: "< - this rebuilds the FPrinters list"
Т.е. парни что-то "закостыляли" и оставили себе "напоминалку".
Что Borland, что MicroSoft - "на высоте".
"Вкус" программирования тут по моему скромному мнению - отсутствует напрочь.
А я привык "учиться у других".
Но конкретно тут - "сложно учиться".
Ну и Embarcadero к сожалению код так и не переработала, только щедро добавили IfDef:
[PrintingPermission(SecurityAction.LinkDemand, Level=PrintingPermissionLevel.AllPrinting)]
{$IF DEFINED(CLR)}
procedure TPrinter.SetPrinter(ADevice, ADriver, APort: string; ADeviceMode: IntPtr);
{$ELSE}
procedure TPrinter.SetPrinter(ADevice, ADriver, APort: PChar; ADeviceMode: THandle);
{$ENDIF}
var
I, J: Integer;
{$IF DEFINED(CLR)}
LDevMode: TDeviceMode;
{$ENDIF}
begin
CheckPrinting(False);
{$IF DEFINED(CLR)}
if ADeviceMode <> FDeviceMode then
UpdateDeviceMode(ADeviceMode);
if FDeviceMode <> nil then
begin
LDevMode := TDeviceMode(Marshal.PtrToStructure(FDeviceMode, TypeOf(TDeviceMode)));
SetPrinterCapabilities(LDevMode.dmFields);
end;
{$ELSE}
if ADeviceMode <> FDeviceMode then
begin // free the devmode block we have, and take the one we're given
if FDeviceMode <> 0 then
begin
GlobalUnlock(FDeviceMode);
GlobalFree(FDeviceMode);
FDevMode := nil;
end;
FDeviceMode := ADeviceMode;
end;
if FDeviceMode <> 0 then
begin
FDevMode := GlobalLock(FDeviceMode);
SetPrinterCapabilities(FDevMode.dmFields);
end;
{$ENDIF}
FreeFonts;
if FPrinterHandle <> 0 then
begin
ClosePrinter(FPrinterHandle);
FPrinterHandle := 0;
end;
SetState(TPrinterState.psNoHandle);
J := -1;
with Printers do // <- this rebuilds the FPrinters list
for I := 0 to Count - 1 do
begin
if TPrinterDevice(Objects[I]).IsEqual(ADriver, ADevice, APort) then
begin
TPrinterDevice(Objects[I]).Port := APort;
J := I;
Break;
end;
end;
if J = -1 then
begin
J := FPrinters.Count;
FPrinters.AddObject(Format(SDeviceOnPort, [ADevice, APort]),
TPrinterDevice.Create(ADriver, ADevice, APort));
end;
FPrinterIndex := J;
if OpenPrinter(ADevice, FPrinterHandle, nil) then
begin
{$IF DEFINED(CLR)}
if FDeviceMode = nil then // alloc new device mode block if one was not passed in
begin
FDeviceMode := Marshal.AllocHGlobal(
DocumentProperties(0, FPrinterHandle, ADevice, FDeviceMode, FDeviceMode, 0)); //set to intptr 0,0
if FDeviceMode <> nil then
if DocumentProperties(0, FPrinterHandle, ADevice, FDeviceMode, 0, DM_OUT_BUFFER) < 0 then
UpdateDeviceMode(nil)
end;
if FDeviceMode <> nil then
SetPrinterCapabilities(LDevMode.dmFields);
{$ELSE}
if FDeviceMode = 0 then // alloc new device mode block if one was not passed in
begin
FDeviceMode := GlobalAlloc(GHND, DocumentProperties(0, FPrinterHandle, ADevice, nil, nil, 0));
if FDeviceMode <> 0 then
begin
FDevMode := GlobalLock(FDeviceMode);
if DocumentProperties(0, FPrinterHandle, ADevice, FDevMode, nil, DM_OUT_BUFFER) < 0 then
begin
GlobalUnlock(FDeviceMode);
GlobalFree(FDeviceMode);
FDeviceMode := 0;
FDevMode := nil;
end
end;
end;
if FDeviceMode <> 0 then
SetPrinterCapabilities(FDevMode^.dmFields);
{$ENDIF}
end;
end;
...
procedure TPrinter.SetToDefaultPrinter;
var
I: Integer;
ByteCnt, StructCnt: DWORD;
{$IF DEFINED(CLR)}
Device: string;
PrinterInfo, NamePtr: IntPtr;
PD: System.Drawing.Printing.PrintDocument;
{$ELSE}
DefaultPrinter: array[0..1023] of Char;
Cur, Device: PChar;
PrinterInfo: PPrinterInfo5;
{$ENDIF}
begin
ByteCnt := 0;
StructCnt := 0;
if not EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, nil, 0, ByteCnt,
StructCnt) and (GetLastError <> ERROR_INSUFFICIENT_BUFFER) then
begin
// With no printers installed, Win95/98 fails above with "Invalid filename".
// NT succeeds and returns a StructCnt of zero.
if GetLastError = ERROR_INVALID_NAME then
RaiseError(SNoDefaultPrinter)
else
RaiseLastOSError;
end;
{$IF DEFINED(CLR)}
PrinterInfo := Marshal.AllocHGlobal(ByteCnt);
try
EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, PrinterInfo, ByteCnt, ByteCnt,
StructCnt);
if StructCnt > 0 then
begin
NamePtr := Marshal.ReadIntPtr(PrinterInfo, 0); // pPrinterName
Device := Marshal.PtrToStringAuto(NamePtr);
end;
finally
Marshal.FreeHGlobal(PrinterInfo);
end;
if StructCnt <= 0 then {EnumPrinters didnt work, try using CLR}
begin
PD := System.Drawing.Printing.PrintDocument.Create;
Device := PD.DefaultPageSettings.PrinterSettings.PrinterName;
end;
with Printers do
for I := 0 to Count-1 do
begin
if WideSameText(TPrinterDevice(Objects[I]).Device, Device) then
begin
with TPrinterDevice(Objects[I]) do
SetPrinter(Device, Driver, Port, nil);
Exit;
end;
end;
{$ELSE}
PrinterInfo := AllocMem(ByteCnt);
try
EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, PrinterInfo, ByteCnt, ByteCnt,
StructCnt);
if StructCnt > 0 then
Device := PrinterInfo.pPrinterName
else
begin
{$IF DEFINED(UNICODE)}
I := Length(DefaultPrinter);
if not GetDefaultPrinter(DefaultPrinter, I) then
ZeroMemory(@DefaultPrinter[0], I * SizeOf(Char));
{$ELSE}
GetProfileString('windows', 'device', '', DefaultPrinter, SizeOf(DefaultPrinter) - 1);
{$ENDIF}
Cur := DefaultPrinter;
Device := FetchStr(Cur);
end;
with Printers do
for I := 0 to Count-1 do
begin
if AnsiSameText(TPrinterDevice(Objects[I]).Device, Device) then
begin
with TPrinterDevice(Objects[I]) do
SetPrinter(PChar(Device), PChar(Driver), PChar(Port), 0);
Exit;
end;
end;
finally
FreeMem(PrinterInfo);
end;
{$ENDIF}
RaiseError(SNoDefaultPrinter);
end;
В чём была проблема? А в том, что тут:
if Assigned(CreateHandleFunc) then
with TPrinterDevice(Printers.Objects[PrinterIndex]) do
begin
DC := CreateHandleFunc(PChar(Driver), PChar(Device), PChar(Port), DevMode);
Driver был от ОДНОГО (неумолчательного) принтера, а DevMode от ПРЕДЫДУЩЕГО (умолчательного).
И некоторые драйвера принтеров "плюют" на это, а некоторые (видимо более дотошные) - возвращают код ошибки.
Как так получилось - пока до конца не понял. Но - "залечил".
Перечитыванием настроек из принтера.
Но тут - "ПЯТЁРКА с ПЛЮСОМ" Майкрософту.
Вообще не очень понятно - "зачем плодить и дублировать сущности".
Да и к классу TPrinter - у меня много вопросов. С SRP - там явно проблемы.
А уж публикация "кишочков" через GetPrinter/SetPrinter - это отдельная песня.
Ну и DocumentProperties - вот уж точно - НЕ SRP. И швец и жнец и на дуде игрец.
И читает свойства, и пишет, да ещё и диалог показывает.
Молодцы короче - ВСЕ. И Borland, и Microsoft, и Embarcadero, да и я с коллегами. Тоже...
Вот так - не работает:
function Tl3Printer.Clone: Il3Printer;
//#UC START# *49BAA14602EC_4799D40F0004_var*
function CopyData(Handle: THandle): THandle;
var
Src, Dest: PChar;
Size: Integer;
begin
if Handle <> 0 then
begin
Size := GlobalSize(Handle);
Result := GlobalAlloc(GHND, Size);
if Result <> 0 then
try
Src := GlobalLock(Handle);
Dest := GlobalLock(Result);
if (Src <> nil) and (Dest <> nil) then l3Move(Src^, Dest^, Size);
finally
GlobalUnlock(Handle);
GlobalUnlock(Result);
end
end
else Result := 0;
end;
var
l_PrinterIndex : Integer;
l_Device,
l_Driver,
l_Port : Array[0..255] of Char;
l_hDeviceMode : THandle;
//#UC END# *49BAA14602EC_4799D40F0004_var*
begin
//#UC START# *49BAA14602EC_4799D40F0004_impl*
Result := nil;
l_PrinterIndex := Self.PrinterIndex; // Если что-то с принтером, то лучше упадем уже здесь...
Result := Make;
Result.PrinterIndex := l_PrinterIndex;
Result.Copies := Self.Copies;
Result.Title := Self.Get_Title;
Result.FileName := Self.Get_FileName;
Result.Collate := Self.Get_Collate;
// Копируем настройки принтера:
Self.GetPrinter(l_Device, l_Driver, l_Port, l_hDeviceMode);
Result.SetPrinter(l_Device, l_Driver, l_Port, CopyData(l_hDeviceMode));
//#UC END# *49BAA14602EC_4799D40F0004_impl*
end;//Tl3Printer.Clone
А вот так - работает:
function Tl3Printer.Clone: Il3Printer; //#UC START# *49BAA14602EC_4799D40F0004_var* var l_PrinterIndex : Integer; l_Device, l_Driver, l_Port : Array[0..255] of Char; l_hDeviceMode : THandle; //#UC END# *49BAA14602EC_4799D40F0004_var* begin //#UC START# *49BAA14602EC_4799D40F0004_impl* Result := nil; l_PrinterIndex := Self.PrinterIndex; // Если что-то с принтером, то лучше упадем уже здесь... Result := Make; Result.PrinterIndex := l_PrinterIndex; Result.Copies := Self.Copies; Result.Title := Self.Get_Title; Result.FileName := Self.Get_FileName; Result.Collate := Self.Get_Collate; // Копируем настройки принтера: Self.GetPrinter(l_Device, l_Driver, l_Port, l_hDeviceMode); Result.SetPrinter(l_Device, l_Driver, l_Port, 0); // - тут ЗА СЧЁТ нуля - настройки - ПЕРЕЧИТАЮТСЯ, а не СКОПИРУЮТСЯ. //#UC END# *49BAA14602EC_4799D40F0004_impl* end;//
Комментарий - "Если что-то с принтером, то лучше упадем уже здесь..." - тоже - "доставляет".
Но "с волками жить"...
Комментариев нет:
Отправить комментарий