Windows Clipboard History

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
ShotKey
Posts: 18
Joined: 01 Mar 2023, 07:32

Windows Clipboard History

Post by ShotKey » 01 Mar 2023, 08:26

Hello everybody,

I simply want to access the last ~10 items of the Windows Clipboard Manager (Win+V), each via a dedicated hotkey (including keys 0-9). Something like...

Code: Select all

^+5::
  Send, {LWin down}v{LWin up}
  Sleep, 500
  Send {Down 5}{Enter}
return
However,

Code: Select all

Sleep, 500
is of course not ideal, because it's statically coded. So I would rather do a loop waiting for some GUI element (probably best the black frame that appears around the selected clipboard item with a slight but visible lag). However, turns out that the Window appearing upon pressing Win+V is hard to spy, or rather the spied info is hard to reproduce except with https://www.autohotkey.com/docs/v1/lib/MouseGetPos.htm#ExWatchCursor, which would not be practical in this context of course.

So can you think of a way to determine when that black frame appears upon pressing Win+V? Or a completely different approach?

In any case, the script should be able to:

1. keep track of the last say 10 clipboard items
2. each item should be accessible by its own, dedicated hotkey (incl. numbers, e.g. 1 would access the most recent item, 9 the 9th down the history)
3. accept any data format that Windows Clipboard History also accepts
4. ideally act on OnClipboardChange() (because Win+V itself does omit some copied items like e.g. copied from a PW manager; but only if possible because e.g. your solution doesn't work with the Win-V-Window itself)
5. not derange the order of the last 10 items in the process
6. probably do without Send() because it's slow, unreliable and only for text
7. preferraby not create any helper files, but in any case no more than 10

So nothing too exceptional, at least on the surface. But can it be done with AHK (v1)?
[Mod edit: Topic moved to v1 help.]
Can you do it?

WinSpy.png
Win+V spied
WinSpy.png (47.96 KiB) Viewed 1837 times

teadrinker
Posts: 4330
Joined: 29 Mar 2015, 09:41
Contact:

Re: Windows Clipboard History

Post by teadrinker » 01 Mar 2023, 09:49

What is at the moment:

Code: Select all

ClipHist := new ClipboardHistory
if !ClipHist.IsHistoryEnabled {
   MsgBox Clipboard history disabled
   ExitApp
}
MsgBox Clipboard history enabled
MsgBox, % ClipHist.PutHistoryItemIntoClipboard(2) ; puts the second history item into Clipboard and returns succsess status

Loop % ClipHist.Count
   MsgBox,, Item %A_Index%, % ClipHist.GetHistoryItemText(A_Index), .7

class ClipboardHistory
{
; https://is.gd/bYyogJ ; Clipboard Class (MSDN)
; https://is.gd/2z2Y9G ; Windows.ApplicationModel.DataTransfer.0.h (GitHub)
; https://is.gd/T4Lb7b ; asyncinfo.h (GitHub)
   __New() {
      static IID_IClipboardStatics2 := "{D2AC1B6A-D29F-554B-B303-F0452345FE02}"
      if (A_OSVersion ~= "^\D") {
         MsgBox, 48, Class ClipboardHistory, This class requires Windows 10 or later!
         Return ""
      }
      WrtStr := new WrtString("Windows.ApplicationModel.DataTransfer.Clipboard")
      VarSetCapacity(CLSID, 16)
      DllCall("ole32\CLSIDFromString", "WStr", IID_IClipboardStatics2, "Ptr", &CLSID)
      WrtStr.GetFactory(&CLSID, pIClipboardStatics2)
      this.ClipboardStatics2 := new IClipboardStatics2( pIClipboardStatics2 )
   }
   
   __Delete() {
      this.ClipboardStatics2 := ""
   }
   
   IsHistoryEnabled[] {
      get {
         Return this.ClipboardStatics2.IsHistoryEnabled
      }
   }
   
   Count[] {
      get {
         Return this._GetClipboardHistoryItems()
      }
   }
   
   ClearHistory() {
      Return this.ClipboardStatics2.ClearHistory()
   }
   
   GetHistoryItemText(index) { ; 1 based
      if !pIClipboardHistoryItem := this._GetClipboardHistoryItemByIndex(index)
         Return
      ClipboardHistoryItem := new IClipboardHistoryItem( pIClipboardHistoryItem )
      pIDataPackageView := ClipboardHistoryItem.Content
      DataPackageView := new IDataPackageView( pIDataPackageView )
      pIReadOnlyList := DataPackageView.AvailableFormats
      ReadOnlyList := new IReadOnlyList( pIReadOnlyList )
      Loop % ReadOnlyList.Count
         HSTRING := ReadOnlyList.Item[A_Index - 1]
      until (new WrtString(HSTRING, true)).GetText() = "Text" && textFound := true
      if !textFound
         Return
      DataPackageView.GetTextAsync(pIAsyncOperation)
      HSTRING := this._AsyncOperationGetResults(pIAsyncOperation)
      Return (new WrtString(HSTRING, true)).GetText()
   }
   
   PutHistoryItemIntoClipboard(index) { ; 1 based
      static SetHistoryItemAsContentStatus := ["Success", "AccessDenied", "ItemDeleted"]
      if !pIClipboardHistoryItem := this._GetClipboardHistoryItemByIndex(index)
         Return
      ClipboardHistoryItem := new IClipboardHistoryItem( pIClipboardHistoryItem )
      status := this.ClipboardStatics2.SetHistoryItemAsContent( pIClipboardHistoryItem )
      Return SetHistoryItemAsContentStatus[ status + 1 ]
   }
   
   _GetClipboardHistoryItemByIndex(index) { ; 1 based
      count := this._GetClipboardHistoryItems(ReadOnlyList)
      if (count < index) {
         MsgBox, 48, % " ", Index "%index%" exceeds items count!
         Return
      }
      Return pIClipboardHistoryItem := ReadOnlyList.Item[index - 1]
   }
   
   _GetClipboardHistoryItems(ByRef ReadOnlyList := "") {
      this.ClipboardStatics2.GetHistoryItemsAsync( pIAsyncOperation )
      pIClipboardHistoryItemsResult := this._AsyncOperationGetResults(pIAsyncOperation)
      ClipboardHistoryItemsResult := new IClipboardHistoryItemsResult( pIClipboardHistoryItemsResult )
      pIReadOnlyList := ClipboardHistoryItemsResult.Items
      ReadOnlyList := new IReadOnlyList( pIReadOnlyList )
      Return ReadOnlyList.Count
   }
   
   _AsyncOperationGetResults(pIAsyncOperation) {
      static AsyncStatus  := ["Started", "Completed", "Canceled", "Error"]
      AsyncOperation := new IAsyncOperation( pIAsyncOperation )
      AsyncOperation.QueryIAsyncInfo(pIAsyncInfo)
      AsyncInfo := new IAsyncInfo( pIAsyncInfo )
      Loop {
         Sleep, 10
         status := AsyncStatus[ AsyncInfo.Status + 1 ]
      } until status != "Started"
      if (status != "Completed")
         throw "AsyncInfo error, status: """ . status . """"
      AsyncOperation.GetResults( pResult )
      Return pResult
   }
}

class IClipboardStatics2 extends _InterfaceBase
{
   GetHistoryItemsAsync(ByRef pIAsyncOperation) {
      hr := DllCall(this.VTable(6), "Ptr", this.ptr, "UIntP", pIAsyncOperation)
      this.IsError(A_ThisFunc, hr)
   }
   ClearHistory() {
      hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", res)
      this.IsError(A_ThisFunc, hr)
      Return res
   }
   SetHistoryItemAsContent(pIClipboardHistoryItem) {
      hr := DllCall(this.VTable(9), "Ptr", this.ptr, "Ptr", pIClipboardHistoryItem, "UIntP", res)
      this.IsError(A_ThisFunc, hr)
      Return res
   }
   IsHistoryEnabled[] {
      get {
         hr := DllCall(this.VTable(10), "Ptr", this.ptr, "UIntP", res)
         this.IsError(A_ThisFunc, hr)
         Return res
      }
   }
}

class IClipboardHistoryItemsResult extends _InterfaceBase
{
   Items[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "PtrP", pIReadOnlyList)
         this.IsError(A_ThisFunc, hr)
         Return pIReadOnlyList
      }
   }
}

class IClipboardHistoryItem extends _InterfaceBase
{
   Content[] {
      get {
         hr := DllCall(this.VTable(8), "Ptr", this.ptr, "PtrP", pIDataPackageView)
         this.IsError(A_ThisFunc, hr)
         Return pIDataPackageView
      }
   }
}

class IDataPackageView extends _InterfaceBase
{
   AvailableFormats[] {
      get {
         hr := DllCall(this.VTable(9), "Ptr", this.ptr, "PtrP", pIReadOnlyList)
         this.IsError(A_ThisFunc, hr)
         Return pIReadOnlyList
      }
   }
   GetTextAsync(ByRef pIAsyncOperation) {
      hr := DllCall(this.VTable(12), "Ptr", this.ptr, "UIntP", pIAsyncOperation)
      this.IsError(A_ThisFunc, hr)
   }
}

class IReadOnlyList extends _InterfaceBase
{
   Item[index] {
      get {
         hr := DllCall(this.VTable(6), "Ptr", this.ptr, "Int", index, "PtrP", pItem)
         this.IsError(A_ThisFunc, hr)
         Return pItem
      }
   }
   Count[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", count)
         this.IsError(A_ThisFunc, hr)
         Return count
      }
   }
}

class IAsyncOperation extends _InterfaceBase
{
   QueryIAsyncInfo(ByRef pIAsyncInfo) {
      static IID_IAsyncInfo := "{00000036-0000-0000-C000-000000000046}"
      pIAsyncInfo := ComObjQuery(this.ptr, IID_IAsyncInfo)
   }
   GetResults(ByRef pResult) {
      hr := DllCall(this.VTable(8), "Ptr", this.ptr, "PtrP", pResult)
      this.IsError(A_ThisFunc, hr)
   }
}

class IAsyncInfo extends _InterfaceBase
{
   Status[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", status)
         this.IsError(A_ThisFunc, hr)
         Return status
      }
   }
}

class _InterfaceBase {
   __New(ptr) {
      this.ptr := ptr
   }
   __Delete() {
      ObjRelease(this.ptr)
   }
   VTable(idx) {
      Return NumGet(NumGet(this.ptr + 0) + A_PtrSize*idx)
   }
   IsError(method, result, exc := true) {
      if (result = 0)
         Return 0
      error := StrReplace(method, ".", "::") . " failed.`nResult: "
                              . ( result = "" ? "No result" : SysError(Format("{:#x}", result & 0xFFFFFFFF)) )
                              . "`nErrorLevel: " . ErrorLevel
      if !exc
         Return error
      throw error
   }
}

class WrtString {
   __New(string, isHandle := false) {
      if isHandle
         this.HSTRING := string
      else {
         DllCall("Combase\WindowsCreateString", "WStr", string, "UInt", StrLen(string), "PtrP", HSTRING)
         this.HSTRING := HSTRING
      }
   }
   __Delete() {
      DllCall("Combase\WindowsDeleteString", "Ptr", this.HSTRING)
   }
   GetText() {
      pBuff := DllCall("Combase\WindowsGetStringRawBuffer", "Ptr", this.HSTRING, "UIntP", len, "Ptr")
      Return StrGet(pBuff, len, "UTF-16")
   }
   GetFactory(riid, ByRef pInterface) {
      hr := DllCall("Combase\RoGetActivationFactory", "Ptr", this.HSTRING, "Ptr", riid, "PtrP", pInterface)
      if (hr != 0)
         throw SysError(hr)
   }
}

SysError(errorNum = "") {
	static flags := (FORMAT_MESSAGE_ALLOCATE_BUFFER := 0x100) | (FORMAT_MESSAGE_FROM_SYSTEM := 0x1000)
	(errorNum = "" && errorNum := A_LastError)
	DllCall("FormatMessage", "UInt", flags, "UInt", 0, "UInt", errorNum, "UInt", 0, "PtrP", pBuff, "UInt", 512, "Str", "")
	Return (str := StrGet(pBuff)) ? str : ErrorNum
}

ShotKey
Posts: 18
Joined: 01 Mar 2023, 07:32

Re: Windows Clipboard History

Post by ShotKey » 02 Mar 2023, 09:03

Thank you, teadrinker, this is some pretty impressive code.

I slightly adapted your script to paste e.g. the 3rd last clipboard history item:

Code: Select all

Clipboard := ClipHist.GetHistoryItemText(3)
SendInput ^v
However, this pushes that history item as a new item to the top of the clipboard history list, so if next I wanted to access e.g. the 4th item, I'd already need to do ClipHist.GetHistoryItemText(5). I'd quickly lose count.

Is there a way to paste an item without adding it to the top of the history list or to remove it immediately thereafter? Of course I could also use Send, but it's only for text and too slow and unreliable for longer texts...?

teadrinker
Posts: 4330
Joined: 29 Mar 2015, 09:41
Contact:

Re: Windows Clipboard History

Post by teadrinker » 02 Mar 2023, 11:13

Use PutHistoryItemIntoClipboard(index)

ShotKey
Posts: 18
Joined: 01 Mar 2023, 07:32

Re: Windows Clipboard History

Post by ShotKey » 02 Mar 2023, 13:58

Super, that works. Thank you! :bravo:

ShotKey
Posts: 18
Joined: 01 Mar 2023, 07:32

Re: Windows Clipboard History

Post by ShotKey » 02 Mar 2023, 14:04

It only works for text, though, right?

teadrinker
Posts: 4330
Joined: 29 Mar 2015, 09:41
Contact:

Re: Windows Clipboard History

Post by teadrinker » 02 Mar 2023, 15:01

Nope, for any contents. You could just try that. ;)

ShotKey
Posts: 18
Joined: 01 Mar 2023, 07:32

Re: Windows Clipboard History

Post by ShotKey » 03 Mar 2023, 05:45

Yes, you're right, I had tried it with a file and it didn't work, but in the meantime I realized that's because this doesn't work with the Windows Clipboard History itself, either. Images do work.

But I'd nevertheless need your help with another issue, please. Namely all of the below function calls return the same clipboard history item (the most recent one). But I want the last ~ 10 items, not just the last one, of course.

Code: Select all

CapsLock & 1::
	ClipHist.PutHistoryItemIntoClipboard(1)
    SendInput ^v
return

CapsLock & 2::
	ClipHist.PutHistoryItemIntoClipboard(2)
    SendInput ^v
return

CapsLock & 3::
	ClipHist.PutHistoryItemIntoClipboard(3)
    SendInput ^v
return
What do I need to ?

teadrinker
Posts: 4330
Joined: 29 Mar 2015, 09:41
Contact:

Re: Windows Clipboard History

Post by teadrinker » 03 Mar 2023, 09:14

ShotKey wrote: I had tried it with a file and it didn't work, but in the meantime I realized that's because this doesn't work with the Windows Clipboard History itself
Yep, files don't apper in the History, I also noticed this just now. :)
ShotKey wrote: all of the below function calls return the same clipboard history item
Most likely, all these function calls do nothing, you just have in Clipboard what you currently have in it.
ShotKey wrote:

Code: Select all

ClipHist.PutHistoryItemIntoClipboard(1)
ClipHist is not a sort of a magic word, it's just a variable that should contain an instanse of the ClipboardHistory class, so it must be put there before. Or you can do it without a variable at all:

Code: Select all

CapsLock & 1::
CapsLock & 2::
CapsLock & 3::
CapsLock & 4::
CapsLock & 5::
   (new ClipboardHistory).PutHistoryItemIntoClipboard(SubStr(A_ThisHotkey, -1))
   Send ^v
   Return
Last edited by teadrinker on 03 Mar 2023, 10:12, edited 1 time in total.

ShotKey
Posts: 18
Joined: 01 Mar 2023, 07:32

Re: Windows Clipboard History

Post by ShotKey » 03 Mar 2023, 09:31

Now it works, thank you very much! :)

niCode
Posts: 294
Joined: 17 Oct 2022, 22:09

Re: Windows Clipboard History

Post by niCode » 01 Apr 2023, 01:42

Sorry, didn't see any other way to contact you about this other than replying here. Do you happen to have a version of this for v2? I've been trying my damnest to convert it myself but kept running into errors and can't get past a particular one. It's driving me bonkers. If not, I appreciate any time you take responding to let me know :)
teadrinker wrote:
01 Mar 2023, 09:49

Code: Select all

ClipHist := new ClipboardHistory
if !ClipHist.IsHistoryEnabled {
   MsgBox Clipboard history disabled
   ExitApp
}
MsgBox Clipboard history enabled
MsgBox, % ClipHist.PutHistoryItemIntoClipboard(2) ; puts the second history item into Clipboard and returns succsess status

Loop % ClipHist.Count
   MsgBox,, Item %A_Index%, % ClipHist.GetHistoryItemText(A_Index), .7

class ClipboardHistory
{
; https://is.gd/bYyogJ ; Clipboard Class (MSDN)
; https://is.gd/2z2Y9G ; Windows.ApplicationModel.DataTransfer.0.h (GitHub)
; https://is.gd/T4Lb7b ; asyncinfo.h (GitHub)
   __New() {
      static IID_IClipboardStatics2 := "{D2AC1B6A-D29F-554B-B303-F0452345FE02}"
      if (A_OSVersion ~= "^\D") {
         MsgBox, 48, Class ClipboardHistory, This class requires Windows 10 or later!
         Return ""
      }
      WrtStr := new WrtString("Windows.ApplicationModel.DataTransfer.Clipboard")
      VarSetCapacity(CLSID, 16)
      DllCall("ole32\CLSIDFromString", "WStr", IID_IClipboardStatics2, "Ptr", &CLSID)
      WrtStr.GetFactory(&CLSID, pIClipboardStatics2)
      this.ClipboardStatics2 := new IClipboardStatics2( pIClipboardStatics2 )
   }
   
   __Delete() {
      this.ClipboardStatics2 := ""
   }
   
   IsHistoryEnabled[] {
      get {
         Return this.ClipboardStatics2.IsHistoryEnabled
      }
   }
   
   Count[] {
      get {
         Return this._GetClipboardHistoryItems()
      }
   }
   
   ClearHistory() {
      Return this.ClipboardStatics2.ClearHistory()
   }
   
   GetHistoryItemText(index) { ; 1 based
      if !pIClipboardHistoryItem := this._GetClipboardHistoryItemByIndex(index)
         Return
      ClipboardHistoryItem := new IClipboardHistoryItem( pIClipboardHistoryItem )
      pIDataPackageView := ClipboardHistoryItem.Content
      DataPackageView := new IDataPackageView( pIDataPackageView )
      pIReadOnlyList := DataPackageView.AvailableFormats
      ReadOnlyList := new IReadOnlyList( pIReadOnlyList )
      Loop % ReadOnlyList.Count
         HSTRING := ReadOnlyList.Item[A_Index - 1]
      until (new WrtString(HSTRING, true)).GetText() = "Text" && textFound := true
      if !textFound
         Return
      DataPackageView.GetTextAsync(pIAsyncOperation)
      HSTRING := this._AsyncOperationGetResults(pIAsyncOperation)
      Return (new WrtString(HSTRING, true)).GetText()
   }
   
   PutHistoryItemIntoClipboard(index) { ; 1 based
      static SetHistoryItemAsContentStatus := ["Success", "AccessDenied", "ItemDeleted"]
      if !pIClipboardHistoryItem := this._GetClipboardHistoryItemByIndex(index)
         Return
      ClipboardHistoryItem := new IClipboardHistoryItem( pIClipboardHistoryItem )
      status := this.ClipboardStatics2.SetHistoryItemAsContent( pIClipboardHistoryItem )
      Return SetHistoryItemAsContentStatus[ status + 1 ]
   }
   
   _GetClipboardHistoryItemByIndex(index) { ; 1 based
      count := this._GetClipboardHistoryItems(ReadOnlyList)
      if (count < index) {
         MsgBox, 48, % " ", Index "%index%" exceeds items count!
         Return
      }
      Return pIClipboardHistoryItem := ReadOnlyList.Item[index - 1]
   }
   
   _GetClipboardHistoryItems(ByRef ReadOnlyList := "") {
      this.ClipboardStatics2.GetHistoryItemsAsync( pIAsyncOperation )
      pIClipboardHistoryItemsResult := this._AsyncOperationGetResults(pIAsyncOperation)
      ClipboardHistoryItemsResult := new IClipboardHistoryItemsResult( pIClipboardHistoryItemsResult )
      pIReadOnlyList := ClipboardHistoryItemsResult.Items
      ReadOnlyList := new IReadOnlyList( pIReadOnlyList )
      Return ReadOnlyList.Count
   }
   
   _AsyncOperationGetResults(pIAsyncOperation) {
      static AsyncStatus  := ["Started", "Completed", "Canceled", "Error"]
      AsyncOperation := new IAsyncOperation( pIAsyncOperation )
      AsyncOperation.QueryIAsyncInfo(pIAsyncInfo)
      AsyncInfo := new IAsyncInfo( pIAsyncInfo )
      Loop {
         Sleep, 10
         status := AsyncStatus[ AsyncInfo.Status + 1 ]
      } until status != "Started"
      if (status != "Completed")
         throw "AsyncInfo error, status: """ . status . """"
      AsyncOperation.GetResults( pResult )
      Return pResult
   }
}

class IClipboardStatics2 extends _InterfaceBase
{
   GetHistoryItemsAsync(ByRef pIAsyncOperation) {
      hr := DllCall(this.VTable(6), "Ptr", this.ptr, "UIntP", pIAsyncOperation)
      this.IsError(A_ThisFunc, hr)
   }
   ClearHistory() {
      hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", res)
      this.IsError(A_ThisFunc, hr)
      Return res
   }
   SetHistoryItemAsContent(pIClipboardHistoryItem) {
      hr := DllCall(this.VTable(9), "Ptr", this.ptr, "Ptr", pIClipboardHistoryItem, "UIntP", res)
      this.IsError(A_ThisFunc, hr)
      Return res
   }
   IsHistoryEnabled[] {
      get {
         hr := DllCall(this.VTable(10), "Ptr", this.ptr, "UIntP", res)
         this.IsError(A_ThisFunc, hr)
         Return res
      }
   }
}

class IClipboardHistoryItemsResult extends _InterfaceBase
{
   Items[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "PtrP", pIReadOnlyList)
         this.IsError(A_ThisFunc, hr)
         Return pIReadOnlyList
      }
   }
}

class IClipboardHistoryItem extends _InterfaceBase
{
   Content[] {
      get {
         hr := DllCall(this.VTable(8), "Ptr", this.ptr, "PtrP", pIDataPackageView)
         this.IsError(A_ThisFunc, hr)
         Return pIDataPackageView
      }
   }
}

class IDataPackageView extends _InterfaceBase
{
   AvailableFormats[] {
      get {
         hr := DllCall(this.VTable(9), "Ptr", this.ptr, "PtrP", pIReadOnlyList)
         this.IsError(A_ThisFunc, hr)
         Return pIReadOnlyList
      }
   }
   GetTextAsync(ByRef pIAsyncOperation) {
      hr := DllCall(this.VTable(12), "Ptr", this.ptr, "UIntP", pIAsyncOperation)
      this.IsError(A_ThisFunc, hr)
   }
}

class IReadOnlyList extends _InterfaceBase
{
   Item[index] {
      get {
         hr := DllCall(this.VTable(6), "Ptr", this.ptr, "Int", index, "PtrP", pItem)
         this.IsError(A_ThisFunc, hr)
         Return pItem
      }
   }
   Count[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", count)
         this.IsError(A_ThisFunc, hr)
         Return count
      }
   }
}

class IAsyncOperation extends _InterfaceBase
{
   QueryIAsyncInfo(ByRef pIAsyncInfo) {
      static IID_IAsyncInfo := "{00000036-0000-0000-C000-000000000046}"
      pIAsyncInfo := ComObjQuery(this.ptr, IID_IAsyncInfo)
   }
   GetResults(ByRef pResult) {
      hr := DllCall(this.VTable(8), "Ptr", this.ptr, "PtrP", pResult)
      this.IsError(A_ThisFunc, hr)
   }
}

class IAsyncInfo extends _InterfaceBase
{
   Status[] {
      get {
         hr := DllCall(this.VTable(7), "Ptr", this.ptr, "UIntP", status)
         this.IsError(A_ThisFunc, hr)
         Return status
      }
   }
}

class _InterfaceBase {
   __New(ptr) {
      this.ptr := ptr
   }
   __Delete() {
      ObjRelease(this.ptr)
   }
   VTable(idx) {
      Return NumGet(NumGet(this.ptr + 0) + A_PtrSize*idx)
   }
   IsError(method, result, exc := true) {
      if (result = 0)
         Return 0
      error := StrReplace(method, ".", "::") . " failed.`nResult: "
                              . ( result = "" ? "No result" : SysError(Format("{:#x}", result & 0xFFFFFFFF)) )
                              . "`nErrorLevel: " . ErrorLevel
      if !exc
         Return error
      throw error
   }
}

class WrtString {
   __New(string, isHandle := false) {
      if isHandle
         this.HSTRING := string
      else {
         DllCall("Combase\WindowsCreateString", "WStr", string, "UInt", StrLen(string), "PtrP", HSTRING)
         this.HSTRING := HSTRING
      }
   }
   __Delete() {
      DllCall("Combase\WindowsDeleteString", "Ptr", this.HSTRING)
   }
   GetText() {
      pBuff := DllCall("Combase\WindowsGetStringRawBuffer", "Ptr", this.HSTRING, "UIntP", len, "Ptr")
      Return StrGet(pBuff, len, "UTF-16")
   }
   GetFactory(riid, ByRef pInterface) {
      hr := DllCall("Combase\RoGetActivationFactory", "Ptr", this.HSTRING, "Ptr", riid, "PtrP", pInterface)
      if (hr != 0)
         throw SysError(hr)
   }
}

SysError(errorNum = "") {
	static flags := (FORMAT_MESSAGE_ALLOCATE_BUFFER := 0x100) | (FORMAT_MESSAGE_FROM_SYSTEM := 0x1000)
	(errorNum = "" && errorNum := A_LastError)
	DllCall("FormatMessage", "UInt", flags, "UInt", 0, "UInt", errorNum, "UInt", 0, "PtrP", pBuff, "UInt", 512, "Str", "")
	Return (str := StrGet(pBuff)) ? str : ErrorNum
}

User avatar
RDC
Posts: 112
Joined: 29 Jan 2023, 10:22

Re: Windows Clipboard History

Post by RDC » 01 Apr 2023, 08:44

Unless you are just wanting this done by AHK scripts specifically, Ditto clipboard manager retains as many last entries as you want. It doesn't create hotkeys 1-10x, but I imagine the smart folks here at AHK could whip up something that uses the retention of Ditto's multiple entries to accomplish the hotkey part of the request. Just another option that may work for others.
https://ditto-cp.sourceforge.io/

image.png
image.png (79.7 KiB) Viewed 1577 times
image.png
image.png (46.55 KiB) Viewed 1577 times

teadrinker
Posts: 4330
Joined: 29 Mar 2015, 09:41
Contact:

Re: Windows Clipboard History

Post by teadrinker » 01 Apr 2023, 15:47

niCode wrote: Do you happen to have a version of this for v2?
Not yet, maybe later. :)


niCode
Posts: 294
Joined: 17 Oct 2022, 22:09

Re: Windows Clipboard History

Post by niCode » 02 Apr 2023, 20:26

teadrinker wrote:
02 Apr 2023, 18:10
@niCode
Clipboard History v2
Thank you so much! It works :)

Post Reply

Return to “Ask for Help (v1)”