6

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:

enter image description here

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:

  • Windows 7

    enter image description here

  • Windows 10 enter image description here

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

1 Not me; Embarcadero

Community
  • 1
  • 1
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
  • 1
    The `ERROR_NOT_SUPPORTED` may not be accurate, as the documentation (as you state) only reports that the function returns NULL on failure. However, it may well be that the XPS document writer does not support returning an information context on Win10.. Did you try setting your HP printer as the default (or setting it as the active printer with `PrinterIndex`) to see if that also fails? – Ken White Oct 04 '15 at 03:28
  • FWIW this seems like a pure winapi question – David Heffernan Oct 04 '15 at 09:26
  • @DavidHeffernan I thought about that but a) i didn't want to scare off people with talk of VCL framework code (i.e. not my code) and b) i wanted to pre-empt anyone who might ask [*why are you doing that?*](http://stackoverflow.com/a/6411318/12597), and i would have no idea how to answer them - since i wasn't the one doing it! – Ian Boyd Oct 04 '15 at 12:57
  • 1
    I suspect though, that the winapi experts may steer clear of a Delphi question – David Heffernan Oct 04 '15 at 13:10
  • If you use [`EnumPrinters`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd162692%28v=vs.85%29.aspx) do you get the name you expect for the printer? (Use [`Level 2`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd162845%28v=vs.85%29.aspx) to get the printer name and driver name) – theB Oct 04 '15 at 13:30
  • @theB Yes. It returns `HP DeskJet 930C/932C/935C`/`HP DeskJet 930C/932C/935C` and `Microsoft XPS Document Writer v4`/`Microsoft XPS Document Writer`. Both of which i've now tried. – Ian Boyd Oct 04 '15 at 13:57
  • @DavidHeffernan It must be a bug in the Delphi compiler, or XE6 Winapi translations. There's no way `CreateDC('WINSPOOL', 'HP DeskJet 930C/932C/935C', nil, nil);` can fail in Windows 10 and nobody noticed before now. Notepad prints. It *has* to be Delphi :( – Ian Boyd Oct 04 '15 at 14:12
  • You confirmed that I presume, with a C++ compiler? – David Heffernan Oct 04 '15 at 14:47
  • @DavidHeffernan Even more inscideous than that. It worked when i called CreateDCA from Delphi 7. Then i returned to XE6 and it was fixed... As though the entire print subsystem got into an invalid state. I'm going to reboot shortly and see if the failure returns. – Ian Boyd Oct 04 '15 at 14:51
  • Sounds like it could be environmental – David Heffernan Oct 04 '15 at 15:32
  • @DavidHeffernan I wonder if its similar to the common bug in the VCL (QC107919) where a `devmode` structure contains invalid garbage. The VCL code is already wrong in other ways (containing Win16 legacy code). Its random whether it will happen in the launched process; meanwhile other applications can print. It's going to be an impossible to solve Delphi-on-Windows-10 bug. Possibly related to unaligned structures, random garbage that just happens to tickle the driver the wrong way, ASLR, DEP... that only me and my customers find on Windows 10. – Ian Boyd Oct 05 '15 at 14:20
  • Would it be possible to wrap the desired functionality into a native DLL (written by you in C++) and call _that_ from the Delphi code? It's more of a workaround, but it would at least identify whether the issue is in the built-in library or on the Windows side of the call. _(Note that I'm not a Delphi programmer, so feel free to ignore this if it's fundamentally impossible)_ – theB Oct 05 '15 at 14:41

0 Answers0