<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Серый форум &mdash; AHK v2: Класс для работы с историей буфера обмена Windows 10]]></title>
	<link rel="self" href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=17722&amp;type=atom" />
	<updated>2023-04-02T22:27:13Z</updated>
	<generator>PunBB</generator>
	<id>https://forum.script-coding.com/viewtopic.php?id=17722</id>
		<entry>
			<title type="html"><![CDATA[AHK v2: Класс для работы с историей буфера обмена Windows 10]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=157447#p157447" />
			<content type="html"><![CDATA[<p>Частичная реализация класса <a href="https://learn.microsoft.com/en-us/uwp/api/windows.applicationmodel.datatransfer.clipboard?view=winrt-19041">Clipboard</a> из Windows <a href="https://learn.microsoft.com/en-us/uwp/api/">UWP</a> для AHK v2. Реализованы методы, нужные для работы с <a href="https://support.microsoft.com/en-us/windows/clipboard-in-windows-c436501e-985d-1c8d-97ea-fe46ddf338c6#WindowsVersion=Windows_10">историей буфера обмена</a> Windows 10. Доступные свойства и методы собраны в класс ClipboardHistory, по их названию должно быть понятно, что они делают. Остальные классы являются вспомогательными. Примеры использования в коде.<br /></p><div class="codebox"><pre><code>#Requires AutoHotkey v2.0.2 ; and OS &gt;= Windows 10

if !ClipboardHistory.HistoryEnabled {
    if MsgBox(&#039;Clipboard history disabled, want to enable?&#039;, &#039;Clipboard History&#039;, &#039;YN&#039;) = &#039;No&#039;
        ExitApp
    ClipboardHistory.HistoryEnabled := true
}
MsgBox &#039;Clipboard history enabled&#039;
MsgBox ClipboardHistory.GetHistoryItemText(1), &#039;First item text&#039;
MsgBox ClipboardHistory.DeleteHistoryItem(1), &#039;Boolean success status&#039;   ; deletes the first history item and returns boolean success status
MsgBox ClipboardHistory.GetHistoryItemText(1), &#039;First item text&#039;
MsgBox ClipboardHistory.PutHistoryItemIntoClipboard(2), &#039;Success status&#039; ; puts the second history item into Clipboard and returns succsess status
MsgBox A_Clipboard, &#039;Clipboard content&#039;

formats := &#039;&#039;
for fmt in ClipboardHistory.GetAvailableFormats(2) { ; available formats of the second history item
    formats .= fmt . &#039;`n&#039;
}
MsgBox formats, &#039;Available Formats&#039;

historyItemCount := ClipboardHistory.Count
Loop historyItemCount {
    MsgBox ClipboardHistory.GetHistoryItemText(A_Index), &#039;Item &#039; . A_Index . &#039; of &#039; . historyItemCount, &#039;T.7&#039;
}

class ClipboardHistory
{
; properties
    static HistoryEnabled {
        get =&gt; CBH_API.HistoryEnabled
        set =&gt; CBH_API.HistoryEnabled := value ; boolean
    }
    static Count =&gt; CBH_API.GetClipboardHistoryItems()

; methods
    static ClearHistory()                     =&gt; CBH_API.ClearHistory()
    static DeleteHistoryItem(index)           =&gt; CBH_API.DeleteHistoryItem(index) ; 1 based index
    static GetHistoryItemText(index)          =&gt; CBH_API.GetHistoryItemText(index)
    static PutHistoryItemIntoClipboard(index) =&gt; CBH_API.PutHistoryItemIntoClipboard(index)
    static GetAvailableFormats(index)         =&gt; CBH_API.GetAvailableFormats(index)
}

class CBH_API
{
    /*
        https://is.gd/bYyogJ     Clipboard Class (MSDN)
        https://is.gd/2z2Y9G     Windows.ApplicationModel.DataTransfer.0.h (GitHub)
        https://is.gd/T4Lb7b     asyncinfo.h (GitHub)
    */
    static HistoryEnabled {
        get =&gt; IClipboardStatics2.IsHistoryEnabled
        set =&gt; RegWrite(!!value, &#039;REG_DWORD&#039;, &#039;HKCU\SOFTWARE\Microsoft\Clipboard&#039;, &#039;EnableClipboardHistory&#039;)
    }

    static ClearHistory() =&gt; IClipboardStatics2.ClearHistory()

    static DeleteHistoryItem(index) { ; 1 based
        if !pIClipboardHistoryItem := this.GetClipboardHistoryItemByIndex(index)
            return false
        bool := IClipboardStatics2.DeleteItemFromHistory(pIClipboardHistoryItem)
        ObjRelease(pIClipboardHistoryItem)
        return bool
    }

    static GetHistoryItemText(index) {
        if !DataPackageView := this.GetDataPackageView(index)
            return
        ReadOnlyList := IReadOnlyList(DataPackageView.AvailableFormats)
        textFound := false
        Loop ReadOnlyList.Count
            HSTRING := ReadOnlyList.Item[A_Index - 1]
        until WrtString(HSTRING).GetText() = &#039;Text&#039; &amp;&amp; textFound := true
        if !textFound
            return
        HSTRING := this.Await(DataPackageView.GetTextAsync())
        return WrtString(HSTRING).GetText()
    }

    static GetAvailableFormats(index) {
        if !DataPackageView := this.GetDataPackageView(index)
            return
        ReadOnlyList := IReadOnlyList(DataPackageView.AvailableFormats)
        AvailableFormats := []
        Loop ReadOnlyList.Count {
            HSTRING := ReadOnlyList.Item[A_Index - 1]
            AvailableFormats.Push(WrtString(HSTRING).GetText())
        }
        return AvailableFormats
    }

    static GetDataPackageView(index) {
        if !pIClipboardHistoryItem := this.GetClipboardHistoryItemByIndex(index)
            return
        pIDataPackageView := IClipboardHistoryItem(pIClipboardHistoryItem).Content
        return IDataPackageView(pIDataPackageView)
    }

    static PutHistoryItemIntoClipboard(index) {
        static SetHistoryItemAsContentStatus := [&#039;Success&#039;, &#039;AccessDenied&#039;, &#039;ItemDeleted&#039;]
        if !pIClipboardHistoryItem := this.GetClipboardHistoryItemByIndex(index)
            return
        status := IClipboardStatics2.SetHistoryItemAsContent(pIClipboardHistoryItem)
        ObjRelease(pIClipboardHistoryItem)
        return SetHistoryItemAsContentStatus[status + 1]
    }

    static GetClipboardHistoryItemByIndex(index) { ; 1 based
        count := this.GetClipboardHistoryItems(&amp;ReadOnlyList)
        if (count &lt; index) {
            MsgBox &#039;Index &quot;&#039; . index . &#039;&quot; exceeds items count!&#039;
            return
        }
        return pIClipboardHistoryItem := ReadOnlyList.Item[index - 1]
    }

    static GetClipboardHistoryItems(&amp;ReadOnlyList := &#039;&#039;) {
        pIClipboardHistoryItemsResult := this.Await(IClipboardStatics2.GetHistoryItemsAsync())
        ClipboardHistoryItemsResult := IClipboardHistoryItemsResult(pIClipboardHistoryItemsResult)
        pIReadOnlyList := ClipboardHistoryItemsResult.Items
        ReadOnlyList := IReadOnlyList(pIReadOnlyList)
        return ReadOnlyList.Count
    }

    static Await(pIAsyncOperation) {
        static AsyncStatus := [&#039;Started&#039;, &#039;Completed&#039;, &#039;Canceled&#039;, &#039;Error&#039;]
        AsyncOperation := IAsyncOperation(pIAsyncOperation)
        pIAsyncInfo := AsyncOperation.QueryIAsyncInfo()
        AsyncInfo := IAsyncInfo(pIAsyncInfo)
        Loop {
            Sleep 10
            status := AsyncStatus[AsyncInfo.Status + 1]
        } until status != &#039;Started&#039;
        if (status != &#039;Completed&#039;)
            throw OSError(&#039;AsyncInfo error, status: &quot;&#039; . status . &#039;&quot;&#039;, A_ThisFunc)
        return AsyncOperation.GetResults()
    }
}

class IClipboardStatics2
{
    static __New() {
        static IID_IClipboardStatics2 := &#039;{D2AC1B6A-D29F-554B-B303-F0452345FE02}&#039;, VT_UNKNOWN := 0xD
        if !(A_OSVersion ~= &#039;^10\.&#039;)
            throw OSError(&#039;This class requires Windows 10 or later!&#039;, A_ThisFunc)
        
        WrtStr := WrtString(&#039;Windows.ApplicationModel.DataTransfer.Clipboard&#039;)
        DllCall(&#039;Ole32\CLSIDFromString&#039;, &#039;Str&#039;, IID_IClipboardStatics2, &#039;Ptr&#039;, CLSID := Buffer(16))
        this.comObj := ComValue(VT_UNKNOWN, WrtStr.GetFactory(CLSID))
        this.ptr := this.comObj.ptr
    }
    static GetHistoryItemsAsync() =&gt; (ComCall(6, this, &#039;UIntP&#039;, &amp;pIAsyncOperation := 0), pIAsyncOperation)

    static ClearHistory() =&gt; (ComCall(7, this, &#039;UIntP&#039;, &amp;res := 0), res)

    static DeleteItemFromHistory(pIClipboardHistoryItem) =&gt; (ComCall(8, this, &#039;Ptr&#039;, pIClipboardHistoryItem, &#039;UIntP&#039;, &amp;res := 0), res)

    static SetHistoryItemAsContent(pIClipboardHistoryItem) =&gt; (ComCall(9, this, &#039;Ptr&#039;, pIClipboardHistoryItem, &#039;UIntP&#039;, &amp;res := 0), res)

    static IsHistoryEnabled =&gt; (ComCall(10, this, &#039;UIntP&#039;, &amp;res := 0), res)
}

class IClipboardHistoryItemsResult extends _InterfaceBase
{
    Items =&gt; (ComCall(7, this, &#039;PtrP&#039;, &amp;pIReadOnlyList := 0), pIReadOnlyList)
}

class IClipboardHistoryItem extends _InterfaceBase
{
    Content =&gt; (ComCall(8, this, &#039;PtrP&#039;, &amp;pIDataPackageView := 0), pIDataPackageView)
}

class IDataPackageView extends _InterfaceBase
{
    AvailableFormats =&gt; (ComCall(9, this, &#039;PtrP&#039;, &amp;pIReadOnlyList := 0), pIReadOnlyList)

    GetTextAsync() =&gt; (ComCall(12, this, &#039;UIntP&#039;, &amp;pIAsyncOperation := 0), pIAsyncOperation)
}

class IReadOnlyList extends _InterfaceBase
{
    Item[index] =&gt; (ComCall(6, this, &#039;Int&#039;, index, &#039;PtrP&#039;, &amp;pItem := 0), pItem)

    Count =&gt; (ComCall(7, this, &#039;UIntP&#039;, &amp;count := 0), count)
}

class IAsyncOperation extends _InterfaceBase
{
    QueryIAsyncInfo() =&gt; ComObjQuery(this, IID_IAsyncInfo := &#039;{00000036-0000-0000-C000-000000000046}&#039;)

    GetResults() =&gt; (ComCall(8, this, &#039;PtrP&#039;, &amp;pResult := 0), pResult)
}

class IAsyncInfo extends _InterfaceBase
{
    Status =&gt; (ComCall(7, this, &#039;PtrP&#039;, &amp;status := 0), status)
}

class _InterfaceBase
{
    __New(ptr) {
        this.comObj := ComValue(VT_UNKNOWN := 0xD, ptr)
        this.ptr := this.comObj.ptr
    }
}

class WrtString
{
    __New(stringOrHandle) {
        if Type(stringOrHandle) = &#039;Integer&#039;
            this.ptr := stringOrHandle
        else {
            DllCall(&#039;Combase\WindowsCreateString&#039;, &#039;WStr&#039;, stringOrHandle, &#039;UInt&#039;, StrLen(stringOrHandle), &#039;PtrP&#039;, &amp;HSTRING := 0)
            this.ptr := HSTRING
        }
    }
    __Delete() {
        DllCall(&#039;Combase\WindowsDeleteString&#039;, &#039;Ptr&#039;, this)
    }
    GetText() {
        buf := DllCall(&#039;Combase\WindowsGetStringRawBuffer&#039;, &#039;Ptr&#039;, this, &#039;UIntP&#039;, &amp;len := 0, &#039;Ptr&#039;)
        return StrGet(buf, len, &#039;UTF-16&#039;)
    }
    GetFactory(riid) {
        hr := DllCall(&#039;Combase\RoGetActivationFactory&#039;, &#039;Ptr&#039;, this, &#039;Ptr&#039;, riid, &#039;PtrP&#039;, &amp;pInterface := 0)
        if (hr != 0)
            throw OSError(WrtString.SysError(hr), A_ThisFunc)
        return pInterface
    }
    static SysError(nError := &#039;&#039;) {
        static flags := (FORMAT_MESSAGE_ALLOCATE_BUFFER := 0x100) | (FORMAT_MESSAGE_FROM_SYSTEM := 0x1000)
        (nError = &#039;&#039; &amp;&amp; nError := A_LastError)
        DllCall(&#039;FormatMessage&#039;, &#039;UInt&#039;, flags, &#039;UInt&#039;, 0, &#039;UInt&#039;, nError, &#039;UInt&#039;, 0, &#039;PtrP&#039;, &amp;pBuf := 0, &#039;UInt&#039;, 128)
        err := (str := StrGet(pBuf)) = &#039;&#039; ? nError : str
        DllCall(&#039;LocalFree&#039;, &#039;Ptr&#039;, pBuf)
        return err
    }
}</code></pre></div><p>Связанная тема <a href="https://forum.script-coding.com/viewtopic.php?id=16028">AHK: UWP, Clipboard Class</a>, в ней выложен вариант для v1 (но с меньшей функциональностью).</p>]]></content>
			<author>
				<name><![CDATA[teadrinker]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24515</uri>
			</author>
			<updated>2023-04-02T22:27:13Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=157447#p157447</id>
		</entry>
</feed>
