My goal is to open a printer connected via USB using the CreateFile
(and then issue some WriteFile
s and ReadFile
s).
If the printer was an LPT one, I would simply do CreateFile("LPT1:", ...)
. But for USB printers, there is a special device path that must be passed to CreateFile
in order to open that printer.
This device path, as I was able to find, is retrieved via SetupDiGetClassDevs
-> SetupDiEnumDeviceInterfaces
-> SetupDiGetDeviceInterfaceDetail
-> DevicePath
member and looks like this:
\\?\usb#vid_0a5f&pid_0027#46a072900549#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
All that is fine, but what I have as the input is the human-readable printer name, as seen in Devices and Printers
. The SetupDi*
functions don't seem to use that, they only operate on device instance IDs. So the question is now how to get device instance ID from the printer name one would pass to OpenPrinter
.
It's not difficult to observe that the GUID part of the above is the GUID_DEVINTERFACE_USBPRINT
, and \\?\usb
is fixed, so the only bit I'm really interested in is vid_0a5f&pid_0027#46a072900549#
. This path I can easily look up manually in the printer properties dialog:
Go to Devices and Printers
Right-click the printer
Properties
Switch to Hardware tab
Select the printing device, such as ZDesigner LP2844-Z
Properties
Switch to Details tab
Select 'Parent' from the dropdown.
But I have no idea how to do that programmatically provided the only thing given is the printer name as seen in the Device and Printers panel.
P.S. 1: I'm not interested in opening the printer with OpenPrinter
and then using WritePrinter
/ ReadPrinter
. That has been done, works fine, but now the goal is different.
P.S. 2: I'll be OK with a simpler way to convert the readable printer name to something that can be passed to CreateFile
.
P.S. 3: This question, to which I have posted an answer, is very related to what I ultimately want to do.
P.S. 4: The other way round is also fine: If it is possible to obtain the readable name from the SP_DEVINFO_DATA
structure, that will also be the answer, although a less convenient one.
Try this (Python code):
Use
WinObj
from Microsoft to get the specific device name. http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx . This will quickly get you the proper device name to use withCreateFile
to write directly to your USB printer or simply writing directly to a USB printer adapter with old school parallel port output for custom circuitry!To open the port associated with a specific printer, you may need to use
ntcreatefile
. Use theEnumPrinters
function to return aprinter_info_2
structure containing the port name for each printer. This port name can then be opened withntcreatefile
(an NT internal version ofCreateFile
) which is explained here: http://msdn.microsoft.com/en-us/library/bb432380(v=vs.85).aspxWhy does this work? There are three namespace levels in windows NT file/device names and the port name retrieved from
EnumPrinters
can only be opened withntcreatefile
because it is only in the NT namespace. There may be an equivalent win32 namespace link for certain devices and roundabout ways to match them with a printer name but this is difficult as others have shown in prior answers.Check out the
Global??
folder inWinObj
tool to show the symbolic links between win32 namespace and NT namespace on your machine. The old schoolCOM1
,COM2
,LPT1
, etc. device names are simply windows NT namespace symbolic links as well. Google "win32 nt namespace" for a more detailed explanation. (Sorry, but as a new user, I can only post 2 hyperlinks.)Below is what I finally have been able to come up with.
Please confirm that
SYSTEM\CurrentControlSet\Control\Print\Printers\{0}\PNPData
is a supported path, and not just happens to be there in the current implementation, subject to future changes.There's a little problem with structure alignment, for which I've posted a separate question.
Usage:
Try this ... let me know if this helps ...
Don't have a USB printer to test against, but this provides the information you are looking for (including for USB devices)...
Also, for a URI, change the '\'s to '#'s in the URI you are intending of building.
so ..
becomes
====
As GSerg pointed out that Win32_Printer Class helps with the above code, but doesn't provide the device id.
But if I use Win32_Printer class and print out the "PortName" property, that, for the printers I have installed gives be a port/filename that I can use with CreateFile() and open the device.
e.g.:
Here, writing to "XPSPORT:" or "SHRFAX:" sends data to the printer. What does this do for your USB printer?
I am not really a c++ guy, but I don't really think trying to generate the device id from the name is the way to go. However, you can
Use
EnumPrinters
and read thePRINTER_INFO_2 structure
to get the driver name, and then read the driver details from registry like in this example.Generate the name for yourself by finding out the printer details possibly from the PRINTER INFO structures and constructing it correctly. See http://msdn.microsoft.com/en-us/library/windows/hardware/ff553356(v=vs.85).aspx for details.
EDIT: You can actually get names + device instance ids of printers from registry: