'This module contains UIAutomation code.
Option Explicit
'Create a secure PDF by automating the Adobe Acrobat Pro XI menu sequence: View -> Tools -> Protection -> Restrict Editing and setting a Permissions password
'for the currently open PDF and saving the file.
'
'Arguments
'
'AcrobatHwnd Handle to the Adobe Acrobat Pro application window with the PDF open in it
'PermissionsPassword Password to use for the Restrict Editing -> Permissions Password
'SaveAsFile Optional. If specified it can be the folder path and/or file name to save the secured PDF as.
' It has the following meanings:
' "" or argument omitted - the secured PDF will be saved in its existing folder with its existing file name
' only a folder path is specified - the secured PDF will be saved in the specified folder with its existing file name
' only a file name is specified - the secured PDF will be saved in its existing folder with the specified file name
' folder path and file name are specified - the secured PDF will be saved in the specified folder with the specified file name
'ReplaceExistingFile True - replace the specified SaveAsFile, if it exists; False - don't replace the specified SaveAsFile
#If VBA7 Then
Public Function UIAutomation_Restrict_Editing(AcrobatHwnd As LongPtr, PermissionsPassword As String, Optional SaveAsFile As String = "", Optional ReplaceExistingFile As Boolean = True) As String
#Else
Public Function UIAutomation_Restrict_Editing(AcrobatHwnd As Long, PermissionsPassword As String, Optional SaveAsFile As String = "", Optional ReplaceExistingFile As Boolean = True) As String
#End If
Dim UIAuto As IUIAutomation
Dim AcrobatMain As IUIAutomationElement
Dim ControlTypeCond As IUIAutomationCondition, NameCond As IUIAutomationCondition, ControlTypeAndNameCond As IUIAutomationCondition
Dim ProtectionTree As IUIAutomationElement
Dim RestrictEditingButton As IUIAutomationElement
Dim PasswordWindow As IUIAutomationElement
Dim PasswordEdit As IUIAutomationElement
Dim PasswordInputPattern As IUIAutomationValuePattern
Dim OpenPattern As IUIAutomationLegacyIAccessiblePattern
Dim OKButton As IUIAutomationElement
Dim InvokePattern As IUIAutomationInvokePattern
Dim SaveButton As IUIAutomationElement
Dim TreeWalker As IUIAutomationTreeWalker
Dim PDFwindowTitle As String
'Create UIAutomation object
Set UIAuto = New CUIAutomation
'Get the main Acrobat window of the displayed PDF. This is a Window control with the class name AcrobatSDIWindow
Set AcrobatMain = UIAuto.ElementFromHandle(ByVal AcrobatHwnd)
AcrobatMain.SetFocus
DoEvents
Sleep 200
'Press Alt+V, T, R (View menu -> Tools -> Protection) to display the Protection options within the Tools menu.
'Restrict Editing is the first item within the Protection options
PressKeys_Alt vbKeyV
DoEvents
Sleep 200
PressKeys_Key vbKeyT
DoEvents
Sleep 200
PressKeys_Key vbKeyR
DoEvents
Sleep 200
'Find the Protection item in the Tools panel
'Name: "Protection"
'ControlType: UIA_TreeItemControlTypeId (0xC368)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TreeItemControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Protection")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Do
Set ProtectionTree = AcrobatMain.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
DoEvents
Sleep 200
Loop While ProtectionTree Is Nothing
'Find the Restrict Editing button within the Protection items
'Name: "Restrict Editing"
'ControlType: UIA_ButtonControlTypeId (0xC350)
'LegacyIAccessible.ChildId: 0
'LegacyIAccessible.DefaultAction: "Press"
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Restrict Editing")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set RestrictEditingButton = ProtectionTree.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
If Not RestrictEditingButton Is Nothing Then
'Click Restrict Editing button until the Password dialogue window appears
Do
RestrictEditingButton.SetFocus
PressKeys_Key vbKeyReturn
DoEvents
Sleep 100
'Get Password dialogue window
'Name: "Password"
'ControlType: UIA_WindowControlTypeId (0xC370)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Password")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set PasswordWindow = AcrobatMain.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
DoEvents
Sleep 100
Loop While PasswordWindow Is Nothing
'Get Permissions Password edit control
'Name: "Permissions Password:"
'ControlType: UIA_EditControlTypeId (0xC354)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Permissions Password:")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set PasswordEdit = PasswordWindow.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
'Enter the password in Password input box
Set PasswordInputPattern = PasswordEdit.GetCurrentPattern(UIA_ValuePatternId)
PasswordEdit.SetFocus
PasswordInputPattern.SetValue PermissionsPassword
'Get Confirm Password edit control
'Name: "Confirm Password:"
'ControlType: UIA_EditControlTypeId (0xC354)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Confirm Password:")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set PasswordEdit = PasswordWindow.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
'Enter the password in Confirm Password input box
Set PasswordInputPattern = PasswordEdit.GetCurrentPattern(UIA_ValuePatternId)
PasswordEdit.SetFocus
PasswordInputPattern.SetValue PermissionsPassword
'Find and click OK button to set the password
'Name: "OK"
'ControlType: UIA_ButtonControlTypeId (0xC350)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "OK")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set OKButton = PasswordWindow.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
OKButton.SetFocus
Set InvokePattern = OKButton.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
DoEvents
Sleep 500
'After clicking OK, the following window is displayed:
'Adobe Acrobat
'
' Security settings will not be applied to the document until you save the document. You will be able to
' continue to change security settings until you close the document.
' [ OK ]
'Find the dialogue window
'Name: "Adobe Acrobat"
'ControlType: UIA_WindowControlTypeId (0xC370)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Adobe Acrobat")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set PasswordWindow = AcrobatMain.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
'Find and click the OK button
'Name: "OK"
'ControlType: UIA_ButtonControlTypeId (0xC350)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "OK")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set OKButton = PasswordWindow.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
OKButton.SetFocus
Set InvokePattern = OKButton.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
DoEvents
Sleep 500
'Press Ctrl+S to open Save As dialogue
PressKeys_Ctrl vbKeyS
UIAutomation_Restrict_Editing = Save_As_File(UIAuto, AcrobatMain, SaveAsFile, ReplaceExistingFile)
'If the secure PDF file was saved then wait until the PDF window title contains ".pdf (SECURED)"
If UIAutomation_Restrict_Editing Then
Do
PDFwindowTitle = Window_Title(AcrobatHwnd)
DoEvents
Sleep 200
Loop Until InStr(1, PDFwindowTitle, ".pdf (SECURED)", vbTextCompare) > 0
End If
Else
MsgBox "Restrict Editing button not found in Tools -> Protection. Is this PDF already secured?", vbExclamation
UIAutomation_Restrict_Editing = False
End If
End Function
Private Function Save_As_File(UIAuto As IUIAutomation, AcrobatMain As IUIAutomationElement, SaveAsFile As String, ReplaceExistingFile As Boolean) As Boolean
Dim ControlTypeCond As IUIAutomationCondition
Dim NameCond As IUIAutomationCondition
Dim ControlTypeAndNameCond As IUIAutomationCondition
Dim SaveAsWindow As IUIAutomationElement
Dim SaveButton As IUIAutomationElement
Dim FileNameInput As IUIAutomationElement
Dim FileNameInputPattern As IUIAutomationValuePattern
Dim InvokePattern As IUIAutomationInvokePattern
Dim Desktop As IUIAutomationElement
Dim ProgressBar As IUIAutomationElement
Dim AddressToolBar As IUIAutomationElement
Dim SaveAsButton As IUIAutomationElement
Dim CancelButton As IUIAutomationElement
Dim FileExistsWindow As IUIAutomationElement
Dim ExistingFileText As IUIAutomationElement
Dim YesOrNoButton As IUIAutomationElement
Dim folderPath As String, fileName As String, fullFileName As String
Dim existingFile As String
Dim p As Long
'Set this function's return value to indicate that the file was saved. This is the default value. The return value is False only when an
'existing file is not replaced
Save_As_File = True
'Find the Save As window, waiting until it exists. This is a child of the Acrobat SDI window
'Name: "Save As"
'ControlType: UIA_WindowControlTypeId (0xC370)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Do
Set SaveAsWindow = AcrobatMain.FindFirst(TreeScope_Children, ControlTypeAndNameCond)
DoEvents
Sleep 100
Loop While SaveAsWindow Is Nothing
'Find the File name input edit box inside the Save As window
'Name: "File name:"
'ControlType: UIA_EditControlTypeId (0xC354)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_EditControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "File name:")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set FileNameInput = SaveAsWindow.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
'Extract the default file name from the edit box
fileName = FileNameInput.GetCurrentPropertyValue(UIA_ValueValuePropertyId)
'If the caller has specified the 'Save As' PDF file name, then put it in the File name input box in the Save As window,
'overwriting the existing file name. Otherwise the File name input box is untouched and the existing file name is used and
'the PDF will be saved in its existing folder
p = InStrRev(SaveAsFile, "\")
If SaveAsFile = "" Or p = 0 Then
'Folder path not specified so read the folder path from the Save As address bar
'Find Progress Bar within the Save As dialogue window. A child of this contains the PDF's folder path address
'ControlType: UIA_ProgressBarControlTypeId (0xC35C)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ProgressBarControlTypeId)
Set ProgressBar = SaveAsWindow.FindFirst(TreeScope_Descendants, ControlTypeCond)
'Name: "Address: D:\Temp\Excel\PDF\2021"
'ControlType: UIA_ToolBarControlTypeId (0xC365)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ToolBarControlTypeId)
Set AddressToolBar = ProgressBar.FindFirst(TreeScope_Descendants, ControlTypeCond)
folderPath = Split(AddressToolBar.GetCurrentPropertyValue(UIA_NamePropertyId), "Address: ")(1)
If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
End If
If SaveAsFile <> "" Then
If p > 0 Then
'Extract folder path from specified file
folderPath = Left(SaveAsFile, p)
End If
If p < Len(SaveAsFile) Then
'File name specified in - use it
fileName = Mid(SaveAsFile, p + 1)
Else
'File name not specified - use default file name
SaveAsFile = SaveAsFile & fileName
End If
'Put the specified file name in the input box
Set FileNameInputPattern = FileNameInput.GetCurrentPattern(UIA_ValuePatternId)
FileNameInput.SetFocus
FileNameInputPattern.SetValue SaveAsFile
'Alternative code to put the full file name in the input box using IUIAutomationLegacyIAccessiblePattern
'Same effect as using UIA_ValuePatternId above.
'
'Set FileNameInputPatternLegacy = FileNameInput.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
'FileNameInputPatternLegacy.Select 1 '1=SELFLAG_TAKEFOCUS
'FileNameInputPatternLegacy.SetValue fullFileName
'If the Save button is clicked now, the Save As dialogue thinks the file is being saved in the default folder with the
'original file name, not the file specified in SaveAsFile. To overcome this, and use SaveAsFile, put a single space at
'the start of the file input box containing the full file name
PressKeys_Key vbKeySpace
DoEvents
Sleep 100
End If
If LCase(Right(fileName, 4)) <> ".pdf" Then fileName = fileName & ".pdf"
fullFileName = folderPath & fileName
Debug.Print "SaveAsFile = " & SaveAsFile
Debug.Print "File name = " & fileName
Debug.Print "Folder path = " & folderPath
Debug.Print "Full file name = " & fullFileName
'Find the Save button inside the Save As window
'Name: "Save"
'ControlType: UIA_ButtonControlTypeId (0xC350)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Save")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set SaveButton = SaveAsWindow.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
'Click the Save button to save the PDF
SaveButton.SetFocus
Set InvokePattern = SaveButton.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
DoEvents
Sleep 500
If Dir(fullFileName) <> vbNullString Then
Set Desktop = UIAuto.GetRootElement
'The file already exists so wait until the Save As warning window appears, with Yes and No buttons
'Name: "Save As"
'ControlType: UIA_WindowControlTypeId (0xC370)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Save As")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Do
Set FileExistsWindow = Desktop.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
DoEvents
Sleep 100
Loop While FileExistsWindow Is Nothing
'Find the text control within the Save As dialogue. Its name contains the full name of the existing file
'Name: "D:\Temp\Excel\PDF\A4 A5 Portrait Landscape pages.pdf
' The file already exists.
' Replace existing file?"
'ControlType: UIA_TextControlTypeId (0xC364)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_TextControlTypeId)
Set ExistingFileText = FileExistsWindow.FindFirst(TreeScope_Descendants, ControlTypeCond)
existingFile = ExistingFileText.GetCurrentPropertyValue(UIA_NamePropertyId)
'Find the Yes or No button, a child of the Save As dialogue, depending on the replaceExistingFile flag
'Name: "Yes"
'ControlType: UIA_ButtonControlTypeId (0xC350)
'AccessKey: "Alt+y"
'Name: "No"
'ControlType: UIA_ButtonControlTypeId (0xC350)
'AccessKey: "Alt+n"
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, IIf(ReplaceExistingFile, "Yes", "No"))
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set YesOrNoButton = FileExistsWindow.FindFirst(TreeScope_Children, ControlTypeAndNameCond)
'Set focus on the Yes or No button and click it
YesOrNoButton.SetFocus
DoEvents
Sleep 100
Set InvokePattern = YesOrNoButton.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
DoEvents
Sleep 200
If Not ReplaceExistingFile Then
'Find and click the Cancel button in the Save As dialogue
'Name: "Cancel"
'ControlType: UIA_ButtonControlTypeId (0xC350)
Set ControlTypeCond = UIAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_ButtonControlTypeId)
Set NameCond = UIAuto.CreatePropertyCondition(UIA_NamePropertyId, "Cancel")
Set ControlTypeAndNameCond = UIAuto.CreateAndCondition(ControlTypeCond, NameCond)
Set CancelButton = SaveAsWindow.FindFirst(TreeScope_Descendants, ControlTypeAndNameCond)
CancelButton.SetFocus
Set InvokePattern = CancelButton.GetCurrentPattern(UIA_InvokePatternId)
InvokePattern.Invoke
DoEvents
Sleep 200
'Indicate that the file was not saved
Save_As_File = False
End If
End If
End Function