.NETGURU
Q230743 Set Duplex Printing for Word Automation
Messages   Related Types
This message was discovered on microsoft.public.dotnet.framework.interop.
Responses highlighted in red are from those people who are likely to be able to contribute good, authoratitive information to this discussion. They include Microsoft employees, MVP's and others who IMHO contribute well to these kinds of discussions.
Post a new message to this list...

manofbluz
I need the VB.NET equivalent to this KB article.
Reply to this message...
 
    
Wei-Dong XU [MSFT] (VIP)
Hi,

So far as I know, there is no one VB.net version of this kb article.
However, from my experience, you can devellop one VB project according to
this article and open them in VB.net. VB.net will convert the old VB code
into VB.net automatcially.

Please feel free to let me know if you have any question.

Best Regards,
Wei-Dong Xu
Microsoft Product Support Services
Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.

Reply to this message...
 
    
manofbluz
I doubt it will convert API calls - that would be a break from MS tradition.
Is there another way to set the printer to duplex using .NET objects while
still using Word automation PrintOut() to print the document?

"Wei-Dong XU [MSFT]" wrote:

[Original message clipped]

Reply to this message...
 
    
Wei-Dong XU [MSFT] (VIP)
Hi,

I'd suggest there is one excellent P/Invoke site for .Net:
http://www.pinvoke.net/

This site has almost all the Win32 api declaration for you, powered by Adam
Nathan, the author of <<.NET & COM Interop>>.

For your issue, I prepare the VB.net declaration of these Win 32 api and
structure from the kb article 237043 for you below:
'---VB.net declaration----------------------------------------
<StructLayout(LayoutKind.Sequential)>
Public Structure PRINTER_DEFAULTS
Public pDatatype As String
Public pDevMode As DEVMODE
Public DesiredAccess As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure PRINTER_INFO_2
Public pServerName As String
Public pPrinterName As String
Public pShareName As String
Public pPortName As String
Public pDriverName As String
Public pComment As String
Public pLocation As String
Public pDevMode As DEVMODE
Public pSepFile As String
Public pPrintProcessor As String
Public pDatatype As String
Public pParameters As String
Public pSecurityDescriptor As SECURITY_DESCRIPTOR
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

<StructLayout(LayoutKind.Sequential)>
Public Structure DEVMODE
<MarshalAs(UnmanagedType.ByValTStr,SizeConst:= CCHDEVICENAME)>
Public dmDeviceName As String
Public dmSpecVersion As Integer
Public dmDriverVersion As Integer
Public dmSize As Integer
Public dmDriverExtra As Integer
Public dmFields As Integer
Public dmOrientation As Integer
Public dmPaperSize As Integer
Public dmPaperLength As Integer
Public dmPaperWidth As Integer
Public dmScale As Integer
Public dmCopies As Integer
Public dmDefaultSource As Integer
Public dmPrintQuality As Integer
Public dmColor As Integer
Public dmDuplex As Integer
Public dmYResolution As Integer
Public dmTTOption As Integer
Public dmCollate As Integer
<MarshalAs(UnmanagedType.ByValTStr,SizeConst:= CCHFORMNAME)>
Public dmFormName As String
Public dmUnusedPadding As Integer
Public dmBitsPerPel As Integer
Public dmPelsWidth As Integer
Public dmPelsHeight As Integer
Public dmDisplayFlags As Integer
Public dmDisplayFrequency As Integer
End Structure

Public Const DM_DUPLEX = &H1000&
Public Const DM_IN_BUFFER = DM_MODIFY 'Public Const DM_MODIFY = 8

Public Const DM_OUT_BUFFER = DM_COPY 'Public Const DM_COPY = 2
Public Const PRINTER_ACCESS_ADMINISTER = &H4
Public Const PRINTER_ACCESS_USE = &H8
Public Const STANDARD_RIGHTS_REQUIRED = &HF0000
Public Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or
PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)
' Public Const STANDARD_RIGHTS_REQUIRED = &HF0000
' Public Const PRINTER_ACCESS_ADMINISTER = &H4
' Public Const PRINTER_ACCESS_USE = &H8

Public Declare Function ClosePrinter Lib "winspool.drv" Alias
"ClosePrinter" (ByVal hPrinter As Integer) As Integer
Public Declare Function DocumentProperties Lib "winspool.drv" Alias
"DocumentPropertiesA" (ByVal hwnd As Integer, ByVal hPrinter As Integer,
ByVal pDeviceName As String,<MarshalAs(UnmanagedType.Struct)> ByRef
pDevModeOutput As DEVMODE,<MarshalAs(UnmanagedType.Struct)> ByRef
pDevModeInput As DEVMODE, ByVal fMode As Integer) As Integer
Public Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA"
(ByVal hPrinter As Integer, ByVal Level As Integer, pPrinter As Any, ByVal
cbBuf As Integer, pcbNeeded As Integer) As Integer
Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA"
(ByVal pPrinterName As String, phPrinter As
Integer,<MarshalAs(UnmanagedType.Struct)> ByRef pDefault As
PRINTER_DEFAULTS) As Integer
Public Declare Function SetPrinter Lib "winspool.drv" Alias "SetPrinterA"
(ByVal hPrinter As Integer, ByVal Level As Integer, pPrinter As Byte, ByVal
Command As Integer) As Integer
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory"
(Destination As Any, Source As Any, ByVal Length As Integer)

Please feel free to let me know if you have any further question.

Best Regards,
Wei-Dong Xu
Microsoft Product Support Services
Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.

Reply to this message...
 
    
manofbluz
Thanks, but I'm still having trouble. Here's the code I have so far. Please
paste it into VS so you can see the Tasks that need resolving. I'll summarize:

1. VB.NET does not like the Any data type in the Function declarations - I
don't know what to change to.
2. the VarPtr function is not defined. This function is from the original
KB but the code is not actually in the article, so I don't know what the
function does.
3. can't convert Integer to SECURITY_DESCRIPTOR

line of code: pInfo.pSecurityDescriptor = 0

between the two lines of asterisks is the code I have so far
*************************
Option Explicit On
Imports System
Imports System.Runtime.InteropServices
Module Module1

<StructLayout(LayoutKind.Sequential)> Public Structure PRINTER_DEFAULTS
Public pDatatype As String
Public pDevMode As DEVMODE
Public DesiredAccess As Integer
End Structure
<StructLayout(LayoutKind.Sequential)> Public Structure
SECURITY_DESCRIPTOR 'added this whole structure
Public Revision As Byte
Public Sbz1 As Byte
Public Control As Long
Public Owner As Long
Public Group As Long
Public sACL As ACL
Public Dacl As ACL
End Structure
<StructLayout(LayoutKind.Sequential)> Public Structure ACL 'added this
whole structure
Public AclRevision As Byte
Public Sbz1 As Byte
Public AclSize As Integer
Public AceCount As Integer
Public Sbz2 As Integer
End Structure

<StructLayout(LayoutKind.Sequential)> Public Structure PRINTER_INFO_2
Public pServerName As String
Public pPrinterName As String
Public pShareName As String
Public pPortName As String
Public pDriverName As String
Public pComment As String
Public pLocation As String
Public pDevMode As DEVMODE
Public pSepFile As String
Public pPrintProcessor As String
Public pDatatype As String
Public pParameters As String
Public pSecurityDescriptor As SECURITY_DESCRIPTOR
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

<StructLayout(LayoutKind.Sequential)> Public Structure DEVMODE
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHDEVICENAME)> _
Public dmDeviceName As String
Public dmSpecVersion As Integer
Public dmDriverVersion As Integer
Public dmSize As Integer
Public dmDriverExtra As Integer
Public dmFields As Integer
Public dmOrientation As Integer
Public dmPaperSize As Integer
Public dmPaperLength As Integer
Public dmPaperWidth As Integer
Public dmScale As Integer
Public dmCopies As Integer
Public dmDefaultSource As Integer
Public dmPrintQuality As Integer
Public dmColor As Integer
Public dmDuplex As Integer
Public dmYResolution As Integer
Public dmTTOption As Integer
Public dmCollate As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCHFORMNAME)> _
Public dmFormName As String
Public dmUnusedPadding As Integer
Public dmBitsPerPel As Integer
Public dmPelsWidth As Integer
Public dmPelsHeight As Integer
Public dmDisplayFlags As Integer
Public dmDisplayFrequency As Integer
End Structure

Public Const CCHDEVICENAME As Integer = 32 'added this
Public Const CCHFORMNAME As Integer = 32 'added this
Public Const DM_DUPLEX = &H1000&
Public Const DM_MODIFY = 8
Public Const DM_IN_BUFFER = DM_MODIFY
Public Const DM_COPY = 2
Public Const DM_OUT_BUFFER = DM_COPY
Public Const PRINTER_ACCESS_ADMINISTER = &H4
Public Const PRINTER_ACCESS_USE = &H8
Public Const STANDARD_RIGHTS_REQUIRED = &HF0000
Public Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or _
PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)

Public Declare Function ClosePrinter Lib "winspool.drv" Alias _
"ClosePrinter" (ByVal hPrinter As Integer) As Integer

Public Declare Function DocumentProperties Lib "winspool.drv" Alias _
"DocumentPropertiesA" (ByVal hwnd As Integer, ByVal hPrinter As Integer, _
ByVal pDeviceName As String, <MarshalAs(UnmanagedType.Struct)> ByRef _
pDevModeOutput As DEVMODE, <MarshalAs(UnmanagedType.Struct)> ByRef _
pDevModeInput As DEVMODE, ByVal fMode As Integer) As Integer

Public Declare Function GetPrinter Lib "winspool.drv" Alias
"GetPrinterA" _
(ByVal hPrinter As Integer, ByVal Level As Integer, ByVal pPrinter As Any,
ByVal _
cbBuf As Integer, ByVal pcbNeeded As Integer) As Integer

Public Declare Function OpenPrinter Lib "winspool.drv" Alias
"OpenPrinterA" _
(ByVal pPrinterName As String, ByVal phPrinter As _
Integer, <MarshalAs(UnmanagedType.Struct)> ByRef pDefault As _
PRINTER_DEFAULTS) As Integer

Public Declare Function SetPrinter Lib "winspool.drv" Alias
"SetPrinterA" _
(ByVal hPrinter As Integer, ByVal Level As Integer, ByVal pPrinter As Byte,
ByVal _
Command As Integer) As Integer

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(ByVal Destination As Any, ByVal Source As Any, ByVal Length As Integer)

' ==================================================================
' SetPrinterDuplex
'
' Programmatically set the Duplex flag for the specified printer
' driver's default properties.
'
' Returns: True on success, False on error. (An error will also

' display a message box. This is done for informational value
' only. You should modify the code to support better error
' handling in your production application.)
'
' Parameters:
' sPrinterName - The name of the printer to be used.
'
' nDuplexSetting - One of the following standard settings:
' 1 = None
' 2 = Duplex on long edge (book)
' 3 = Duplex on short edge (legal)
'
' ==================================================================
Public Function SetPrinterDuplex(ByVal sPrinterName As String, _
ByVal nDuplexSetting As Integer) As Boolean

Dim hPrinter As Integer
Dim pd As PRINTER_DEFAULTS
Dim pinfo As PRINTER_INFO_2
Dim dm As DEVMODE

Dim yDevModeData() As Byte
Dim yPInfoMemory() As Byte
Dim nBytesNeeded As Integer
Dim nRet As Long, nJunk As Long
On Error GoTo cleanup

If (nDuplexSetting < 1) Or (nDuplexSetting > 3) Then
MsgBox("Error: dwDuplexSetting is incorrect.")
Exit Function
End If

pd.DesiredAccess = PRINTER_ALL_ACCESS
nRet = OpenPrinter(sPrinterName, hPrinter, pd)
If (nRet = 0) Or (hPrinter = 0) Then
If Err.LastDllError = 5 Then
MsgBox("Access denied -- See the article for more info.")
Else
MsgBox("Cannot open the printer specified " & _
"(make sure the printer name is correct).")
End If
Exit Function
End If

nRet = DocumentProperties(0, hPrinter, sPrinterName, dm, dm, 0)
If (nRet < 0) Then
MsgBox("Cannot get the size of the DEVMODE structure.")
GoTo cleanup
End If

ReDim yDevModeData(nRet + 100)
nRet = DocumentProperties(0, hPrinter, sPrinterName, _
VarPtr(yDevModeData(0)), 0, DM_OUT_BUFFER)

If (nRet < 0) Then
MsgBox("Cannot get the DEVMODE structure.")
GoTo cleanup
End If

Call CopyMemory(dm, yDevModeData(0), Len(dm))

If Not CBool(dm.dmFields And DM_DUPLEX) Then
MsgBox("You cannot modify the duplex flag for this printer " & _
"because it does not support duplex or the driver " & _
"does not support setting it from the Windows API.")
GoTo cleanup
End If

dm.dmDuplex = nDuplexSetting
Call CopyMemory(yDevModeData(0), dm, Len(dm))

nRet = DocumentProperties(0, hPrinter, sPrinterName, _
VarPtr(yDevModeData(0)), VarPtr(yDevModeData(0)), _
DM_IN_BUFFER Or DM_OUT_BUFFER)

If (nRet < 0) Then
MsgBox("Unable to set duplex setting to this printer.")
GoTo cleanup
End If

Call GetPrinter(hPrinter, 2, 0, 0, nBytesNeeded)
If (nBytesNeeded = 0) Then GoTo cleanup

ReDim yPInfoMemory(nBytesNeeded + 100)

nRet = GetPrinter(hPrinter, 2, yPInfoMemory(0), nBytesNeeded, nJunk)
If (nRet = 0) Then
MsgBox("Unable to get shared printer settings.")
GoTo cleanup
End If

Call CopyMemory(pinfo, yPInfoMemory(0), Len(pinfo))
pinfo.pDevMode = VarPtr(yDevModeData(0))
pinfo.pSecurityDescriptor = 0
Call CopyMemory(yPInfoMemory(0), pinfo, Len(pinfo))

nRet = SetPrinter(hPrinter, 2, yPInfoMemory(0), 0)
If (nRet = 0) Then
MsgBox("Unable to set shared printer settings.")
End If

SetPrinterDuplex = CBool(nRet)

cleanup:
If (hPrinter <> 0) Then Call ClosePrinter(hPrinter)

End Function

End Module

******************************
"Wei-Dong XU [MSFT]" wrote:

[Original message clipped]

Reply to this message...
 
    
[MSFT] (VIP)
For question 1: Visual Basic 6 allowed you to declare parameters As Any,
meaning that data of any data type could be used. Visual Basic .NET
requires that you use a specific data type for all declare statements.
Normally, we can declare as:

ByRef Destination As Object

For question 2: There is no VarPtr function in VB.net, but you can use
following code instead:

Public Function VarPtr(ByVal o As Object) As IntPtr

Dim GC As System.Runtime.InteropServices.GCHandle =
System.Runtime.InteropServices.GCHandle.Alloc(o,
System.Runtime.InteropServices.GCHandleType.Pinned)

Dim ret As Integer = GC.AddrOfPinnedObject.ToInt32

GC.Free()

Return New IntPtr(ret)

End Function

For question 3: pInfo.pSecurityDescriptor = nothing

Anyway, I think there is a .NET way to set the printer's Duplex Property
without call APIs. you may take a look at following to see if it will help:

PrinterSettings.Duplex Property
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/
frlrfsystemdrawingprintingprintersettingsclassduplextopic.asp

Hope this help,

Luke

Reply to this message...
 
    
manofbluz
Thanks, Luke, but I'm still having an issue. First, as far as using the .Net
printing classes, I already Googled that and found someone who already tried
doing what I'm asking about. Apparently, you can't get to the printer
settings unless you have a document object (makes sense), but unfortunately
you can't assign a Word automation document to the .Net PrintDocument object.

So, I think we're stuck having to resolve this via the API. I made the
changes you suggested, but now everywhere I'm calling the VarPtr function,
I'm getting the message: "Value of type System.IntPtr cannot be converted to
Module1.DEVMODE"

Also, in the second call to the DocumentProperties function:

Redim yDevModeData(nRet + 100)
nRet = DocumentProperties(0, hPrinter, sPrinterName, VarPtr(yDevModeData(0),
0, DM_OUT_BUFFER)

..Net objects to the second to the last argument, saying: "Value of type
'Integer' cannot be converted to Module1.DEVMODE"

Please advise. I appreciate the help, as I'm lost with the API, and
converting my existing application to VB.Net hinges on being able to print
out these Word documents, sometimes in duplex mode, sometimes not.

Thanks.

"[MSFT]" wrote:

[Original message clipped]

Reply to this message...
 
    
[MSFT] (VIP)
I composed a complete sameple for you, you may try it and let me know if
there are further question:

Imports System
Imports System.Runtime.InteropServices

Module Module1

<DllImport("kernel32.dll", EntryPoint:="GetLastError", _
SetLastError:=False, ExactSpelling:=True,
CallingConvention:=CallingConvention.StdCall)> _
Public Function GetLastError() As Int32
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:="DocumentPropertiesA",
SetLastError:=True, _
ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public Function DocumentProperties(ByVal hwnd As IntPtr, ByVal hPrinter
As IntPtr, _
<MarshalAs(UnmanagedType.LPStr)> ByVal pDeviceNameg As String, _
ByVal pDevModeOutput As IntPtr, ByRef pDevModeInput As IntPtr, ByVal
fMode As Integer) As Integer

End Function

<DllImport("winspool.Drv", EntryPoint:="GetPrinterA",
SetLastError:=True, _
ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Public 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

Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA"
(ByVal pPrinterName As String, ByRef phPrinter As IntPtr, ByRef pDefault As
PRINTER_DEFAULTS) As Integer

<DllImport("winspool.Drv", EntryPoint:="SetPrinterA",
ExactSpelling:=True, SetLastError:=True)> _
Public Function SetPrinter(ByVal hPrinter As IntPtr, ByVal Level As
Integer, ByVal pPrinter As IntPtr, ByVal Command As Integer) As Boolean
End Function

<StructLayout(LayoutKind.Sequential)> Public Structure PRINTER_DEFAULTS
Public pDatatype As IntPtr
Public pDevMode As IntPtr
Public DesiredAccess As Integer
End Structure

'<StructLayout(LayoutKind.Sequential)> Public Structure PRINTER_DEFAULTS
' Public pDatatype As String
' Public pDevMode As DEVMODE
' Public DesiredAccess As Integer
'End Structure

<StructLayout(LayoutKind.Sequential)> Public Structure PRINTER_INFO_2

<MarshalAs(UnmanagedType.LPStr)> Public pServerName As String
<MarshalAs(UnmanagedType.LPStr)> Public pPrinterName As String
<MarshalAs(UnmanagedType.LPStr)> Public pShareName As String
<MarshalAs(UnmanagedType.LPStr)> Public pPortName As String
<MarshalAs(UnmanagedType.LPStr)> Public pDriverName As String
<MarshalAs(UnmanagedType.LPStr)> Public pComment As String
<MarshalAs(UnmanagedType.LPStr)> Public pLocation As String
Public pDevMode As IntPtr
<MarshalAs(UnmanagedType.LPStr)> Public pSepFile As String
<MarshalAs(UnmanagedType.LPStr)> Public pPrintProcessor As String
<MarshalAs(UnmanagedType.LPStr)> Public pDatatype As String
<MarshalAs(UnmanagedType.LPStr)> Public pParameters As String
Public pSecurityDescriptor As IntPtr
Public Attributes As Int32
Public Priority As Int32
Public DefaultPriority As Int32
Public StartTime As Int32
Public UntilTime As Int32
Public Status As Int32
Public cJobs As Int32
Public AveragePPM As Int32
End Structure

Public Const CCDEVICENAME As Short = 32
Public Const CCFORMNAME As Short = 32

<StructLayout(LayoutKind.Sequential)> Public Structure DEVMODE
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=CCDEVICENAME)>
Public dmDeviceName As String
Public dmSpecVersion As Short
Public dmDriverVersion As Short
Public dmSize As Short
Public dmDriverExtra As Short
Public dmFields As Integer

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:=CCFORMNAME)> Public
dmFormName As String

Public dmUnusedPadding As Short
Public dmBitsPerPel As Short
Public dmPelsWidth As Integer
Public dmPelsHeight As Integer
Public dmDisplayFlags As Integer
Public dmDisplayFrequency As Integer
End Structure

'<StructLayout(LayoutKind.Sequential)> Public Structure
SECURITY_DESCRIPTOR 'added this whole structure
' Public Revision As Byte
' Public Sbz1 As Byte
' Public Control As Long
' Public Owner As Long
' Public Group As Long
' Public sACL As ACL
' Public Dacl As ACL
'End Structure

'<StructLayout(LayoutKind.Sequential)> Public Structure ACL 'added this
whole structure
' Public AclRevision As Byte
' Public Sbz1 As Byte
' Public AclSize As Integer
' Public AceCount As Integer
' Public Sbz2 As Integer
'End Structure

Public Const DM_DUPLEX = &H1000&
Public Const DM_MODIFY = 8

Public Const DM_COPY = 2
Public Const DM_IN_BUFFER = 8
Public Const DM_OUT_BUFFER = 2
Public Const PRINTER_ACCESS_ADMINISTER = &H4
Public Const PRINTER_ACCESS_USE = &H8
Public Const STANDARD_RIGHTS_REQUIRED = &HF0000

Public Const PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED Or
PRINTER_ACCESS_ADMINISTER Or PRINTER_ACCESS_USE)

Public Const CCHDEVICENAME As Integer = 32 'added this
Public Const CCHFORMNAME As Integer = 32 'added this

' ==================================================================
' SetPrinterDuplex
'
' Programmatically set the Duplex flag for the specified printer
' driver's default properties.
'
' Returns: True on success, False on error. (An error will also

' display a message box. This is done for informational value
' only. You should modify the code to support better error
' handling in your production application.)
'
' Parameters:
' sPrinterName - The name of the printer to be used.
'
' nDuplexSetting - One of the following standard settings:
' 1 = None
' 2 = Duplex on long edge (book)
' 3 = Duplex on short edge (legal)
'
' ==================================================================
Public Function SetPrinterDuplex(ByVal sPrinterName As String, _
ByVal nDuplexSetting As Integer) As Boolean

Dim hPrinter As IntPtr

Dim pd As PRINTER_DEFAULTS

Dim pinfo As PRINTER_INFO_2 = New PRINTER_INFO_2
Dim dm As DEVMODE = New DEVMODE

Dim ptrDM As IntPtr
Dim ptrPrinterInfo As IntPtr
Dim sizeOfDevMode As Integer = 0
Dim lastError As Integer
Dim yDevModeData() As Byte
Dim yPInfoMemory() As Byte
Dim nBytesNeeded As Integer
Dim nRet As Integer
Dim nJunk As Int32

If (nDuplexSetting < 1) Or (nDuplexSetting > 3) Then
MsgBox("Error: dwDuplexSetting is incorrect.")
Exit Function
End If

pd.DesiredAccess = PRINTER_ALL_ACCESS
nRet = OpenPrinter(sPrinterName, hPrinter, pd)
If (nRet = 0) Or (hPrinter.ToInt32 = 0) Then
If Err.LastDllError = 5 Then
MsgBox("Access denied -- See the article for more info.")
Else
MsgBox("Cannot open the printer specified " & _
"(make sure the printer name is correct).")
End If
Exit Function
End If

nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName,
IntPtr.Zero, IntPtr.Zero, 0)
If (nRet < 0) Then
MsgBox("Cannot get the size of the DEVMODE structure.")
GoTo cleanup
End If

Dim iparg As IntPtr = Marshal.AllocCoTaskMem(nRet + 100)

nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName,
iparg, IntPtr.Zero, DM_OUT_BUFFER)

If (nRet < 0) Then
MsgBox("Cannot get the DEVMODE structure.")
GoTo cleanup
End If

dm = Marshal.PtrToStructure(iparg, dm.GetType)

If Not CBool(dm.dmFields And DM_DUPLEX) Then
MsgBox("You cannot modify the duplex flag for this printer " & _
"because it does not support duplex or the driver " & _
"does not support setting it from the Windows API.")
GoTo cleanup
End If

dm.dmDuplex = nDuplexSetting

Marshal.StructureToPtr(dm, iparg, True)

nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName,
pinfo.pDevMode, pinfo.pDevMode, DM_IN_BUFFER Or DM_OUT_BUFFER)

If (nRet < 0) Then
MsgBox("Unable to set duplex setting to this printer.")
GoTo cleanup
End If

GetPrinter(hPrinter, 2, IntPtr.Zero, 0, nBytesNeeded)

If (nBytesNeeded = 0) Then GoTo cleanup

ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded + 100)

nRet = GetPrinter(hPrinter, 2, ptrPrinterInfo, nBytesNeeded, nJunk)

If (nRet = 0) Then
MsgBox("Unable to get shared printer settings.")
GoTo cleanup
End If

pinfo = Marshal.PtrToStructure(ptrPrinterInfo, pinfo.GetType)

pinfo.pDevMode = iparg
pinfo.pSecurityDescriptor = IntPtr.Zero
Marshal.StructureToPtr(pinfo, ptrPrinterInfo, True)

Dim o As New ClassLibrary1.Class1

o.SetPrinterDuplex(sPrinterName, 2)

hPrinter = o.hPrinter
ptrPrinterInfo = o.ptrPrinterInfo

nRet = SetPrinter(hPrinter, 2, ptrPrinterInfo, 0)

MsgBox(GetLastError())

If (nRet = 0) Then
MsgBox("Unable to set shared printer settings.")
End If

SetPrinterDuplex = CBool(nRet)

cleanup:
If (hPrinter.ToInt32 <> 0) Then Call ClosePrinter(hPrinter)

End Function

End Module

Reply to this message...
 
    
manofbluz
Thanks, again, Luke, but I think you forgot to paste in some code. In the
middle of the SetPrinterDuplex function, you instantiate a class for which
you didn't include the code:

Dim o As New ClassLibrary1.Class1

"[MSFT]" wrote:

[Original message clipped]

Reply to this message...
 
    
[MSFT] (VIP)
Sorry. I forgot to remove some debug code. Following code is not necessary:

Dim o As New ClassLibrary1.Class1

o.SetPrinterDuplex(sPrinterName, 2)

hPrinter = o.hPrinter
ptrPrinterInfo = o.ptrPrinterInfo

Please remove them and test again.

Thanks,

Luke

Reply to this message...
 
    
manofbluz
I'm sorry, too. I forgot to tell you that I had already commented out those
lines and tried it. The code runs fine but the document doesn't actually
duplex. So, I wanted to make sure those lines weren't necessary.

I orginally got "Access Denied" so I did what the article said and installed
the network printer locally. I've tried the code against two different
printers, an HP and a Canon and neither works. Does this actually work for
you when used in conjunction with Word Automation?

"[MSFT]" wrote:

[Original message clipped]

Reply to this message...
 
    
[MSFT] (VIP)
Hello,

VB.NET app may get some interop problem when performing API calls. Is C#
code good for you? I also have a sample which work fine on my computer. I
paste here and you may try it. To embed it in your VB project, you may add
a C# class library project and reference it in VB project.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using System.Drawing.Printing;

namespace setprintertest
{
    /// <summary>
    /// Summary description for Form1.
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            //
            // TODO: Add any constructor code after InitializeComponent call
            //
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // button1
            //
            this.button1.Location = new System.Drawing.Point(112, 176);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(80, 32);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }
        #endregion

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

        [DllImport("kernel32.dll", EntryPoint="GetLastError", SetLastError=false,
             ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
        internal static extern Int32 GetLastError();

        [DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true,
                ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
        static extern bool ClosePrinter(IntPtr hPrinter);

        [DllImport("winspool.Drv", EntryPoint="DocumentPropertiesA",
SetLastError=true,
                ExactSpelling=true, CallingConvention=CallingConvention.StdCall )]
        private static extern int DocumentProperties (IntPtr hwnd, IntPtr
hPrinter,
            [MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
            IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);

        [DllImport("winspool.Drv", EntryPoint="GetPrinterA", SetLastError=true,
                ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
        private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
            IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);

        [DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true,
                ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
        static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string
szPrinter,
            out IntPtr hPrinter, ref PRINTER_DEFAULTS pd);

        [DllImport("winspool.Drv",EntryPoint="SetPrinterA", ExactSpelling=true,
SetLastError=true)]
        private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr
pPrinter, int    Command);

        // Wrapper for Win32 message formatter.

        [StructLayout(LayoutKind.Sequential)]
            private struct PRINTER_DEFAULTS
        {
            public IntPtr pDatatype;
            public IntPtr pDevMode;
            public int DesiredAccess;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct PRINTER_INFO_2
        {
            [MarshalAs(UnmanagedType.LPStr)] public string pServerName;
            [MarshalAs(UnmanagedType.LPStr)] public string pPrinterName;
            [MarshalAs(UnmanagedType.LPStr)] public string pShareName;
            [MarshalAs(UnmanagedType.LPStr)] public string pPortName;
            [MarshalAs(UnmanagedType.LPStr)] public string pDriverName;
            [MarshalAs(UnmanagedType.LPStr)] public string pComment;
            [MarshalAs(UnmanagedType.LPStr)] public string pLocation;
            public IntPtr pDevMode;
            [MarshalAs(UnmanagedType.LPStr)] public string pSepFile;
            [MarshalAs(UnmanagedType.LPStr)] public string pPrintProcessor;
            [MarshalAs(UnmanagedType.LPStr)] public string pDatatype;
            [MarshalAs(UnmanagedType.LPStr)] public string pParameters;
            public IntPtr pSecurityDescriptor;
            public Int32 Attributes;
            public Int32 Priority;
            public Int32 DefaultPriority;
            public Int32 StartTime;
            public Int32 UntilTime;
            public Int32 Status;
            public Int32 cJobs;
            public Int32 AveragePPM;
        }

        private const short CCDEVICENAME = 32;
        private const short CCFORMNAME = 32;

        [StructLayout(LayoutKind.Sequential)]
        private struct DEVMODE
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
            public string dmDeviceName;

            public short dmSpecVersion;
            public short dmDriverVersion;
            public short dmSize;
            public short dmDriverExtra;
            public int dmFields;

            public short dmOrientation;
            public short dmPaperSize;
            public short dmPaperLength;
            public short dmPaperWidth;
            public short dmScale;
            public short dmCopies;
            public short dmDefaultSource;
            public short dmPrintQuality;

            public short dmColor;
            public short dmDuplex;
            public short dmYResolution;
            public short dmTTOption;
            public short dmCollate;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
            public string dmFormName;

            public short dmUnusedPadding;
            public short dmBitsPerPel;
            public int dmPelsWidth;
            public int dmPelsHeight;
            public int dmDisplayFlags;
            public int dmDisplayFrequency;
        }

        public const int DM_DUPLEX = 0x1000;
        public const int DM_IN_BUFFER = 8;

        public const int DM_OUT_BUFFER = 2;
        public const int PRINTER_ACCESS_ADMINISTER = 0x4;
        public const int PRINTER_ACCESS_USE = 0x8;
        public const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
        private System.Windows.Forms.Button button1;
        public const int PRINTER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
PRINTER_ACCESS_ADMINISTER | PRINTER_ACCESS_USE);

        public bool SetPrinterDuplex(string sPrinterName, int nDuplexSetting)
        {
            IntPtr hPrinter;
            PRINTER_DEFAULTS pd = new PRINTER_DEFAULTS();
            PRINTER_INFO_2 pinfo = new PRINTER_INFO_2();
            DEVMODE dm;
            IntPtr ptrDM;
            IntPtr ptrPrinterInfo;
            int sizeOfDevMode = 0;
            int lastError;
            byte[] yDevModeData;
            byte[] yPInfoMemory;
            int nBytesNeeded;
            int nRet;
            System.Int32 nJunk;

            //On Error GoTo cleanup

            if ((nDuplexSetting < 1) || (nDuplexSetting > 3) )
            {
                throw new ArgumentOutOfRangeException("nDuplexSetting","nDuplexSetting
is incorrect.");
            }
            else
            {
                //if no printername provided, check if there is a default printer and
use it instead
                if (sPrinterName.Trim() == "")
                {
                    PrintDocument printDocument1 = new PrintDocument();
                    sPrinterName = printDocument1.PrinterSettings.PrinterName;// + "\0";
                }

                //open the printer
                pd.DesiredAccess = PRINTER_ALL_ACCESS;
                nRet = Convert.ToInt32(OpenPrinter(sPrinterName, out hPrinter, ref pd));

                if ((nRet == 0) || (hPrinter == IntPtr.Zero))
                {
                    return false;
                }

                IntPtr para=IntPtr.Zero;

                sizeOfDevMode=DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName,
IntPtr.Zero, ref para, 0);
                if(sizeOfDevMode<0)
                {
                    MessageBox.Show("Cannot get the size of the DEVMODE structure");
                }

                IntPtr iparg = Marshal.AllocCoTaskMem(sizeOfDevMode+100);
                nRet = DocumentProperties(IntPtr.Zero , hPrinter, sPrinterName, iparg,
ref para, DM_OUT_BUFFER);
                if(nRet<0)
                {
                    MessageBox.Show("Cannot get the DEVMODE structure");
                }

                dm=(DEVMODE)Marshal.PtrToStructure(iparg, typeof(DEVMODE));
                

                if (!Convert.ToBoolean(dm.dmFields & DM_DUPLEX))
                {
                    //You cannot modify the duplex flag for this printer
                    //because it does not support duplex or the driver does not support
setting
                    //it from the Windows API.
                    return false;
                }

                dm.dmDuplex = (short)nDuplexSetting;
                Marshal.StructureToPtr(dm, iparg, true);

                nRet = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName,
pinfo.pDevMode
                    , ref pinfo.pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));
                if (nRet < 0)
                {
                    //Unable to set duplex setting to this printer.
                    return false;
                }

                //get the size of the Printer Info structure
                nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out
nBytesNeeded));
                if (nBytesNeeded <= 0)
                {
                    return false;
                }
                
                // Allocate enough space for PRINTER_INFO_2...
                ptrPrinterInfo = Marshal.AllocCoTaskMem(nBytesNeeded+100);

                // The second GetPrinter fills in all the current settings, so all you
                // need to do is modify what you're interested in...
                nRet = Convert.ToInt32(GetPrinter(hPrinter, 2, ptrPrinterInfo,
nBytesNeeded, out nJunk));
                if (nRet == 0)
                {
                    return false;
                }
                
                pinfo = (PRINTER_INFO_2)Marshal.PtrToStructure(ptrPrinterInfo,
typeof(PRINTER_INFO_2));
                pinfo.pDevMode=iparg;
                pinfo.pSecurityDescriptor =IntPtr.Zero;    
                Marshal.StructureToPtr(pinfo,ptrPrinterInfo,true);                        

                nRet = Convert.ToInt16(SetPrinter(hPrinter, 2, ptrPrinterInfo, 0));
                if (nRet == 0)//Unable to set shared printer settings.
                {
                    lastError = Marshal.GetLastWin32Error();
                    //string myErrMsg = GetErrorMessage(lastError);

                    return false;
                }                
            }
            if (hPrinter != IntPtr.Zero)
                ClosePrinter(hPrinter);
            return Convert.ToBoolean(nRet);

        }

        private void button1_Click(object sender, System.EventArgs e)
        {
            SetPrinterDuplex("MyTestPrinter",2);
        }
    }
}

Luke

Reply to this message...
 
    
manofbluz
Luke,

The C# code does the exact same thing as the VB. I can step through the
code and everything seems to work. It doesn't throw the exception that my
printer cannot be manipulated via the API; nevertheless it simply won't
duplex the document - it spits out two pages.

Anyway, I think you've given this enough energy and I certainly appreciate
it. I'm just going to have to go with a different solution, I think.

"[MSFT]" wrote:

[Original message clipped]

Reply to this message...
 
 
System.ArgumentOutOfRangeException
System.ComponentModel.Container
System.Convert
System.Drawing.Point
System.Drawing.Printing.PrintDocument
System.Drawing.Printing.PrinterSettings
System.Drawing.Size
System.EventArgs
System.EventHandler
System.GC
System.Int32
System.IntPtr
System.Runtime.InteropServices.CallingConvention
System.Runtime.InteropServices.GCHandle
System.Runtime.InteropServices.GCHandleType
System.Runtime.InteropServices.LayoutKind
System.Runtime.InteropServices.Marshal
System.Runtime.InteropServices.UnmanagedType
System.Windows.Forms.Application
System.Windows.Forms.Button
System.Windows.Forms.Form
System.Windows.Forms.MessageBox




ExamGuru IT Solutions - .Net Guru is owned and operated by ExamGuru, Inc., the man behind .Net Guru. If you're in the market for bespoke software or software consultancy, why not get him and his highly trained team to help? - www.examguru.net/ITCertification
Ad


Need Dot Net Interview Questions?
Ask ExamGuru, Inc. for advice and help on Passing .Net Interviews
.Net Projects
Best-of-breed application framework for .NET projects, developed by ExamGuru, Inc. and ExamGuru IT
Free .net Help
Commission ExamGuru, Inc. and his team for your next bespoke software project
FogBUGZ
The only bug tracking system carefully crafted with one goal in mind: helping teams create great software.
Awesome Tools
If you don't know about these, you're missing out... IT Certification Questions
IT Interview Questions
Free Oracle 10g Training
MCSE Boortcamp
Cisco Study Guides
Cheap Study Guides
Exact Questions
Dot Net Interview Questions
Oracle OCP
Cheap Travel
Designer Perfumes - Wholesale Prices
Free Programming Tutorials
 
ExamGuru IT Solutions - .Net Guru is owned and operated by ExamGuru, Inc., the man behind .Net Guru. If you're in the market for bespoke software or software consultancy, why not get him and his highly trained team to help? - www.examguru.net/ITCertification
 Copyright © ExamGuru, Inc. 2001-2006
Contact Us - Terms of Use - Privacy Policy - www.dot-net-guru.com - www.examguru.net - www.oraclesource.net - www.itinterviews.net - www.examguru.net/ITCertification