locked
I need to send a simple binary string to a printer port, such as "00000011" RRS feed

  • Question

  • I need to be able to send a binary string such as 00000011 to a printer port using visual basic.  Any suggestions?
    Friday, July 3, 2009 8:59 PM

Answers

  • After going over the code in KB 154078, I used the P/Invoke Interop Assistant to generate the .Net code for these print related API calls.  The result of the generated code is the following VB module:

    Module Win32Api
        Public Class NativeMethods
            '''Return Type: BOOL->int
            '''pPrinterName: LPSTR->CHAR*
            '''phPrinter: LPHANDLE->HANDLE*
            '''pDefault: LPPRINTER_DEFAULTSA->_PRINTER_DEFAULTSA*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="OpenPrinterA")> _
            Public Shared Function OpenPrinterA(<System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> ByVal pPrinterName As String, ByRef phPrinter As System.IntPtr, <System.Runtime.InteropServices.InAttribute()> ByVal pDefault As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="ClosePrinter")> _
            Public Shared Function ClosePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="StartPagePrinter")> _
            Public Shared Function StartPagePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: DWORD->unsigned int
            '''hPrinter: HANDLE->void*
            '''Level: DWORD->unsigned int
            '''pDocInfo: LPBYTE->BYTE*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="StartDocPrinterA")> _
            Public Shared Function StartDocPrinterA(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr, ByVal Level As UInteger, <System.Runtime.InteropServices.InAttribute()> ByRef pDocInfo As DOCINFOA) As UInteger
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="EndDocPrinter")> _
            Public Shared Function EndDocPrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="EndPagePrinter")> _
            Public Shared Function EndPagePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            '''pBuf: LPVOID->void*
            '''cbBuf: DWORD->unsigned int
            '''pcWritten: LPDWORD->DWORD*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="WritePrinter")> _
            Public Shared Function WritePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr, <System.Runtime.InteropServices.InAttribute()> ByVal pBuf As System.IntPtr, ByVal cbBuf As UInteger, <System.Runtime.InteropServices.OutAttribute()> ByRef pcWritten As UInteger) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
        End Class
    
        <System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)> _
        Public Structure DOCINFOA
            '''int
            Public cbSize As Integer
    
            '''LPCSTR->CHAR*
            <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> _
            Public lpszDocName As String
    
            '''LPCSTR->CHAR*
            <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> _
            Public lpszOutput As String
    
            '''LPCSTR->CHAR*
            <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> _
            Public lpszDatatype As String
    
            '''DWORD->unsigned int
            Public fwType As UInteger
        End Structure
    End Module

    Add this module to the VB project in order to access the Win32 API imports on your form.

    It should then be possible to send raw data to an installed printer using code something like the following (assuming I've got my COM interop correct - that's not something I'm very experienced with):

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        WriteRawDataToPrinter("Generic / Text Only", "TestPrintJob", "00000011")
    End Sub
    
    Private Sub WriteRawDataToPrinter(ByVal printerName As String, ByVal printJobName As String, _
                                      ByVal dataToWrite As String)
        Dim printerPointer As IntPtr
        Dim dataWritten As UInteger
        Dim printJobID As UInteger
    
        If Win32Api.NativeMethods.OpenPrinterA(printerName, printerPointer, 0) Then
            Dim documentInfo As Win32Api.DOCINFOA
            documentInfo.lpszDocName = printJobName
            documentInfo.lpszOutput = Nothing
            documentInfo.lpszDatatype = Nothing
    
            printJobID = Win32Api.NativeMethods.StartDocPrinterA(printerPointer, 1, documentInfo)
    
            If printJobID > 0 Then
                If Win32Api.NativeMethods.StartPagePrinter(printerPointer) Then
    
                    Dim msgPointer As IntPtr
                    msgPointer = System.Runtime.InteropServices.Marshal.StringToHGlobalUni(dataToWrite)
    
                    If Win32Api.NativeMethods.WritePrinter(printerPointer, msgPointer, _
                             dataToWrite.Length, dataWritten) Then
                        If dataToWrite.Length <> dataWritten Then
                            MessageBox.Show("Incorrect ammount of data written.")
                        End If
                    Else
                        MessageBox.Show("The printer could not be written.")
                    End If
    
                    If Not Win32Api.NativeMethods.EndPagePrinter(printerPointer) Then
                        MessageBox.Show("Error ending page printer.")
                    End If
    
                    System.Runtime.InteropServices.Marshal.FreeHGlobal(msgPointer)
                Else
                    MessageBox.Show("The page printer could not be started.")
                End If
            Else
                MessageBox.Show("The document printer could not be started.")
            End If
    
            If Not Win32Api.NativeMethods.EndDocPrinter(printerPointer) Then
                MessageBox.Show("Error ending doc printer.")
            End If
    
            If Not Win32Api.NativeMethods.ClosePrinter(printerPointer) Then
                MessageBox.Show("Error closing printer.")
            End If
        Else
            MessageBox.Show("The Printer Name you typed wasn't recognized.")
        End If
    End Sub
    


    Using the Printers folder in the Control Panel, you should be able to add a new "Generic / Text Only" printer using LPT1.  Printing to this printer using the code above should bypass the driver and send the raw bytes of your string directly to the device connected to the LPT1 port.

    I don't have a device to actually test this with, but I see that running the code does initiate a print job in the spooler for the selected printer (no paper comes out as my printers don't know what to do with the raw data and ignore the job).  So I think this code is working as intended, but I can't be sure...

    It may take a little more tweaking, but I would expect this to get close to being able to send a string of "0101010101" directly to a device on LPT1...

    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Wednesday, July 8, 2009 8:26 PM
    Moderator

All replies

  • Hmm

    This aint easy... Generatiing the code for you binary string is.... but sending it is requires a krernel driver or dll file of some sort... I have done this a long time ago with a dll file..

    You should see this link... Maybe thats a start:

    http://www.codeproject.com/KB/vb/Inpout32_read.aspx


    Kenneth
    Saturday, July 4, 2009 2:13 PM
  • Hi,

    I have two ideas for you.

    First, you used to be able to simply open a file called LPT1 and write to it.  I'm not sure if that will work under Vista, but it worked on XP.  It's been a while, but it seems like it had to be in the root directory, and it might have had to have an extension like LPT1.txt.  That used to work for all hardware such as LPT1, LPT2, COM1, COM2, etc (although com ports were more troublesome as you had other parameters to deal with (speed, parity, etc).

    Another approach to this would be to use a serial to parallel converter, available from places like Black Box or B&B Electronics for maybe $100.  That allows your program to use a serial port, which has a relatively easy to use interface, instead of the more difficult parallel port.  I'm not sure what your application is, or how difficult the parallel port will end up being to use, but $100 to avoid the headaches might be a good deal.

    Ray
    Tuesday, July 7, 2009 7:06 PM
  • Since it is a string you wish to send, how about using the old "TYPE" command and redirecting the output to LPT1?

    Something like:

        Private Sub WriteStringToLPT1(ByVal stringToWrite As String)
            Dim procInfo As New System.Diagnostics.ProcessStartInfo
            procInfo.CreateNoWindow = True
            procInfo.FileName = "cmd"
            procInfo.RedirectStandardInput = True
            procInfo.UseShellExecute = False
    
            Dim dataPrinter As System.Diagnostics.Process
            dataPrinter = System.Diagnostics.Process.Start(procInfo)
    
            Dim tmpFileName As String = System.IO.Path.GetTempFileName
            System.IO.File.WriteAllText(tmpFileName, stringToWrite)
    
            Dim cmdString As String = String.Concat("TYPE ", tmpFileName, " > LPT1")
            dataPrinter.StandardInput.WriteLine(cmdString)
    
            dataPrinter.StandardInput.WriteLine("EXIT")
            System.IO.File.Delete(tmpFileName)
        End Sub
    So that will launch a command prompt in the background, write the desired string out to a temp file, issue the TYPE command to print the temp file to LPT1 and then exit command prompt and delete the temp file.  Note that LPT1 will have to have a device available or the code will hang (there should be additional work done to allow for a timeout, but this was just to keep the example simple).

    See if that gets anything close to what you are trying to do...
    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Tuesday, July 7, 2009 7:46 PM
    Moderator
  • Hi Reed

    When I did write and read to the parrallel port on an old win95 ... I read som place that the values you are sending and reciving are stored in the registery... Is this still the case?

    Do you have an simple eexample for reading the ports at a printer too?
    Kenneth
    Tuesday, July 7, 2009 8:13 PM
  • Hi Kenneth,

    I'm not sure about the registry usage you are referring to in Win95...

    In the code in my example above is taking advantage of an old DOS command, so it would predate any OS with a Registry.  So I wouldn't think that any registry usage comes into play here, unless it was part of the DOS emulation implementation in later versions of Windows...

    I do not have any samples of accessing a printer port directly to either read or write... I would guess that there are Win32 API calls which would give you access to the printer ports directly, but I don't know that they would be... wait... ah, here we go...

    http://support.microsoft.com/kb/q154078/

    The example code is in VB6, but should be pretty simple to convert to .net since it appears to be mostly using API calls.  I haven't gone through it closely, but this appears to be a start in the right direction - it at least appears to call out the relevant Win32 API functions and should give a starting point for further MSDN research.
    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Tuesday, July 7, 2009 9:54 PM
    Moderator
  • Hi Kenneth

    When I did write and read to the parrallel port on an old win95 ...
    Win95 has never been supported for Net so the problem solves itself for this forum


    Success
    Cor
    Wednesday, July 8, 2009 4:50 AM
  • Do you mean with a binary string the hex value 03 or do you mean something else, it is for me very confusing.

    It looks like an unicode string but also simply to a string of bits in a byte.
    Success
    Cor
    Wednesday, July 8, 2009 5:14 AM
  • Hi Kenneth

    When I did write and read to the parrallel port on an old win95 ...
    Win95 has never been supported for Net so the problem solves itself for this forum


    Success
    Cor

    Hi Cor

    I finally found the dll file I was importing to read and write to the parallell port... And I was using WinXP not Win95...

    http://logix4u.net/Legacy_Ports/Parallel_Port/Inpout32.dll_for_Windows_98/2000/NT/XP.html

    As I remember it worked like a charm:)
    Wow... it been a while... as I remember i was using Borland C++ to reate the program

    Kenneth
    Wednesday, July 8, 2009 3:41 PM
  • Kenneth - Just try to put that Borland stuff behind you, and remember that we all support you here.

    btw, Jan Axelson [Lake View Research] wrote that DLL.       http://www.lvr.com/
    Wednesday, July 8, 2009 5:19 PM
  • Kenneth - Just try to put that Borland stuff behind you, and remember that we all support you here.

    btw, Jan Axelson [Lake View Research] wrote that DLL.       http://www.lvr.com/

    Thamks jinzai.0
    No problem in forgetting Borland C++... It is hovever worse that I somethimes design subs and functions in vb like you do in C++.... :)
    Jan Axelson...  no wonder it worked so beautifully....
    Kenneth
    Wednesday, July 8, 2009 7:57 PM
  • After going over the code in KB 154078, I used the P/Invoke Interop Assistant to generate the .Net code for these print related API calls.  The result of the generated code is the following VB module:

    Module Win32Api
        Public Class NativeMethods
            '''Return Type: BOOL->int
            '''pPrinterName: LPSTR->CHAR*
            '''phPrinter: LPHANDLE->HANDLE*
            '''pDefault: LPPRINTER_DEFAULTSA->_PRINTER_DEFAULTSA*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="OpenPrinterA")> _
            Public Shared Function OpenPrinterA(<System.Runtime.InteropServices.InAttribute(), System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> ByVal pPrinterName As String, ByRef phPrinter As System.IntPtr, <System.Runtime.InteropServices.InAttribute()> ByVal pDefault As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="ClosePrinter")> _
            Public Shared Function ClosePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="StartPagePrinter")> _
            Public Shared Function StartPagePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: DWORD->unsigned int
            '''hPrinter: HANDLE->void*
            '''Level: DWORD->unsigned int
            '''pDocInfo: LPBYTE->BYTE*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="StartDocPrinterA")> _
            Public Shared Function StartDocPrinterA(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr, ByVal Level As UInteger, <System.Runtime.InteropServices.InAttribute()> ByRef pDocInfo As DOCINFOA) As UInteger
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="EndDocPrinter")> _
            Public Shared Function EndDocPrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="EndPagePrinter")> _
            Public Shared Function EndPagePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
    
            '''Return Type: BOOL->int
            '''hPrinter: HANDLE->void*
            '''pBuf: LPVOID->void*
            '''cbBuf: DWORD->unsigned int
            '''pcWritten: LPDWORD->DWORD*
            <System.Runtime.InteropServices.DllImportAttribute("WinSpool.drv", EntryPoint:="WritePrinter")> _
            Public Shared Function WritePrinter(<System.Runtime.InteropServices.InAttribute()> ByVal hPrinter As System.IntPtr, <System.Runtime.InteropServices.InAttribute()> ByVal pBuf As System.IntPtr, ByVal cbBuf As UInteger, <System.Runtime.InteropServices.OutAttribute()> ByRef pcWritten As UInteger) As <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)> Boolean
            End Function
        End Class
    
        <System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)> _
        Public Structure DOCINFOA
            '''int
            Public cbSize As Integer
    
            '''LPCSTR->CHAR*
            <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> _
            Public lpszDocName As String
    
            '''LPCSTR->CHAR*
            <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> _
            Public lpszOutput As String
    
            '''LPCSTR->CHAR*
            <System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)> _
            Public lpszDatatype As String
    
            '''DWORD->unsigned int
            Public fwType As UInteger
        End Structure
    End Module

    Add this module to the VB project in order to access the Win32 API imports on your form.

    It should then be possible to send raw data to an installed printer using code something like the following (assuming I've got my COM interop correct - that's not something I'm very experienced with):

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        WriteRawDataToPrinter("Generic / Text Only", "TestPrintJob", "00000011")
    End Sub
    
    Private Sub WriteRawDataToPrinter(ByVal printerName As String, ByVal printJobName As String, _
                                      ByVal dataToWrite As String)
        Dim printerPointer As IntPtr
        Dim dataWritten As UInteger
        Dim printJobID As UInteger
    
        If Win32Api.NativeMethods.OpenPrinterA(printerName, printerPointer, 0) Then
            Dim documentInfo As Win32Api.DOCINFOA
            documentInfo.lpszDocName = printJobName
            documentInfo.lpszOutput = Nothing
            documentInfo.lpszDatatype = Nothing
    
            printJobID = Win32Api.NativeMethods.StartDocPrinterA(printerPointer, 1, documentInfo)
    
            If printJobID > 0 Then
                If Win32Api.NativeMethods.StartPagePrinter(printerPointer) Then
    
                    Dim msgPointer As IntPtr
                    msgPointer = System.Runtime.InteropServices.Marshal.StringToHGlobalUni(dataToWrite)
    
                    If Win32Api.NativeMethods.WritePrinter(printerPointer, msgPointer, _
                             dataToWrite.Length, dataWritten) Then
                        If dataToWrite.Length <> dataWritten Then
                            MessageBox.Show("Incorrect ammount of data written.")
                        End If
                    Else
                        MessageBox.Show("The printer could not be written.")
                    End If
    
                    If Not Win32Api.NativeMethods.EndPagePrinter(printerPointer) Then
                        MessageBox.Show("Error ending page printer.")
                    End If
    
                    System.Runtime.InteropServices.Marshal.FreeHGlobal(msgPointer)
                Else
                    MessageBox.Show("The page printer could not be started.")
                End If
            Else
                MessageBox.Show("The document printer could not be started.")
            End If
    
            If Not Win32Api.NativeMethods.EndDocPrinter(printerPointer) Then
                MessageBox.Show("Error ending doc printer.")
            End If
    
            If Not Win32Api.NativeMethods.ClosePrinter(printerPointer) Then
                MessageBox.Show("Error closing printer.")
            End If
        Else
            MessageBox.Show("The Printer Name you typed wasn't recognized.")
        End If
    End Sub
    


    Using the Printers folder in the Control Panel, you should be able to add a new "Generic / Text Only" printer using LPT1.  Printing to this printer using the code above should bypass the driver and send the raw bytes of your string directly to the device connected to the LPT1 port.

    I don't have a device to actually test this with, but I see that running the code does initiate a print job in the spooler for the selected printer (no paper comes out as my printers don't know what to do with the raw data and ignore the job).  So I think this code is working as intended, but I can't be sure...

    It may take a little more tweaking, but I would expect this to get close to being able to send a string of "0101010101" directly to a device on LPT1...

    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Wednesday, July 8, 2009 8:26 PM
    Moderator
  • @Reed Kimble:

    Why not set that printer to print to a file, then examine the file's content? (I have print code that I can only test in this manner, or by printing to a printer with a PDF reader/writer as the output.)

    btw, that's awesome code...give yourself another badge. (You can put it on the back, if you need more room.)
    Wednesday, July 8, 2009 9:00 PM
  • @Reed Kimble:

    Why not set that printer to print to a file, then examine the file's content? (I have print code that I can only test in this manner, or by printing to a printer with a PDF reader/writer as the output.)

    btw, that's awesome code...give yourself another badge. (You can put it on the back, if you need more room.)

    The only locally attached printer I happen to have at the moment is a Zebra label printer (a test device for another project).  The driver for this printer doesn't offer a print-to-file option.

    Its a good idea though!  If I happen to get the free time, I can go to my IT Closet and probably find a simple dot matrix printer that works with raw data (relies on ASCII print codes added to the data to control the print job).

    LOL on the badge comment ;)  But I can't take too much credit for that code (if it even works properly!) since the P/Invoke tool did most of the hard work.  =P

    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Thursday, July 9, 2009 3:03 PM
    Moderator
  • (Note the edit to the code - I had some of the operations out of place and added a check to confirm the length of data written.)
    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Thursday, July 9, 2009 3:14 PM
    Moderator