Dicas do OsmarJr

Obtendo o Status da Impressora pelo Visual Basic


O Que Faltou no Objeto Printer

O original está nesta página

A impressão é uma parte bastante problemática no desenvolvimento de aplicativos completos e profissionais no Visual Basic. Isto foi facilitado, em grande parte, com o nove objeto Printer, introduzido no Visual Basic 4 (e no VBA do Office 2000).

Mas o objeto tem, ainda, algumas limitações. A maior delas é que não podemos saber se a impressora está pronta, ocupada, sem papel, etc. a partir do aplicativo.

Por sorte, existe uma chamada de API, GetPrinter, que devolve muitas informações sobre a impressora.

Private Declare Function GetPrinterApi Lib "winspool.drv" Alias _
       "GetPrinterA"
(ByVal hPrinter As Long, _
         
ByVal Level As Long, _
         buffer
As Long, _
         
ByVal pbSize As Long, _
         pbSizeNeeded
As Long) As Long

Isto pega o "gancho" da impressora em hPrinter e preenche o buffer fornecido com informações do driver da impressora. Para obter esse "gancho" do objeto Printer é necessário o uso da chamada de API OpenPrinter.

Este gancho deve ser liberado pelo uso da chamada de API ClosePrinter tão logo seu uso esteja terminado.

Private Type PRINTER_DEFAULTS
  pDatatype As String
  pDevMode As DEVMODE
  DesiredAccess As Long
End Type

Private Declare Function OpenPrinter Lib "winspool.drv" _
    Alias "OpenPrinterA" (ByVal pPrinterName As String, _
    phPrinter As Long, pDefault As PRINTER_DEFAULTS) As Long

Private Declare Function ClosePrinter Lib "winspool.drv" _
    (ByVal hPrinter As Long) As Long

Passamos o Printer.DeviceName para pegar o gancho.

Dim lret As Long
Dim pDef As PRINTER_DEFAULTS

lret = OpenPrinter(Printer.DeviceName, mhPrinter, pDef)

Os Diferentes Status

Existe uma variedade de status padrão que podem ser devolvidos pelo driver da impressora.

Public Enum Printer_Status
   PRINTER_STATUS_READY = &H0
   PRINTER_STATUS_PAUSED = &H1
   PRINTER_STATUS_ERROR = &H2
   PRINTER_STATUS_PENDING_DELETION = &H4
   PRINTER_STATUS_PAPER_JAM = &H8
   PRINTER_STATUS_PAPER_OUT = &H10
   PRINTER_STATUS_MANUAL_FEED = &H20
   PRINTER_STATUS_PAPER_PROBLEM = &H40
   PRINTER_STATUS_OFFLINE = &H80
   PRINTER_STATUS_IO_ACTIVE = &H100
   PRINTER_STATUS_BUSY = &H200
   PRINTER_STATUS_PRINTING = &H400
   PRINTER_STATUS_OUTPUT_BIN_FULL = &H800
   PRINTER_STATUS_NOT_AVAILABLE = &H1000
   PRINTER_STATUS_WAITING = &H2000
   PRINTER_STATUS_PROCESSING = &H4000
   PRINTER_STATUS_INITIALIZING = &H8000
   PRINTER_STATUS_WARMING_UP = &H10000
   PRINTER_STATUS_TONER_LOW = &H20000
   PRINTER_STATUS_NO_TONER = &H40000
   PRINTER_STATUS_PAGE_PUNT = &H80000
   PRINTER_STATUS_USER_INTERVENTION = &H100000
   PRINTER_STATUS_OUT_OF_MEMORY = &H200000
   PRINTER_STATUS_DOOR_OPEN = &H400000
   PRINTER_STATUS_SERVER_UNKNOWN = &H800000
   PRINTER_STATUS_POWER_SAVE = &H1000000
End Enum

 As Estruturas de Dados

Como cada impressora é responsável por devolver estes dados, deve existir um padrão ao qual estes dados estão em conformidade para que um aplicativo seja capaz de consultar diferentes impressoras. Na verdade, existem nove padrões diferentes de tipos de dads que podem ser devolvidos pela chamada de API GetPrinter no Windows 2000 (apenas as duas primeiras são padrão em todas as versões atuais do Windows).

Destas, a mais interessante é a segunda, chamada de PRINTER_INFO_2.

Private Type PRINTER_INFO_2
   pServerName As String
   pPrinterName As String
   pShareName As String
   pPortName As String
   pDriverName As String
   pComment As String
   pLocation As String
   pDevMode As Long
   pSepFile As String
   pPrintProcessor As String
   pDatatype As String
   pParameters As String
   pSecurityDescriptor As Long
   Attributes As Long
   Priority As Long
   DefaultPriority As Long
   StartTime As Long
   UntilTime As Long
   Status As Long
   JobsCount As Long
   AveragePPM As Long
End Type

Entretanto, não é assim tão simples como passar esta estrutura para a chamada de API GetPrinter, já que a impressora pode devolver mais informações do que as contidas na estrutura e, se não for alocado espaço suficiente de buffer para elas, o aplicativo vai travar.

Isso quer dizer que o preenchimento das informações do driver de impressão se torna um processo de dois passos:

  Dim lret As Long
  Dim SizeNeeded As Long

  Dim buffer() As Long

  ReDim Preserve buffer(0 To 1) As Long
  lret = GetPrinterApi(mhPrinter, Index, buffer(0), UBound(buffer), SizeNeeded)
  ReDim Preserve buffer(0 To (SizeNeeded / 4) + 3) As Long
  lret = GetPrinterApi(mhPrinter, Index, buffer(0), UBound(buffer) * 4, SizeNeeded)

Entretanto o buffer é apenas uma matriz de dados do tipo Long. Alguns dos dados contidos na estrutura de dados PRINTER_INFO_2 são do tipo String. Estes devem ser coletados nos endereços armazenados nas posições apropriadas do buffer.

Obtendo uma String a Partir de um ponteiro

Para obter uma string a partir de um ponteiro é usada a chamada de API CopyMemory e temos, também, uma outra chamada de API, IsBadString, que pode ser usada para verificar se o endereço apontado contém, realmente, uma string válida.

'\\ Rotinas de manipulação de memória
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
'\\ Validação de pponteiro StringFromPointer
Private Declare Function IsBadStringPtrByLong Lib "kernel32" Alias "IsBadStringPtrA" (ByVal lpsz As Long, ByVal ucchMax As Long) As Long

A recuperação de uma string a partir de um ponteiro é uma coisa bastante usada, valendo a pena ter esta função no nosso arsenal.

Public Function StringFromPointer(lpString As Long, lMaxLength As Long) As String

  Dim sRet As String
  Dim lret As Long

  If lpString = 0 Then
    StringFromPointer = ""
    Exit Function
  End If

  If IsBadStringPtrByLong(lpString, lMaxLength) Then
    '\\ Ocorreu um erro – não tentar usar este ponteiro
      StringFromPointer = ""
    Exit Function
  End If

  '\\ Pré-inicializa a string de retorno...
  sRet = Space$(lMaxLength)
  CopyMemory ByVal sRet, ByVal lpString, ByVal Len(sRet)
  If Err.LastDllError = 0 Then
    If InStr(sRet, Chr$(0)) > 0 Then
      sRet = Left$(sRet, InStr(sRet, Chr$(0)) - 1)
    End If
  End If

  StringFromPointer = sRet

End Function

Assim usamos isto para preencher nossa variável PRINTER_INFO_2:

With mPRINTER_INFO_2 '\\ Esta é uma variável do tipo PRINTER_INFO_2
   .pServerName = StringFromPointer(buffer(0), 1024)
   .pPrinterName = StringFromPointer(buffer(1), 1024)
   .pShareName = StringFromPointer(buffer(2), 1024)
   .pPortName = StringFromPointer(buffer(3), 1024)
   .pDriverName = StringFromPointer(buffer(4), 1024)
   .pComment = StringFromPointer(buffer(5), 1024)
   .pLocation = StringFromPointer(buffer(6), 1024)
   .pDevMode = buffer(7)
   .pSepFile = StringFromPointer(buffer(8), 1024)
   .pPrintProcessor = StringFromPointer(buffer(9), 1024)
   .pDatatype = StringFromPointer(buffer(10), 1024)
   .pParameters = StringFromPointer(buffer(11), 1024)
   .pSecurityDescriptor = buffer(12)
   .Attributes = buffer(13)
   .Priority = buffer(14)
   .DefaultPriority = buffer(15)
   .StartTime = buffer(16)
   .UntilTime = buffer(17)
   .Status = buffer(18)
   .JobsCount = buffer(19)
   .AveragePPM = buffer(20)
End With

 

Home

Contato | Copyright©Osmar José Correia Júnior | 24-Nov-2005 18:23