When printing in Excel, my workplace has an additional pop up window for selecting print options. It is not part of Excel (I believe it is a canon printer dialogue window). These options allow you to specify to print in colour, staple and collate etc. They are not excel print options.
In the past, I have used a macro which uses SendKeys
to replicate the keyboard shortcuts used to select (in Excel) Page Layout (alt P), Page Setup (alt I), and then 'Options' in the Page Setup screen (alt O). Once selecting 'Options', the printer dialogue screen opens and the macro continued to use SendKeys
to select the profile in this window (each profile contains options to print in colour, staple and collate etc). The piece of code is as follows:
Sub Test()
Application.SendKeys ("%p"), True 'Selects Page Layout
Application.SendKeys ("%i"), True 'Selects Print Titles
Application.SendKeys ("%o"), True 'Selects Options
Application.SendKeys ("p"), True 'Selects 'Portrait' default (this needs to be set up initially)
Application.SendKeys "{TAB 19}", True 'Tabs to OK
Application.Wait (Now() + TimeValue("00:00:01"))
Application.SendKeys "~", True 'Hits enter to close screen
Application.Wait (Now() + TimeValue("00:00:01"))
Application.SendKeys "~", True 'Hits enter to close screen
End Sub
Since moving to Windows 10/Office 2016 - the SendKeys
fails at the point where the separate printer window opens (specifically at the line starting with Application.SendKeys ("p"), True
and beyond). Basically, the macro will open up the printer settings window but do nothing after that.
I have tried looking for a replacement to SendKeys
, but I am struggling to understand how I can - via VBA - automate the process to hit p (selects portrait profile in print dialogue window), hit tab 19 times (to get to the exit screen button), and hit enter twice (to close subsequent dialogue windows - which are excel windows). To be clear - the 'portrait' profile mentioned is a specific printer profile which specifies a number of options including orientation, 2 sided printing, binding location, color mode and the staple/collate/group preference.
I would be quite happy to replace all of the SendKeys
commands if possible as I understand they are not reliable/supported.
[Update 14.05.2019]:
So I've looked into trying to replace the sendkeys with 'Keybd_Event' instead, but this hits exactly the same roadblock (works right until the printer dialogue window opens).
[Update 20.05.2019]
@Selkie's solution worked, and I have marked it as the answer.
This was the code that I used in the end, although still need to tweak it so that it loops through selected sheets:
Sub PrinterSetUp()
Dim filepath As String
Dim Msg As Integer
'Filepath can't have a space in it
filepath = "Directory\PrinterScriptPortrait.vbs"
If Dir(filepath) <> "" Then
'Hurray it exists
Else
'It doesn't exist yet, create the file
WriteVBSScript (filepath)
End If
Shell "wscript " & filepath, vbNormalFocus
'no code after here, otherwise everything breaks.
End Sub
Sub WriteVBSScript(filepath As String)
Dim VBScriptString As String
Dim fso As Object
Dim oFile As Object
'Needs to be done as a VBS script because VBA and sendkeys don't play well with dialog boxes.
Set fso = CreateObject("Scripting.FileSystemObject")
Set oFile = fso.CreateTextFile(filepath)
VBScriptString = VBScriptString & "Set WSHShell = WScript.CreateObject( " & Chr(34) & "WScript.Shell" & Chr(34) & " )" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.AppActivate " & Chr(34) & " Excel.exe " & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "%p" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "%i" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "%o" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "p" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "{TAB 19}" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "~" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "~" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
oFile.WriteLine VBScriptString
oFile.Close
Set fso = Nothing
Set oFile = Nothing
End Sub
So this is really tricky, and I only recently managed to figure it out.
Basically, when you open up a window like that, sendkeys
stops working - from Excel.
The solution? Invoke the sendkeys externally.
Here's a sample code that I wrote to change the printer type to Duplex printing on the 15 sheets after the sheet where the button is located:
Sub PrinterSetUp()
Dim filepath As String
Dim Msg As Integer
'This code is super janky, I apologize.
'Filepath can't have a space in it
filepath = "Filepath\PrinterScript.vbs"
'Select the printer we want to print to
Application.Dialogs(xlDialogPrinterSetup).Show
If Dir(filepath) <> "" Then
'Hurray it exists
Else
'It doesn't exist yet, create the file
WriteVBSScript (filepath)
End If
Shell "wscript " & filepath, vbNormalFocus
'no code after here, otherwise everything breaks.
End Sub
Sub WriteVBSScript(filepath As String)
Dim VBScriptString As String
Dim fso As Object
Dim oFile As Object
'Needs to be done as a VBS script because VBA and sendkeys don't play well with dialog boxes.
Set fso = CreateObject("Scripting.FileSystemObject")
Set oFile = fso.CreateTextFile(filepath)
VBScriptString = VBScriptString & "Set WSHShell = WScript.CreateObject( " & Chr(34) & "WScript.Shell" & Chr(34) & " )" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.AppActivate " & Chr(34) & " Excel.exe " & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 1000" & vbNewLine
VBScriptString = VBScriptString & "For i = 1 To 14" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "^" & Chr(34) & "&" & Chr(34) & "{PGDN}" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "%psp" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 2500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "{TAB 1}" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "o" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 3500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "{TAB 4}" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "n" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "y" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "{TAB 2}" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "l" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "{Enter}" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 500" & vbNewLine
VBScriptString = VBScriptString & "WSHshell.SendKeys " & Chr(34) & "{Enter}" & Chr(34) & " " & vbNewLine
VBScriptString = VBScriptString & "wsh.sleep 500" & vbNewLine
VBScriptString = VBScriptString & "Next"
oFile.WriteLine VBScriptString
oFile.Close
Set fso = Nothing
Set oFile = Nothing
End Sub
How it works: It checks that there's an external VBS script. If it doesn't find it, it'll write it, then it'll use powershell to invoke the script. The script will then use sendkeys "externally" from Excel to make everything work - overriding Excel's innate "Can't use sendkeys for dialogs"
I had to lower my standards pretty far down before being able to do this, and I don't recommend it. I believe for most everything besides duplex printing, there are other options. However, this does make sendkeys work with Excel dialogs, which is what you were asking about.
You will of course need to edit the code to work for what you're trying to do with the sendkeys - an easier way would be to simply write the VBS script directly - I needed my script to work on whatever computer and whatever filepath directory I happened to be on, hence the write-then-use features.
Your script translated would look something like:
VBScriptString = VBScriptString & " Application.SendKeys (" & Chr(34)& "%p" & Chr(34)& "), True 'Selects Page Layout" & vbnewline
VBScriptString = VBScriptString & " Application.SendKeys (" & Chr(34)& "%i" & Chr(34)& "), True 'Selects Print Titles" & vbnewline
VBScriptString = VBScriptString & " Application.SendKeys (" & Chr(34)& "%o" & Chr(34)& "), True 'Selects Options" & vbnewline
VBScriptString = VBScriptString & " Application.SendKeys (" & Chr(34)& "p" & Chr(34)& "), True 'Selects 'Portrait' default (this needs to be set up initially)" & vbnewline
VBScriptString = VBScriptString & " Application.SendKeys " & Chr(34)& "{TAB 19}" & Chr(34)& ", True 'Tabs to OK" & vbnewline
VBScriptString = VBScriptString & " Application.Wait (Now() + TimeValue(" & Chr(34)& "00:00:01" & Chr(34) & "))" & vbnewline
VBScriptString = VBScriptString & " Application.SendKeys " & Chr34 & "~" & Chr(34)& ", True 'Hits enter to close screen" & vbnewline
VBScriptString = VBScriptString & " Application.Wait (Now() + TimeValue(" & Chr(34)& "00:00:01" & Chr(34)& "))" & vbnewline
VBScriptString = VBScriptString & " Application.SendKeys " & Chr(34)& "~" & Chr(34)& ", True 'Hits enter to close screen" & vbnewline
Of course, you might want to use the default method:
Worksheets("Sheet1").PageSetup.Orientation = xlPortrait
This is a much easier way to make a sheet print in portrait mode