VBA macro for printing is having performance issues - vba

I have a subroutine in an Access db that takes the active Word document and sends the first of every n pages to one printer, and the rest to another. The macro works, but it runs progressively slower with each loop. So far it's taken 1h 40m to print 300 pages (100 recipients, 3 pages each).
Anyone out there know how I can speed things up?
Here's an example of the typical params being passed in: cmdPrintStart(3, 1, 247, "\Main", "\Letterhead")
Public Sub cmdPrintStart(tbPageCount As Long, tbStart As Long, tbEnd As Long, Printer1 As String, Printer2 As String)
On Error GoTo Exit_Handler
Dim wApp As Word.Application
SetWordApp wApp 'This sub sets wApp to an instance of Word, or creates one if none is found.
With wApp.ActiveDocument
Dim DefaultPrinter As String, i As Long
DefaultPrinter = ActivePrinter
For i = tbStart To tbEnd
Debug.Print = "Printing recipient " & i & " of " & (tbStart - tbEnd + 1) & "..."
'Switch active printer and print first page of section
ActivePrinter = Printer1
.PrintOut Range:=wdPrintFromTo, From:="p1s" & i, To:="p1s" & i
'Switch active printer and print the rest of the section
ActivePrinter = Printer2
.PrintOut Range:=wdPrintFromTo, From:="p2s" & i, To:="p" & tbPageCount & "s" & i
Next i
End With
ActivePrinter = DefaultPrinter
Exit_Handler:
If Err Then
MsgBox "Unexpected error #" & Str(Err.Number) & " occurred: " & Err.Description, vbCritical, "Well shoot."
Debug.Print = "Printed up to recipient " & i & " before encountering an error."
Else
Debug.Print = "All done! Printed recipients " & tbStart & " to " & tbEnd & "."
End If
End Sub
Further info:
The purpose is to send page 1 of each section in a mail merge document to a tray with letterhead paper, and the other pages to normal paper, printed in collated order. Surprisingly, there doesn't seem to be anything in Word that allows you to do this.
The active document is a post-merged document. If it will improve performance, I can try writing a sub to print from the mail merge template with the connected data source instead.
Thanks in advance for your help.

I'm just taking a wild guess here: does each loop open a new instance of Word in memory, with the result that after many loop executions you're running out of memory? Maybe try dereferencing your Word object at the end of each loop, something like
wApp.Close
Set wApp = nothing
I don't know that it will help but it surely can't hurt.

Related

Find mailfolder in Outlook with Redemption

I try to find a folder in an Outlook account (I use Multiple accounts) using VBA and Redemption by using the FIND method but I cannot get it to work. On the Redemption webpage there is a reference made to an example and this may help but unfortunately the example isn't there.
Here's my code so far:
Public Function FindFolderRDO(strCrit As String) As String
If Not TempVars![appdebug] Then On Error GoTo Err_Proc
Dim objRdoSession As Redemption.RDOSession
Dim objRdoFolder As RDOFolder
Dim strFoundFolder As String
Dim objFoundFolder As RDOFolder
Dim strFolderName As String
Set objRdoSession = CreateObject("Redemption.RDOSession")
objRdoSession.Logon
objRdoSession.MAPIOBJECT = Outlook.Session.MAPIOBJECT
strFolderName = "\\[mailbox name]\[foldername]\[foldername]" 'actual names removed
Set objRdoFolder = objRdoSession.GetFolderFromPath(strFolderName)
Debug.Print objRdoFolder.Parent.Name 'Prints the folder name
Set objFoundFolder = objRdoFolder.Folders.Find("LIKE 'strCrit%' ") 'Does not work
Debug.Print objFoundFolder.Name
strFoundFOlder = objRdoFolder.Folders.Find("LIKE 'strCrit%' ") 'Does not work
Debug.Print strFoundFOlder
Exit_Proc:
On Error Resume Next
Set objRdoFolder = Nothing
Set objRdoSession = Nothing
Set objFoundFolder = Nothing
Exit Function
Err_Proc:
Select Case Err.Number
Case Else
MsgBox "Error: " & CStr(Err.Number) & vbCrLf & _
"Desc: " & Err.Description & vbCrLf & vbCrLf & _
"Source: " & Err.Source & vbCrLf & _
"Library: " & Application.CurrentProject.Name & vbCrLf & _
"Module: Mod_RDO" & vbCrLf & _
"Function: FindFolderRDO" & vbCrLf, _
vbCritical, "Error"
End Select
Resume Exit_Proc
End Function
Purpose of this function is to find a subfolder (can be up to 4 dimensions deep) having an unique case number of 6 numbers (for example "200332") on the first 6 positions. This function should provide NULL if not found or the full path and the name of the found folder.
I can create the full path with a seperate function (calling the parent folder until top level) but maybe there is a procedure in Redemption such as "fullpath" which I overlooked.
Eventually I want to use this function to delete, move or rename the mailbox folder.
My main question is how to use the "Find(Filter)" method. But any reply on the full path is welcome as well.
Thx! Art.
You are you trying to find a suborder with a name that starts with "strCrit"?
You are almost there:
Set objFoundFolder = objRdoFolder.Folders.Find("Name LIKE 'strCrit%' ")

What would cause VB6 to throw an error that is not handled by the error handler?

I have a few lines of code wrapped in two write log events. I know that the first part of the code is happening because I can see the first entry of the log that says the process has started but the entry that says the process completed is not being written. Somewhere in the code an error is being thrown but the error handler is not catching it for some reason. I've tried checking the event viewer for any logs about my application that Windows may have written, like it causing a crash or Windows closing it for some reason but there's nothing there. Is there a way that VB6 would skip an error handler and if so, how could I find evidence of it?
My code looks something like this:
On Error GoTo ErrorHandler
Dim fso As FileSystemObject
Dim DirPattern As String
Dim FileName As String
Dim DaysOld As Integer
' Init log name to ensure Today's value will be put in there
g_PurgeLogName = ""
Call WriteToLog("Purging of Text Files Started")
sql = "SELECT * FROM [Text File Purge Profiles] " & _
"WHERE User = '" & g_User & "' " & _
"ORDER BY ProfileName;"
Dim rs As ADODB.Recordset
Set rs = ic.RsReadOnly(cnMF, sql)
With rs
Do While Not .EOF
Set fso = New FileSystemObject
' create dirPattern to use with "Dir" command
DirPattern = fso.BuildPath(!PurgePath, !FileMask)
' get first file matching this pattern
FileName = Dir(DirPattern)
Do While Len(FileName)
FileName = fso.BuildPath(!PurgePath, FileName)
' how many days ago was this file created?
DaysOld = DateDiff("d", fso.GetFile(FileName).DateCreated, Now)
If DaysOld > !RetainDays Then
Debug.Print "Deleting " & FileName
fso.DeleteFile FileName, True
DoEvents
End If
' get next file matching this pattern
FileName = Dir
Loop
.MoveNext
Loop
.Close
End With
Call WriteToLog("Purging of Text Files Completed")
Set fso = Nothing
Set rs = Nothing
Exit Sub
'=============
ErrorHandler:
'=============
Call MsgBox2("An error occured while trying to process the Text File purge" & _
vbLf & vbLf & err.Number & ": " & err.Description, vbCritical)
Call WriteToLog("Text File Purge did not complete")
Call WriteToLog(vbTab & err.Number & ": " & err.Description)
Call ClearScreen
Call err.Clear
I can see that there could be issues with deleting files the program doesn't have permission to access or that there could be an issue with the query somehow but as far as I can tell, any problems that could arise should be handled by the error handler and the log would still have more text than the "process started" bit.

Adding event code to checkbox in PowerPoint VBA

I am developing a PowerPoint 2010 deck that presents the user with a series of pages containing one statement, one checkbox (built from a label element to enable changing the size of the checkbox) and forward/back arrows on each page.
Since this will be used on numerous projects with varying numbers of pages I am building the “deck” dynamically using PowerPoint VBA to construct the pages dynamically from an Excel spreadsheet containing the list of individual statements.
I have been able to write the VBA code to open the Excel file, read the statements into an array in PowerPoint and construct the appropriate number of pages with all of the elements on the page. To this point everything works fine. Where I am having difficulty is in assigning the click action to the checkbox.
Here is the code that is called by the page building routine to insert the checkbox (obviously there is more code prior to this for accessing the Excel file, creating the pages and adding the “statement” text boxes...all of which works):
Sub AddSelectBox(Index As Integer, pptBuildingSlide As Slide)
'Add Checkbox
With pptBuildingSlide.Shapes.AddOLEObject(Left:=342, Top:=294, Width:=42, Height:=42, ClassName:="Forms.Label.1")
.Name = "label" & Index
.OLEFormat.Object.Font.Name = "Wingdings 2"
.OLEFormat.Object.Font.Charset = "2"
.OLEFormat.Object.Caption = "£"
.OLEFormat.Object.Font.Size = 40
End With
'Add Checkbox Click Code
'(CODE FOR ADDING CLICK EVENT TO EACH BOX GOES HERE)
End Sub
The checkbox on each page has a discreet name keyed to the page number (e.g. Label1, Label2, etc.). I need to add the following code to each checkbox on each page to toggle the checkmark so later in the program I can see which were checked by reading the “caption” attributes. (The font is set to “Wingdings 2” to give a blank box and a checked box on click)
Private Sub Label1_Click()
If Label1.Caption = "£" Then
Label1.Caption = "R"
Else
Label1.Caption = "£"
End If
End Sub
I have searched the web looking for any references to add event code dynamically and found a number of examples (e.g. Assign on-click VBA function to a dynamically created button on Excel Userform) but almost all are for Excel or Access. I should point out that coding is “not my day job” and I have managed to get this far reading “Mastering VBA for Office 2003” and web searching…so my ability to translate those examples to PowerPoint has come up short. Thanks for any help you can offer.
5/29 Additional information:
I came across the .CreateEventProc method as a way to write code into VBA. The example I found was written for Excel at this site. I've gotten this far with it (the message box code would be replaced with the click code but I was just using this for testing to avoid introducing other errors)...
Sub CreateEventProcedure()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim LineNum As Long
Const DQUOTE = """" ' one " character
Set VBProj = ActivePresentation.VBProject
Set VBComp = VBProj.VBComponents(Slides(1))
Set CodeMod = VBComp.CodeModule
With CodeMod
LineNum = .CreateEventProc("Click", "Label1")
LineNum = LineNum + 1
.InsertLines LineNum, " MsgBox " & DQUOTE & "Hello World" & DQUOTE
End With
End Sub
...but get a "Compile Error: Sub or Function not defined" at (slides(1)). Any help cleaning it up (if it is in fact an appropriate solution) would be appreciated.
Do you have to use a label? (I understand the size thing but you can maybe add a shape which would be easier.)
Something based on:
This can only work if you allow access to the VBE in Security (cannot be done in code)
Sub makeBox()
Dim strCode As String
With ActivePresentation.Slides(1).Shapes.AddShape(msoShapeRectangle, 10, 10, 20, 20)
.Fill.Visible = False
.Line.Visible = False
With .TextFrame.TextRange.Font
.Name = "Wingdings 2"
.Size = 40
.Color.RGB = vbBlack
End With
.TextFrame.TextRange = "£"
With .ActionSettings(ppMouseClick)
.Action = ppActionRunMacro
.Run = "chex"
End With
End With
strCode = "Sub chex(oshp As Shape)" & vbCrLf & "If oshp.TextFrame.TextRange =" & Chr(34) & "£" & Chr(34) & "Then" _
& vbCrLf & "oshp.TextFrame.TextRange = " & Chr(34) & "R" & Chr(34) & vbCrLf _
& "Else" & vbCrLf & "oshp.TextFrame.TextRange =" & Chr(34) & "£" & Chr(34) & vbCrLf & "End If" & vbCrLf & "End Sub"
With ActivePresentation.VBProject.VBComponents.Add(vbext_ct_StdModule)
.CodeModule.AddFromString (strCode)
End With
End Sub

Excel vba open all word document in a folder and print by getting number of copies from user

I am new to Macro
By googling I coded this and I have changed some part for my use.
Problem is Runtime error is coming. And I don't know how to print all word documents in folder both .doc and .docx
My Requirement
Want to print all word document in folder A (both .doc and .docx).
Print active document ( Number of copies want to be get from User ).
Close active document.
Repeat 2 and 3 for all document in folder A
My code will get page number to print from case selected by the user
case 1 will print 1st two pages one by one.
case 2 will print 3rd to reset of the pages.
case 3 will print full document.
In my office duplex is default printer setup to print. But I will be using letter head. I need this macro to solve my issue. I tried simplex macro code to print but its not working.
Sub prnt()
Dim c As Integer
Dim i As Integer
Dim strName As String
'get print type
strName = InputBox(Prompt:="Choose Your Option" & vbNewLine & "" & vbNewLine & "1. Letter Head" & vbNewLine & "2. A4 Sheet" & vbNewLine & "3. Comp Plan" & vbNewLine & "", _
Title:="ENTER YOUR PRINT TYPE", Default:="Your Choice here")
If strName = "Your Choice here" Or strName = vbNullString Then
MsgBox "Sorry...! Choose Correct option"
Exit Sub
Else
'case to choose option
Select Case strName
Case "1"
Dim file
Dim path As String
Dim ans As String
'get number of copies from user
c = InputBox("Please enter number of copies")
ans = MsgBox("Are you sure you want to print " & _
c & "?", _
vbQuestion + vbYesNo, "Print pages")
If ans = vbNo Then
Exit Sub
Else
'path to the folder
path = "E:\print\"
file = Dir(path & "*.docx")
Do While file ""
Documents.Open Filename:=path & file
For i = 1 To 2 'loop 2 pages
ActiveDocument.PrintOut , Copies:=c, Range:=wdPrintRangeOfPages, Pages:=i
Next
ActiveDocument.Close
' set file to next in Dir
file = Dir()
Loop
End If
Case "2"
Case "3"
Case Else
MsgBox "Sorry...! Choose Correct option"
End Select
End If
End Sub
There's bad programming practice to work on strings instead of numbers.
See this:
Sub Test()
Dim noofcopies As Integer
noofcopies = GetNumberOfCopies()
MsgBox noofcopies
End Sub
Function GetNumberOfCopies() As Integer
Dim iRetVal As Integer
On Error GoTo Err_GetNumberOfCopies
iRetVal = CInt(InputBox("Enter no. of copies to print" & vbCr & vbCr & _
"Enter proper integer value between 1 and 3" & vbCr & _
"0 (zero) equals to Cancel", "No. of copies", "1"))
If iRetVal > 3 Then iRetVal = 3
Exit_GetNumberOfCopies:
GetNumberOfCopies = iRetVal
Exit Function
Err_GetNumberOfCopies:
Err.Clear
Resume 0
End Function
Use the same logic to get print option ;)

Excel VBA & VB6 Printer

I have the following code, this code was written in VB6 but i can not open the form or check any references.
Private Sub PopulatePrinterCombo(cmbDestination As ComboBox)
Dim objPrinter As Printer 'a printer in the Printers collection object
'Add the printers to the combo box
For Each objPrinter In printers
cmbPrinter.AddItem objPrinter.DeviceName
Next
'Set current selection to the default printer
cmbDestination.Text = Printer.DeviceName
End Sub
I am currently copying the code onto Excel VBA macro, the problem is the Dim objPrinter As Printer code, i keep getting an error message saying "USER DEFINED TYPE NOT DEFINED", do i need a reference to add on VBA to be able to get the option of declaring a variable as a "Printer" or something?
My second question is that i do not fully understand the "Printers" in the line For Each objPrinter In printers, what is "Printers"? can someone please explain that to me.
Thank you
PART 2
I am now trying to print files, i have the following as my code:
'Initialize values
intDraftsPrinted = 0
If objDraftPaths.Count > 1 Then
Else
intSelectedDraftCount = CountSelectedDrafts
End If
'prompt user to make sure
intMsgBoxResponse = MsgBox("You selected " & intSelectedDraftCount & " part numbers. After removing duplicates" & vbNewLine & "there were " & objDraftPaths.Count & " unique draft files found." & vbNewLine & "Do you want to print these files?", vbYesNo, "TD Printer")
If intMsgBoxResponse <> vbYes Then
intSelectedDraftCount = 0 'So the following for loop will not entered
Else
intSelectedDraftCount = objDraftPaths.Count
End If
For i = 1 To intSelectedDraftCount
booSuccess = False
'open the draft file
Set objDraftDocument = OpenSolidEdgeDraft(objDraftPaths.Item(i))
If objDraftDocument Is Nothing Then
'could not open file
MsgBox "Could not open the following draft file:" & vbNewLine & _
objDraftPaths.Item(i), vbExclamation, "Solid Edge Error"
Else
'Print the draft file
For Each objSheet In objDraftDocument.Sheets
strSheetSize = DetermineSheetSize(objSheet)
If strSheetSize <> "" Then
'Determine orientation
If InStr(1, strSheetSize, "90") <> 0 Then
'Print as landscape
intOrientation = vbPRORLandscape
Else
'Print as portrait
intOrientation = vbPRORPortrait
End If
'Specify Sheet Size
Select Case Left(strSheetSize, 1)
Case "A"
intPaperSize = VBRUN.PrinterObjectConstants.vbPRPSLetter
Case "B"
intPaperSize = VBRUN.PrinterObjectConstants.vbPRPS11x17
Case "C"
intPaperSize = VBRUN.PrinterObjectConstants.vbPRPSCSheet
Case "D"
intPaperSize = VBRUN.PrinterObjectConstants.vbPRPSDSheet
Case "E"
intPaperSize = VBRUN.PrinterObjectConstants.vbPRPSESheet
Case Else
intPaperSize = VBRUN.PrinterObjectConstants.vbPRPSLetter
End Select
'Enable error handling
On Error Resume Next
'Activate the current sheet
objSheet.Activate
If Err Then
'Could not activate sheet
MsgBox "An error occurred while attempting to print: " & vbNewLine & objDraftPaths.Item(i) & vbNewLine & "The error was:" & vbNewLine & "Error Number: " & Err.Number & vbNewLine & "Error Description: " & Err.Description, vbExclamation, "Solid Edge Error"
Err.Clear
Else
'Print to the printer specified by the combo box
objDraftDocument.PrintOut cmbPrinter.Text, 1, intOrientation, intPaperSize, , , , igPrintSelected
If Err Then
'Could not print document
MsgBox "An error occurred while attempting to print: " & vbNewLine & objDraftPaths.Item(i) & vbNewLine & "The error was:" & vbNewLine & "Error Number: " & Err.Number & vbNewLine & "Error Description: " & Err.Description, vbExclamation, "Solid Edge Error"
Err.Clear
Else
booSuccess = True
End If
End If
'Disable error handling
On Error GoTo 0
End If
Next
'Close the file
objDraftDocument.Close False
intDraftsPrinted = intDraftsPrinted + 1
End If
Next i
'Dereference objects
Set objSheet = Nothing
Set objDraftDocument = Nothing
'Set objDraftPaths = Nothing
PrintSelectedDrafts = intDraftsPrinted
Now the problem comes when i hits the line that says: intOrientation = vbPRORLandscape
in excel VBA, it does not recognize "vbPRORLandscape" as well as the next line "vbPRORPortrait". Is there a way to fix that?
Also, i have a feeling that VBRUN.PrinterObjectConstants.vbPRPSLetter and the rest of those lines might not work out as well. It works in VB6 though.
Thank you
It appears the Printers Collection is available in the MS Access VBA environment but I do not believe it is intrinsic to the Excel VBA environment.
I use the WshNetwork object of Windows Script Host to list the available printers. I use the subroutine below to populate a ComboBox with the list of printers that are connected to the system. In order for this code to work you will need to add the "Windows Script Host Object Model" reference to your VBA project. (Menu: Tools > References [Select from list])
I added the (j) loop to alphabetize the list.
Sub populatePrintersList()
Dim nwo As New WshNetwork
Dim i As Integer
Dim j As Integer
Dim bAdd As Boolean
bAdd = True
cmbPrinter.Clear
For i = 0 To (nwo.EnumPrinterConnections.Count / 2) - 1
For j = 0 To cmbPrinter.ListCount - 1
If nwo.EnumPrinterConnections(i * 2 + 1) < cmbPrinter.List(j) Then
cmbPrinter.AddItem nwo.EnumPrinterConnections(i * 2 + 1), j
bAdd = False
Exit For
End If
Next j
If bAdd Then cmbPrinter.AddItem nwo.EnumPrinterConnections(i * 2 + 1): bAdd = True
Next i
cmbPrinter.ListIndex = 0
End Sub
Part 2:
MSDN contains reference material for the Worksheet.PrintOut method: Worksheet.PrintOut
In depth documentation for the methods and properties of the Worksheet.PageSetup object can also be found on MSDN: Worksheet.PageSetup
I suggest using these resources to find a plethora of answers.