Printer HardwareID

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I'm trying to get the hardware ID for the printers on a machine. Below is my
code. The problem appears to get in my second call to GetPrinterDriver. I
think it may be a problem with the DRIVER_INFO_6 definition. Any help would
be appreciated.

Thanks in Advance.
--
Eric Howard
Director of Process Automation
Synergis Technologies, Inc.

<StructLayout(LayoutKind.Sequential)> _
Private Structure DRIVER_INFO_6
Public cVersion As Int32
Public pName As String ' QMS 810
Public pEnvironment As String ' Win32 x86
Public pDriverPath As String ' c:\drivers\pscript.dll
Public pDataFile As String ' c:\drivers\QMS810.PPD
Public pConfigFile As String ' c:\drivers\PSCRPTUI.DLL
Public pHelpFile As String ' c:\drivers\PSCRPTUI.HLP
Public pDependentFiles As String '
Public pMonitorName As String ' "PJL monitor"
Public pDefaultDataType As String ' "EMF"
Public pszzPreviousNames As String
Public ftDriverDate As FILETIME
Public dwlDriverVersion As Int32
Public pszMfgName As String
Public pszOEMUrl As String
Public pszHardwareID As String
Public pszProvider As String
End Structure

<DllImport("winspool.drv", EntryPoint:="OpenPrinterA", _
SetLastError:=True, CharSet:=CharSet.Ansi, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function OpenPrinter(ByVal pPrinterName As String, _
ByRef phPrinter As Int32, _
ByVal pDefault As Int32 _
) As Boolean

End Function

<DllImport("winspool.drv", EntryPoint:="ClosePrinter", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function ClosePrinter(ByVal hPrinter As Int32) As Boolean

End Function

<DllImport("winspool.drv", EntryPoint:="GetPrinterDriverA", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Private Function GetPrinterDriver(ByVal hPrinter As Int32, _
ByVal pEnvironment As String, _
ByVal Level As Int32, _
ByRef pDriverInfo As DRIVER_INFO_6, _
ByVal cdBuf As Int32, _
ByRef pcbNeeded As Int32 _
) As Boolean

End Function

Sub Main()
Dim lIndex As Integer
Dim sName As String

Dim hResult As Int32
Dim hPrinter As Int32
Dim vInfo As DRIVER_INFO_6
Dim lBytesNeeded As Integer

For lIndex = 0 To PrinterSettings.InstalledPrinters.Count - 1
sName = PrinterSettings.InstalledPrinters.Item(lIndex)

If Not OpenPrinter(sName, hPrinter, 0) Then
Throw New ApiException(Marshal.GetLastWin32Error,
"OpenPrinter")
Else
Try
GetPrinterDriver(hPrinter, vbNullString, 6, vInfo, 0,
lBytesNeeded)
GetPrinterDriver(hPrinter, vbNullString, 6, vInfo,
lBytesNeeded, lBytesNeeded)
Catch ex As Exception
If ClosePrinter(hPrinter) Then
Throw ex
Else
Throw New ApiException("ClosePrinter")
End If
End Try
End If

Console.WriteLine(sName)
Next

Console.ReadLine()
End Sub
 
Your structure is fine. I usually prefer to use IntPtr for handles although
its ok to use Integer as well.
More importantly, since the function requires a pointer to a structure, we
declare the API prototype to take in an IntPtr which will point to a buffer
to store the structure that is returned by the function. Then we use
Marshal.PtrToStructure to retrieve the structure from the pointer.
here's the code:


<DllImport("winspool.drv", EntryPoint:="OpenPrinterA", _
SetLastError:=True, CharSet:=CharSet.Ansi, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function OpenPrinter(ByVal pPrinterName As String, _
ByRef phPrinter As IntPtr, _
ByVal pDefault As Int32 _
) As Boolean
End Function

<DllImport("winspool.drv", EntryPoint:="ClosePrinter", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean

End Function

<DllImport("winspool.drv", EntryPoint:="GetPrinterDriverA", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Shared Function GetPrinterDriver(ByVal hPrinter As IntPtr, _
ByVal pEnvironment As String, _
ByVal Level As Int32, _
ByVal pDriverInfo As IntPtr, _
ByVal cdBuf As Int32, _
ByRef pcbNeeded As Int32 _
) As Boolean
End Function

Private Sub GetPrinterInfo( )
Dim lIndex As Integer
Dim sName As String

Dim hResult As Int32
Dim hPrinter As IntPtr
Dim InfoPtr As IntPtr
Dim lBytesNeeded As Integer

For lIndex = 0 To Printing.PrinterSettings.InstalledPrinters.Count - 1
sName = Printing.PrinterSettings.InstalledPrinters.Item(lIndex)

If Not OpenPrinter(sName, hPrinter, 0) Then
MessageBox.Show("Could not open printer")
End If

' Allocate an unmanaged buffer in which to store the returned
structure
' InfoPtr is the pointer pointing to that buffer
InfoPtr = Marshal.AllocHGlobal( _
Marshal.SizeOf(GetType(DRIVER_INFO_6)))

GetPrinterDriver( _
hPrinter, vbNullString, 6, InfoPtr, _
0, lBytesNeeded)

GetPrinterDriver( _
hPrinter, vbNullString, 6, InfoPtr, _
lBytesNeeded, lBytesNeeded)

Dim Info As DRIVER_INFO_6 = CType( _
Marshal.PtrToStructure( _
InfoPtr, GetType(DRIVER_INFO_6)), _
DRIVER_INFO_6)

Console.WriteLine(Info.pName)
Console.WriteLine(Info.pDriverPath)
Console.WriteLine(Info.pDataFile)

If Not ClosePrinter(hPrinter) Then
MessageBox.Show("Could not close printer")
End If

Marshal.FreeHGlobal(InfoPtr)

Next
End Sub



hope that helps..
Imran.
 
Thanks. That got me further, however I get an error at the
Marshal.PtrToStructure line. The error is:

An unhandled exception of type 'System.NullReferenceException' occurred in
mscorlib.dll

Additional information: Object reference not set to an instance of an object.

Can you offer any insite?

Thanks In Advance,

Eric
 
Are you getting that error for the exact same code? I tried it on my machine
and it's working fine. If you have something different, could you post the
code so that we could look into whats wrong?

Imran.
 
I create a console app to test this code. Here's the code for the module.

Imports System.Drawing.Printing
Imports System.Runtime.InteropServices

Module Module1

<StructLayout(LayoutKind.Sequential)> _
Private Structure DRIVER_INFO_6
Public cVersion As Int32
Public pName As String ' QMS 810
Public pEnvironment As String ' Win32 x86
Public pDriverPath As String ' c:\drivers\pscript.dll
Public pDataFile As String ' c:\drivers\QMS810.PPD
Public pConfigFile As String ' c:\drivers\PSCRPTUI.DLL
Public pHelpFile As String ' c:\drivers\PSCRPTUI.HLP
Public pDependentFiles As String '
Public pMonitorName As String ' "PJL monitor"
Public pDefaultDataType As String ' "EMF"
Public pszzPreviousNames As String
Public ftDriverDate As FILETIME
Public dwlDriverVersion As Int64
Public pszMfgName As String
Public pszOEMUrl As String
Public pszHardwareID As String
Public pszProvider As String
End Structure

<DllImport("winspool.drv", EntryPoint:="OpenPrinterA", _
SetLastError:=True, CharSet:=CharSet.Ansi, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function OpenPrinter(ByVal pPrinterName As String, _
ByRef phPrinter As IntPtr, _
ByVal pDefault As Int32 _
) As Boolean
End Function

<DllImport("winspool.drv", EntryPoint:="ClosePrinter", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean

End Function

<DllImport("winspool.drv", EntryPoint:="GetPrinterDriverA", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function GetPrinterDriver(ByVal hPrinter As IntPtr, _
ByVal pEnvironment As String, _
ByVal Level As Int32, _
ByVal pDriverInfo As IntPtr, _
ByVal cdBuf As Int32, _
ByRef pcbNeeded As Int32 _
) As Boolean
End Function


Sub Main()
Dim lIndex As Integer
Dim sName As String

Dim hResult As Int32
Dim hPrinter As IntPtr
Dim InfoPtr As IntPtr
Dim lBytesNeeded As Integer

For lIndex = 0 To PrinterSettings.InstalledPrinters.Count - 1
sName = PrinterSettings.InstalledPrinters.Item(lIndex)

Console.WriteLine(sName)

If Not OpenPrinter(sName, hPrinter, 0) Then
Console.WriteLine("Could not open printer")
End If

' Allocate an unmanaged buffer in which to store the returned
structure
' InfoPtr is the pointer pointing to that buffer
InfoPtr = Marshal.AllocHGlobal( _
Marshal.SizeOf(GetType(DRIVER_INFO_6)))

GetPrinterDriver( _
hPrinter, vbNullString, 6, InfoPtr, _
0, lBytesNeeded)

GetPrinterDriver( _
hPrinter, vbNullString, 6, InfoPtr, _
lBytesNeeded, lBytesNeeded)

Dim Info As DRIVER_INFO_6
Info = CType( _
Marshal.PtrToStructure( _
InfoPtr, GetType(DRIVER_INFO_6)), _
DRIVER_INFO_6)

Console.WriteLine(vbTab + Info.pName)
Console.WriteLine(vbTab + Info.pDriverPath)
Console.WriteLine(vbTab + Info.pDataFile)
Console.WriteLine(vbTab + Info.pszHardwareID)
Console.WriteLine()

If Not ClosePrinter(hPrinter) Then
Console.WriteLine("Could not close printer")
End If

Marshal.FreeHGlobal(InfoPtr)

Next

Console.ReadLine()
End Sub

End Module
 
My Bad. I just re-ran the code I just posted and everything worked. Maybe my
machine needed a rest. :-) Thanks for your help.
 
Eric Howard said:
My Bad. I just re-ran the code I just posted and everything worked. Maybe my
machine needed a rest. :-) Thanks for your help.
And probably you too :)

Glad that worked out :)
 
You are correct about me needing more rest.

I'm still having some problems. The code runs fine for the meat of the
process however when the application ends, I get the following error:

The instruction at "0x00000000" referenced memory at "0x00000000". The
memory could not be "read".

I'm assuming this is some sort of memory allocation error or such. Where am
I not clearing things properly?

Below is the code I am using for a console application:

Imports System.Drawing.Printing
Imports System.Runtime.InteropServices

Module Module1

<StructLayout(LayoutKind.Sequential)> _
Public Structure DRIVER_INFO_6
Public cVersion As Int32
Public pName As String ' QMS 810
Public pEnvironment As String ' Win32 x86
Public pDriverPath As String ' c:\drivers\pscript.dll
Public pDataFile As String ' c:\drivers\QMS810.PPD
Public pConfigFile As String ' c:\drivers\PSCRPTUI.DLL
Public pHelpFile As String ' c:\drivers\PSCRPTUI.HLP
Public pDependentFiles As String '
Public pMonitorName As String ' "PJL monitor"
Public pDefaultDataType As String ' "EMF"
Public pszzPreviousNames As String
Public ftDriverDate As FILETIME
Public dwlDriverVersion As Int64
Public pszMfgName As String
Public pszOEMUrl As String
Public pszHardwareID As String
Public pszProvider As String
End Structure

<DllImport("winspool.drv", EntryPoint:="OpenPrinterA", _
SetLastError:=True, CharSet:=CharSet.Ansi, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function OpenPrinter(ByVal pPrinterName As String, _
ByRef phPrinter As IntPtr, _
ByVal pDefault As Integer _
) As Boolean
End Function

<DllImport("winspool.drv", EntryPoint:="ClosePrinter", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean

End Function

<DllImport("winspool.drv", EntryPoint:="GetPrinterDriverA", _
SetLastError:=True, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Public Function GetPrinterDriver(ByVal hPrinter As IntPtr, _
ByVal pEnvironment As String, _
ByVal Level As Integer, _
ByVal pDriverInfo As IntPtr, _
ByVal cdBuf As Integer, _
ByRef pcbNeeded As Integer _
) As Boolean
End Function

Sub Main()
Dim sName As String

For Each sName In PrinterSettings.InstalledPrinters
Console.WriteLine(sName)
Dim bVirtual As Boolean = IsPrinterVirtual(sName)

Console.WriteLine(vbTab + "IsVirtual?: " + bVirtual.ToString +
vbTab)

Console.ReadLine()
Next

Console.Write("Done: ")
Console.ReadLine()
End Sub

Public Function IsPrinterVirtual(ByVal printerName As String) As Boolean
Dim hPrinter As IntPtr = IntPtr.Zero
Dim InfoPtr As IntPtr = IntPtr.Zero
Dim lBytesNeeded As Integer = 0
Dim bReturn As Boolean = True

Try
If Not OpenPrinter(printerName, hPrinter, 0) Then
Console.WriteLine("Could not open printer")
End If

' Allocate an unmanaged buffer in which to store the returned
structure
' InfoPtr is the pointer pointing to that buffer
InfoPtr = Marshal.AllocHGlobal( _
Marshal.SizeOf(GetType(DRIVER_INFO_6)))

GetPrinterDriver( _
hPrinter, vbNullString, 6, InfoPtr, _
0, lBytesNeeded)

GetPrinterDriver( _
hPrinter, vbNullString, 6, InfoPtr, _
lBytesNeeded, lBytesNeeded)

If Not ClosePrinter(hPrinter) Then
Console.WriteLine("Could not close printer")
End If

Dim Info As DRIVER_INFO_6
Info = CType( _
Marshal.PtrToStructure( _
InfoPtr, GetType(DRIVER_INFO_6)), _
DRIVER_INFO_6)

If Info.pszHardwareID = Nothing Then
bReturn = True
Else
bReturn = False
End If

Marshal.FreeHGlobal(InfoPtr)

Catch ex As Exception
Console.WriteLine(ex.Message)
End Try

Return bReturn

End Function

End Module
 
Eric,

Instead of Marshal.FreeHGlobal, try using Marshal.DestroyStructure:

Marshal.DestroyStructure(InfoPtr, GetType(DRIVER_INFO_6))


hope that helps..
Imran.
 
Back
Top