You are currently offline, waiting for your internet to reconnect

HOW TO: Determine Printer Status and Print Job Status from Visual Basic

This article was previously published under Q202480
This article describes how to use Windows application programming interface (API) functions in Visual Basic to determine the printer status or the print job status programmatically. Although an application does not typically have to check the status of a printer before printing, it may be useful to determine the status of a printer or print job programmatically.

back to the top

Description of the Technique

The term "printer" can refer to a hardware device, a queue, a driver, or even a port. In this article, the term "printer" is defined as a local print queue. The code sample in this article returns the statuses that the operating system reports. This is the same status that the spooler reports, which you can check by watching the local print queue.

For example, to view the queue on a computer that is running Microsoft Windows 98, click Start, point to Settings, click Printers, and then double-click the icon for the printer whose queue you want to view.

NOTE: You cannot communicate directly with the physical printer. You should not have to do this because the operating system must arbitrate access to the hardware.

The sample code in this article examines the local print queue, which obtains information from the port monitor, which in turn communicates with the physical device. For a more detailed description of how this works, see the articles that are listed in the "References" section of this article.

This sample reports the status from the printer and from the jobs, but note that the job status information is generally more reliable for applications. Ideally, you should examine the jobs and the printer statuses separately, and the code should infer the "meta" status of the queue. However, for most uses, your code can rely on the job statuses. Also, this example loops through the jobs and checks and reports the status of each job. This is because your job may follow another job that is reporting a problem, such as being out of paper or getting jammed.

NOTE: The system only checks the status when the system has a job to spool. Otherwise, the queue is considered "ready" because the queue can accept jobs, even if the hardware is in an error state. For example, if the last job that was printed used the last piece of paper, the operating system does not know this until the system tries to print again.

Additionally, although there are many statuses that may be reported, many are not supported in practice. The printer hardware and the port monitor determine which statuses to report. For example, if the printer is out of paper and offline, the status may be reported as "Printing" because that is what the job is trying to do. Therefore, a queue that displays "Ready" does not guarantee that your print job will complete successfully.

This code sample examines only the local queue, which should suffice for most applications. However, connecting to remote printers can become fairly complex. You can have chained queues, in which the port for the local queue is actually another queue. You can have printer pooling, in which multiple printers work from a common super queue. When the architecture becomes more complex, the code to retrieve a meaningful status also becomes more complex, and the usefulness of the status is reduced.

On Microsoft Windows 95, Microsoft Windows 98, and Microsoft Windows Millennium Edition only, you can also examine the PrinterInfo.Attributes field for the bit PRINTER_ATTRIBUTE_WORK_OFFLINE. This state is something that typically occurs because of user action (for example, if the user right-clicks a printer icon and then clicks Work Offline). This state does not occur because of the state change of a print job, although the operating system can force state if the printer cannot be connected to despool a job. In that case, the status is typically reported as USER_INERVENTION_REQUIRED.

back to the top

Step-by-Step Example

  1. Create a new Standard EXE project in Visual Basic. By default, Form1 is created.
  2. On the Project menu, click Add Module, and then add the following code:
    Option ExplicitPublic Declare Function lstrcpy Lib "kernel32" _   Alias "lstrcpyA" _   (ByVal lpString1 As String, _   ByVal lpString2 As String) _   As LongPublic Declare Function OpenPrinter Lib "winspool.drv" _   Alias "OpenPrinterA" _   (ByVal pPrinterName As String, _   phPrinter As Long, _   pDefault As PRINTER_DEFAULTS) _   As LongPublic Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA" _   (ByVal hPrinter As Long, _   ByVal Level As Long, _   pPrinter As Byte, _   ByVal cbBuf As Long, _   pcbNeeded As Long) _   As LongPublic Declare Function ClosePrinter Lib "winspool.drv" _   (ByVal hPrinter As Long) _   As LongPublic Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _   (Destination As Any, _   Source As Any, _   ByVal Length As Long)Public Declare Function EnumJobs Lib "winspool.drv" Alias "EnumJobsA" _   (ByVal hPrinter As Long, _   ByVal FirstJob As Long, _   ByVal NoJobs As Long, _   ByVal Level As Long, _   pJob As Byte, _   ByVal cdBuf As Long, _   pcbNeeded As Long, _   pcReturned As Long) _   As Long   ' constants for PRINTER_DEFAULTS structurePublic Const PRINTER_ACCESS_USE = &H8Public Const PRINTER_ACCESS_ADMINISTER = &H4' constants for DEVMODE structurePublic Const CCHDEVICENAME = 32Public Const CCHFORMNAME = 32Public Type PRINTER_DEFAULTS   pDatatype As String   pDevMode As Long   DesiredAccess As LongEnd TypePublic Type DEVMODE   dmDeviceName As String * CCHDEVICENAME   dmSpecVersion As Integer   dmDriverVersion As Integer   dmSize As Integer   dmDriverExtra As Integer   dmFields As Long   dmOrientation As Integer   dmPaperSize As Integer   dmPaperLength As Integer   dmPaperWidth As Integer   dmScale As Integer   dmCopies As Integer   dmDefaultSource As Integer   dmPrintQuality As Integer   dmColor As Integer   dmDuplex As Integer   dmYResolution As Integer   dmTTOption As Integer   dmCollate As Integer   dmFormName As String * CCHFORMNAME   dmLogPixels As Integer   dmBitsPerPel As Long   dmPelsWidth As Long   dmPelsHeight As Long   dmDisplayFlags As Long   dmDisplayFrequency As LongEnd TypeType SYSTEMTIME   wYear As Integer   wMonth As Integer   wDayOfWeek As Integer   wDay As Integer   wHour As Integer   wMinute As Integer   wSecond As Integer   wMilliseconds As IntegerEnd TypeType JOB_INFO_2   JobId As Long   pPrinterName As Long   pMachineName As Long   pUserName As Long   pDocument As Long   pNotifyName As Long   pDatatype As Long   pPrintProcessor As Long   pParameters As Long   pDriverName As Long   pDevMode As Long   pStatus As Long   pSecurityDescriptor As Long   Status As Long   Priority As Long   Position As Long   StartTime As Long   UntilTime As Long   TotalPages As Long   Size As Long   Submitted As SYSTEMTIME   time As Long   PagesPrinted As LongEnd TypeType PRINTER_INFO_2   pServerName As Long   pPrinterName As Long   pShareName As Long   pPortName As Long   pDriverName As Long   pComment As Long   pLocation As Long   pDevMode As Long   pSepFile As Long   pPrintProcessor As Long   pDatatype As Long   pParameters As Long   pSecurityDescriptor As Long   Attributes As Long   Priority As Long   DefaultPriority As Long   StartTime As Long   UntilTime As Long   Status As Long   cJobs As Long   AveragePPM As LongEnd TypePublic Const ERROR_INSUFFICIENT_BUFFER = 122Public Const PRINTER_STATUS_BUSY = &H200Public Const PRINTER_STATUS_DOOR_OPEN = &H400000Public Const PRINTER_STATUS_ERROR = &H2Public Const PRINTER_STATUS_INITIALIZING = &H8000Public Const PRINTER_STATUS_IO_ACTIVE = &H100Public Const PRINTER_STATUS_MANUAL_FEED = &H20Public Const PRINTER_STATUS_NO_TONER = &H40000Public Const PRINTER_STATUS_NOT_AVAILABLE = &H1000Public Const PRINTER_STATUS_OFFLINE = &H80Public Const PRINTER_STATUS_OUT_OF_MEMORY = &H200000Public Const PRINTER_STATUS_OUTPUT_BIN_FULL = &H800Public Const PRINTER_STATUS_PAGE_PUNT = &H80000Public Const PRINTER_STATUS_PAPER_JAM = &H8Public Const PRINTER_STATUS_PAPER_OUT = &H10Public Const PRINTER_STATUS_PAPER_PROBLEM = &H40Public Const PRINTER_STATUS_PAUSED = &H1Public Const PRINTER_STATUS_PENDING_DELETION = &H4Public Const PRINTER_STATUS_PRINTING = &H400Public Const PRINTER_STATUS_PROCESSING = &H4000Public Const PRINTER_STATUS_TONER_LOW = &H20000Public Const PRINTER_STATUS_USER_INTERVENTION = &H100000Public Const PRINTER_STATUS_WAITING = &H2000Public Const PRINTER_STATUS_WARMING_UP = &H10000Public Const JOB_STATUS_PAUSED = &H1Public Const JOB_STATUS_ERROR = &H2Public Const JOB_STATUS_DELETING = &H4Public Const JOB_STATUS_SPOOLING = &H8Public Const JOB_STATUS_PRINTING = &H10Public Const JOB_STATUS_OFFLINE = &H20Public Const JOB_STATUS_PAPEROUT = &H40Public Const JOB_STATUS_PRINTED = &H80Public Const JOB_STATUS_DELETED = &H100Public Const JOB_STATUS_BLOCKED_DEVQ = &H200Public Const JOB_STATUS_USER_INTERVENTION = &H400Public Const JOB_STATUS_RESTART = &H800Public Function GetString(ByVal PtrStr As Long) As String   Dim StrBuff As String * 256      'Check for zero address   If PtrStr = 0 Then      GetString = " "      Exit Function   End If      'Copy data from PtrStr to buffer.   CopyMemory ByVal StrBuff, ByVal PtrStr, 256      'Strip any trailing nulls from string.   GetString = StripNulls(StrBuff)End FunctionPublic Function StripNulls(OriginalStr As String) As String   'Strip any trailing nulls from input string.   If (InStr(OriginalStr, Chr(0)) > 0) Then      OriginalStr = Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)   End If   'Return modified string.   StripNulls = OriginalStrEnd FunctionPublic Function PtrCtoVbString(Add As Long) As String    Dim sTemp As String * 512    Dim x As Long    x = lstrcpy(sTemp, Add)    If (InStr(1, sTemp, Chr(0)) = 0) Then         PtrCtoVbString = ""    Else         PtrCtoVbString = Left(sTemp, InStr(1, sTemp, Chr(0)) - 1)    End IfEnd FunctionPublic Function CheckPrinterStatus(PI2Status As Long) As String   Dim tempStr As String      If PI2Status = 0 Then   ' Return "Ready"      CheckPrinterStatus = "Printer Status = Ready" & vbCrLf   Else      tempStr = ""   ' Clear      If (PI2Status And PRINTER_STATUS_BUSY) Then         tempStr = tempStr & "Busy  "      End If            If (PI2Status And PRINTER_STATUS_DOOR_OPEN) Then         tempStr = tempStr & "Printer Door Open  "      End If            If (PI2Status And PRINTER_STATUS_ERROR) Then         tempStr = tempStr & "Printer Error  "      End If            If (PI2Status And PRINTER_STATUS_INITIALIZING) Then         tempStr = tempStr & "Initializing  "      End If            If (PI2Status And PRINTER_STATUS_IO_ACTIVE) Then         tempStr = tempStr & "I/O Active  "      End If      If (PI2Status And PRINTER_STATUS_MANUAL_FEED) Then         tempStr = tempStr & "Manual Feed  "      End If            If (PI2Status And PRINTER_STATUS_NO_TONER) Then         tempStr = tempStr & "No Toner  "      End If            If (PI2Status And PRINTER_STATUS_NOT_AVAILABLE) Then         tempStr = tempStr & "Not Available  "      End If            If (PI2Status And PRINTER_STATUS_OFFLINE) Then         tempStr = tempStr & "Off Line  "      End If            If (PI2Status And PRINTER_STATUS_OUT_OF_MEMORY) Then         tempStr = tempStr & "Out of Memory  "      End If            If (PI2Status And PRINTER_STATUS_OUTPUT_BIN_FULL) Then         tempStr = tempStr & "Output Bin Full  "      End If            If (PI2Status And PRINTER_STATUS_PAGE_PUNT) Then         tempStr = tempStr & "Page Punt  "      End If            If (PI2Status And PRINTER_STATUS_PAPER_JAM) Then         tempStr = tempStr & "Paper Jam  "      End If      If (PI2Status And PRINTER_STATUS_PAPER_OUT) Then         tempStr = tempStr & "Paper Out  "      End If            If (PI2Status And PRINTER_STATUS_OUTPUT_BIN_FULL) Then         tempStr = tempStr & "Output Bin Full  "      End If            If (PI2Status And PRINTER_STATUS_PAPER_PROBLEM) Then         tempStr = tempStr & "Page Problem  "      End If            If (PI2Status And PRINTER_STATUS_PAUSED) Then         tempStr = tempStr & "Paused  "      End If      If (PI2Status And PRINTER_STATUS_PENDING_DELETION) Then         tempStr = tempStr & "Pending Deletion  "      End If            If (PI2Status And PRINTER_STATUS_PRINTING) Then         tempStr = tempStr & "Printing  "      End If            If (PI2Status And PRINTER_STATUS_PROCESSING) Then         tempStr = tempStr & "Processing  "      End If            If (PI2Status And PRINTER_STATUS_TONER_LOW) Then         tempStr = tempStr & "Toner Low  "      End If      If (PI2Status And PRINTER_STATUS_USER_INTERVENTION) Then         tempStr = tempStr & "User Intervention  "      End If            If (PI2Status And PRINTER_STATUS_WAITING) Then         tempStr = tempStr & "Waiting  "      End If            If (PI2Status And PRINTER_STATUS_WARMING_UP) Then         tempStr = tempStr & "Warming Up  "      End If            'Did you find a known status?      If Len(tempStr) = 0 Then         tempStr = "Unknown Status of " & PI2Status      End If            'Return the Status      CheckPrinterStatus = "Printer Status = " & tempStr & vbCrLf   End IfEnd Function					
  3. Add three CommandButton controls.
  4. Add three TextBox controls to Form1, and then configure the TextBox controls as follows:
    1. Set the MultiLine property of each TextBox control to True.
    2. Size each TextBox to approximately five inches wide and three inches long.
    3. Set the ScrollBars property of each TextBox control to 2 - Vertical.
  5. Add a Timer control to Form1.
  6. Add the following code to the Form's module:
    Option ExplicitPrivate Sub Command1_Click()   'Enable the timer to begin printer status checks.   Timer1.Enabled = True      'Enable and disable start and stop buttons.   Command1.Enabled = False   Command2.Enabled = True   Command3.Enabled = TrueEnd SubPrivate Sub Command2_Click()   'Disable timer to stop further printer checks.   Timer1.Enabled = False      'Enable and disable start and stop buttons.   Command1.Enabled = True   Command2.Enabled = False   Command3.Enabled = TrueEnd SubPrivate Sub Command3_Click()   'Clear the status info.   Text1.Text = ""   Text2.Text = ""   Text3.Text = ""End SubPrivate Sub Form_Load()   'Initialize captions for control buttons.   Command1.Caption = "Start"   Command2.Caption = "Stop"   Command3.Caption = "Clear"      'Clear the status info.   Text1.Text = ""   Text2.Text = ""   Text3.Text = ""      Command1.Enabled = True   'Disable stop and clear buttons.   Command2.Enabled = False   Command3.Enabled = False      'Set interval for printer status checking to 1/2 second.   Timer1.Enabled = False   Timer1.Interval = 500End SubPrivate Sub Timer1_Timer()   Dim PrinterStatus As String   Dim JobStatus As String   Dim ErrorInfo As String      'Clear the status info for new info/status.   Text1.Text = ""   Text2.Text = ""   Text3.Text = ""      'Call sub to perform check.   Text1.Text = CheckPrinter(PrinterStatus, JobStatus)   Text2.Text = PrinterStatus   Text3.Text = JobStatusEnd SubPublic Function CheckPrinter(PrinterStr As String, JobStr As String) As String   Dim hPrinter As Long   Dim ByteBuf As Long   Dim BytesNeeded As Long   Dim PI2 As PRINTER_INFO_2   Dim JI2 As JOB_INFO_2   Dim PrinterInfo() As Byte   Dim JobInfo() As Byte   Dim result As Long   Dim LastError As Long   Dim PrinterName As String   Dim tempStr As String   Dim NumJI2 As Long   Dim pDefaults As PRINTER_DEFAULTS   Dim I As Integer      'Set a default return value if no errors occur.   CheckPrinter = "Printer info retrieved"      'NOTE: You can pick a printer from the Printers Collection   'or use the EnumPrinters() API to select a printer name.      'Use the default printer of Printers collection.   'This is typically, but not always, the system default printer.   PrinterName = Printer.DeviceName      'Set desired access security setting.   pDefaults.DesiredAccess = PRINTER_ACCESS_USE      'Call API to get a handle to the printer.   result = OpenPrinter(PrinterName, hPrinter, pDefaults)   If result = 0 Then      'If an error occurred, display an error and exit sub.      CheckPrinter = "Cannot open printer " & PrinterName & _         ", Error: " & Err.LastDllError      Exit Function   End If   'Init BytesNeeded   BytesNeeded = 0   'Clear the error object of any errors.   Err.Clear   'Determine the buffer size that is needed to get printer info.   result = GetPrinter(hPrinter, 2, 0&, 0&, BytesNeeded)      'Check for error calling GetPrinter.   If Err.LastDllError <> ERROR_INSUFFICIENT_BUFFER Then      'Display an error message, close printer, and exit sub.      CheckPrinter = " > GetPrinter Failed on initial call! <"      ClosePrinter hPrinter      Exit Function   End If      'Note that in Charles Petzold's book "Programming Windows 95," he   'states that because of a problem with GetPrinter on Windows 95 only, you    'must allocate a buffer as much as three times larger than the value    'returned by the initial call to GetPrinter. This is not done here.   ReDim PrinterInfo(1 To BytesNeeded)      ByteBuf = BytesNeeded      'Call GetPrinter to get the status.   result = GetPrinter(hPrinter, 2, PrinterInfo(1), ByteBuf, _     BytesNeeded)      'Check for errors.   If result = 0 Then      'Determine the error that occurred.      LastError = Err.LastDllError()            'Display error message, close printer, and exit sub.      CheckPrinter = "Couldn't get Printer Status!  Error = " _         & LastError      ClosePrinter hPrinter      Exit Function   End If   'Copy contents of printer status byte array into a   'PRINTER_INFO_2 structure to separate the individual elements.   CopyMemory PI2, PrinterInfo(1), Len(PI2)      'Check if printer is in ready state.   PrinterStr = CheckPrinterStatus(PI2.Status)      'Add printer name, driver, and port to list.   PrinterStr = PrinterStr & "Printer Name = " & _     GetString(PI2.pPrinterName) & vbCrLf   PrinterStr = PrinterStr & "Printer Driver Name = " & _     GetString(PI2.pDriverName) & vbCrLf   PrinterStr = PrinterStr & "Printer Port Name = " & _     GetString(PI2.pPortName) & vbCrLf      'Call API to get size of buffer that is needed.   result = EnumJobs(hPrinter, 0&, &HFFFFFFFF, 2, ByVal 0&, 0&, _      BytesNeeded, NumJI2)      'Check if there are no current jobs, and then display appropriate message.   If BytesNeeded = 0 Then      JobStr = "No Print Jobs!"   Else      'Redim byte array to hold info about print job.      ReDim JobInfo(0 To BytesNeeded)            'Call API to get print job info.      result = EnumJobs(hPrinter, 0&, &HFFFFFFFF, 2, JobInfo(0), _        BytesNeeded, ByteBuf, NumJI2)            'Check for errors.      If result = 0 Then         'Get and display error, close printer, and exit sub.         LastError = Err.LastDllError         CheckPrinter = " > EnumJobs Failed on second call! <  Error = " _            & LastError         ClosePrinter hPrinter         Exit Function      End If            'Copy contents of print job info byte array into a      'JOB_INFO_2 structure to separate the individual elements.      For I = 0 To NumJI2 - 1   ' Loop through jobs and walk the buffer          CopyMemory JI2, JobInfo(I * Len(JI2)), Len(JI2)                       ' List info available on Jobs.          Debug.Print "Job ID" & vbTab & JI2.JobId          Debug.Print "Name Of Printer" & vbTab & _            GetString(JI2.pPrinterName)          Debug.Print "Name Of Machine That Created Job" & vbTab & _            GetString(JI2.pMachineName)          Debug.Print "Print Job Owner's Name" & vbTab & _            GetString(JI2.pUserName)          Debug.Print "Name Of Document" & vbTab & GetString(JI2.pDocument)          Debug.Print "Name Of User To Notify" & vbTab & _            GetString(JI2.pNotifyName)          Debug.Print "Type Of Data" & vbTab & GetString(JI2.pDatatype)          Debug.Print "Print Processor" & vbTab & _            GetString(JI2.pPrintProcessor)          Debug.Print "Print Processor Parameters" & vbTab & _            GetString(JI2.pParameters)          Debug.Print "Print Driver Name" & vbTab & _            GetString(JI2.pDriverName)          Debug.Print "Print Job 'P' Status" & vbTab & _            GetString(JI2.pStatus)          Debug.Print "Print Job Status" & vbTab & JI2.Status          Debug.Print "Print Job Priority" & vbTab & JI2.Priority          Debug.Print "Position in Queue" & vbTab & JI2.Position          Debug.Print "Earliest Time Job Can Be Printed" & vbTab & _            JI2.StartTime          Debug.Print "Latest Time Job Will Be Printed" & vbTab & _            JI2.UntilTime          Debug.Print "Total Pages For Entire Job" & vbTab & JI2.TotalPages          Debug.Print "Size of Job In Bytes" & vbTab & JI2.Size          'Because of a bug in Windows NT 3.51, the time member is not set correctly.          'Therefore, do not use the time member on Windows NT 3.51.          Debug.Print "Elapsed Print Time" & vbTab & JI2.time          Debug.Print "Pages Printed So Far" & vbTab & JI2.PagesPrinted                       'Display basic job status info.          JobStr = JobStr & "Job ID = " & JI2.JobId & _             vbCrLf & "Total Pages = " & JI2.TotalPages & vbCrLf                    tempStr = ""   'Clear          'Check for a ready state.          If JI2.pStatus = 0& Then   ' If pStatus is Null, check Status.            If JI2.Status = 0 Then               tempStr = tempStr & "Ready!  " & vbCrLf            Else  'Check for the various print job states.               If (JI2.Status And JOB_STATUS_SPOOLING) Then                  tempStr = tempStr & "Spooling  "               End If                              If (JI2.Status And JOB_STATUS_OFFLINE) Then                  tempStr = tempStr & "Off line  "               End If                              If (JI2.Status And JOB_STATUS_PAUSED) Then                  tempStr = tempStr & "Paused  "               End If                              If (JI2.Status And JOB_STATUS_ERROR) Then                  tempStr = tempStr & "Error  "               End If                              If (JI2.Status And JOB_STATUS_PAPEROUT) Then                  tempStr = tempStr & "Paper Out  "               End If                              If (JI2.Status And JOB_STATUS_PRINTING) Then                  tempStr = tempStr & "Printing  "               End If                              If (JI2.Status And JOB_STATUS_USER_INTERVENTION) Then                  tempStr = tempStr & "User Intervention Needed  "               End If                              If Len(tempStr) = 0 Then                  tempStr = "Unknown Status of " & JI2.Status               End If            End If        Else            ' Dereference pStatus.            tempStr = PtrCtoVbString(JI2.pStatus)        End If                    'Report the Job status.          JobStr = JobStr & tempStr & vbCrLf          Debug.Print JobStr & tempStr      Next I   End If      'Close the printer handle.   ClosePrinter hPrinterEnd Function					
  7. Go to your print queue, and then pause the printer. Note that you may need a local printer instead of a network printer to pause the print queue.
  8. Run the sample project, and then click Start.
  9. Perform one or more print operations from another application such as Notepad. The printer information is displayed in the text boxes. Notice that job details appear in the Immediate window.
  10. If necessary, click Stop on the form to scroll through the text boxes and the Immediate window to view this printer information.
back to the top


  • Only a specific device driver can obtain real-time, accurate printer status information. This code obtains the same status that the Windows Spooler reports.
  • The exact status that is reported may vary with different printers and drivers.
back to the top
For additional information about how printer status and print job status are set, click the article number below to view the article in the Microsoft Knowledge Base:
160129 HOWTO: Get the Status of a Printer and a Print Job
For additional information about how to use the EnumPrinters API function, click the article number below to view the article in the Microsoft Knowledge Base:
166008 ACC: Enumerating Local and Network Printers
For additional information about how to retrieve print job information in Microsoft Visual C++, click the article numbers below to view the articles in the Microsoft Knowledge Base:
228769 HOWTO: Retrieve Print Job Information
158828 HOWTO: How To Call Win32 Spooler Enumeration APIs Properly
back to the top
printer queue

Article ID: 202480 - Last Review: 07/16/2004 16:47:00 - Revision: 1.1

  • Microsoft Visual Basic 5.0 Enterprise Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition
  • Microsoft Visual Basic 5.0 Professional Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic 4.0 Standard Edition
  • Microsoft Visual Basic 4.0 32-Bit Enterprise Edition
  • Microsoft Visual Basic 4.0 Professional Edition
  • kbhowto kbhowtomaster kbapi kbspooler KB202480