Access Denied Error while changing network Printer Settings using "SetPrinter" from VB.NET
-
sexta-feira, 22 de julho de 2011 13:02
Hello,
I am developing a Word Add-in using VB.Net (VSTO 2010), where I would like to control some of the printer settings (such as duplex, print quality and orientation). I am trying to use the Winspool.drv's SetPrinter, but I get an "Access Denied" error when I call the SetPrinter procedure on a networked printer. The program works on my local machine where I am able to change the printer settings for a printer connected to my machine. However the Access Denied error occurs on the test machine, (Where the user has lower privileges - the user does not have admin privileges on the printer).Word 2010 is able to change duplex settings on the test machine, so I'm pretty sure I am not doing something right. I have tried with different access levels to no avail. I would appreciate it if someone could help me out here.I am using VSTO 2010 on Word 2010 and Windows 7 64 bit for this add-in. Here is the code that I am using. I built this code following the example from this article http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q140/2/85.asp&NoWebContent=1.
The steps being followed here are:
- Use OpenPrinter to get a handle to the printer
- Call GetPrinter to get the size of the buffer required for the PRINTER_INFO_2 structure
- Allocate the memory
- Call GetPrinter again to fill in the buffer.
- If the DEVMODE structure did not get filled in by GetPrinter, use DocumentProperties
- Call DocumentProperties once to get the size of memory required for DEVMODE structure
- Allocate the memory for the DEVMODE structure
- Call DocumentProperties once again to fill in the DEVMODE structure
- Make the duplex flag in the DEVMODE structure
- Call DocumentProperties to merge the changes in the DEVMODE settings with the current settings.
- Call SetPrinter to make the changes.
- Close the printer handle
- Release the memory allocated.Imports System.Runtime.InteropServices Imports System.Collections Imports System.ComponentModel Public Class ModifyPrinter #Region "Structures Definitions" <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _ Private Structure PRINTER_INFO_2 <MarshalAs(UnmanagedType.LPTStr)> _ Public pServerName As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pPrinterName As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pShareName As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pPortName As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pDriverName As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pComment As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pLocation As String Public pDevMode As IntPtr <MarshalAs(UnmanagedType.LPTStr)> _ Public pSepFile As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pPrintProcessor As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pDatatype As String <MarshalAs(UnmanagedType.LPTStr)> _ Public pParameters As String Public pSecurityDescriptor As IntPtr Public Attributes As Integer Public Priority As Integer Public DefaultPriority As Integer Public StartTime As Integer Public UntilTime As Integer Public Status As Integer Public cJobs As Integer Public AveragePPM As Integer End Structure <Flags()> _ Enum DM As Integer Orientation = &H1 PaperSize = &H2 PaperLength = &H4 PaperWidth = &H8 Scale = &H10 Position = &H20 NUP = &H40 DisplayOrientation = &H80 Copies = &H100 DefaultSource = &H200 PrintQuality = &H400 Color = &H800 Duplex = &H1000 YResolution = &H2000 TTOption = &H4000 Collate = &H8000 FormName = &H10000 LogPixels = &H20000 BitsPerPixel = &H40000 PelsWidth = &H80000 PelsHeight = &H100000 DisplayFlags = &H200000 DisplayFrequency = &H400000 ICMMethod = &H800000 ICMIntent = &H1000000 MediaType = &H2000000 DitherType = &H4000000 PanningWidth = &H8000000 PanningHeight = &H10000000 DisplayFixedOutput = &H20000000 End Enum <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _ Public Structure DEVMODE Public Const CCHDEVICENAME As Integer = 32 Public Const CCHFORMNAME As Integer = 32 <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHDEVICENAME)> _ Public dmDeviceName As String Public dmSpecVersion As Short Public dmDriverVersion As Short Public dmSize As Short Public dmDriverExtra As Short Public dmFields As DM Public dmOrientation As Short Public dmPaperSize As Short Public dmPaperLength As Short Public dmPaperWidth As Short Public dmScale As Short Public dmCopies As Short Public dmDefaultSource As Short Public dmPrintQuality As Short Public dmColor As Short Public dmDuplex As Short Public dmYResolution As Short Public dmTTOption As Short Public dmCollate As Short <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHFORMNAME)> _ Public dmFormName As String Public dmLogPixels As Short Public dmBitsPerPel As Integer ' Declared wrong in the full framework Public dmPelsWidth As Integer Public dmPelsHeight As Integer Public dmDisplayFlags As Integer Public dmDisplayFrequency As Integer Public dmICMMethod As Integer Public dmICMIntent As Integer Public dmMediaType As Integer Public dmDitherType As Integer Public dmReserved1 As Integer Public dmReserved2 As Integer Public dmPanningWidth As Integer Public dmPanningHeight As Integer Public dmPositionX As Integer ' Using a PointL Struct does not work Public dmPositionY As Integer End Structure <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _ Public Structure PRINTER_DEFAULTS Public pDatatype As IntPtr Public pDevMode As IntPtr Public DesiredAccess As Integer End Structure #End Region #Region "Constants" Private Enum AccessMask READ_CONTROL = &H20000 WRITE_DAC = &H40000 PRINTER_ACCESS_USE = &H8 PRINTER_ALL_ACCESS = 983052 PRINTER_WRITE = 131080 PRINTER_ACCESS_ADMINISTER = 4 SERVER_EXECUTE = 131074 SERVER_WRITE = 131075 SERVER_ALL_ACCESS = 983043 End Enum Private Const PRINTER_ACCESS_CODE = (AccessMask.READ_CONTROL Or AccessMask.PRINTER_ACCESS_USE) Private Const ERROR_INSUFFICIENT_BUFFER = 122 Private Const DUPLEX_LONG_EDGE As Integer = 2 Private Const DRAFT_RESOLUTION As Integer = 300 Private Const DM_DUPLEX = &H1000& Private Enum DocProperties DM_UPDATE = 1 DM_COPY = 2 DM_PROMPT = 4 DM_MODIFY = 8 DM_IN_BUFFER = DM_MODIFY DM_IN_PROMPT = DM_PROMPT DM_OUT_BUFFER = DM_COPY DM_OUT_DEFAULT = DM_UPDATE End Enum #End Region #Region "DLL Import Statements" <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:="GetPrinterA", _ SetLastError:=True, _ CharSet:=CharSet.Ansi, _ ExactSpelling:=True, _ CallingConvention:=CallingConvention.StdCall)> _ Private Shared Function GetPrinter(ByVal hPrinter As IntPtr, _ ByVal dwLevel As Int32, _ ByVal pPrinter As IntPtr, _ ByVal dwBuf As Int32, _ ByRef dwNeeded As Int32) As Boolean End Function <DllImport("winspool.Drv", _ EntryPoint:="DocumentPropertiesA", _ SetLastError:=True, _ ExactSpelling:=True, _ CallingConvention:=CallingConvention.StdCall, _ CharSet:=CharSet.Ansi)> _ Private Shared Function DocumentProperties(ByVal hwnd As IntPtr, _ ByVal hPrinter As IntPtr, _ <MarshalAs(UnmanagedType.LPWStr)> ByVal pDeviceName As String, _ ByVal pDevModeOutput As IntPtr, _ ByVal pDevModeInput As IntPtr, _ ByVal fMode As Integer) As Integer End Function <DllImport("winspool.Drv", _ CharSet:=CharSet.Ansi, _ EntryPoint:="ClosePrinter", _ SetLastError:=True, _ ExactSpelling:=True, _ CallingConvention:=CallingConvention.StdCall)> _ Private Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean End Function <DllImport("winspool.Drv", _ EntryPoint:="SetPrinterA", _ SetLastError:=True, _ CharSet:=CharSet.Ansi, _ ExactSpelling:=True, _ CallingConvention:=CallingConvention.StdCall)> _ Private Shared Function SetPrinter(ByVal hPrinter As IntPtr, _ ByVal Level As UInteger, _ ByVal pPrinter As Integer, _ ByVal Command As UInteger) As Boolean End Function #End Region ''' <summary> ''' Changes the duplex setting of the passed in printer using the Winspool.drv class. ''' </summary> ''' <param name="psPrinterName"></param> ''' <returns></returns> ''' <remarks></remarks> Public Function SetPrinterDuplex(ByVal psPrinterName As String) As Boolean Dim bStatus As Boolean = True Dim ptrPrinter, ptrPrinterInfo, ptrDevMode As System.IntPtr Dim stPrinterInfo As PRINTER_INFO_2 Dim pd As New PRINTER_DEFAULTS() Dim iBytesNeeded, iLastError, iStatus As Integer Dim dm As DEVMODE ' Open a handle to the printer pd.pDatatype = 0 pd.pDevMode = 0 pd.DesiredAccess = PRINTER_ACCESS_CODE bStatus = OpenPrinter(psPrinterName, ptrPrinter, pd) If bStatus = False Then ' Close the printer and exit. iLastError = Marshal.GetLastWin32Error() MsgBox("Error while calling OpenPrinter") Throw New Win32Exception(iLastError) End If ' First call to GetPrinter to get memory size needed for PRINTER_INFO_2 structure. This call will ' return an insufficient buffer error. If GetPrinter fails for any other reason, then exit the process. bStatus = GetPrinter(ptrPrinter, 2, Nothing, 0, iBytesNeeded) iLastError = Marshal.GetLastWin32Error() If bStatus = False And iLastError <> ERROR_INSUFFICIENT_BUFFER Then ' Close printer and exit iLastError = Marshal.GetLastWin32Error() MsgBox("Error on first call to GetPrinter") ClosePrinter(ptrPrinter) Throw New Win32Exception(iLastError) End If ' Allocate the required amount of space ptrPrinterInfo = Marshal.AllocCoTaskMem(iBytesNeeded) ' Second call to GetPrinter to get all the current settings, so we can modify only the settings ' that we are interested in changing. bStatus = GetPrinter(ptrPrinter, 2, ptrPrinterInfo, iBytesNeeded, iBytesNeeded) If bStatus = False Then iLastError = Marshal.GetLastWin32Error() MsgBox("Error with 2nd GetPrinter call") ClosePrinter(ptrPrinter) Marshal.FreeCoTaskMem(ptrPrinterInfo) Throw New Win32Exception(iLastError) End If ' Convert the pointer to PRINTER_INFO_2 to the structure. stPrinterInfo = DirectCast(Marshal.PtrToStructure(ptrPrinterInfo, GetType(PRINTER_INFO_2)), PRINTER_INFO_2) ' If GetPrinter did not fill in the DEVMODE, then try to get it from DocumentProperties. If stPrinterInfo.pDevMode = IntPtr.Zero Then iBytesNeeded = 0 ' Call Document Properties once to get the size of the buffer required to iBytesNeeded = DocumentProperties(IntPtr.Zero, ptrPrinter, psPrinterName, IntPtr.Zero, IntPtr.Zero, 0) ' If the call failed, then close the printer, release the memory and throw an error If iBytesNeeded <= 0 Then iLastError = Marshal.GetLastWin32Error() ClosePrinter(ptrPrinter) Marshal.FreeCoTaskMem(ptrPrinterInfo) MsgBox("Error during 1st call to DocumentProperties") Throw New Win32Exception(iLastError) End If ' Allocate the memory ptrDevMode = Marshal.AllocCoTaskMem(iBytesNeeded) ' Call DocumentProperties again to populate the DEVMODE properties iStatus = DocumentProperties(IntPtr.Zero, ptrPrinter, psPrinterName, ptrDevMode, IntPtr.Zero, DocProperties.DM_OUT_BUFFER) If iStatus <> 0 Then iLastError = Marshal.GetLastWin32Error() ClosePrinter(ptrPrinter) Marshal.FreeCoTaskMem(ptrPrinterInfo) Marshal.FreeCoTaskMem(ptrDevMode) MsgBox("Error during 2nd call to DocumentProperties") Throw New Win32Exception(iLastError) End If stPrinterInfo.pDevMode = ptrDevMode 'Marshal.StructureToPtr(stPrinterInfo, ptrPrinterInfo, True) End If ' If the driver doesn't support the duplex change, then inform the user and exit. dm = DirectCast(Marshal.PtrToStructure(stPrinterInfo.pDevMode, GetType(DEVMODE)), DEVMODE) If Not Convert.ToBoolean(dm.dmFields And DM_DUPLEX) Then Return False End If ' Change the duplex flag in DEVMODE. dm.dmDuplex = DUPLEX_LONG_EDGE ' Call DocumentProperties once again to update the drive dependent part of DEVMODE. iStatus = DocumentProperties(IntPtr.Zero, ptrPrinter, psPrinterName, _ stPrinterInfo.pDevMode, stPrinterInfo.pDevMode, _ DocProperties.DM_IN_BUFFER Or DocProperties.DM_OUT_BUFFER) If iStatus = 0 Then iLastError = Marshal.GetLastWin32Error() MsgBox("Error calling Document Properties to update driver dependent parts of DEVMODE") ClosePrinter(ptrPrinter) Marshal.FreeCoTaskMem(ptrPrinterInfo) Marshal.FreeCoTaskMem(ptrDevMode) Throw New Win32Exception(iLastError) End If Marshal.StructureToPtr(stPrinterInfo, ptrPrinterInfo, False) ' Set the security descriptor to 0 since it's not being used. stPrinterInfo.pSecurityDescriptor = IntPtr.Zero bStatus = SetPrinter(ptrPrinter, 2, ptrPrinterInfo, 0) If bStatus = False Then iLastError = Marshal.GetLastWin32Error() MsgBox("Error calling SetPrinter") ClosePrinter(ptrPrinter) Marshal.FreeCoTaskMem(ptrPrinterInfo) Marshal.FreeCoTaskMem(ptrDevMode) Throw New Win32Exception(iLastError) Else ClosePrinter(ptrPrinter) Marshal.FreeCoTaskMem(ptrPrinterInfo) Marshal.FreeCoTaskMem(ptrDevMode) Return True End If End Function End Class
ThanksVikram
- Movido Andreas JohanssonModerator sexta-feira, 22 de julho de 2011 14:12 Since using interop calls more suiable forum here (From:Common Language Runtime)
Todas as Respostas
-
segunda-feira, 25 de julho de 2011 07:21Moderador
Hello Vikram,
Thanks for your post.
Please check this article about changing Printer settings using Windows API which describes how to handle this similar error. Please have a try and let us know the situatin on your side.
http://www.lessanvaezi.com/changing-printer-settings-using-the-windows-api/
(Changing Printer settings using the Windows API)"In my testing, this worked fine on a local printer but when trying to change the settings on a network printer I was getting “access denied” errors. After some investigation, I got an explanation for what was happening. The following two articles provided the needed insight:
If you ask for STANDARD_RIGHTS_REQUIRED, you may as well ask for the moon
http://blogs.msdn.com/oldnewthing/archive/2008/02/27/7912126.aspxChanging printer settings using C# (also see one of the first comments in the thread)
http://www.codeproject.com/KB/dotnet/NET_Printer_Library.aspx?display=PrintAll "If you have any concerns, please feel free to follow up.
Have a nice day.
Best regards
Liliane Teng [MSFT]
MSDN Community Support | Feedback to us
Get or Request Code Sample from Microsoft
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
-
segunda-feira, 25 de julho de 2011 15:52
Hi Liliane,
Thanks for your response. I went through the links that you provided and that still doesn't seem to resolve the problem. I cannot change the printer settings if I don't use PRINTER_ALL_ACCESS. Since asking for All Access is not a good idea (and not supported in an network environment), the only way for this work is to use the lower privileges (READ_CONTROL
OrPRINTER_ACCESS_USE).One thing I discovered during my testing is that when I use VBA (the code listed in Lessan Vaezi's blog), I can use the lower privileges setting of PRINTER_NORMAL_ACCESS and change the settings on the network printer, but I cannot change it from VB.NET. I am completely baffled by this. What Is the difference between calling SetPrinter from VBA and VB.NET?
Thanks
Vikram -
quarta-feira, 3 de agosto de 2011 19:16Proprietário
1) Could you let me know the exact line of code?
2) Does the issue happen on 32-bit machines?
--Trevor H.
Send files to Hotmail.com: "MS_TREVORH" -
quinta-feira, 4 de agosto de 2011 12:30
1) Could you let me know the exact line of code?
2) Does the issue happen on 32-bit machines?
--Trevor H.
Send files to Hotmail.com: "MS_TREVORH"1. The error occurs in line number 352 (bStatus = SetPrinter(ptrPrinter, 2, ptrPrinterInfo, 0)).
2. I haven't tried this on 32 bit machines, but I will run a test to find out and let you know.
Thanks for your help.
Vikram
-
terça-feira, 9 de agosto de 2011 16:01ProprietárioDo you need more help?
--Trevor H.
Send files to Hotmail.com: "MS_TREVORH" -
quarta-feira, 10 de agosto de 2011 18:29
Yes, I am still not able to figure this out. I did try it out on a 32 bit machine and it does work on a local printer. I have to try this out on a network printer.
Thanks for your help
Vikram -
quarta-feira, 10 de agosto de 2011 20:53Proprietário
Please...
1) Use PRINTER_INFO_9 instead of PRINTER_INFO_2.
2) pd.DesiredAccess = PRINTER_ACCESS_USE... and let us know.
--Trevor H.
Send files to Hotmail.com: "MS_TREVORH"- Marcado como Resposta Trevor HancockMicrosoft Employee, Moderator quarta-feira, 17 de agosto de 2011 15:40
- Não Marcado como Resposta Vikram Cadambe quinta-feira, 18 de agosto de 2011 08:41
-
quinta-feira, 18 de agosto de 2011 08:44
Hi Trevor,
Sorry for the delay in the reply, but I tried using PRINTER_INFO_9 and PRINTER_ACCESS_USE and it crashed Word. I am not sure if that is the answer. Here is the definition for PRINTER_INFO_9 that I used:
Thanks for your help.<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _ Private Structure PRINTER_INFO_9 Public pDevMode As IntPtr End Structure
Vikram
-
quinta-feira, 25 de agosto de 2011 08:47Hello Vikram,
could you solve your problem? I am facing exactly the same problems ... Using Printer_Info_2 leads to Access denied errors, using Printer_Info_9 crashes the windows explorer (further investigations have shown that after calling SetPrinter with Printer_Info_9 any call to OpenPrinter (e.g. from Word or Acrobat) hangs.) -
sábado, 1 de outubro de 2011 21:36
This:For the PRINTER_INFO_2 and PRINTER_INFO_3 structures that contain a pointer to a security descriptor, the function can set only those components of the security descriptor that the caller has permission to modify. To set particular security descriptor components, you must specify the necessary access rights when you call the OpenPrinter or OpenPrinter2 function to retrieve a handle to the printer. The following table shows the access rights required to modify the various security descriptor components.Access permission Security descriptor component WRITE_OWNER OwnerPrimary groupWRITE_DAC Discretionary access-control list (DACL) ACCESS_SYSTEM_SECURITY System access-control list (SACL) Means that you must open a handle with WRITE_DAC and WRITE_OWNER if you want to use PRINTER_INFO_2.For GetPrinter you will need READ_CONTROL and PRINTER_ACCESS_USE
This lets me switch between long edge and top edge (1 and 2), I can't set 0 for some reason.Option Strict On Option Explicit On Option Infer Off Imports System.Runtime.InteropServices Imports System.Collections Imports System.ComponentModel Imports ConsoleApplication8.Consts Public Class ModifyPrinter Public Shared Sub Main() Dim mp As New ModifyPrinter mp.SetPrinterDuplex("HP LaserJet P2050 Series PCL6") End Sub <DllImport("winspool.Drv", CharSet:=CharSet.Ansi, SetLastError:=True)> _ Private Shared Function OpenPrinter(ByVal pPrinterName As String, _ ByRef hPrinter As IntPtr, _ ByRef pDefault As PRINTER_DEFAULTS) As Boolean End Function <DllImport("winspool.Drv", CharSet:=CharSet.Ansi, SetLastError:=True)> _ Private Shared Function GetPrinter(ByVal hPrinter As IntPtr, _ ByVal dwLevel As Int32, _ ByVal pPrinter As IntPtr, _ ByVal dwBuf As Int32, _ ByRef dwNeeded As Int32) As Boolean End Function <DllImport("winspool.Drv", CharSet:=CharSet.Ansi, SetLastError:=True)> _ Private Shared Function DocumentProperties(ByVal hwnd As IntPtr, _ ByVal hPrinter As IntPtr, _ ByVal pDeviceName As String, _ ByVal pDevModeOutput As IntPtr, _ ByVal pDevModeInput As IntPtr, _ ByVal fMode As Integer) As Integer End Function <DllImport("winspool.Drv", CharSet:=CharSet.Ansi, SetLastError:=True)> _ Private Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Boolean End Function <DllImport("winspool.Drv", CharSet:=CharSet.Ansi, SetLastError:=True)> _ Private Shared Function SetPrinter(ByVal hPrinter As IntPtr, _ ByVal Level As UInteger, _ ByVal pPrinter As IntPtr, _ ByVal Command As UInteger) As Boolean End Function Friend Function SetPrinterDuplex(ByVal psPrinterName As String) As Boolean Dim ptrPrinter, ptrPrinterInfo, ptrDevMode As System.IntPtr Dim iBytesNeeded, iLastError, iStatus As Integer Dim pd As New PRINTER_DEFAULTS() ' So - this is different - we need READ_CONTROL, PRINTER_ACCESS_USE to read stuff. ' And we need PRINTER_ACCESS_ADMINISTER, WRITE_DAC, WRITE_OWNER to set stuff. ' Seems like overkill - I'd go with using PRINTER_INFO_9 pd.DesiredAccess = AccessMask.READ_CONTROL Or AccessMask.PRINTER_ACCESS_USE Or AccessMask.PRINTER_ACCESS_ADMINISTER Or AccessMask.WRITE_DAC Or AccessMask.WRITE_OWNER Try Dim bStatus As Boolean = OpenPrinter(psPrinterName, ptrPrinter, pd) If bStatus = False Then Throw New Win32Exception() bStatus = GetPrinter(ptrPrinter, 2, Nothing, 0, iBytesNeeded) iLastError = Marshal.GetLastWin32Error() If bStatus = False AndAlso iLastError <> ERROR_INSUFFICIENT_BUFFER Then Throw New Win32Exception() ptrPrinterInfo = Marshal.AllocHGlobal(iBytesNeeded) bStatus = GetPrinter(ptrPrinter, 2, ptrPrinterInfo, iBytesNeeded, iBytesNeeded) If bStatus = False Then Throw New Win32Exception() Dim stPrinterInfo As PRINTER_INFO_2 = DirectCast(Marshal.PtrToStructure(ptrPrinterInfo, GetType(PRINTER_INFO_2)), PRINTER_INFO_2) Dim dm As DEVMODE = DirectCast(Marshal.PtrToStructure(stPrinterInfo.pDevMode, GetType(DEVMODE)), DEVMODE) If Not Convert.ToBoolean(dm.dmFields And DM_DUPLEX) Then Return False End If Console.WriteLine("Duplex before: " & dm.dmDuplex) ' run again to see if it's changed dm.dmDuplex = 2 ' valid values 1 & 2 Console.WriteLine("Trying to set to: " & dm.dmDuplex) ' jo0ls added this to copy the devmode back to memory: Marshal.StructureToPtr(dm, stPrinterInfo.pDevMode, True) iStatus = DocumentProperties(IntPtr.Zero, ptrPrinter, psPrinterName, _ stPrinterInfo.pDevMode, stPrinterInfo.pDevMode, _ DocProperties.DM_IN_BUFFER Or DocProperties.DM_OUT_BUFFER) If iStatus = 0 Then Throw New Win32Exception() Marshal.StructureToPtr(stPrinterInfo, ptrPrinterInfo, False) stPrinterInfo.pSecurityDescriptor = IntPtr.Zero bStatus = SetPrinter(ptrPrinter, 2, ptrPrinterInfo, 0) If bStatus = False Then Throw New Win32Exception Return True Finally If ptrPrinter <> IntPtr.Zero Then ClosePrinter(ptrPrinter) If ptrPrinterInfo <> IntPtr.Zero Then Marshal.FreeHGlobal(ptrPrinterInfo) If ptrDevMode <> IntPtr.Zero Then Marshal.FreeHGlobal(ptrDevMode) End Try End Function End Class
The stuff that's not in that listing are in Vikram's post earlier. SetPrinter should have an IntPtr for pPrinter, so that's changed. Also the original code does not write the devmode structure back to memory after setting the desired duplex value - so there's a line required to do that. -
sábado, 1 de outubro de 2011 22:17
For printer_info_9 you don't need as many rights.<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _ Friend Structure PRINTER_INFO_9 Public pDevMode As IntPtr End Structure Friend Function SetPrinterDuplex(ByVal psPrinterName As String) As Boolean Dim ptrPrinter, ptrPrinterInfo, ptrDevMode As System.IntPtr Dim iBytesNeeded, iStatus As Integer Dim pd As New PRINTER_DEFAULTS pd.DesiredAccess = AccessMask.READ_CONTROL Or AccessMask.PRINTER_ACCESS_USE Or AccessMask.PRINTER_ACCESS_ADMINISTER Try Dim bStatus As Boolean = OpenPrinter(psPrinterName, ptrPrinter, pd) If bStatus = False Then Throw New Win32Exception() bStatus = GetPrinter(ptrPrinter, 9, Nothing, 0, iBytesNeeded) If bStatus = False AndAlso Marshal.GetLastWin32Error() <> ERROR_INSUFFICIENT_BUFFER Then Throw New Win32Exception() ptrPrinterInfo = Marshal.AllocHGlobal(iBytesNeeded) bStatus = GetPrinter(ptrPrinter, 9, ptrPrinterInfo, iBytesNeeded, iBytesNeeded) If bStatus = False Then Throw New Win32Exception() Dim stPrinterInfo As PRINTER_INFO_9 = DirectCast(Marshal.PtrToStructure(ptrPrinterInfo, GetType(PRINTER_INFO_9)), PRINTER_INFO_9) Dim dm As DEVMODE = DirectCast(Marshal.PtrToStructure(stPrinterInfo.pDevMode, GetType(DEVMODE)), DEVMODE) If Not Convert.ToBoolean(dm.dmFields And DM_DUPLEX) Then Return False Console.WriteLine("Duplex before: " & dm.dmDuplex) ' run again to see if it's changed dm.dmDuplex = 1 ' DMDUP_SIMPLEX = 1, DMDUP_VERTICAL = 2, DMDUP_HORIZONTAL = 3 Console.WriteLine("Trying to set to: " & dm.dmDuplex) Marshal.StructureToPtr(dm, stPrinterInfo.pDevMode, False) iStatus = DocumentProperties(IntPtr.Zero, ptrPrinter, psPrinterName, _ stPrinterInfo.pDevMode, stPrinterInfo.pDevMode, _ DocProperties.DM_IN_BUFFER Or DocProperties.DM_OUT_BUFFER) If iStatus = 0 Then Throw New Win32Exception() bStatus = SetPrinter(ptrPrinter, 9, ptrPrinterInfo, 0) If bStatus = False Then Throw New Win32Exception Return True Finally If ptrPrinter <> IntPtr.Zero Then ClosePrinter(ptrPrinter) If ptrPrinterInfo <> IntPtr.Zero Then Marshal.FreeHGlobal(ptrPrinterInfo) If ptrDevMode <> IntPtr.Zero Then Marshal.FreeHGlobal(ptrDevMode) End Try End FunctionI didn't put the constants in the last post:Friend Enum AccessMask PRINTER_ACCESS_ADMINISTER = 4 PRINTER_ACCESS_USE = 8 READ_CONTROL = &H20000 WRITE_DAC = &H40000 WRITE_OWNER = &H80000 End Enum
-
terça-feira, 24 de abril de 2012 08:59
For network printers try
' SERVER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SERVER_ACCESS_ADMINISTER | SERVER_ACCESS_ENUMERATE
SERVER_ALL_ACCESS = &HF0003Duncan Jones

