Link to home
Start Free TrialLog in
Avatar of Mikael Jansson
Mikael JanssonFlag for Sweden

asked on

winspool.drv calls to remote computer gives access denied. Need help how to authenticate

I am getting printer drivers enumeration from a computer by calling EnumPrinterDrivers in winspool.drv and it works just great using the code below, but when I try to get it from a computer outside my domain or where my credentials are not right i get "access denied" (that is of course a obvious behaviour). My question is how can I create a connection "authenticate" to the computer prior to calling EnumPrinterDrivers with correct username and password. It has to be done in vb.net code.

are there some .net functionallity that solves this for me or are there any dll-functions that I can do to authenticate to the remote system.

I am using vb.net 2005

here is the code that works just fine except for connecting to computers with other credentials.

------------------- Start Dll-function declaration  --------------------
  ' Declaration
  Private Overloads Declare Function EnumPrinterDrivers Lib "winspool.drv" Alias "EnumPrinterDriversA" ( _
    ByVal pName As String, _
    ByVal pEnvironment As String, _
    ByVal Level As Int32, _
    ByVal pDriverInfo As IntPtr, _
    ByVal cpBuf As Int32, _
    ByRef pcbNeeded As Int32, _
    ByRef pcReturned As Int32 _
  ) As Int32

------------------- End Dll-function declaration  --------------------

------------------- Start main code --------------------

    Dim iReturn As Int32
    Dim pcbNeeded As Int32
    Dim pcReturned As Int32
    Dim bufDriver As IntPtr
    Dim iCount As Int32
    Dim diDriverInfo As DRIVER_INFO_3 = New DRIVER_INFO_3
    Dim serverName As String = ""      ' empty for local computer
    Dim environment As String = ""     ' empty for all

    Try
      iReturn = EnumPrinterDrivers(serverName, environment, 3, bufDriver, 0, pcbNeeded, pcReturned)
      If pcbNeeded > 0 Then
        bufDriver = Marshal.AllocHGlobal(pcbNeeded)
        iReturn = EnumPrinterDrivers(serverName, environment, 3, bufDriver, pcbNeeded, pcbNeeded, pcReturned)

        Dim lpNext As IntPtr
        lpNext = bufDriver
        For iCount = 1 To pcReturned
          Marshal.PtrToStructure(lpNext, diDriverInfo)
          lpNext = New IntPtr(lpNext.ToInt32 + Marshal.SizeOf(diDriverInfo))
              debug.print diDriverInfo.pName
        Next
        Marshal.FreeHGlobal(bufDriver)
      Else
        Throw New Win32Exception(Marshal.GetLastWin32Error())

      End If
    Catch ex As Win32Exception
      MessageBox.Show(ex.Message)
    End Try

------------------- End main code ---------------------

-------- Start Driver info declaration class -------------

'DRIVER_INFO_3 structure
<StructLayout(LayoutKind.Sequential)> _
    Public Class DRIVER_INFO_3
  Public cVersion As Int32
  Public pName As String
  Public pEnvironment As String
  Public pDriverPath As String
  Public pDataFile As String
  Public pConfigFile As String
  Public pHelpFile As String
  Public pDependentFiles As String
  Public pMonitorName As String
  Public pDefaultDataType As String
End Class

-------- End Driver info declaration class -------------


/ Mikael
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

What information are you using with EnumPrinterDrivers?  You can get a lot of information easily with WMI, using the Win32_Printer class.

Bob
Avatar of Mikael Jansson

ASKER

It is actually only a little part of the system I am building. I am using a lot of WMI already but I need to delete single jobs from a print queue, delete drivers (incl. files) from a computer (even remote). In WMI I know that I can do remote connect quite easy but it seams that WMI has some limitations when for example need to remove a single job from a queue etc.

/ Mikael
This is what I was talking about with the Win32_Printer class:

' Add a reference to System.Management.dll to the project.

Imports System.Management

Public Enum PrinterStatus
  Other = 1
  Unknown
  Idle
  Printing
  Warmup
  Stopped
  Offline
End Enum 'PrinterStatus

Public Class Win32_Printer

  Public DriverName As String
  Public IsNetwork As Boolean
  Public IsShared As Boolean
  Public PortName As String
  Public PrinterLocation As String
  Public PrinterName As String
  Public ServerName As String
  Public ShareName As String
  Public Status As PrinterStatus
  Public WorkOffline As Boolean


  Public Overloads Shared Function GetList() As Win32_Printer()
    Return GetList(Nothing)
  End Function  'GetList

  Public Overloads Shared Function GetList(ByVal machineName As String, ByVal userName As String, ByVal userPassword As String) As Win32_Printer()

    Dim options As New ConnectionOptions
    options.Username = userName
    options.Password = userPassword

    Dim path As New ManagementPath
    path.Server = "\\" & machineName.TrimStart("\") & "\root\cimv2"

    Dim scope As New ManagementScope(path)
    scope.Options = options

    ' Remote machine
    Return GetList(scope)

  End Function   'GetList(machine, user, password)

  Public Overloads Shared Function GetList(ByVal scope As ManagementScope) As Win32_Printer()

    Dim query As String = "Select * From Win32_Printer"

    Dim searcher As New ManagementObjectSearcher(query)

    If Not scope Is Nothing Then
      searcher.Scope = scope
    End If

    Dim listPrinters As New ArrayList

    For Each entryCurrent As ManagementObject In searcher.Get()

      Dim printer As New Win32_Printer

      printer.DriverName = entryCurrent("DriverName")
      printer.IsNetwork = entryCurrent("Network")
      printer.IsShared = entryCurrent("Shared")
      printer.PortName = entryCurrent("PortName")
      printer.PrinterLocation = entryCurrent("Location")
      printer.PrinterName = entryCurrent("Name")
      printer.ServerName = entryCurrent("ServerName")
      printer.ShareName = entryCurrent("ShareName")
      printer.Status = Convert.ToInt32(entryCurrent("PrinterStatus"))
      printer.WorkOffline = entryCurrent("WorkOffline")

      listPrinters.Add(printer)

    Next entryCurrent

    Return CType(listPrinters.ToArray(GetType(Win32_Printer)), Win32_Printer())

  End Function  'GetList

End Class
Notice the DriverName property.

Bob
I am using similar code already for enumeration of printers but as far as I know WMI can not handle delete of printer drivers including all depending files, thats the main reason why I am using winspool.drv functions. But if you know a simpler way with WMI to do that I all ears. This also for cancel a specific job in a queue, all I could find in WMI is to "CancelAllJobs" and thats not what I am looking for.

I also noticed a performance difference between WMI and winspool.drv calls when enumerating e.g. printers or drivers, winspool.drv calls was faster

/ Mikael
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi TheLearnedOne,
I might have been digging to deep to test things so I forgot to answer back here, sorry for that. Your last answer is probably solving my issue but I still have some things that is still not working but of course you earned your points so I will accept the answer.

Thanks for your help and sorry that I forgot to get back to finish the question

/ Mikael