问题
I've written wrapper classes for Windows' print spooler APIs, which work... mostly.
The only thing that does not work is applying printer settings.
First I call DocumentProperties() to successfully acquire and play with printer settings.
Then I try to apply those settings using ResetDC() but nothing happens. The function takes a valid handle, and returns the same valid handle, meaning it should have applied the settings. But, nothing seems to have happened: the printouts are unaffected by the changes in printer settings.
I've even tried to use SetPrinter() with level 9, also without effect.
This task is getting fairly urgent. Any suggestions?
Using Delphi XE2, Windows 7 64-bit.
Okay, skip all the bulk of the big OO wrappers. Here's a condensed procedural version. (Note that you must give it an XPS file.)
Printer settings changes are being applied (when using Info2_Apply()) and can be seen in MS Word. They're just being ignored when printing. That's the mystery.
I've tried so many things that I'm running out of options. Help would be.... extremely much appreciated.
I've finally run out of options.
Here's the last version of my test code. It's self contained and contains more functions than you need - a consequence of desperately trying anything I could think of.
If anyone else can get printer settings to work, could you please let me know?
Populate a combo box with printer names using:
uses
Printers
ComboBox1.Items.Assign(Printer.Printers);
Printing procedure (my test code):
uses
Winapi.WinSpool
procedure PrintXPS(PrinterName, FileNameXPS: string; ParentFormHandle: THandle = 0);
// Printer handle
procedure Printer_Open(out Printer: THandle; Defaults: PPrinterDefaultsW = nil);
begin
if not OpenPrinterW(PWideChar(PrinterName), Printer, Defaults) then
RaiseLastOSError;
end;
procedure Printer_Close(Printer: THandle);
begin
if not ClosePrinter(Printer) then
RaiseLastOSError;
end;
// Printer defaults
procedure Defaults_Obtain(out DefaultsHandle: THandle; out Defaults: PPrinterDefaultsW);
begin
DefaultsHandle := GlobalAlloc(GHND, SizeOf(TPrinterDefaultsW));
if DefaultsHandle = 0 then
RaiseLastOSError;
Defaults := GlobalLock(DefaultsHandle);
if Defaults = nil then
RaiseLastOSError;
end;
// Print settings
procedure Settings_Obtain(Printer: THandle; out SettingsHandle: THandle; out Settings: PDeviceModeW);
var
DeviceModeSize: integer;
begin
DeviceModeSize := DocumentProperties(0, Printer, PWideChar(PrinterName), nil, nil, 0);
if DeviceModeSize < 0 then
RaiseLastOSError;
// Allocate memory
SettingsHandle := GlobalAlloc(GHND, DeviceModeSize);
if SettingsHandle = 0 then
RaiseLastOSError;
// Lock memory
Settings := GlobalLock(SettingsHandle);
if Settings = nil then
RaiseLastOSError;
// Populate memory
if DocumentProperties(ParentFormHandle, Printer, PWideChar(PrinterName), Settings, Settings, DM_OUT_BUFFER) < 0 then
RaiseLastOSError;
end;
procedure Settings_Show(Printer: THandle; var Settings: PDeviceModeW; Options: Cardinal);
var
Return: integer;
begin
Return := DocumentProperties(ParentFormHandle, Printer, PWideChar(PrinterName), Settings, Settings, Options);
if Return < 0 then
RaiseLastOSError;
end;
// DC
function ObtainDC(Printer: THandle; DeviceMode: PDeviceModeW): HDC;
begin
Result := CreateDC(nil, PWideChar(PrinterName), nil, DeviceMode);
if Result = 0 then
RaiseLastOSError;
end;
procedure ApplyDC(DC: HDC; DeviceMode: PDeviceModeW);
begin
if ResetDC(DC, DeviceMode^) = 0 then
RaiseLastOSError;
end;
// PRINTER_INFO_2
procedure Info2_Obtain(Printer: THandle; out Info2Handle: THandle; out Info2: PPrinterInfo2W);
var
InfoSize: Cardinal;
begin
GetPrinterW(Printer, 2, nil, 0, @InfoSize);
// Get printer info memory
Info2Handle := GlobalAlloc(GHND, InfoSize);
if Info2Handle = 0 then
RaiseLastOSError;
// Lock printer info memory
Info2 := GlobalLock(Info2Handle);
if Info2 = nil then
RaiseLastOSError;
// Get printer info data
if not GetPrinterW(Printer, 2, Info2, InfoSize, @InfoSize) then
RaiseLastOSError;
end;
procedure Info2_Apply(Printer: THandle; Info2: PPrinterInfo2W);
begin
if not SetPrinterW(Printer, 2, Info2, 0) then
RaiseLastOSError;
end;
// PRINTER_INFO_8
procedure Info8_Fetch(Printer: THandle; Settings: PDeviceModeW);
var
lBuffer: PPrinterInfo8W;
lBufferSize: Cardinal;
begin
GetPrinterW(Printer, 8, nil, 0, @lBufferSize);
GetMem(lBuffer, lBufferSize);
try
FillChar(lBuffer^, lBufferSize, 0);
// Make the call
lBuffer.pDevMode := Settings;
if not GetPrinterW(Printer, 8, lBuffer, lBufferSize, @lBufferSize) then
RaiseLastOSError;
finally
FreeMem(lBuffer, lBufferSize);
end;
end;
procedure Info8_Apply(Printer: THandle; Settings: PDeviceModeW);
var
lPrinterInfo8: TPrinterInfo8W;
begin
lPrinterInfo8.pDevMode := Settings;
if not SetPrinterW(Printer, 8, @lPrinterInfo8, 0) then
RaiseLastOSError;
end;
// PRINTER_INFO_9
procedure Info9_Fetch(Printer: THandle; Settings: PDeviceModeW);
var
lBuffer: PPrinterInfo9W;
lBufferSize: Cardinal;
begin
GetPrinterW(Printer, 9, nil, 0, @lBufferSize);
GetMem(lBuffer, lBufferSize);
try
FillChar(lBuffer^, lBufferSize, 0);
// Make the call
lBuffer.pDevMode := Settings;
if not GetPrinterW(Printer, 9, lBuffer, lBufferSize, @lBufferSize) then
RaiseLastOSError;
finally
FreeMem(lBuffer, lBufferSize);
end;
end;
procedure Info9_Apply(Printer: THandle; Settings: PDeviceModeW);
var
lPrinterInfo9: TPrinterInfo9W;
begin
lPrinterInfo9.pDevMode := Settings;
if not SetPrinterW(Printer, 9, @lPrinterInfo9, 0) then
RaiseLastOSError;
end;
// Print jobs
function JobCreate(Printer: THandle; FileName: string): Cardinal;
var
lBufferSize: Cardinal;
lAddJobInfo: PAddJobInfo1W;
begin
// Create job
AddJobW(Printer, 1, nil, 0, lBufferSize);
GetMem(lAddJobInfo, lBufferSize);
try
if not AddJobW(Printer, 1, lAddJobInfo, lBufferSize, lBufferSize) then
RaiseLastOSError;
Result := lAddJobInfo.JobId;
// Copy the file into place
CopyFile(PWideChar(FileName), lAddJobInfo.Path, True);
finally
FreeMem(lAddJobInfo, lBufferSize);
end;
end;
procedure JobStart(Printer: THandle; JobID: Cardinal);
begin
if not ScheduleJob(Printer, JobID) then
RaiseLastOSError;
end;
// General cleanup
procedure ReleaseHandle(Handle: THandle);
begin
if not GlobalUnlock(Handle) then
;//RaiseLastOSError;
if GlobalFree(Handle) <> 0 then
;//RaiseLastOSError;
end;
var
PrinterA{, PrinterB}: THandle;
Defaults: PPrinterDefaultsW;
DefaultsHandle: THandle;
DataType: string;
Settings: PDeviceModeW;
SettingsHandle: THandle;
Info2: PPrinterInfo2W;
Info2Handle: THandle;
// DC: HDC;
JobID: Cardinal;
begin
if not FileExists(FileNameXPS) then
raise Exception.Create('File not found: ' + FileNameXPS);
// Get DataType
Printer_Open(PrinterA);
try
Info2_Obtain(PrinterA, Info2Handle, Info2);
try
DataType := WideCharToString(Info2.pDatatype);
finally
ReleaseHandle(Info2Handle);
end;
finally
Printer_Close(PrinterA);
end;
Defaults_Obtain(DefaultsHandle, Defaults);
try
Defaults.pDatatype := PWideChar(DataType);
Defaults.pDevMode := nil;
Defaults.DesiredAccess := PRINTER_ALL_ACCESS;
Printer_Open(PrinterA, Defaults);
try
Info2_Obtain(PrinterA, Info2Handle, Info2);
try
Settings_Show(PrinterA, Info2.pDevMode, DM_IN_BUFFER or DM_IN_PROMPT or DM_OUT_BUFFER);
// Try according to:
// - Remarks section in http://msdn.microsoft.com/en-us/library/windows/desktop/dd145082%28v=vs.85%29.aspx
// - Comment on code line 246 in http://www.lessanvaezi.com/changing-printer-settings-using-the-windows-api/
Info2.pSecurityDescriptor := nil;
Info2_Apply(PrinterA, Info2);
JobID := JobCreate(PrinterA, FileNameXPS);
JobStart(PrinterA, JobID);
finally
ReleaseHandle(Info2Handle);
end;
finally
Printer_Close(PrinterA);
end;
finally
ReleaseHandle(DefaultsHandle);
end;
end;
来源:https://stackoverflow.com/questions/10345179/resetdc-does-nothing-print-spooler-apis-for-windows