I am trying to call CreateDC
to create a printer device context:
printerDC := CreateDC('WINSPOOL', 'HP DeskJet 930C/932C/935C', nil, nil);
The code works in Windows 7, but fails in Windows 10 - returning null
.
I really am using a real printer:
What to pass
The MSDN documentation says that if you want to get a printer device context you must pass WINSPOOL
as the driver:
How To: Retrieve a Printer Device Context
To render to a specific printer, you must specify "WINSPOOL" as the device and pass the correct name of the printer to CreateDC. You can also pass a DEVMODE structure in the call to CreateDC if you want to provide device-specific initialization data for the device driver when you create the device context.
The following example shows a call to CreateDC in which the "WINSPOOL" driver is selected and the printer name is specified by name. C++
printerDC = CreateDC( L"WINSPOOL", printerName, NULL, NULL);
I mention it, because there is a lot of code out there that passes:
- empty string
- null
- the name of the device
- the name of the driver
winspool
WINSPOOL
Background
Deep in the Delphi VCL framework of Vcl.Printers
is a call to the Windows function CreateIC
. On my Windows 10 desktop, the calls fails (returns NULL rather than a valid information context).
The CreateIC
function does not document any way in which it can fail. Nor does it document that it supports calling GetLastError
to get the error. But if i call GetLastError
i get error code 50:
ERROR_NOT_SUPPORTED
50 (0x32)
The request is not supported.
Minimum reproducible
I extracted the code from Vcl.Printers.pas and boiled it down to a simple reproducable sample:
procedure TForm1.Button1Click(Sender: TObject);
var
driver, device, output: string;
dc: HDC;
le: DWORD;
begin
driver := '';
device := 'Microsoft XPS Document Writer';
output := '';
dc := CreateIC(PChar(driver), PChar(device), PChar(output), nil);
if dc = 0 then
begin
le := GetLastError;
raise Exception.CreateFmt('Could not get information context for printer "%s": %s (%d)', [device, SysErrorMessage(le), le]);
end;
end;
The code fails on my Windows 10 desktop, but works on Windows 7.
And in both cases i'm using the same printer:
What am i1 doing wrong?
CreateDC also fails
CreateIC
is just a "lightweight" form of CreateDC
(you can use it to get information about a device, but you can't draw GDI with it). It also means that CreateDC
also fails on Windows 10:
procedure TForm1.Button1Click(Sender: TObject);
var
driver, device: string;
dc: HDC;
le: DWORD;
begin
driver := '';
device := 'Microsoft XPS Document Writer';
dc := CreateDC(PChar(driver), PChar(device), nil, nil);
if dc = 0 then
begin
le := GetLastError;
raise Exception.CreateFmt('Could not get device context for printer "%s": %s (%d)', [device, SysErrorMessage(le), le]);
end;
end;
Some people have suggested duplicating the device name into the driver name:
CreateDC('Microsoft XPS Document Writer', 'Microsoft XPS Document Writer', nil, nil);
and i've also seen try placing WINSPOOL
in the driver name:
CreateDC('WINSPOOL', 'Microsoft XPS Document Writer', nil, nil);
It's all printers
I started out this question thinking it was just CreateIC
.
- then i discovered that
CreateDC
also fails, so i updated the title - then i thought it was because i happened to be using the Microsoft XPS Document Writer, so i updated the title
- then i discovered it's all printers on Windows 10, so i updated the title again
This is the natural evolution as people suggest things, and i discover more about the nature of the problem.
| Driver | Device | Result |
|------------------------------------|---------------------------------|--------|
| '' (empty string) | 'Microsoft XPS Document Writer' | Fails |
| 'Microsoft XPS Document Writer' | 'Microsoft XPS Document Writer' | Fails |
| 'WINSPOOL' | 'Microsoft XPS Document Writer' | Fails |
| nil | 'Microsoft XPS Document Writer' | Fails |
| 'Microsoft XPS Document Writer v4' | 'Microsoft XPS Document Writer' | Fails |
| '' (empty string) | 'HP DeskJet 930C/932C/935C' | Fails |
| 'HP DeskJet 930C/932C/935C' | 'HP DeskJet 930C/932C/935C' | Fails |
| 'WINSPOOL' | 'HP DeskJet 930C/932C/935C' | Fails |
| nil | 'HP DeskJet 930C/932C/935C' | Fails |
For all i know, calling CreateDC
to create a display device context is also broken in Windows 10. Like you, i've not tested it.
Bonus Reading
- QC127390: Operation not supported on selected printer
- Random "Printer selected is not valid" errors on Win2K8R2
- MSDN Forums: XPS and StartDoc
- GDI Drawing and Printing
- CreateDC() problem with printer drivers on Windows Server 200x
- Printing from a Windows Service
- MSDN: How To: Retrieve a Printer Device Context
1 Not me; Embarcadero