Loading data blob to the Clipboard (using WinClip library)

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Loading data blob to the Clipboard (using WinClip library)

16 Mar 2024, 17:44

I'm saving data captured from the Clipboard to a Blob field in a SQLite table. Later, I want to retrieve the clip data and load it to the Clipboard.

From the SQLite table, I get the address and size of the blob in a SQLite record. I can use successfully these two fields to create a temporary file and load it to the Clipboard using the WinClip.Load() method.

Code: Select all

; intAddr: address of a SQLite Blob field containing data captured earlier from the Clipboard
; intSize: size of the same SQLite Blob field

hFileData := FileOpen(strFilePath, "w")
VarSetCapacity(oBlob, intSize)
DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", &oBlob, "Ptr", intAddr, "Ptr", intSize)
hFileData.RawWrite(&oBlob, intSize)
hFileData.Close()

WinClip.Load(strFilePath) ; this is working
Now, my question... I would like to do the same but loading the blob directly to the Clipboard without using a temporary file. I tried creating the extended method WinClipJL.SetData() below but it does not work. Do you see what could be wrong? There may be a problem the way I prepare the data for the _toclipboard() method?

Code: Select all

; intAddr: address of a SQLite Blob field containing data captured earlier from the Clipboard
; intSize: size of the same SQLite Blob field

; prepare the Blob for WinClipJL.SetData()
VarSetCapacity(oBlob, intSize)
DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", &oBlob, "Ptr", intAddr, "Ptr", intSize)

; call WinClip extended method SetData
intSetSize := WinClipJL.SetData(oBlob, intSize) ; this is NOT working

Code: Select all

class WinClipJL extends WinClip
{
	SetData( dataBuff, dataSize )
	; adaped from the WinClip.Load() method
	{
		if !( size := this._toclipboard( dataBuff, dataSize ) )
			return 0
		return size
	}
}
I could post a working example but maybe one can help me just reading these code snippets? Thanks.
Last edited by JnLlnd on 17 Mar 2024, 08:23, edited 1 time in total.
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
just me
Posts: 9466
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Loading data blob to the Clipboard

17 Mar 2024, 07:45

At least:

Code: Select all

class WinClipJL extends WinClip
{
	SetData( dataBuff, dataSize )
dataBuff must be passed ByRef.
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Loading data blob to the Clipboard

17 Mar 2024, 08:22

Thanks @just me for the input.

I tried it but there must be something more to fix because I got the same result (this._toclipboard() returns 0). I added the line clipSize := VarSetCapacity( dataBuff ) to check if the data in dataBuff is the excepted size. It is.

Code: Select all

SetData( ByRef dataBuff, dataSize )
{
    clipSize := VarSetCapacity( dataBuff )
    if ( clipSize <> dataSize)
        return 0
    ; this confirms that dataBuff size is OK
    if !( size := this._toclipboard( dataBuff, clipSize ) )
        return 0
    return size
}
If this can help, here is the source code for this._toclipboard() from the WinClip library found here.

Code: Select all

    _toclipboard( ByRef data, size )
    {
        if !WinClipAPI.OpenClipboard()
            return 0
        offset := 0
        lastPartOffset := 0
        WinClipAPI.EmptyClipboard()
        while ( offset < size )
        {
            if !( fmt := NumGet( data, offset, "UInt" ) )
                break
            offset += 4
            if !( dataSize := NumGet( data, offset, "UInt" ) )
                break
            offset += 4
            if ( ( offset + dataSize ) > size )
                break
            if !( pData := WinClipAPI.GlobalLock( WinClipAPI.GlobalAlloc( 0x0042, dataSize ) ) )
            {
                offset += dataSize
                continue
            }
            WinClipAPI.memcopy( pData, &data + offset, dataSize )
            if ( fmt == this.ClipboardFormats.CF_ENHMETAFILE )
                pClipData := WinClipAPI.SetEnhMetaFileBits( pData, dataSize )
            else
                pClipData := pData
            if !pClipData
                continue
            WinClipAPI.SetClipboardData( fmt, pClipData )
            if ( fmt == this.ClipboardFormats.CF_ENHMETAFILE )
                WinClipAPI.DeleteEnhMetaFile( pClipData )
            WinClipAPI.GlobalUnlock( pData )
            offset += dataSize
            lastPartOffset := offset
        }
        WinClipAPI.CloseClipboard()
        return lastPartOffset
    }
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
just me
Posts: 9466
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Loading data blob to the Clipboard (using WinClip library)

18 Mar 2024, 02:56

Hi @JnLlnd,

there are only 2 possibilities to return 0 from _toclipboard():
  1. Code: Select all

           if !WinClipAPI.OpenClipboard()
    The clipboard couldn't be opened.
  2. Code: Select all

            while ( offset < size )
            {
    			...
    
    Size is 0 or less or the contents of data is invalid.
You can simply check it by adding a few message boxes to _toclipboard() to see where it breaks.
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Loading data blob to the Clipboard (using WinClip library)

18 Mar 2024, 11:46

Hi @just me ,

I have some progress to report, at least on the investigation side.

I stepped through the _toclipboard() method with Scite4AHK debugger. At first, I tested with a small content of 1 character ("#"). The method succeeded. Then I tried with larger contents.

It fails as soon as the size of the text in the clip exceeds 1016 chars (a total of 2054 bytes for Unicode values, header and CF_LOCALE data).

When the size of text is 1017 chars, the returned values are:

Code: Select all

        while ( offset < size ) ; size -> 2056
        {
            if !( fmt := NumGet( data, offset, "UInt" ) ) ; 	fmt --> 65470464 (instead of the expected 13 CF_UNICODETEXT format value)
                break
            offset += 4
            if !( dataSize := NumGet( data, offset, "UInt" ) ) ; dataSize --> EMPTY (instead of the expected size of Unicode text 2036)
                break
Ant it breaks.

I checked the content and size of the data in the SQLlite table and it looks OK. Here is how I retreive the data from the table:

Code: Select all

		strDbSQL := "SELECT ClipboardAll FROM History ORDER BY ClipID DESC LIMIT 1 OFFSET " . intPosition - 1 . ";"
		if !o_Db.Query(strDbSQL, oRecordSet)
			and SQLiteError("SELECT_CLIP_POSITION", strDbSQL)
			return 4 ; Error loading clip
		
		oRecordSet.Next(saDbRow) ; get first (and only) row in the table
		oRecordSet.Free()
	
		this.intClipboardAllSize := saDbRow[1].Size
		this.intClipboardAllAddr := saDbRow[1].GetAddress("Blob")
Then I use the size and address in the function posted in my OP:

Code: Select all

		intAddr := this.intClipboardAllAddr
		intSize := this.intClipboardAllSize
		VarSetCapacity(oBlob, intSize)
		DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", &oBlob, "Ptr", intAddr, "Ptr", intSize)
		intSetSize := WinClipJL.SetData(oBlob, intSize)
There must be a problem in the way I retrieve the data from the database or prepare the data for the _toclipboard() method.
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Loading data blob to the Clipboard (using WinClip library)

18 Mar 2024, 12:19

Additional info about this statement in my OP:
I can use successfully these two fields to create a temporary file and load it to the Clipboard using the WinClip.Load() method.
This was not true. There is an issue with the file I create for the Load method when its size exceeds 2054 bytes. I examined the diff between a correct file of 2054 bytes and a file of 2056 bytes.

First the correct data. See from position 0x08h to 0x0F the text "ipsu":

Image

Now the second file. The data seems to be corrupted from position 0x08 to 0x0F. But then looks OK starting from position 0x100:

Image

Does it help understanding the issue?
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
User avatar
JnLlnd
Posts: 487
Joined: 29 Sep 2013, 21:29
Location: Montreal, Quebec, Canada
Contact:

Re: Loading data blob to the Clipboard (using WinClip library)

18 Mar 2024, 16:17

I think I got the solution. Instead of using DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", &oBlob, "Ptr", intAddr, "Ptr", intSize) when I need to load the data to the clipboard, I use it right after I retreived the data from the SQLlite database. If I don't do it immediately there must be some background script in my script that corrupts the data at intAddr before I move it to oBlob. I'll be back if this solution is not definitive.

Thanks @just me for for leading me in the right direction.
:thumbup: Author of freeware Quick Access Popup, the powerful Windows folders, apps and documents launcher!
:P Now working on Quick Clipboard Editor
:ugeek: The Automator's Courses on AutoHotkey
just me
Posts: 9466
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Loading data blob to the Clipboard (using WinClip library)

20 Mar 2024, 02:28

Hi @JnLlnd,

Code: Select all

		oRecordSet.Next(saDbRow) ; get first (and only) row in the table
		oRecordSet.Free()
	
		this.intClipboardAllSize := saDbRow[1].Size
		this.intClipboardAllAddr := saDbRow[1].GetAddress("Blob")

Code: Select all

		intAddr := this.intClipboardAllAddr
		intSize := this.intClipboardAllSize
		VarSetCapacity(oBlob, intSize)
		DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", &oBlob, "Ptr", intAddr, "Ptr", intSize)
Most probably the object saDbRow has been destroyed between this two parts and the released memory of saDbRow.Blob has been reused by the script.
JnLlnd wrote:Now the second file. The data seems to be corrupted from position 0x08 to 0x0F. But then looks OK starting from position 0x10:
Date are corrupted from position 0x00 to 0x0F.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bing [Bot], haomingchen1998 and 88 guests