How do I enable stapler when printing? - vba

I'm trying to print a word document from excel.
This works except I need to enable the stapler for this print job.
My printer, a Xerox workcentre 5755, can put one or two staples in the top left corner.
Excel apparently can manage this, I do NOT need to go into printer driver properties to enable the stapling, I can enable that directly from File-Print page.
When I go to this page, below "Settings" there is a dropdown written "No Staples" with a stapler symbol.
If I click that, I get choice of "No Staples", "Staple Top Left" and "Two Staples on Top Left" as well as other choices that are greyed out.
I tried recording a macro to print with staple top left and duplex printing.
This is what it gave me.
ActiveWindow.SelectedSheets.PrintOut Copies:=1, Collate:=True, _
IgnorePrintAreas:=False
This does neither stapling nor duplex printing.
I read multiple threads on the subject but they are all old and don't really answer the question since in all cases the asker needed to change driver settings in properties so that is not my case. I have a printer that exposes the stapler function to excel.
Still I had to resort to one of the answers I found and that is to use sendkeys, which is highly unreliable.
Here is what my code looks like.
Sub PrintChecklist()
Dim myprinter As String
Dim PrintersList() As String
Dim printer_name As String
Dim x As Long
Dim wordapp As Word.Application
Dim CBC As CommandBarControl
Set wordapp = CreateObject("word.Application")
wordapp.Documents.Open "C:\Users\XXXXX\Desktop\pool3.doc"
wordapp.Visible = True
myprinter = Application.ActivePrinter
wordapp.WindowState = wdWindowStateMaximize
PrintersList() = GetPrinterFullNames
For x = 1 To UBound(PrintersList)
If InStr(1, PrintersList(x), "MYPRINTERNAME", vbTextCompare) > 0 Then _
printer_name = PrintersList(x)
Next x
wordapp.ActivePrinter = printer_name
DoEvents
wordapp.Application.Activate
' only works in only one word opened
Dim lRet As Long
lRet = FindWindow("OpusApp", vbNullString)
SetForegroundWindow lRet
On Error Resume Next
Set CBC = Application.VBE.CommandBars.FindControl(ID:=752)
On Error GoTo 0
If Not CBC Is Nothing Then
CBC.Execute
DoEvents
'~~> File --> Print
SendKeys "%fp"
Sleep 3000
SendKeys "k"
Sleep 100
SendKeys "{DOWN}"
Sleep 100
SendKeys "~"
Sleep 100
SendKeys "%fp"
Sleep 100
Sleep 1000
SendKeys "s"
SendKeys "3-5,1-2"
SendKeys "%fp"
Sleep 1000
SendKeys "d"
SendKeys "{DOWN}"
SendKeys "~"
SendKeys "%fp"
Sleep 2000
SendKeys "p"
SendKeys "{NUMLOCK}"
End If
Sleep 5000
wordapp.ActivePrinter = myprinter
wordapp.Quit SaveChanges:=wdDoNotSaveChanges
End Sub
It works but if there is a delay or if the user touches anything in that 10 second span, then it all goes wrong very quickly.
Any advice welcomed !

Since this has gotten attention but no official answer, here is how I have dealt with this.
My printer is a network printer, so I have added it a second time to my computer under a different name. I set the default for that printer to be "with top left staple"
Now if I want to print with a staple, I print to that printer name instead of the other.
A bit quirky but it works reliably

Related

VBA statements for print only execute in debug mode

strong textI have a simple word form that I want to print. I pass the printer name to a sub routine (myprint) in the variable oprinter. The variable printcomplete will pass back the name of the printer that was successful in printing the data.
This logic works when I am in debug mode but appears to bypass the print commands when not in debug. I have tried adding delays, I have added a MSGBOX after the print statement (prior to
printcomplete = oprinter > Exit Sub). The MSGBOX does display - so it should have executed the print statement. I do not get the msgbox under myprinterr.
Any idea why it won't print out of debug mode?
Here is my code:
'Print MS Word Form to one of three networked printers
Sub MyPrint(oprinter, printcomplete)
Dim sPrinter As String
On Error GoTo myprinterr
Sleep (5000)
With Dialogs(wdDialogFilePrintSetup)
sPrinter = .Printer
.Printer = oprinter
.DoNotSetAsSysDefault = True
.Execute
Sleep (5000)
Application.PrintOut FileName:=""
.Printer = sPrinter
.Execute
End With
Sleep (5000)
MSGBOX "Did it print? "
printcomplete = oprinter
Exit Sub
myprinterr:
MsgBox "oops printer error on: " & oprinter
End Sub
In the code I've noticed several lines of code:
CreateObject("Excel.Application").Wait (Now + TimeValue("00:00:05")) 'delay to try to get print to work when not in debug mode
There is no need to create a new Excel Application instance each time.
If you want to introduce any delay you can use the Sleep Windows API function instead. It suspends the execution of the current thread until the time-out interval elapses.

Can you interrupt the vba code to make a sheet selection?

I will try to be as clear as possible in the description, so here goes nothing:
I have created a code in which the user selects his excel file and then the macro copies the Sheet from that file into my macro Workbook.
MyFile = Application.GetOpenFilename()
Workbooks.Open (MyFile)
ActiveSheet.Copy After:=wbook.Sheets(1)
ActiveSheet.Name = "Selected file"
Workbooks.Open (MyFile)
ActiveWorkbook.Close SaveChanges:=False
This is working, but what I realized is, that there might be cases where the selected file has multiple Sheets.
Is there a way to write the macro in which if my selected file has 1 sheet it runs the above code and if it has more than one sheet to let me select the sheet I want and then run the rest of the code?
Edit:
I thought of another way to handle this — perhaps closer to what you were looking for . . .
It's just an expansion of the basic pause routine that I use occasionally.
This is my "regular" Pause routine (using the Timer function):
Sub Pause(seconds As Single)
Dim startTime As Single
startTime = Timer 'get current timer count
Do
DoEvents 'let Windows "catch up"
Loop Until Timer > startTime + seconds 'repeat until time's up
End Sub
...so, it gave me an idea.
Honestly, I was a little surprised to discover that this works, since it's basically running two sections of code simultaneously.
Code for WaitForUserActivity :
Here's the code I used in the demo above:
Option Explicit
Public isPaused As Boolean
Sub WaitForUserActivity() 'THE 'RUN DEMO' BUTTON runs this sub.
Dim origSheet As String
isPaused = True 'flag "pause mode" as "on"
origSheet = ActiveSheet.Name 'remember current worksheet name
MsgBox "This will 'pause' code execution until you" & vbLf & _
"click the 'Continue' button, or select a different a worksheet."
Application.StatusBar = "PAUSED: Click ""Continue"", or select a worksheet."
Do 'wait for button click or ws change
DoEvents 'yield execution so that the OS can process other events
Loop Until (Not isPaused) Or (ActiveSheet.Name <> origSheet)
If isPaused Then 'the active worksheet was changed
MsgBox "Worksheet '" & ActiveSheet.Name & "' was selected." _
& vbLf & vbLf & "Now the program can continue..."
Else 'the button was clicked
MsgBox "The 'Continue' button was clicked." _
& vbLf & vbLf & "Now the program can continue..."
End If
Application.StatusBar = "Ready"
End Sub
Sub btnContinue() 'THE 'CONTINUE' BUTTON runs this sub.
isPaused = False 'flag "pause mode" as "off"
End Sub
To run the demo:
place the above code in a regular module
make sure the workbook has at least two worksheets
create two command buttons:
one for the "Run Demo" button, assign macro: WaitForUserActivity
one for the "Continue" button, assign macro: btnContinue
click the "Run Demo" button
The key command in the code is the DoEvents Function, which "yields execution so that the operating system can process other events."
DoEvents passes control to the operating system. Control is returned after the operating system has finished processing the events in its queue and all keys in the SendKeys queue have been sent.
DoEvents is most useful for simple things like allowing a user to cancel a process after it has started, for example a search for a file. For long-running processes, yielding the processor is better accomplished by using a Timer or delegating the task to an ActiveX EXE component - and the operating system takes care of multitasking and time slicing.
Any time you temporarily yield the processor within an event procedure, make sure the procedure is not executed again from a different part of your code before the first call returns; this could cause unpredictable results.
Further details (and warnings) at the source.
Original Answer:
Some suggested solutions:
Instead of "stopping" the code you could prompt the user to specify which worksheet.
The easiest way would be with an InputBox where the user would enter an ID number or otherwise identify the worksheet.
More complicated but more robust and professional-looking would be a custom dialog box with the help of a userform. There are several examples and tutorials online such as this one.
You could "pause" execution to give the user a set amount of time to select a worksheet, with a simple timer loop, ad you could even check the worksheet name to see if the user picked a new one, something like this:
Dim startTime As Single, shtName As String
If ThisWorkbook.Worksheets.Count = 1 Then
MsgBox "There is only one worksheet in this workbook."
Else
shtName = ActiveSheet.Name 'get name of active sheet
MsgBox "You have 5 seconds to select a worksheet after clicking OK.", _
vbOKOnly + vbInformation, "Select a worksheet... fast!"
startTime = Timer
Do
DoEvents
Loop Until Timer > startTime + 5
'check if user picked a new worksheet
If ActiveSheet.Name = shtName Then
MsgBox "You didn't select a new worksheet!"
Else
MsgBox "Thanks for selecting a new worksheet!"
End If
End If
It's a little hoakey but could work, especially if proper checks to make sure you've got the correct worksheet now.
I suppose you could create an worksheet event procedure that would run when a worksheet is activated, and checked a global variable to see if your "import procedure" was running, and if so, resume your code... but that would be messy and confusing and would require the code to exist in the workbook you're "importing".
Or, better than any of those would be to programmatically/logically determine which worksheet you need based on the contents of the worksheet. Is there a title? A certain date? Maybe the newest worksheet? Something in a certain cell? There must be something that differentiates it from the others.
Hopefully this gives you some ideas towards a non-linear solution. 😉
As in whole, I would recommend ashleedawg's solution, but if you
insisted on maintaining your code structure, your code could look
something like this:
You can distinguish between amount of Sheets a Workbook has using .Count property of the Sheets object (or Worksheets if you do not want to include Charts) and use InputBox to check for the sheet you want to look for.
MyFile = Application.GetOpenFilename()
Workbooks.Open (MyFile)
If ThisWorkbook.Sheets.Count = 1 Then
ThisWorkbook.ActiveSheet.Copy After:=wbook.Sheets(1)
ThisWorkbook.ActiveSheet.Name = "Selected File"
Else
Dim checkfor As String
checkfor = InputBox("What Sheet should I execute the code for?")
Dim i As Integer
For i = 0 To ThisWorkbook.Sheets.Count
If Trim(LCase(checkfor)) = Trim(LCase(Sheets(i).Name))) Then
ThisWorkbook.Sheets(i).Copy After := wbook.Sheets(1)
ThisWorkbook.Sheets(i).Name = "Selected file"
End If
Next i
End If
Workbooks.Open (MyFile)
ActiveWorkbook.Close SaveChanges:=False
Might need some further tweaking, because I was unsure what exactly you wanted to achieve.

How to fix Excel-2016 not running Excel-2010 VBA code for printing correctly?

This loop runs typically 4 to 8 times. It updates 30+ textboxes and prints. Has been working great. But we updated from office 2010 to 2016 (and to Office 365), so now it runs but all the textboxes on the printed pages have the same value from iteration 1. Happens on all printers including PDFcreator. But afterwards the sheet is in the state I expect for the last iteration. It's like the code outruns the printer. But adding a delay of even 10 sec does not help. Oddly, as I try different things I see on the first iter that the textboxes update (from the previous runs last iter). Seems like it should update every iter.
Sub printParamSheets()
On Error GoTo errHandler
Dim Bin1_Matl, Bin1_Parts 'Declaring types won't help or speed up
Dim iCond, conditions
Application.EnableEvents = True 'Is not helping with issue
For iCond = 1 To conditions
With Sheet2
'Assign from sheet2 to variables
Bin1_Matl = .Range("A64").Offset(0, iCond * 2).Value2
Bin1_Parts = .Range("B64").Offset(0, iCond * 2).Value2
'about 30 more of these
End With
With Sheet8 'Assign Sheet8 named ranges from variables above.
'Could skip intermed vars but nice for debugging.
'ALL LINKED TO ACTIVEX TEXT BOXES on Sheet8, atop an image.
.Range("Bin1Matl").Value2 = Bin1_Matl
.Range("Bin1Parts").Value2 = Bin1_Parts
'about 30 more of them
.Calculate 'Is not helping with issue
Dim ctrl As OLEObject 'Is not helping with issue
For Each ctrl In .OLEObjects
If TypeName(ctrl.Object) = "TextBox" Then
'ctrl.Update 'error, but was not in 2010 anyway
End If
Next ctrl
Application.Wait (Now + TimeValue("00:00:05")) 'Is not helping
Application.ScreenUpdating = True 'Never toggled off , no help
DoEvents 'Is not helping with issue
.PrintOut from:=1, To:=1, Copies:=1, Collate:=True
End With
Next iCond
Exit Sub
errHandler:
Application.EnableEvents = True 'Don't need but cannot hurt
Resume Next
End Sub
I will try skipping the extra intermediate assignments, going straight from sheet2 to the textboxes. But I'd like to know the cause because I have other code 'in the wild' that doesn't necessarily use any activex objects that may be affected. Hoping the symptom is not unique to our site and so others may benefit from an answer.

IWebBrowser2::Navigate2 occasionally freezes when opening several new tabs

I'm writing an Excel script to open a list of PDFs in Internet Explorer tabs. It works fine most of the time, but occasionally when I try to close my browser window, a few of the tabs will close, then it stops and all IE instances will freeze, so I have to kill them all in Task Manager. Note that I can avoid the problem by closing each tab individually.
I'm running IE8 and Excel 2007, for the record. Here's my code:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
ShowBrowserWarning
Dim TheHTML As String, PDFs, PDF, First, SerialValue, Test, k
If Target.Column = 1 And Target.Count = 1 Then
' Get the serial number from the adjacent column
SerialValue = Cells(Target.Row, Target.Column + 1)
TheHTML = ShowHTML("http://ucmwww.dnr.state.la.us/ucmsearch/findAllDocuments.aspx?brief=False&query=xwellserialnumber+LIKE+'" & SerialValue & "'+AND+xdocumenttype+LIKE+'WELL ENGINEERING/MECHANICAL'&format=HTML&sortfield=xdate")
Set PDFs = ExtractPDFs(TheHTML)
If PDFs Is Nothing Then
MsgBox "No associated well engineering/mechanical PDFs."
Else
First = True
Dim ie As Object
Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = True
For Each PDF In PDFs
'While ie.Busy
' Dim testvar
' testvar = 1 + 1
'Wend
If First Then
' Open new IE window
ie.Navigate2 PDF.Value
First = False
Else
' Open tab in existing IE window
ie.Navigate2 PDF.Value, 2048
End If
Next
End If
End If
End Sub
What gives? Why does it freeze like that? Does it have anything to do this issue? (Please try not to laugh at my ignorance!) Any help is much appreciated.
Edit: see the italicized text above. I didn't quite describe the problem accurately!
And what about Browser-Busy check? Could it help to avoid the issue?
For Each PDF In PDFs
While ie.Busy
DoEvents
Wend
If First Then
' Open new IE window
ie.Navigate2 PDF.Value
First = False
Else
' Open tab in existing IE window
ie.Navigate2 PDF.Value, 2048
End If
Next
Or just wait between the browser.Navigate calls for a while to give the browser enough time to load one dokument before starting to load next one. Try different time-periods and watch if the freezing issue could be avoided this way.
For Each PDF In PDFs
DoEventsForTimePeriod timePeriodInSeconds:=15 ' try different time periods here
If First Then
' Open new IE window
ie.Navigate2 PDF.Value
First = False
Else
' Open tab in existing IE window
ie.Navigate2 PDF.Value, 2048
End If
Next
Private Sub DoEventsForTimePeriod(ByVal timePeriodInSeconds As Single)
' VBA.Timer: Returns a Single representing the number of seconds elapsed since midnight.
Dim pause As Single: pause = VBA.Timer + timePeriodInSeconds
Do While VBA.Timer < pause
DoEvents ' Yield to other processes.
Loop
End Sub
Well, I´m new too but, as far as can see, I would set ie = Nothing at the end of the sub to loose any relation between VBA and InternetExplorer Application

I cant unlock a vbaproject with VBA

Down below is my function, for some reason it does not work. If I place a breakpoint in the last if statement and removes sending "%{F11}" it does work. So my guess is that "%F11" is not working. Does anyone have an idea?
Sub UnprotecPassword(wb As Workbook, ByVal projectPassword As String)
Dim currentActiveWb As Workbook
If wb.VBProject.Protection <> vbext_pp_locked Then
Exit Sub
End If
Set currentActiveWb = ActiveWorkbook
wb.Activate
SendKeys "%{F11}"
SendKeys "^r" ' Set focus to Explorer
SendKeys "{TAB}" ' Tab to locked project
SendKeys "~" ' Enter
SendKeys projectPassword
SendKeys "~" ' Enter
If (wb.VBProject.Protection = vbext_pp_locked) Then
MsgBox ("failed to unlock")
End If
currentActiveWb.Activate
End Sub
To test this, Let's create a new workbook called Book2.xlsm.
for testing purpose paste this code in the Book2 Module.
Sub Book2Macro()
End Sub
Protect it with a password say a and then close it. This is necessary for the Locking to take effect.
Now create a new workbook say Book1 and in the module paste this code.
Sub Sample()
UnprotecPassword Workbooks("Book2.xlsm"), "a"
End Sub
Sub UnprotecPassword(wb As Workbook, ByVal projectPassword As String)
Dim currentActiveWb As Workbook
If wb.VBProject.Protection <> 1 Then
Exit Sub
End If
Set currentActiveWb = ActiveWorkbook
wb.Activate
SendKeys "%{F11}"
SendKeys "^r" ' Set focus to Explorer
SendKeys "{TAB}" ' Tab to locked project
SendKeys "~" ' Enter
SendKeys projectPassword
SendKeys "~" ' Enter
If (wb.VBProject.Protection = vbext_pp_locked) Then
MsgBox ("failed to unlock")
End If
currentActiveWb.Activate
End Sub
Now open the 1st workbook that we created; Book2.xlsm. Check the VBA Editor for Book2 and you will notice that it is password protected. You will also notice that it is the active workbook. Activate Book1 by clicking the View Tab | Switch Workbooks | Book1
Now click on Developer tab | Macros If you can't see Developer tab then I would recommend going through this link.
Click on the the Sample Macro in the Macro Dialog Box and you are done.
If you check the VBA Editor, you will notice that the VBA Editor for Book2 is now unlocked/accessible.
Sendkeys are unreliable depending on your use of it. If you use it correctly then that are pretty much reliable :)
There is one more way to unlock the VBA Password but that is pretty complex and involves invoking the API like FindWindow etc...
Check out these posts for code samples:
http://www.mrexcel.com/archive/VBA/29825.html
http://www.vbaexpress.com/forum/showthread.php?t=30687
And these posts are for info:
http://www.excelforum.com/excel-programming/490883-why-doesnt-sendkeys-work-consistently.html
http://www.ozgrid.com/forum/showthread.php?t=13006
They discuss why using Sendkeys is not very reliable in a multitasking environment and many discourage the use for commercial purpose.
However, for unprotecting VBA projects, it appears to be the only solution.
Hope it helps!