Search code examples
vbapowershellwindows-10clipboard

How to clear Windows 10 Clipboard history via VBA and PowerShell command


I want to clear Windows Operating System Clipboard history via VBA and PowerShell command.

I am a Windows 10 user.

This is the PowerShell command line which clears Windows Operating System Clipboard history.

PS C:\Windows\system32> Restart-Service -Name "cbdhsvc*" -force

The following link might be helpful in order to understand what I am talking about.

https://mspoweruser.com/clear-clipboard-windows/#:~:text=Press%20Windows%20%2B%20I%20on%20your,menu%20of%20the%20Settings%20app.


Solution

  • Prerequisite:

    • You must be running on at least Windows 10 or at least Windows Server 2016 with the Desktop Experience option installed, as only then is the clipboard-history feature available.

    There are two ways to clear the clipboard history using PowerShell:

    • Note:

      • Both methods by design do not clear pinned items from the history, i.e. items the current user has explicitly chosen to remember indefinitely.

      • If your intent is to merely clear the clipboard, i.e. remove its current content (as available for regular pasting), use
        Set-Clipboard $null, though note that in PowerShell 7+ this technically doesn't clear the clipboard, but replaces the current content with an empty string. If that is a concern, use
        Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::Clear()
        Note that clearing the clipboard does not remove the previous content from the clipboard history.

    • Option 1: Using the static ::ClearHistory() method of the Windows.ApplicationModel.DataTransfer.Clipboard class.

      # Windows PowerShell only (not PowerShell 7+)
      [Windows.ApplicationModel.DataTransfer.Clipboard, Windows, ContentType = WindowsRuntime]::ClearHistory()
      
      • This has the advantage of not requiring elevation (Run as Administrator).

      • However, because it is a UWP class, it works in Windows PowerShell only, because PowerShell (Core) 7+ at least as of the .NET 8 version underlying the current version, 7.4.x, does not have access to such classes.

      • Also, this works only from the foreground runspace (UI thread) and can therefore not be used from background jobs (including thread-based jobs), for instance.

    • Option 2: Using the Restart-Service cmdlet to stop and restart the clipboard history Windows service:

      #requires -RunAsAdministrator
      Set-Clipboard $null; Restart-Service cbdhsvc_*
      
      • Unfortunately, this method only works from an elevated PowerShell session.

      • However, it also works in PowerShell 7+ and can also be used in (elevated) non-foreground calls, such as from jobs.

      • The Set-Clipboard $null call ensures that the current clipboard data is removed too (something that the .NET API call above does automatically), though note that in PowerShell 7+ - unlike in Windows PowerShell - this doesn't clear the current clipboard, but replaces its content with an empty string, which is recorded as such in the history; however, due to restarting the service and thereby clearing the history after this call, that won't matter.

      • If you're running on an older system (prior to Window 10 or possibly a very early, long-obsolete build of Windows 10), the wildcard expression will not match an existing service name and the operation will be a quiet no-op.

        • To rule out this case, you can run the following:

          if (-not (Get-Service cbdhsvc_*)) { throw "Clipboard history service not found." }
          
      • A wildcard pattern is needed, because the clipboard history service's full name changes on every login due to a (pseudo) random suffix composed of _ followed by 5 hex digits, e.g. cbdhsvc_5e5a8.

        • It does so, because it is a per-user service, as explained in this SuperUser answer. Curiously, however, stopping and restarting such services still requires elevation.

    Invoke the clipboard history-clearing PowerShell command from VBA:

    • Use the .Run() method of the WScript.Shell COM object...

    • ... to invoke powershell.exe, the Windows PowerShell CLI, with the ::ClearHistory() call shown at the top.

      • Note: The UI-thread stipulation therefore applies here too: the call must be made from the UI thread of the application that hosts VBA.

    The code below runs the PowerShell console window that is invariably created hidden, by using vbHide window style in the .Run() call.
    For troubleshooting, switch to vbNormalFocus, and place -NoExit before -Command in the powershell.exe call.

    Dim exitCode as Integer
    
    ' Run the PowerShell command hidden (vbHide), synchronously (True)
    ' and obtain its exit code.
    exitCode = CreateObject("WScript.Shell").Run( _
     "powershell.exe -NoProfile -Command [Windows.ApplicationModel.DataTransfer.Clipboard, Windows, ContentType = WindowsRuntime]::ClearHistory()", _
     vbHide, _
     True _
    )
    
    If exitCode = 0 Then ' Command succeeded.
        ' Report success.
        MsgBox "Successfully cleared the clipboard history.", vbInformation
    Else ' Command indicated failure.
        MsgBox "An unexpected error occurred while trying to clear the clipboard history", vbExclamation
    End If
    

    If, for some reason, the powershell.exe call can not be made from the UI thread of the VBA-hosting application, you'll need to use the Restart-Service method, and doing so then invariably requires elevation.

    • You must then invoke powershell.exe in a nested manner so that an inner powershell.exe call can request elevation via Start-Process -Verb RunAs.

      • Important: Unless the application hosting VBA happens to be running with elevation itself, you'll get a UAC dialog to confirm or authorize the elevation request.

        • As in direct use of PowerShell, the implication is that a non-admin user cannot run the command unless an administrator is present to supply their credentials in order to authorize the request.

        • By security-minded design, you cannot automate responding to UAC dialogs, and the only way to avoid them from a non-elevated process is to disable (parts of) UAC altogether, which is strongly discouraged for security reasons, or - but only if you are member of the Administrators group in principle - to define a scheduled task that performs the desired operation. See this answer for details.

      • The code below runs the PowerShell console windows that are created hidden, by using vbHide instead of vbNormalFocus in the .Run() call, and by using -WindowStyle Hidden in the nested PowerShell Start-Process call.
        For troubleshooting, switch to vbNormalFocus and use -NoExit in both powershell.exe calls.

    Dim exitCode as Integer
    
    ' Run the PowerShell command with elevation, synchronously, 
    ' and obtain the exit code.
    exitCode = CreateObject("WScript.Shell").Run("powershell.exe Start-Process -Wait -Verb RunAs -WindowStyle Hidden powershell.exe 'Restart-Service cbdhsvc_*'", vbHide, True)
    
    If exitCode = 0 Then ' Command succeeded.
        ' Display the output.
        MsgBox "Successfully cleared the clipboard history.", vbInformation
    Else ' Command indicated failure.
        MsgBox "An unexpected error occurred while trying to clear the clipboard history", vbExclamation
    End If