[DllImport("winspool.drv", CharSet=CharSet.Auto, SetLastError=true)]
static extern int OpenPrinter(string pPrinterName, out IntPtr phPrinter, ref PRINTER_DEFAULTS pDefault);
[DllImport("winspool.drv",SetLastError=true)]
static extern int OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);
Both signatures will work fine, it is a matter of whether you need a higher level of access to the printer or not. For 'read' only access such as monitoring printer queues, you don't need to pass in a PRINTER_DEFAULTS instance for the last parameter, you can just pass zero (0) if you declare the extern function pDefault parameter as an int. You will need a PRINTER_DEFAULTS structure if you need to change any properties of the printer such as setting the duplex value of the printer.
I had a problem using the above definitions and then made my own which avoided the stack imbalance in .NET 2.0 using-
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, Int32 pDefault);
If you use the class-version of PRINTER_DEFAULTS, this declaration will be useful (no ref needed, because it is a class):
[DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern unsafe bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, PRINTER_DEFAULTS pDefault);
This signature will work with pDefault = null or pDefault = class object.
<DllImport("winspool.drv", EntryPoint:="OpenPrinterA", _
SetLastError:=True, CharSet:=CharSet.Ansi, _
ExactSpelling:=True, _
CallingConvention:=CallingConvention.StdCall)> _
Private Shared Function OpenPrinter(ByVal pPrinterName As String, _
ByRef phPrinter As IntPtr, _
ByVal pDefault As IntPtr) As Boolean
End Function
<DllImport("winspool.drv", EntryPoint:="OpenPrinterA", ExactSpelling:=True, _
SetLastError:=True, CallingConvention:=CallingConvention.StdCall, _
CharSet:=CharSet.Ansi)> _
Private Shared Function OpenPrinter(ByVal pPrinterName As String, _
ByRef hPrinter As IntPtr, ByVal pDefault As PRINTER_DEFAULTS) As Boolean
End Function
Using .NET 2.0 the last parameter caused a stack mis-match problem so use:
<DllImport("winspool.drv", EntryPoint:="OpenPrinterA", ExactSpelling:=True, _
SetLastError:=True, CallingConvention:=CallingConvention.StdCall, _
CharSet:=CharSet.Ansi)> _
Private Shared Function OpenPrinter(ByVal pPrinterName As String, _
ByRef hPrinter As IntPtr, ByRef pDefault As PRINTER_DEFAULTS) As Boolean
End Function
[<DllImport("winspool.drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)>]
extern bool private OpenPrinter([<MarshalAs(UnmanagedType.LPStr)>] string pPrinterName,
IntPtr& phPrinter, PRINTER_DEFAULTS& pDefault)
As always, only do SetLastError=true if you actually intend to call GetLastError (This includes throwing a Win32Exception if the call fails)
Sample Code:
How to cancel a print job:
IntPtr pHandle;
PRINTER_DEFAULTS defaults = new PRINTER_DEFAULTS();
byte b = 0;
OpenPrinter(printerName, out pHandle, defaults);
SetJobA(pHandle, (int)jobID, 0, ref b, (int)Job_Control.Cancel);
ClosePrinter(pHandle);
The System.Management API allows for lots of printer stuff, but doesn't let you delete a print job in Win2K.