Getting a handle on usbprint.sys

When you plug in a usb printer into a computer running Windows 2000, 2003 or XP the “plug and pray” subsystem loads and attaches the usbprint.sys bus driver to it, in essence taking over the device.

So its Thursday morning at work and I decide that since the Mac OS X version of our software can print directly to USB printers bypassing the operating system, its about time we added that functionality to the Linux and Windows versions.

Start on the Linux version

A quick scan through the /usr/src/linux/drivers/usb/devio.c source file reveals all about the usbdevfs file system and the ioctls required. Browse through the USB spec, a few calls to open() and ioctl(), and urb here, an urb there and I’m all done. Piece of cake.

Start on the Windows version

Shudder… Past experiences with Windows and anything to do with low level access to devices has taught me that I am in for the long haul. Each time I try to do something like this it always take 3 – 4 days or excruciating effort and unbearable pain. Oh well, I strap myself to my trusty Aeron chair and away I go.

Now from the MSDN documentation I manage to surmise that as soon as a USB printer is plugged in, the “plug and pray” subsystem loads up the usbprint.sys device driver and passes control over to it. In turn usbprint.sys creates a device node, initialises it and then sets up a device interface so user space programs can access it using the usual device semantics.

Great I think. I’ll just call CreateFile() to open the device and then use WriteFile() to send data to it. Couldn’t be easier. “clickety clack”, the code just flies from my fingers. CreateFile(name….). hmm.. what’s the name of the device. If it was Linux it would be /dev/usblp0 or something like that. Now what is it for Windows. A scan through the MSDN documentation reveals nothing. A search of google produces lots of hits to people asking the same question, but no answers. “Oh, oh this is going to be bad I think”. If there are no answers out there it means it is a closely guarded Microsoft secret and I am going to have to do some deep digging myself.

A more methodical scan of the MSDN docs and Google at least hints to what calls I need to make. In essence you do the following.

  1. Call SetupDiGetClassDevs(interface_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE) to get a list of devices.
  2. Call SetupDiEnumDeviceInterfaces() to get a list of available device interfaces.
  3. Call SetupDiGetDeviceInterfaceDetail() to fetch information for the actual device interface we are going to use.
  4. Use the DevicePath member of the PSP_DEVICE_INTERFACE_DETAIL_DATA structure returned by the SetupDiGetDeviceInterfaceDetail() call as the devicename/filename in the CreateFile() call.
  5. Call WriteFile() repeatedly to send data to the device.
  6. Call CloseHandle() to close the device

So in order to be able to find the name of the device, I need to find the GUID of the interface that usbprint.sys creates when it adds the devicenode to the system. Search… search… search… nothing. Search some more, nothing. Look through the Windows SDK and DDK include files with grep. Still nothing. Oh well up comes trusty old regedit, everything windows does is in the registry, and I start to wade through the myriad of nodes and entries for the GUID. After a few hours of despair I find it. The magic number is 0x28d78fad, 0x5a12, 0x11D1, 0xae, 0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2. You’d think Microsoft would have included it in one of the .h files (devguid.h or usbprint.h) just like they did the interface GUID for keyboards, mice, hid usb devices, usbcam devices etc. etc. etc.

Oh well at least I can now call open CreateFile and Write to the printer. So here we go:

HANDLE usbHandle = CreateFile(interfacename, GENERIC_WRITE, FILE_SHARE_READ,
                              NULL, OPEN_EXISTING, 0, NULL);
WriteFile(usbhandle, buf, numbytes, &byteswrtten);


The WriteFile call fails and GetLastError() returns 1, ie. the operation is not supported.

More searching, reading, nothing turns up. It keeps failing. I remember that the DDK includes source code for the local portmonitor so I take a look to see how it calls CreateFile for the local port interface. I modify mine to be the same and try again.

HANDLE usbHandle = CreateFile(interfacename, GENERIC_WRITE, FILE_SHARE_READ,
                              NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
                              FILE_FLAG_SEQUENTIAL_SCAN, NULL);
WriteFile(usbhandle, buf, numbytes, &byteswrtten);


Success, the printer starts outputting my job. What is different above? Well replacing OPEN_EXISTING with OPEN_ALWAYS seems to have forced the driver to start the device so that the WriteFile interface is available. Previously OPEN_EXISTING would not try and create a file so the device would not start. At least that is what I think.

4 days later and I am all done.

So here is a quick snippet of some of my code to help others out there that might have the same problem. You will need both the windows SDK and DDK installed and configured correctly for this code to compile. You will also need to link your executable with the setupapi library.

Below I have included the code as both an attachment and inline. It is not meant to compile out of the box. It is only there as a guide.

usbprint.c

/* Code to find the device path for a usbprint.sys controlled
* usb printer and print to it
*/



#include <devioctl.h>
#include <usb.h>
#include <usbiodef.h>
#include <usbioctl.h>
#include <usbprint.h>
#include <setupapi.h>
#include <devguid.h>
#include <wdmguid.h>


/* This define is required so that the GUID_DEVINTERFACE_USBPRINT variable is
 * declared an initialised as a static locally, since windows does not include it
 * in any of its libraries
 */

#define SS_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
static const GUID name \
= { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }

SS_DEFINE_GUID(GUID_DEVINTERFACE_USBPRINT, 0x28d78fad, 0x5a12, 0x11D1, 0xae,
               0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2);

void SomeFunctionToWriteToUSB()
{
  HDEVINFO devs;
  DWORD devcount;
  SP_DEVINFO_DATA devinfo;
  SP_DEVICE_INTERFACE_DATA devinterface;
  DWORD size;
  GUID intfce;
  PSP_DEVICE_INTERFACE_DETAIL_DATA interface_detail;
  HANDLE usbHandle;
  DWORD dataType;

  intfce = GUID_DEVINTERFACE_USBPRINT;
  devs = SetupDiGetClassDevs(&intfce, 0, 0, DIGCF_PRESENT |
                             DIGCF_DEVICEINTERFACE);
  if (devs == INVALID_HANDLE_VALUE) {
    return;
  }
  devcount = 0;
  devinterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  while (SetupDiEnumDeviceInterfaces(devs, 0, &intfce, devcount, &devinterface)) {
    /* The following buffers would normally be malloced to he correct size
     * but here we just declare them as large stack variables
     * to make the code more readable
     */
    char driverkey[2048];
    char interfacename[2048];
    char location[2048];
    char description[2048];

    /* If this is not the device we want, we would normally continue onto the
     * next one or so something like 
     * if (!required_device) continue; would be added here
     */
    devcount++;
    size = 0;
    /* See how large a buffer we require for the device interface details */
    SetupDiGetDeviceInterfaceDetail(devs, &devinterface, 0, 0, &size, 0);
    devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
    interface_detail = calloc(1, size);
    if (interface_detail) {
      interface_detail->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
      devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
      if (!SetupDiGetDeviceInterfaceDetail(devs, &devinterface, interface_detail,
                                           size, 0, &devinfo)) {
    free(interface_detail);
    SetupDiDestroyDeviceInfoList(devs);
    return;
      }
      /* Make a copy of the device path for later use */
      strcpy(interfacename, interface_detail->DevicePath);
      free(interface_detail);
      /* And now fetch some useful registry entries */
      size = sizeof(driverkey);
      driverkey[0] = 0;
      if (!SetupDiGetDeviceRegistryProperty(devs, &devinfo, SPDRP_DRIVER, &dataType,
                                            (LPBYTE)driverkey, size, 0)) {
    SetupDiDestroyDeviceInfoList(devs);
    return;
      }
      size = sizeof(location);
      location[0] = 0;
      if (!SetupDiGetDeviceRegistryProperty(devs, &devinfo,
                                            SPDRP_LOCATION_INFORMATION, &dataType,
                                            (LPBYTE)location, size, 0)) {
    SetupDiDestroyDeviceInfoList(devs);
    return;
      }
      usbHandle = CreateFile(interfacename, GENERIC_WRITE, FILE_SHARE_READ,
                 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | 
                 FILE_FLAG_SEQUENTIAL_SCAN, NULL);
      if (usbHandle != INVALID_HANDLE_VALUE) {
    /* Now perform all the writing to the device ie.
     * while (some condition) WriteFile(usbHandle, buf, size, &bytes_written);
     */
    CloseHandle(usbHandle);
      }
    }
  }
  SetupDiDestroyDeviceInfoList(devs);
}

Technorati Tags:

127 thoughts on “Getting a handle on usbprint.sys”

  1. Thanks so much. This was so obscure and hard to find. I spent 3 days and almost got to the point of writing my own driver until I found the device class ID here and found the device path. It should have been that easy. Maybe MS will learn and at least publish this at some point.

  2. Hi,
    Thanks for this post.I have implemented the same way as mentioned by you.I’m doing USB communication for my Barcode Printer.The code i’m writing in VS2005.Its compiling fine.I have included SetupApi.lib & SetupApi.h into my project but not all the header file ( #include
    #include
    #include
    #include
    #include
    #include
    )mentioned by you. I have installed the DDK and SDK given by microsoft in my PC.But the problem i’m facing is in this line :-
    while (SetupDiEnumDeviceInterfaces(devs, 0, &intfce, devcount, &devinterface))

    While debugging,its not entering into the while loop .Is the function will return true only when the devide is attached? Why i’m asking this is,becoz i have not connected my printer to the USB port.Kindly suggest some answer.If printer device is not attached, kindly tell me how to simulate the condition?? Pls. help.

  3. SetupDiEnumDeviceInterfaces() will only find usbprinter device nodes when a printer is attached. There is no way to simulate that, you simply have to attach a printer. There is no getting around that.

  4. Dear Sir,
    Thanks for giving code.By following you , my writefile function is returning true with Byte to write is equal to Byte written (these are two variables,which i’m checking after write file API).But i’m not able to print.The same string i’m able to print through command prompt.it means that the command that has to sent to the printer is correct,but why its not printing ?.Kindly note that my Zebra barcode printer driver is installed and ok.

  5. I found the ClassGUID is this the required GUID I need?

    Can I just eNumerate and use the Symbolic address and just write to the port with the required data.

    I am new to this so any help would be great.
    Trying to use the USB to Parallel as a data port.

    Thanks

  6. any sample doin this in C#/vb .net ?

    I ‘ll use it for sending raw data to lx300 through usb to paralel converter …

    thanks

  7. Peter, thanks so much for posting this code here. I stumbled across it after googling for 3 days to try and find some way to read and write raw data to a usb printer in C#. I can’t post the code on here because my client would lose their freakin’ mind if they ever found out I did but I will tell anyone wanting to get it working in .NET to go here . There is some sample C# code and all the structure and constant definitions that are not included here (you have to look for them but they are on that site.) It took me an additional 2 days after finding this page to get it working but most of that time was due to bone headed mistakes on my part.

    Thanks again for this invaluable resource!

  8. Great work Peter and others.
    In case it helps anyone, here’s my VB6 conversion.
    (Not elegant yet, but appears to work).

    Option Explicit

    Private Const DIGCF_PRESENT As Integer = &H2
    Private Const DIGCF_DEVICEINTERFACE As Integer = &H10
    Private Const DIGCF_ALLCLASSES As Integer = &H4
    Private Const GENERIC_READ = &H80000000
    Private Const GENERIC_WRITE = &H40000000
    Private Const FILE_FLAG_SEQUENTIAL_SCAN = &H8000000
    Private Const FILE_ATTRIBUTE_HIDDEN = &H2
    Private Const FILE_ATTRIBUTE_NORMAL = &H80
    Private Const FILE_SHARE_READ = &H1
    Private Const FILE_SHARE_WRITE = &H2
    Private Const OPEN_ALWAYS = 4
    Private Const OPEN_EXISTING = 3

    Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
    End Type

    Private Type Device_Interface_Data
    cbSize As Long
    InterfaceClassGuid As GUID
    Flags As Long
    ReservedPtr As Long
    End Type

    Private Type Device_Interface_Detail
    cbSize As Long
    DataPath(256) As Byte
    End Type

    Private Declare Function SetupDiGetDeviceInterfaceDetail _
    Lib “setupapi.dll” Alias “SetupDiGetDeviceInterfaceDetailA” _
    (ByVal DeviceInfoSet As Long, DeviceInterfaceData As Any, _
    DeviceInterfaceDetailData As Any, _
    ByVal DeviceInterfaceDetailDataSize As Long, _
    RequiredSize As Long, ByVal DeviceInfoData As Long) As Long

    Private Declare Function CreateFile Lib “kernel32” Alias “CreateFileA” _
    (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, _
    ByVal dwShareMode As Long, lpSecurityAttributes As Any, _
    ByVal dwCreationDisposition As Long, _
    ByVal dwFlagsAndAttributes As Long, _
    ByVal hTemplateFile As Long) As Long

    Private Declare Sub CloseHandle Lib “kernel32” _
    (ByVal HandleToClose As Long)

    Private Declare Function ReadFile Lib “kernel32” _
    (ByVal Handle As Long, ByVal BufferPtr As Long, _
    ByVal ByteCount As Long, BytesReturnedPtr As Long, _
    ByVal OverlappedPtr As Long) As Long

    Private Declare Function WriteFile Lib “kernel32” _
    (ByVal Handle As Long, Buffer As String, _
    ByVal ByteCount As Long, BytesReturnedPtr As Long, _
    ByVal OverlappedPtr As Long) As Long

    Private Declare Function SetupDiGetClassDevs Lib “setupapi.dll” _
    Alias “SetupDiGetClassDevsA” (GuidPtr As Long, _
    ByVal EnumPtr As Long, ByVal hwndParent As Long, _
    ByVal Flags As Long) As Long

    Private Declare Function SetupDiDestroyDeviceInfoList _
    Lib “setupapi.dll” _
    (ByVal DeviceInfoSet As Long) As Boolean

    Private Declare Function SetupDiEnumDeviceInterfaces _
    Lib “setupapi.dll” (ByVal Handle As Long, _
    ByVal InfoPtr As Long, GuidPtr As Long, _
    ByVal MemberIndex As Long, _
    InterfaceDataPtr As Long) As Boolean

    Private Sub Command1_Click()
    Text1.Text = SendToUsbPrinter(“Hello world.”)
    End Sub

    Function SendToUsbPrinter(PrintOut As String) As Boolean
    Dim PrnGuid As GUID
    Dim Success As Long, Ret As Long
    Dim Openned As Boolean
    Dim Buffer(256) As Byte
    Dim DeviceInterfaceData As Device_Interface_Data
    Dim FunctionClassDeviceData As Device_Interface_Detail
    Dim PnPHandle As Long, BytesReturned As Long
    Dim I As Long
    Dim DeviceName As String, DevIndex As Long, DeviceHandle As Long
    Dim ReqdSize As Long
    Dim BytesWritten As Long

    ‘ \\?\usb#vid_0a5f&pid_000a#41a081100503#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}
    PrnGuid.Data1 = &H28D78FAD
    PrnGuid.Data2 = &H5A12
    PrnGuid.Data3 = &H11D1
    PrnGuid.Data4(0) = &HAE
    PrnGuid.Data4(1) = &H5B
    PrnGuid.Data4(2) = &H0
    PrnGuid.Data4(3) = &H0
    PrnGuid.Data4(4) = &HF8
    PrnGuid.Data4(5) = &H3
    PrnGuid.Data4(6) = &HA8
    PrnGuid.Data4(7) = &HC2
    PnPHandle = SetupDiGetClassDevs(PrnGuid.Data1, 0, 0, _
    DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)
    SendToUsbPrinter = False
    If (PnPHandle = -1) Then
    MsgBox “Could not attach to PnP node”
    Else
    DeviceInterfaceData.cbSize = Len(DeviceInterfaceData)
    DevIndex = 0
    ‘ Should be a Do While -> looking for the correct device-name…
    If SetupDiEnumDeviceInterfaces(PnPHandle, 0, PrnGuid.Data1, _
    DevIndex, DeviceInterfaceData.cbSize) Then
    FunctionClassDeviceData.cbSize = 5
    Success = SetupDiGetDeviceInterfaceDetail(PnPHandle, _
    DeviceInterfaceData, FunctionClassDeviceData, _
    UBound(FunctionClassDeviceData.DataPath), _
    BytesReturned, 0)
    If Success = 0 Then
    MsgBox “Could not get the name of this device”
    Else
    DeviceName = “”
    I = 0
    Do While FunctionClassDeviceData.DataPath(I) 0
    DeviceName = DeviceName & _
    Chr$(FunctionClassDeviceData.DataPath(I))
    I = I + 1
    Loop
    Debug.Print DeviceName
    DeviceHandle = CreateFile(DeviceName, _
    GENERIC_WRITE, FILE_SHARE_READ, _
    0, OPEN_ALWAYS, _
    FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, _
    0)
    If (DeviceHandle = -1) Then
    Debug.Print “Open failed on ” & DeviceName
    Else
    Ret = WriteFile(DeviceHandle, PrintOut, _
    Len(PrintOut), BytesWritten, 0)
    Debug.Print “Sent ” & BytesWritten & ” bytes.”
    SendToUsbPrinter = True
    CloseHandle DeviceHandle
    End If
    End If
    Else
    MsgBox “Device not connected”
    End If
    SetupDiDestroyDeviceInfoList (PnPHandle)
    End If
    End Function

  9. firts of all thanks for the code, is exelent, and thanks too, for reply to me so quickly…
    I manage to have succes in get it done the code you post, but the problem is that i dont really need print anything, what i really want is send a signal and control the way is sended for example make the pin 6, 7, 8, 9 turn on(leds conected to the lpt out in a cable USB-PARALLEL)

    I really sorry if my english is bad…..i am spanish speaker…..

  10. Dear All,

    I am programming a Printer monitoring tool for HP USB Deskjets in .Net.

    I could use SetupDiGetClassDevs(..), SetupDiEnumDeviceInterfaces(..) etc. to obtain the Symbolic Name of the devices which are connected to the PC, but i am not yet successful in obtaining the Friendly name from the Symbolic name. The friendly names are the names which are displayed in “Printers and Faxes”.

    For you ref the symbolic names are of the form

    \??\USB#Vid_03f0&Pid_6545#0000000002310#{a5dcbf10-6530-11d2-901f-00c04fb951ed}

    I would be very glad and thankful if someone could help me by pointing the correct method or steps to follow if possible with a snippet.

    Best regards

    Narayanan

  11. I’m not using the LPT to use a printer the software that i use is to control an osciloscope and it needs to have an address like 278, 378 or 3BC to run ok.

    What do i have to do?

  12. I just can’t return in WriteFile(), it’s blocked.
    I can get \\?\USB name use peter’s GUID and SetupDi functions,no error with CreateFile() ,but when I call Deviceiocontrol() or WriteFile() it’s blocked and no return. I don’t attach a printer , I just plug in the USB to Parallel cable, is it necessary to attach a printer with the cable for all things work ?

  13. Thank you Peter for this information. I was pretty sure I’d wasted money after tons of googling turned up only complaints about misleading labeling, but then I finally found this site and all is saved! =-)

    I wonder how hard it’d be to make a driver that provided the normal parallel port interface to one of these “Parallel Printer Cable”s.

    With the “USB to (not really) Parallel Port Cable” I bought for electronics projects, I got WriteFile to return by grounding the BUSY pin (see wikipedia or any number of other sites for pin definitions) and pulsing the ACK pin. Somebody suggested just grounding ACK, but that only worked for a single byte – I’m guessing the circuit in my cable checks both edges of the ACK signal.
    Another site suggested connecting ACK to STROBE, and that seems to work OK for a few bytes, but then I guess the in-cable microcontroller misses the ACK, because it’ll stop sending after a few bytes. I haven’t tried it yet, but I’m thinking that connecting STROBE to the input of a monostable multivibrator and the output of that to ACK will fix the problem by extending the pulse duration (unless I’m completely wrong and that isn’t the problem).

  14. Hi, i´m tring tio traslate this code(Old Duster says: January 20, 2010 at 2:10 pm) to Visual Studio 2010 (Visual Basic) but i ´don´t have the same solution that Visual Basic 6.0, can you help me??

    Public Class Form1

    Public Sub Command1_Click()

    End Sub

    Function SendToUsbPrinter(ByVal PrintOut As String) As Boolean

    Dim PrnGuid As GUID
    ‘Dim InterfaceClassGuid As GUID
    Dim Success As Long, Ret As Long
    Dim Openned As Boolean
    Dim Buffer(256) As Byte
    Dim DeviceInterfaceData As Device_Interface_Data
    Dim FunctionClassDeviceData As Device_Interface_Detail
    Dim PnPHandle As Long, BytesReturned As Long
    Dim I As Long
    Dim DeviceName As String, DevIndex As Long, DeviceHandle As Long
    Dim ReqdSize As Long
    Dim BytesWritten As Long
    Dim d As Integer

    PrnGuid.Data4 = New Byte() {0, 1, 2, 3, 4, 5, 6, 7}
    FunctionClassDeviceData.DataPath = New Byte() {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    ‘Data4() = Data4({0, 1, 2, 3, 4, 5, 6, 7})
    DeviceInterfaceData.cbSize = 28
    ‘ \\?\usb#vid_0a5f&pid_000a#41a081100503#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}

    PrnGuid.Data1 = &H28D78FAD
    PrnGuid.Data2 = &H5A12
    PrnGuid.Data3 = &H11D1
    PrnGuid.Data4(0) = &HAE
    PrnGuid.Data4(1) = &H5B
    PrnGuid.Data4(2) = &H0
    PrnGuid.Data4(3) = &H0
    PrnGuid.Data4(4) = &HF8
    PrnGuid.Data4(5) = &H3
    PrnGuid.Data4(6) = &HA8
    PrnGuid.Data4(7) = &HC2

    ‘DeviceInterfaceData.InterfaceClassGuid.Data4 = {0, 0, 0, 0, 0, 0, 0, 0}

    PnPHandle = SetupDiGetClassDevs(PrnGuid.Data1, 0, 0, _
    DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)

    SendToUsbPrinter = False
    If (PnPHandle = -1) Then
    MsgBox(“Could not attach to PnP node”)
    Else
    DeviceInterfaceData.cbSize = Len(DeviceInterfaceData)
    DevIndex = 0
    ‘ Should be a Do While -> looking for the correct device-name…
    If SetupDiEnumDeviceInterfaces(PnPHandle, 0, PrnGuid.Data1, _
    DevIndex, DeviceInterfaceData.cbSize) = 0 Then

    FunctionClassDeviceData.cbSize = 5
    Success = SetupDiGetDeviceInterfaceDetail(PnPHandle, _
    DeviceInterfaceData, FunctionClassDeviceData, _
    UBound(FunctionClassDeviceData.DataPath), _
    BytesReturned, 0)
    If Success = 0 Then
    MsgBox(“Could not get the name of this device”)

    Else
    DeviceName = “”
    I = 0
    Do While FunctionClassDeviceData.DataPath(I) = 0
    DeviceName = DeviceName & _
    Chr(FunctionClassDeviceData.DataPath(I))
    I = I + 1

    Loop
    Debug.Print(DeviceName)
    DeviceHandle = CreateFile(DeviceName, _
    GENERIC_WRITE, FILE_SHARE_READ, _
    0, OPEN_ALWAYS, _
    FILE_ATTRIBUTE_NORMAL + FILE_FLAG_SEQUENTIAL_SCAN, _
    0)
    If (DeviceHandle = -1) Then
    Debug.Print(“Open failed on ” & DeviceName)
    Else
    Ret = WriteFile(DeviceHandle, PrintOut, _
    Len(PrintOut), BytesWritten, 0)
    Debug.Print(“Sent ” & BytesWritten & ” bytes.”)
    SendToUsbPrinter = True
    CloseHandle(DeviceHandle)
    End If
    End If
    Else
    MsgBox(“Dispositvo no conectado”)
    End If
    SetupDiDestroyDeviceInfoList(PnPHandle)
    End If
    End Function
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    TextBox1.Text = SendToUsbPrinter(“Hello world.”)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

    End Sub
    End Class

    Module Module1
    Public Const DIGCF_PRESENT As Integer = &H2
    Public Const DIGCF_DEVICEINTERFACE As Integer = &H10
    Public Const DIGCF_ALLCLASSES As Integer = &H4
    Public Const GENERIC_READ = &H80000000
    Public Const GENERIC_WRITE = &H40000000
    Public Const FILE_FLAG_SEQUENTIAL_SCAN = &H8000000
    Public Const FILE_ATTRIBUTE_HIDDEN = &H2
    Public Const FILE_ATTRIBUTE_NORMAL = &H80
    Public Const FILE_SHARE_READ = &H1
    Public Const FILE_SHARE_WRITE = &H2
    Public Const OPEN_ALWAYS = 4
    Public Const OPEN_EXISTING = 3

    Public Structure GUID

    Dim Data1 As Long
    Dim Data2 As Integer
    Dim Data3 As Integer
    Dim Data4() As Byte

    End Structure

    Public Structure Device_Interface_Data
    Dim cbSize As Long
    Dim InterfaceClassGuid As GUID
    Dim Flags As Long
    Dim ReservedPtr As Long
    End Structure

    Public Structure Device_Interface_Detail
    Dim cbSize As Long
    Dim DataPath() As Byte
    End Structure

    Public Property TextBox As Boolean
    Public Declare Function SetupDiGetDeviceInterfaceDetail _
    Lib “setupapi.dll” Alias “SetupDiGetDeviceInterfaceDetailA” _
    (ByVal DeviceInfoSet As Long, ByVal DeviceInterfaceData As Object, _
    ByVal DeviceInterfaceDetailData As Object, _
    ByVal DeviceInterfaceDetailDataSize As Long, _
    ByVal RequiredSize As Long, ByVal DeviceInfoData As Long) As Long

    Public Declare Function CreateFile Lib “” Alias “” (
    ByVal lpFileName As String, ByVal dwDesiredAccess As Long, _
    ByVal dwShareMode As Long, ByVal lpSecurityAttributes As String, _
    ByVal dwCreationDisposition As Long, _
    ByVal dwFlagsAndAttributes As Long, _
    ByVal hTemplateFile As Long) As Long

    Public Declare Sub CloseHandle Lib “” (ByVal HandleToClose As Long)

    Public Declare Function ReadFile Lib “” (ByVal Handle As Long, ByVal BufferPtr As Long, _
    ByVal ByteCount As Long, ByVal BytesReturnedPtr As Long, _
    ByVal OverlappedPtr As Long) As Long

    Public Declare Function WriteFile Lib “” (ByVal Handle As Long, ByVal Buffer As String, _
    ByVal ByteCount As Long, ByVal BytesReturnedPtr As Long, _
    ByVal OverlappedPtr As Long) As Long

    Public Declare Function SetupDiGetClassDevs Lib “setupapi.dll” _
    Alias “SetupDiGetClassDevsA” (ByVal GuidPtr As Long, _
    ByVal EnumPtr As Long, ByVal hwndParent As Long, _
    ByVal Flags As Long) As Long

    Public Declare Function SetupDiDestroyDeviceInfoList _
    Lib “setupapi.dll” _
    (ByVal DeviceInfoSet As Long) As Boolean

    Public Declare Function SetupDiEnumDeviceInterfaces _
    Lib “setupapi.dll” (ByVal Handle As Long, _
    ByVal InfoPtr As Long, ByVal GuidPtr As Long, _
    ByVal MemberIndex As Long, _
    ByVal InterfaceDataPtr As Long) As Boolean

    End Module

    thank you a lot.

  15. Thank´s a lot for your codes, I´ve tried vb6 code but when I send anything, the printer prints a page with several ASCII codes instead of real text, I´ve tried sending strings and PJL commans, could you help me, please?
    Than you a lot.

  16. Iam trying to simulate USB printer device using microsoft’s device simulation framework. When a real USB printer is pluged, two device nodes appear in windows device manager 1. USB Printing support under host controllers(USB enumarator). 2. another node with USBPRINT enumarator as explained in the following link: //msdn.microsoft.com/en-us/library/ff560809(v=vs.85).aspx But when the simulated device pluged in only USB printing support is created. the simualted device sends the response for GET_DEVICE_ID request by sending IEEE 1284 device ID. the string is sent as unsigned char, first two bytes gives the length and rest of the string is MFG,CMD,MDL,CLS,status values. But still the second node in the device manager is not created. any other way to invoke the host to create another node?

  17. Hi, great post!

    I’m trying to port a Windows code that uses usbprint to linux and mac, and I need the linux/mac replacements for IOCTL_USBPRINT_VENDOR_SET_COMMAND and IOCTL_USBPRINT_VENDOR_GET_COMMAND windows ioctl’s. Any idea?

    Thanks in advance

  18. You cannot use this code for Linux or Mac. Those systems have totally different APIs so you will have to write code from scratch. The USB API for those platforms is very well documented so you should have no problem writing code to achieve the same thing on those platforms.

  19. Hi I am new in windows driver programing but I am trying to do using java but I failed.So I try to do usb printing using some native code and marge it with my java application, but till now i did not how can I connect this code in java.I load WSDK and WDDK but how can I compile this code can anyone give me some idea.Please give me some positive reply.

  20. how this code will be compiled from netbeans I presently use ming to compile c code but I can not compile this code though I put all header file into MinGW\lib\gcc\mingw32\4.4.1\include\c++.

  21. Hi

    I want to know that how can we get handle on usbprint.sys using csharp vs2008 which name space will be use for performing this task please help really searched lot but could’nt find any thing 🙁

  22. Thanks! I’m working on a little program to remind me (or my kids!) that they left the printer turned on, so this is just what I need — if the usbHandle is valid, then a printer is on!

    A couple notes: you could
    1) declare
    HANDLE usbHandle;
    DWORD dataType;
    2) add #include

    and it would be much closer to compiling as is. I had to tweak a couple other lines because I work in Unicode.

  23. Thanks Geoffrey for the comment on missing variable declarations and include file. I’ve updated the code so that it should compile out of the box.

Comments are closed.