How to insert Header from blocks saved in Quick Parts using macros? - vba

I haven't understood how to use the object structure in the VBA. I barely get by picking up pieces from stack overflow to accomplish my task. Thanks a lot for everyone who contributes here.
I need to set the header in a document from Quick Parts. I found this
Header on all pages, footer in first page only that works if the header and footer are text only.
ActiveDocument.Sections(1).Headers(wdHeaderFooterPrimary).Range = "Header_2"
ActiveDocument.Sections(1).Footers(wdHeaderFooterPrimary).Range.Text = "Main footer"
ActiveDocument.Sections(1).PageSetup.DifferentFirstPageHeaderFooter = True
ActiveDocument.Sections(1).Headers(wdHeaderFooterFirstPage).Range.Text = "First page header"
ActiveDocument.Sections(1).Footers(wdHeaderFooterFirstPage).Range.Text = "First page footer"
Is it possible to add to the text in quotes - sendkeys() to use the functionality of {f3} that populates the header from quick parts?

Create a range variable, set it to the particular header or footer you want, then replace Selection.Range with that variable. To set a range variable:
Dim myRange as Range
Set myRange = ActiveDocument.Sections(1).Footers(wdHeaderFooterFirstPage).Range
The macro recorder tells me:
Application.Templates( _
"C:\Users\jkorc\AppData\Roaming\Microsoft\Templates\Normal.dotm"). _
BuildingBlockEntries("I have a macro").Insert Where:=Selection.Range, _
RichText:=True
Replace Selection.Range with myRange.
If the QuickPart is AutoText in the template attached to the document, you can use:
ActiveDocument.AttachedTemplate.AutoTextEntries("I have a macro").Insert myRange

From my page on using vba to insert building blocks:
You need to, somehow, identify the building block:
Template
Gallery
Category, and
Name
Recording a macro will work, to a limited extent. Recorded macros
will not work on someone else's system even if they have the same
building blocks or even if they are a different user on the same
computer. Recorded macros will not work if the template contains more
than one building block with the same name, even if they are different
types. Note that the file that comes with Word "Built-In Building
Blocks.dotx" has many building blocks with the same names. You can use
the Building Blocks Organizer and sort by name to see if there are
multiple building blocks with the same name and in the same template.
Because of these limitations, often you will need to actually write a
macro.
However, you can try recording one. The key is to use the Building
Blocks Organizer to insert the Building Block when recording the
macro, rather than a shortcut or another gallery/menu.
Any macro, recorded or written, should be stored in the same template
that holds the building block. That way, if the macro is available,
the building block is available.
Writing a Macro
To do this, you need to know:
The name of the building block. If you have a unique name for the
building block (no other building block of the same name exists in any
building block entry location) then use Graham Mayor's macro found on
one of his sample macro pages. A variation is shown below.
The name (and location) of the template that holds the building block unless the macro is in the same template
How to insert a macro. See Installing Macros and Install/Employ VBA Procedures (Macros).
Building Block Name = "MyBB" (example in this macro, change to fit)
Situation 1 and 1a have the Building Block and the macro in the same template. This simplifies coding because a macro can always tell
the name and location of the template that holds it. That information
is required to use a macro to insert a building block.
Situation 1 - template holds both the building block and the macro
Here is the macro to insert that uniquely-named building block at the insertion point in the document:
Sub InsertMyBB()
' Will not work if there are multiple building blocks with the same name in the template! See below.
'
Const sBBName As String = "MyBB" 'use the name of your building block instead of "MyBB"
'
On Error GoTo Oops
Application.Templates.LoadBuildingBlocks ' Thank you Timothy Rylatt
Application.Templates(ThisDocument.fullname).BuildingBlockEntries(sBBName).Insert Where:=Selection.range, _
RichText:=True ' Insert MyBB Building Block
Exit Sub ' We're done here
Oops: ' Didn't work - building block not there!
MsgBox Prompt:="The Building Block " & sBBName & " cannot be found in " & _
ThisDocument.Name & ".", Title:="Didn't Work!"
On Error GoTo -1
End Sub
This and the following macro are both contained in a demonstration
template that can be downloaded from my downloads page.
Situation 1a - template holding building blocks and macro in same template - multiple building blocks with the same name
In this situation, the previous macro would confuse Word and give
unpredictable (to the user) results. In this case, the macro needs to
know both the gallery and category of the building block. The
following macro assumes that the building block is stored in the
AutoText gallery and in the General category. You can find the name of
the gallery and category using the Building Blocks Organizer. Category
names are plain text. Galleries are referenced in vba as Building
Block Types and use constants. You can find a list of the constants
for the different galleries here.
Sub InsertMyBB()
'
' Assumes that the Building Block is of the type AutoText (wdTypeAutoText) in Category "General"
' See https://msdn.microsoft.com/en-us/library/bb243303(v=office.12).aspx
'
' This is based in part upon contributions from Greg Maxey and Jay Freedman - any errors remain mine
' Written by Charles Kenyon April 2021
'
Const sBBName As String = "MyBB" 'use the name of your building block instead of "MyBB"
Const sTempName As String = ThisDocument.fullname ' puts name and full path of template in string
'
Dim oBB As BuildingBlock
'
On Error Resume Next
Application.Templates.LoadBuildingBlocks ' Thank you Timothy Rylatt
Set oBB = Application.Templates(sTempName).BuildingBlockTypes(wdTypeAutoText) _
.Categories("General").BuildingBlocks(sBBName)
If err.Number = 0 Then
oBB.Insert Selection.range, True
Else
MsgBox Prompt:="The Building Block '" & sBBName & "' cannot be found in " & _
ThisDocument.Name & ".", Title:="Didn't Work!"
End If
lbl_Exit:
On Error GoTo -1
Set oBB = Nothing
End Sub
This and the preceding macro are both contained in a demonstration
template that can be downloaded from my downloads page.
Situation 2 - template holding building block is in Word Startup Folder and named MyBBTemplate.dotx
This template, for some reason, does not hold the macro, it is in a
separate template. We know the name of the container template. The
name of the template containing the macro does not matter for our
purposes.
Sub InsertMyBB()
' Will not work if the Startup Folder is the root directory of a drive, i.e. C:\
' For use with building block stored in a template loaded in the Word Startup Folder that does NOT hold this macro
' Will not work if there are multiple building blocks with the same name in the template!
'
Const sBBName As String = "MyBB" 'use the name of your building block instead of "MyBB"
Const sTempName As String = "MyBBTemplate.dotx" 'use the name of your template instead of "MyBBTemplate.dotx"
On Error GoTo NoStartupPath
Const sStartupFolder As String = Application.Options.DefaultFilePath(wdStartupPath)
'
On Error GoTo Oops ' error handler
Application.Templates.LoadBuildingBlocks ' Thank you Timothy Rylatt
Application.Templates(sStartupPath & "\" & sTemplateName).BuildingBlockEntries(sBBName) _
.Insert Where:=Selection.range, RichText:=True ' Insert MyBB Building Block
Exit Sub ' We're done here
NoStartupPath:
On Error GoTo -1
MsgBox Prompt:="No Startup Folder Set in Options"
Exit Sub
Oops: ' Didn't work - building block not there!
MsgBox Prompt:="The Building Block " & sBBName & " cannot be found in " & _
sTemplateName & ".", Title:="Didn't Work!"
On Error GoTo -1
End Sub
Situation 3 - Insert a building block with a unique name at a bookmark, regardless of location
This macro does NOT care where the building block is stored but its
name must be unique to have predictable results.
Sub InsertMyBuildingBlock_Bookmark()
' Charles Kenyon 09 April 2021
' based on Graham Mayor's macro at
' http://www.gmayor.com/word_vba_examples_3.htm
' In addition to checking the active template, add-in templates and the normal template,
' this macro looks in the building blocks.dotx template.
' Building Block name must be unique!
'
Const BookMarkNAME As String = "delete" ' use the name of the building block - make variable if multiple bookmarks
'
Dim oTemplate As Template
Dim oAddin As AddIn
Dim bFound As Boolean
Dim oRng As range
Dim i As Long
'Define the required building block entry
Const strBuildingBlockName As String = "Building Block Name"
' Set the range
Set oRng = ActiveDocument.Bookmarks(BookMarkNAME).range
'Set the found flag default to False
bFound = False
'Ignore the attached template for now if the
'document is based on the normal template
Application.Templates.LoadBuildingBlocks ' Thank you Timothy Rylatt
If ActiveDocument.AttachedTemplate <> NormalTemplate Then
Set oTemplate = ActiveDocument.AttachedTemplate
'Check each building block entry in the attached template
For i = 1 To oTemplate.BuildingBlockEntries.Count
'Look for the building block name
'and if found, insert it.
If oTemplate.BuildingBlockEntries(i).Name = strBuildingBlockName Then
oTemplate.BuildingBlockEntries(strBuildingBlockName).Insert _
Where:=oRng
'Set the found flag to true
bFound = True
'Clean up and stop looking
GoTo lbl_Exit
End If
Next i
End If
'The entry has not been found
If bFound = False Then
For Each oAddin In AddIns
'Check currently loaded add-ins
If oAddin.Installed = False Then Exit For
Set oTemplate = Templates(oAddin.Path & _
Application.PathSeparator & oAddin.Name)
'Check each building block entry in the each add in
For i = 1 To oTemplate.BuildingBlockEntries.Count
If oTemplate.BuildingBlockEntries(i).Name = strBuildingBlockName Then
'Look for the building block name
'and if found, insert it.
oTemplate.BuildingBlockEntries(strBuildingBlockName).Insert _
Where:=oRng
'Set the found flag to true
bFound = True
'Clean up and stop looking
GoTo lbl_Exit
End If
Next i
Next oAddin
End If
'The entry has not been found. Check the normal template
If bFound = False Then
For i = 1 To NormalTemplate.BuildingBlockEntries.Count
If NormalTemplate.BuildingBlockEntries(i).Name = strBuildingBlockName Then
NormalTemplate.BuildingBlockEntries(strBuildingBlockName).Insert _
Where:=oRng
'set the found flag to true
bFound = True
End If
Next i
End If
'If the entry has still not been found
'finally check the Building Blocks.dotx template
If bFound = False Then
Templates.LoadBuildingBlocks
For Each oTemplate In Templates
If oTemplate.Name = "Building Blocks.dotx" Then Exit For
Next oTemplate
For i = 1 To Templates(oTemplate.fullname).BuildingBlockEntries.Count
If Templates(oTemplate.fullname).BuildingBlockEntries(i).Name = strBuildingBlockName Then
Templates(oTemplate.fullname).BuildingBlockEntries(strBuildingBlockName).Insert _
Where:=oRng
'set the found flag to true
bFound = True
'Clean up and stop looking
GoTo lbl_Exit
End If
Next i
End If
'All sources have been checked and the entry is still not found
If bFound = False Then
'so tell the user.
MsgBox "Entry not found", _
vbInformation, _
"Building Block " _
& Chr(145) & strBuildingBlockName & Chr(146)
End If
lbl_Exit:
set oTemplate = Nothing
set oRng = Nothing
End Sub
For more thoughts/ideas on inserting a building block at a bookmark,
see this thread: Updating bookmark with multiple building blocks based
on checkbox

Related

Delete All Word Macros that are locked with a KNOWN PASSWORD

I have VBA code that is protected and I want to distribute the file once all the code runs, however, I want to remove all the macros before I do so that the user is not prompted to "enable" macros when they open the document.
I have this code that works if the project is "unlocked" but of course it will not work if the project is not unlocked.
I want to be able to UNLOCK the project then have the code below run. Again, I know the password.
This is a unique situation in that I have quite a few pieces of code and functions that run to perform a scan of another document. I think copy the results of the scanned document that includes comments and highlighted text into the existing document that contains all the code. I then want to remove all traces of itself so that the saved document contains no VBA or Macros.
Private Sub NothingHere(objDocument As Object)
' deletes all VBProject components from objDocument
' removes the code from built-in components that can't be deleted
' use like this: RemoveAllMacros ActiveWorkbook ' in Excel
' or like this: RemoveAllMacros ActiveWorkbookDocument ' in Word
' requires a reference to the
' Microsoft Visual Basic for Applications Extensibility library
Dim i As Long, l As Long
If objDocument Is Nothing Then Exit Sub
i = 0
On Error Resume Next
i = objDocument.VBProject.VBComponents.Count
On Error GoTo 0
If i < 1 Then ' no VBComponents or protected VBProject
MsgBox "The VBProject in " & objDocument.Name & _
" is protected or has no components!", _
vbInformation, "Remove All Macros"
Exit Sub
End If
With objDocument.VBProject
For i = .VBComponents.Count To 1 Step -1
On Error Resume Next
.VBComponents.Remove .VBComponents(i)
' delete the component
On Error GoTo 0
Next i
End With
With objDocument.VBProject
For i = .VBComponents.Count To 1 Step -1
l = 1
On Error Resume Next
l = .VBComponents(i).CodeModule.CountOfLines
.VBComponents(i).CodeModule.DeleteLines 1, l
' clear lines
On Error GoTo 0
Next i
End With
End Sub

Add page numbers to the bottom of Word document in the format X of Y

I am trying to add the X of Y page numbers to the bottom right of a Word document. X being the current page number and Y being the total number of pages in the document.
I recorded a macro.
Sub InsertPageLabelsXofY()
If ActiveWindow.View.SplitSpecial <> wdPaneNone Then
ActiveWindow.Panes(2).Close
End If
If ActiveWindow.ActivePane.View.Type = wdNormalView Or ActiveWindow. _
ActivePane.View.Type = wdOutlineView Then
ActiveWindow.ActivePane.View.Type = wdPrintView
End If
ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageFooter
Application.Templates( _
"C:\Users\jhandler\AppData\Roaming\Microsoft\Document Building Blocks\1033\16\Built-In Building Blocks.dotx" _
).BuildingBlockEntries("Bold Numbers 3").Insert Where:=Selection.Range, _
RichText:=True
ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument
End Sub
This works initially, but after a day or so it gives me an error.
Run-time error '5941': The requested member of the collection does not exist
The line that generates the error is:
Application.Templates( _
"C:\Users\jhandler\AppData\Roaming\Microsoft\Document Building Blocks\1033\16\Built-In Building Blocks.dotx" _
).BuildingBlockEntries("Bold Numbers 3").Insert Where:=Selection.Range, _
RichText:=True
Also, I would like for other people to use the macro. Is there some way to save the template in a public area?
The built-in building blocks template is only loaded on demand, i.e. when a building-block it contains is inserted, as per the documentation. You can force it to load by adding the following line to your routine before you attempt to insert:
Application.Templates.LoadBuildingBlocks
However, if you take the advice offered by #CindyMeister (and you should) you will not need to do this as the template containing your code will already by loaded. You could then rewrite your routine as follows, avoiding the need to open and close the footer:
Sub InsertPageLabelsXofY()
Dim sectionNumber As Long
sectionNumber = Selection.Information(wdActiveEndSectionNumber)
Dim footer As Range
Set footer = ActiveDocument.Sections(sectionNumber).Footers(wdHeaderFooterPrimary).Range
Dim tmp As Template
Set tmp = ActiveDocument.AttachedTemplate
tmp.BuildingBlockEntries("Bold Numbers 3").Insert Where:=footer, RichText:=True
End Sub
To make your routine work, change the "Application.Templates ..." line to:
Templates(1).BuildingBlockEntries("Bold Numbers 3").Insert Selection.Range, True
Word's building blocks template is always available as a global template, and the Built-in version is always the first template, so it has an index of 1.
Trying to specify the path to this template is what is causing the error. Yes, it might work sometimes but at other times it doesn't, so it is best to just use the index level and it has the added benefit of allowing your code to be transportable to other systems. If you tried to execute your existing code on a system that does not have your Home directory, "handler" then it will fail.
Sub AddPageXofYtext()
Dim pageNumber, TotalPage As Long
TotalPage = Selection.Information(wdNumberOfPagesInDocument)
ActiveDocument.Styles("Header").ParagraphFormat.Alignment = wdAlignParagraphRight
Selection.Sections.Item(1).Headers(wdHeaderFooterPrimary).Range.Text = "Page " & Selection.Information(wdActiveEndAdjustedPageNumber) & " of " & TotalPage
pageNumber = 1
Do
Selection.GoTo What:=wdGoToPage, Which:=wdGoToNext, Count:=pageN
Selection.InsertBreak Type:=wdSectionBreakContinuous
Selection.Sections.Item(1).Headers(wdHeaderFooterPrimary).LinkToPrevious = False
Selection.Sections.Item(1).Headers(wdHeaderFooterPrimary).Range.Text = "Page " & Selection.Information(wdActiveEndAdjustedPageNumber) & " of " & TotalPage
pageNumber = pageNumber + 1
If pageNumber = TotalPage Then Exit Do
Loop
End Sub

Excel 2016 VBA - Compare 2 PivotTables fields for matching values

Hi please can someone help, Excel 2016 VBA PivotTable objects. I rarely develop in Excel VBA.
Overall goal:
Compare a single column [P_ID] value list from PivotTable2 against PivotTable1 if they exist or not to enable filtering on those valid values in PivotTable1.
I have some Excel 2016 VBA code which I have adapted from a previous answer from a different internet source.
Logic is: gather data from PivotTable2 from the ComparisonTable dataset (in PowerPivot model), field [P_ID] list of values. Generate a test line as input into function to test for existence of field and value in PivotTable1 against the Mastertable dataset, if true add the line as valid if not skip the line.
Finally filter PivotTable1 with the VALID P_ID values.
It works to a point until it gets to the bFieldItemExists function which generates an error:
Run-time error '1004'
Unable to get the PivotItems property of the PivotField class
Can someone please correct the way of this not working?
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
Dim MyArray As Variant, _
ar As Variant, _
x As String, _
y As String, _
str As Variant
MyArray = ActiveSheet.PivotTables("PivotTable2").PivotFields("[ComparisonTable].[P_ID].[P_ID]").DataRange
For Each ar In MyArray
x = "[MasterTable].[P_ID].&[" & ar & "]"
If ar <> "" And bFieldItemExists(x) = True Then
If str = "" Then
str = "[MasterTable].[P_ID].&[" & ar & "]"
Else
str = str & "," & "[MasterTable].[P_ID].&[" & ar & "]"
End If
End If
Next ar
Dim str2() As String
str2 = Split(str, ",")
Application.EnableEvents = False
Application.ScreenUpdating = False
ActiveSheet.PivotTables("PivotTable1").PivotFields("[MasterTable].[P_ID].[P_ID]").VisibleItemsList = Array(str2)
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Function bFieldItemExists(strName As String) As Boolean
Dim strTemp As Variant
' This line does not work!?
strTemp = ActiveSheet.PivotTables("PivotTable1").PivotFields("[MasterTable].[P_ID].[P_ID]").PivotItems(strName)
If Err = 0 Then bFieldItemExists = True Else bFieldItemExists = False
End Function
The 1004 error occurred due to the use of square brackets [ ]. Remove those.
You also need to use the key word Set when you set an object equal to something. For example Set MyArray = ActiveSheet.PivotTables("PivotTable2").PivotFields("ComparisonTable.P_ID.[P_ID").DataRange.
If you don't use Set you will get a VBA run-time error dialog that says Run-time error '91': Object variable or With block variable not set
I cannot guarantee that my edits will completely solve your problem since I don't have your data set and cannot fully test your code. You will need to use the Debug mode in the VBA editor and single step through the code. To this set a breakpoint on the Set mDataRange = Active.... To set a breakpoint go to the Debug menu and choose the "Toggle Breakpoint" sub-menu item or you can press F9 to set the breakpoint.
Now when you make a change to the Pivot table, the Worksheet_PivotTableUpdate event will fire and the code will top execution at that point.
After the code stops executing due to the breakpoint, you can press the F8 key to single step through your code. If you want to resume execution to the next breakpoint you can press F5. Also when you get the VBA error dialog box, you can hit Debug and then use the F8 key to single step or use the debug windows to see what your variables and objects contain. I'm sure there are some good youtube videos on VBA debugging.
As you single step through the code, you can observe what each variable/object contains using the Immediate window, the Watches window and the Locals window. To open these windows, go to the menu item View and click on each of these sub-menu items.
Here's how you need to edit your code before debugging.
Option Explicit
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
'Better practice is to not use the underscore character to
'continue a Dim declaration line
Dim mDataRange As Range
Dim ar As Range
Dim x As String
Dim y As String
Dim str As Variant
'Use Set to assign the object mDataRange a reference to the the right
'hand side of the equation. Remove the square brackets
'MyArray = ActiveSheet.PivotTables("PivotTable2").PivotFields("[ComparisonTable].[P_ID].[P_ID]").DataRange
Set mDataRange = ActiveSheet.PivotTables("PivotTable2").PivotFields("ComparisonTable.P_ID.P_ID").DataRange
For Each ar In mDataRange
'You need to specify what proprerty from ar you
'want to assign to x. Assuming the value stored in
'ar.Value2 is a string, this should work.
'We use value2 because it is the unformmated value
'and is slightly quicker to access than the Text or Value
'properties
'x = "[MasterTable].[P_ID].&[" & ar & "]"
x = "MasterTable.P_ID." & ar.Value2
'Once again specify the Value2 property as containing
'what value you want to test
If ar.Value2 <> "" And bFieldItemExists(x) = True Then
If str = "" Then
'Remove the square brackets and use the specific property
'str = "[MasterTable].[P_ID].&[" & ar & "]"
str = "MasterTable.P_ID." & ar.Value2
Else
'Remove the square brackets and use the specific property
'str = str & "," & "[MasterTable].[P_ID].&[" & ar & "]"
str = str & "," & "MasterTable.P_ID." & ar.Value2
End If
End If
Next ar
Dim str2() As String
str2 = Split(str, ",")
Application.EnableEvents = False
Application.ScreenUpdating = False
'Remove square brackets
'ActiveSheet.PivotTables("PivotTable1").PivotFields("[MasterTable].[P_ID].[P_ID]").VisibleItemsList = Array(str2)
ActiveSheet.PivotTables("PivotTable1").PivotFields("MasterTable.P_ID.P_ID").VisibleItemsList = Array(str2)
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Function bFieldItemExists(strName As String) As Boolean
'Declare a PivotItem to accept the return value
Dim pvItem As PivotItem
'Since you want to trap for an error, you'll need to let the VBA runtime know
'The following code is a pseudo Try/Catch. This tells the VBA runtime to skip
'the fact an error occured and continue on to the next statement.
'Your next statement should deal with the error condition
On Error Resume Next
'Use Set whenever assigning an object it's "value" or reference in reality
Set pvItem = ActiveSheet.PivotTables("PivotTable1").PivotFields("MasterTable.P_ID.P_ID").PivotItems(strName)
'Assuming that an error gets thrown when strName is not found in the pivot
'Err is the error object. You should access the property you wish to test
If Err.Number = 0 Then
bFieldItemExists = True
Else
bFieldItemExists = False
End If
'Return to normal error functioning
On Error GoTo 0
End Function
Finally, I realize that some of this should be in the comments section, but there was too much I needed to explain to help Learner74. BUT most importantly, I hope I helped him. I have used so many suggestions, recommendations and explanations from the VBA Stack Overflow exchange through the years, I just want to pay it back by paying it forward.
Additional USEFUL Links:
Chip Pearson is the go to site and person for all things VBA
Paul Kelly's Excel Macro Mastery is another go to site for Excel and VBA questions.
Microsoft Excel Object Model which is sometimes useful, but needs improvement. Too many of the objects lack examples, but can at least point you in the right direction.

How can I use VBA to lock/unlock all fields in a Microsoft Word 2010 document?

The problem I have got is that my corporate template set uses a SaveDate field in the footer of every word document - which is used to detail when the document was saved, which ties in with our custom document management system.
Subsequently, when users want to make a PDF of an old document, using the Save As PDF function of Office 2010, the Save Date is updated - creating a PDF of the old document, but with today's date. This is wrong. We are just trying to create a true PDF version of whatever the original document has in it.
To get around this, I am writing a macro solution which locks the fields, exports the document as a PDF and then unlocks the fields again.
I have come up against an issue where I can identify and lock all fields in the headers/footers (which is actually what I'm trying to do) but to make it more robust, need to find out a way to lock ALL FIELDS in ALL SECTIONS.
Showing you my code below, how can I identify all fields in all sections? Will this have to be done using the Index facility?
Sub CPE_CustomPDFExport()
'20-02-2013
'The function of this script is to export a PDF of the active document WITHOUT updating the fields.
'This is to create a PDF of the document as it appears - to get around Microsoft Word 2010's native behaviour.
'Route errors to the correct label
'On Error GoTo errHandler
'This sub does the following:
' -1- Locks all fields in the specified ranges of the document.
' -2- Exports the document as a PDF with various arguments.
' -3- Unlocks all fields in the specified ranges again.
' -4- Opens up the PDF file to show the user that the PDF has been generated.
'Lock document fields
Call CPE_LockFields
'Export as PDF and open afterwards
Call CPE_ExportAsPDF
'Unlock document fields
Call CPE_UnlockFields
'errHandler:
' MsgBox "Error" & Str(Err) & ": " &
End Sub
Sub CPE_LockFields()
'Update MS Word status bar
Application.StatusBar = "Saving document as PDF. Please wait..."
'Update MS Word status bar
Application.StatusBar = "Locking fields in all section of the active document..."
'Declare a variable we can use to iterate through sections of the active document
Dim docSec As section
'Loop through all document sections and lock fields in the specified ranges
For Each docSec In ActiveDocument.Sections
docSec.Footers(wdHeaderFooterFirstPage).Range.fields.Locked = True
docSec.Footers(wdHeaderFooterPrimary).Range.fields.Locked = True
docSec.Footers(wdHeaderFooterEvenPages).Range.fields.Locked = True
Next
End Sub
Sub CPE_UnlockFields()
'Update MS Word status bar
Application.StatusBar = "PDF saved to DocMan Temp. Now unlocking fields in active document. Please wait..."
'Declare a variable we can use to iterate through sections of the active document
Dim docSec As section
'Loop through all document sections and unlock fields in the specified ranges
For Each docSec In ActiveDocument.Sections
docSec.Footers(wdHeaderFooterFirstPage).Range.fields.Locked = False
docSec.Footers(wdHeaderFooterPrimary).Range.fields.Locked = False
docSec.Footers(wdHeaderFooterEvenPages).Range.fields.Locked = False
Next
End Sub
Sub CPE_ExportAsPDF()
'Update MS Word status bar
Application.StatusBar = "Saving document as PDF. Please wait..."
'Chop up the filename so that we can remove the file extension (identified by everything right of the first dot)
Dim adFilename As String
adFilename = Left(ActiveDocument.FullName, (InStrRev(ActiveDocument.FullName, ".", -1, vbTextCompare) - 1)) & ".pdf"
'Export to PDF with various arguments (here we specify file name, opening after export and exporting with bookmarks)
With ActiveDocument
.ExportAsFixedFormat outPutFileName:=adFilename, _
ExportFormat:=wdExportFormatPDF, OpenAfterExport:=True, _
OptimizeFor:=wdExportOptimizeForPrint, Range:=wdExportAllDocument, _
Item:=wdExportDocumentContent, IncludeDocProps:=True, KeepIRM:=True, _
CreateBookmarks:=wdExportCreateWordBookmarks, DocStructureTags:=True, _
BitmapMissingFonts:=True, UseISO19005_1:=False
End With
'Update MS Word status bar
Application.StatusBar = "PDF saved to DocMan Temp."
End Sub
Try something like the following to get to all fields in the document, header, footer, background and main text:
Sub LockAllFieldsInDocument(poDoc As Document, Optional pbLock As Boolean = True)
Dim oRange As Range
If Not poDoc Is Nothing Then
For Each oRange In poDoc.StoryRanges
oRange.Fields.Locked = pbLock
Next
End If
Set oRange = Nothing
End Sub
Here is another way to do it. It'll select the entire document and then lock all fields, before deselecting everything.
Sub SelectUnlink()
ActiveDocument.Range(0, 0).Select
Selection.WholeStory
Selection.Range.Fields.Unlink
Selection.End = Selection.Start
End Sub

Error Handling on external Macro

I am just wondering if it would be possible to do error handling on an external macro. Basically what I want to achieve is I have have thousands of excel workbooks that come in daily and I want to open each of them and run the macro from them (easily done just use the Application.run feature )
Application.Run ("'" & ActiveWorkbook & "'!Export")
What I want to achieve is I want to run error resolving function if that external macro incurs an error.
This is what I have so far
Dim str_SearchFile, str_FileName, str_SearchPath As String
Dim wb_WorkBook As Workbook
Application.ScreenUpdating = False
Application.DisplayAlerts = False
str_ThisBook = ActiveWorkbook.Name 'Set the current workbook for later reference
str_SearchPath = Sheets("Control Panel").Range("E2")
str_SearchFile = Sheets("Control Panel").Range("E2") & "\*.xls*" 'Sets the file type to search for
str_NextFile = Dir(str_SearchFile, vbDirectory) 'Sets the amount of files in the directory matching the criterea (.xls)
Do While Len(str_NextFile) > 0
On Error Resume Next
Set wb_WorkBook = Workbooks.Open(Filename:=str_SearchPath & "\" & str_NextFile, Password:="")
If Err.Number = 0 Then
On Error GoTo 0
Application.Run ("'" & str_NextFile & "'!Export")
str_FileName = str_SearchPath & "\Done" & "\" & str_NextFile
wb_WorkBook.Save
wb_WorkBook.Close
FileCopy (str_SearchPath & "\" & str_NextFile), str_FileName
Kill (str_SearchPath & "\" & str_NextFile)
End If
str_NextFile = Dir
Loop
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
Any Advise is very welcome!
Thank you in advance
You won't be able to get this to work the way you are trying.
The MSDN on On Error Statement indicates that it (emphasis added):
Enables an error-handling routine and specifies the location of the
routine within a procedure; can also be used to disable an
error-handling routine.
The VBE Glossary defines a procedure as:
A named sequence of statements executed as a unit. For example,
Function, Property, and Sub are types of procedures. A procedure name
is always defined at module level. All executable code must be
contained in a procedure. Procedures can't be nested within other
procedures.
This means that calling error handling before calling the macro in the other book, will be ignored in the called macro (confirmed through testing).
The only way that you would be able to enable error handling would be to actually modify the code in the workbook prior to calling the macro... which is very complicated. For your reference, here is a webpage giving an example of editing project code from VBA.
The easiest way I would deal with this is to change your external workbook's "Export" sub into a function that returns a value - Say an integer.
What you can then do is put error trapping into that function and, based upon the outcome of the procedure it can return, say:
0 = All Went Well
1 = Failed to do XXX
2 = Failed to do YYY
You could then change your code to something like this:
Select Case Application.Run ("'" & str_NextFile & "'!Export")
Case 0
MsgBox "All Went Well"
Case 1
MsgBox "Failed to do XXX"
Case 2
MsgBox "Failed to do YYY"
End Select
This will allow you to put the error trapping where it belongs and know how the procedure ran.
hope this helps