WinClip - AHKv2 Compatibility

Post your working scripts, libraries and tools for AHK v1.1 and older
egocarib
Posts: 100
Joined: 21 May 2015, 18:21

WinClip - AHKv2 Compatibility

17 Mar 2017, 14:38

I made some minor adjustments to WinClip so that it can work for both AHKv1 and AHKv2. Perhaps it will be useful to others.

All credit to Deo, the original author of WinClip (https://autohotkey.com/board/topic/74670-class-winclip-direct-clipboard-manipulations/)

Changes:
* Updated assignment operator syntax
* Fixed dynamic function calling syntax
* Fixed literal continuation section
* Updated type checking to be compatible cross-version
* Made some changes to test.ahk

WinClipAPI.ahk

Code: Select all

class WinClip_base
{
    __Call( aTarget, aParams* ) {
        if ObjHasKey( WinClip_base, aTarget )
            return WinClip_base[ aTarget ].Call( this, aParams* ) ;updated for AHKv2 compatibility
            ;return WinClip_base[ aTarget ].( this, aParams* )
        throw Exception( "Unknown function '" aTarget "' requested from object '" this.__Class "'", -1 )
    }
    
    Err( msg ) {
        throw Exception( this.__Class " : " msg ( A_LastError != 0 ? "`n" this.ErrorFormat( A_LastError ) : "" ), -2 )
    }
    
    ErrorFormat( error_id ) {
        VarSetCapacity(msg,1000,0)
        if !len := DllCall("FormatMessageW"
                    ,"UInt",FORMAT_MESSAGE_FROM_SYSTEM := 0x00001000 | FORMAT_MESSAGE_IGNORE_INSERTS := 0x00000200      ;dwflags
                    ,"Ptr",0        ;lpSource
                    ,"UInt",error_id    ;dwMessageId
                    ,"UInt",0           ;dwLanguageId
                    ,"Ptr",&msg         ;lpBuffer
                    ,"UInt",500)            ;nSize
            return
        return  strget(&msg,len)
    }
}

class WinClipAPI_base extends WinClip_base
{
    __Get( name ) {
        if !ObjHasKey( this, initialized )
            this.Init()
        else
            throw Exception( "Unknown field '" name "' requested from object '" this.__Class "'", -1 )
    }
}

class WinClipAPI extends WinClip_base
{
    memcopy( dest, src, size ) {
        return DllCall( "msvcrt\memcpy", "ptr", dest, "ptr", src, "uint", size )
    }
    GlobalSize( hObj ) {
        return DllCall( "GlobalSize", "Ptr", hObj )
    }
    GlobalLock( hMem ) {
        return DllCall( "GlobalLock", "Ptr", hMem )
    }
    GlobalUnlock( hMem ) {
        return DllCall( "GlobalUnlock", "Ptr", hMem )
    }
    GlobalAlloc( flags, size ) {
        return DllCall( "GlobalAlloc", "Uint", flags, "Uint", size )
    }
    OpenClipboard() {
        return DllCall( "OpenClipboard", "Ptr", 0 )
    }
    CloseClipboard() {
        return DllCall( "CloseClipboard" )
    }
    SetClipboardData( format, hMem ) {
        return DllCall( "SetClipboardData", "Uint", format, "Ptr", hMem )
    }
    GetClipboardData( format ) {
        return DllCall( "GetClipboardData", "Uint", format ) 
    }
    EmptyClipboard() {
        return DllCall( "EmptyClipboard" )
    }
    EnumClipboardFormats( format ) {
        return DllCall( "EnumClipboardFormats", "UInt", format )
    }
    CountClipboardFormats() {
        return DllCall( "CountClipboardFormats" )
    }
    GetClipboardFormatName( iFormat ) {
        size := VarSetCapacity( bufName, 255*( A_IsUnicode ? 2 : 1 ), 0 )
        DllCall( "GetClipboardFormatName", "Uint", iFormat, "str", bufName, "Uint", size )
        return bufName
    }
    GetEnhMetaFileBits( hemf, ByRef buf ) {
        if !( bufSize := DllCall( "GetEnhMetaFileBits", "Ptr", hemf, "Uint", 0, "Ptr", 0 ) )
            return 0
        VarSetCapacity( buf, bufSize, 0 )
        if !( bytesCopied := DllCall( "GetEnhMetaFileBits", "Ptr", hemf, "Uint", bufSize, "Ptr", &buf ) )
            return 0
        return bytesCopied
    }
    SetEnhMetaFileBits( pBuf, bufSize ) {
        return DllCall( "SetEnhMetaFileBits", "Uint", bufSize, "Ptr", pBuf )
    }
    DeleteEnhMetaFile( hemf ) {
        return DllCall( "DeleteEnhMetaFile", "Ptr", hemf )
    }
    ErrorFormat(error_id) {
        VarSetCapacity(msg,1000,0)
        if !len := DllCall("FormatMessageW"
                    ,"UInt",FORMAT_MESSAGE_FROM_SYSTEM := 0x00001000 | FORMAT_MESSAGE_IGNORE_INSERTS := 0x00000200      ;dwflags
                    ,"Ptr",0        ;lpSource
                    ,"UInt",error_id    ;dwMessageId
                    ,"UInt",0           ;dwLanguageId
                    ,"Ptr",&msg         ;lpBuffer
                    ,"UInt",500)            ;nSize
            return
        return  strget(&msg,len)
    }
    IsInteger( var ) {
        if (var+0 == var) && (Floor(var) == var) ;test for integer while remaining v1 and v2 compatible
            return True
        else 
            return False
    }
    LoadDllFunction( file, function ) {
            if !hModule := DllCall( "GetModuleHandleW", "Wstr", file, "UPtr" )
                    hModule := DllCall( "LoadLibraryW", "Wstr", file, "UPtr" )
            
            ret := DllCall("GetProcAddress", "Ptr", hModule, "AStr", function, "UPtr")
            return ret
    }
    SendMessage( hWnd, Msg, wParam, lParam ) {
         static SendMessageW

         If not SendMessageW
                SendMessageW := this.LoadDllFunction( "user32.dll", "SendMessageW" )

         ret := DllCall( SendMessageW, "UPtr", hWnd, "UInt", Msg, "UPtr", wParam, "UPtr", lParam )
         return ret
    }
    GetWindowThreadProcessId( hwnd ) {
        return DllCall( "GetWindowThreadProcessId", "Ptr", hwnd, "Ptr", 0 )
    }
    WinGetFocus( hwnd ) {
        GUITHREADINFO_cbsize := 24 + A_PtrSize*6
        VarSetCapacity( GuiThreadInfo, GUITHREADINFO_cbsize, 0 )    ;GuiThreadInfoSize = 48
        NumPut(GUITHREADINFO_cbsize, GuiThreadInfo, 0, "UInt")
        threadWnd := this.GetWindowThreadProcessId( hwnd )
        if not DllCall( "GetGUIThreadInfo", "uint", threadWnd, "UPtr", &GuiThreadInfo )
                return 0
        return NumGet( GuiThreadInfo, 8+A_PtrSize,"UPtr")  ; Retrieve the hwndFocus field from the struct.
    }
    GetPixelInfo( ByRef DIB ) {
        ;~ typedef struct tagBITMAPINFOHEADER {
        ;~ DWORD biSize;              0
        ;~ LONG  biWidth;             4
        ;~ LONG  biHeight;            8
        ;~ WORD  biPlanes;            12
        ;~ WORD  biBitCount;          14
        ;~ DWORD biCompression;       16
        ;~ DWORD biSizeImage;         20
        ;~ LONG  biXPelsPerMeter;     24
        ;~ LONG  biYPelsPerMeter;     28
        ;~ DWORD biClrUsed;           32
        ;~ DWORD biClrImportant;      36
        
        bmi := &DIB  ;BITMAPINFOHEADER  pointer from DIB
        biSize := numget( bmi+0, 0, "UInt" )
        ;~ return bmi + biSize
        biSizeImage := numget( bmi+0, 20, "UInt" )
        biBitCount := numget( bmi+0, 14, "UShort" )
        if ( biSizeImage == 0 )
        {
            biWidth := numget( bmi+0, 4, "UInt" )
            biHeight := numget( bmi+0, 8, "UInt" )
            biSizeImage := (((( biWidth * biBitCount + 31 ) & ~31 ) >> 3 ) * biHeight )
            numput( biSizeImage, bmi+0, 20, "UInt" )
        }
        p := numget( bmi+0, 32, "UInt" )  ;biClrUsed
        if ( p == 0 && biBitCount <= 8 )
            p := 1 << biBitCount
        p := p * 4 + biSize + bmi
        return p
    }
    Gdip_Startup() {
        if !DllCall( "GetModuleHandleW", "Wstr", "gdiplus", "UPtr" )
                    DllCall( "LoadLibraryW", "Wstr", "gdiplus", "UPtr" )
        
        VarSetCapacity(GdiplusStartupInput , 3*A_PtrSize, 0), NumPut(1,GdiplusStartupInput ,0,"UInt") ; GdiplusVersion = 1
        DllCall("gdiplus\GdiplusStartup", "Ptr*", pToken, "Ptr", &GdiplusStartupInput, "Ptr", 0)
        return pToken
    }
    Gdip_Shutdown(pToken) {
        DllCall("gdiplus\GdiplusShutdown", "Ptr", pToken)
        if hModule := DllCall( "GetModuleHandleW", "Wstr", "gdiplus", "UPtr" )
            DllCall("FreeLibrary", "Ptr", hModule)
        return 0
    }
    StrSplit(str,delim,omit := "") {
        if (strlen(delim) > 1)
        {
            ;StringReplace,str,str,% delim,ƒ,1      ;■¶╬
            str := StrReplace(str, delim, "ƒ")
            delim := "ƒ"
        }
        ra := Array()
        loop, parse,str,% delim,% omit
            if (A_LoopField != "")
                ra.Insert(A_LoopField)
        return ra
    }
    RemoveDubls( objArray ) {
        while True
        {
            nodubls := 1
            tempArr := Object()
            for i,val in objArray
            {
                if tempArr.haskey( val )
                {
                    nodubls := 0
                    objArray.Remove( i )
                    break
                }
                tempArr[ val ] := 1
            }
            if nodubls
                break
        }
        return objArray
    }
    RegisterClipboardFormat( fmtName ) {
        return DllCall( "RegisterClipboardFormat", "ptr", &fmtName )
    }
    GetOpenClipboardWindow() {
        return DllCall( "GetOpenClipboardWindow" )
    }
    IsClipboardFormatAvailable( iFmt ) {
        return DllCall( "IsClipboardFormatAvailable", "UInt", iFmt )
    }
    GetImageEncodersSize( ByRef numEncoders, ByRef size ) {
        return DllCall( "gdiplus\GdipGetImageEncodersSize", "Uint*", numEncoders, "UInt*", size )
    }
    GetImageEncoders( numEncoders, size, pImageCodecInfo ) {
        return DllCall( "gdiplus\GdipGetImageEncoders", "Uint", numEncoders, "UInt", size, "Ptr", pImageCodecInfo )
    }
    GetEncoderClsid( format, ByRef CLSID ) {
        ;format should be the following
        ;~ bmp
        ;~ jpeg
        ;~ gif
        ;~ tiff
        ;~ png
        if !format
            return 0
        format := "image/" format
        this.GetImageEncodersSize( num, size )
        if ( size = 0 )
            return 0
        VarSetCapacity( ImageCodecInfo, size, 0 )
        this.GetImageEncoders( num, size, &ImageCodecInfo )
        loop,% num
        {
            pici := &ImageCodecInfo + ( 48+7*A_PtrSize )*(A_Index-1)
            pMime := NumGet( pici+0, 32+4*A_PtrSize, "UPtr" )
            MimeType := StrGet( pMime, "UTF-16")
            if ( MimeType = format )
            {
                VarSetCapacity( CLSID, 16, 0 )
                this.memcopy( &CLSID, pici, 16 )
                return 1
            }
        }
        return 0
    }
}
WinClip.ahk (updated 9/26/2019 to incorporate suggested changes by autocart)

Code: Select all

class WinClip extends WinClip_base
{
    __New()
    {
        this.isinstance := 1
        this.allData := ""
    }
 
    _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
    }
    
    _fromclipboard( ByRef clipData )
    {
        if !WinClipAPI.OpenClipboard()
            return 0
        nextformat := 0
        objFormats := object()
        clipSize := 0
        formatsNum := 0
        while ( nextformat := WinClipAPI.EnumClipboardFormats( nextformat ) )
        {
            if this.skipFormats.hasKey( nextformat )
                continue
            if ( dataHandle := WinClipAPI.GetClipboardData( nextformat ) )
            {
                pObjPtr := 0, nObjSize := 0
                if ( nextformat == this.ClipboardFormats.CF_ENHMETAFILE )
                {
                    if ( bufSize := WinClipAPI.GetEnhMetaFileBits( dataHandle, hemfBuf ) )
                        pObjPtr := &hemfBuf, nObjSize := bufSize
                }
                else if ( nSize := WinClipAPI.GlobalSize( WinClipAPI.GlobalLock( dataHandle ) ) )
                    pObjPtr := dataHandle, nObjSize := nSize
                else
                    continue
                if !( pObjPtr && nObjSize )
                    continue
                objFormats[ nextformat ] := { handle : pObjPtr, size : nObjSize }
                clipSize += nObjSize
                formatsNum++
            }
        }
        structSize := formatsNum*( 4 + 4 ) + clipSize  ;allocating 4 bytes for format ID and 4 for data size
        if !structSize
        {
            WinClipAPI.CloseClipboard()
            return 0
        }
        VarSetCapacity( clipData, structSize, 0 )
        ; array in form of:
        ; format   UInt
        ; dataSize UInt
        ; data     Byte[]
        offset := 0
        for fmt, params in objFormats
        {
            NumPut( fmt, &clipData, offset, "UInt" )
            offset += 4
            NumPut( params.size, &clipData, offset, "UInt" )
            offset += 4
            WinClipAPI.memcopy( &clipData + offset, params.handle, params.size )
            offset += params.size
            WinClipAPI.GlobalUnlock( params.handle )
        }
        WinClipAPI.CloseClipboard()
        return structSize
    }
    
    _IsInstance( funcName )
    {
        if !this.isinstance
        {
            throw Exception( "Error in '" funcName "':`nInstantiate the object first to use this method!", -1 )
            return 0
        }
        return 1
    }
    
    _loadFile( filePath, ByRef Data )
    {
        f := FileOpen( filePath, "r","CP0" )
        if !IsObject( f )
            return 0
        f.Pos := 0
        dataSize := f.RawRead( Data, f.Length )
        f.close()
        return dataSize
    }

    _saveFile( filepath, byRef data, size )
    {
        f := FileOpen( filepath, "w","CP0" )
        bytes := f.RawWrite( &data, size )
        f.close()
        return bytes
    }

    _setClipData( ByRef data, size )
    {
        if !size
            return 0
        if !ObjSetCapacity( this, "allData", size )
            return 0
        if !( pData := ObjGetAddress( this, "allData" ) )
            return 0
        WinClipAPI.memcopy( pData, &data, size )
        return size
    }
    
    _getClipData( ByRef data )
    {
        if !( clipSize := ObjGetCapacity( this, "allData" ) )
            return 0
        if !( pData := ObjGetAddress( this, "allData" ) )
            return 0
        VarSetCapacity( data, clipSize, 0 )
        WinClipAPI.memcopy( &data, pData, clipSize )
        return clipSize
    }
    
    __Delete()
    {
        ObjSetCapacity( this, "allData", 0 )
        return
    }
    
    _parseClipboardData( ByRef data, size )
    {
        offset := 0
        formats := object()
        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
            params := { name : this._getFormatName( fmt ), size : dataSize }
            ObjSetCapacity( params, "buffer", dataSize )
            pBuf := ObjGetAddress( params, "buffer" )
            WinClipAPI.memcopy( pBuf, &data + offset, dataSize )
            formats[ fmt ] := params
            offset += dataSize
        }
        return formats
    }
    
    _compileClipData( ByRef out_data, objClip )
    {
        if !IsObject( objClip )
            return 0
        ;calculating required data size
        clipSize := 0
        for fmt, params in objClip
            clipSize += 8 + params.size
        VarSetCapacity( out_data, clipSize, 0 )
        offset := 0
        for fmt, params in objClip
        {
            NumPut( fmt, out_data, offset, "UInt" )
            offset += 4
            NumPut( params.size, out_data, offset, "UInt" )
            offset += 4
            WinClipAPI.memcopy( &out_data + offset, ObjGetAddress( params, "buffer" ), params.size )
            offset += params.size
        }
        return clipSize
    }
    
    GetFormats()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return 0
        return this._parseClipboardData( clipData, clipSize )
    }
    
    iGetFormats()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        return this._parseClipboardData( clipData, clipSize )
    }
    
    Snap( ByRef data )
    {
        return this._fromclipboard( data )
    }
    
    iSnap()
    {
        this._IsInstance( A_ThisFunc )
        if !( dataSize := this._fromclipboard( clipData ) )
            return 0
        return this._setClipData( clipData, dataSize )
    }

    Restore( ByRef clipData )
    {
        clipSize := VarSetCapacity( clipData )
        return this._toclipboard( clipData, clipSize )
    }

    iRestore()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    Save( filePath )
    {
        if !( size := this._fromclipboard( data ) )
            return 0
        return this._saveFile( filePath, data, size )
    }
    
    iSave( filePath )
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
                    return 0
        return this._saveFile( filePath, clipData, clipSize )
    }

    Load( filePath )
    {
        if !( dataSize := this._loadFile( filePath, dataBuf ) )
            return 0
        return this._toclipboard( dataBuf, dataSize )
    }

    iLoad( filePath )
    {
        this._IsInstance( A_ThisFunc )
        if !( dataSize := this._loadFile( filePath, dataBuf ) )
            return 0
        return this._setClipData( dataBuf, dataSize )
    }

    Clear()
    {
        if !WinClipAPI.OpenClipboard()
            return 0
        WinClipAPI.EmptyClipboard()
        WinClipAPI.CloseClipboard()
        return 1
    }
    
    iClear()
    {
        this._IsInstance( A_ThisFunc )
        ObjSetCapacity( this, "allData", 0 )
    }
    
    Copy( timeout := 1, method := 1 )
    {
        this.Snap( data )
        this.Clear()    ;clearing the clipboard
        if( method = 1 )
            SendInput, ^{Ins}
        else
            SendInput, ^{vk43sc02E} ;ctrl+c
        ClipWait,% timeout, 1
        if ( ret := this._isClipEmpty() )
            this.Restore( data )
        return !ret
    }
    
    iCopy( timeout := 1, method := 1 )
    {
        this._IsInstance( A_ThisFunc )
        this.Snap( data )
        this.Clear()    ;clearing the clipboard
        if( method = 1 )
            SendInput, ^{Ins}
        else
            SendInput, ^{vk43sc02E} ;ctrl+c
        ClipWait,% timeout, 1
        bytesCopied := 0
        if !this._isClipEmpty()
        {
            this.iClear()   ;clearing the variable containing the clipboard data
            bytesCopied := this.iSnap()
        }
        this.Restore( data )
        return bytesCopied
    }
    
    Paste( plainText := "", method := 1 )
    {
        ret := 0
        if ( plainText != "" )
        {
            this.Snap( data )
            this.Clear()
            ret := this.SetText( plainText )
        }
        if( method = 1 )
            SendInput, +{Ins}
        else
            SendInput, ^{vk56sc02F} ;ctrl+v
        this._waitClipReady( 3000 )
        if ( plainText != "" )
        {
            this.Restore( data )
        }
        else
            ret := !this._isClipEmpty()
        return ret
    }
    
    iPaste( method := 1 )
    {
        this._IsInstance( A_ThisFunc )
        this.Snap( data )
        if !( bytesRestored := this.iRestore() )
            return 0
        if( method = 1 )
            SendInput, +{Ins}
        else
            SendInput, ^{vk56sc02F} ;ctrl+v
        this._waitClipReady( 3000 )
        this.Restore( data )
        return bytesRestored
    }
    
    IsEmpty()
    {
        return this._isClipEmpty()
    }
    
    iIsEmpty()
    {
        return !this.iGetSize()
    }
    
    _isClipEmpty()
    {
        return !WinClipAPI.CountClipboardFormats()
    }
    
    _waitClipReady( timeout := 10000 )
    {
        start_time := A_TickCount
        sleep 100
        while ( WinClipAPI.GetOpenClipboardWindow() && ( A_TickCount - start_time < timeout ) )
            sleep 100
    }

    iSetText( textData )
    {
        if ( textData = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData, 1 ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    SetText( textData )
    {
        if ( textData = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData, 1 ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    GetRTF()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "Rich Text Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    iGetRTF()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "Rich Text Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    SetRTF( textData )
    {
        if ( textData = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._setRTF( clipData, clipSize, textData ) )
                    return 0
        return this._toclipboard( clipData, clipSize )
    }
    
    iSetRTF( textData )
    {
        if ( textData = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize :=  this._getClipData( clipData )
        if !( clipSize := this._setRTF( clipData, clipSize, textData ) )
                    return 0
        return this._setClipData( clipData, clipSize )
    }

    _setRTF( ByRef clipData, clipSize, textData )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := WinClipAPI.RegisterClipboardFormat( "Rich Text Format" )
        objFormats[ uFmt ] := object()
        sLen := StrLen( textData )
        ObjSetCapacity( objFormats[ uFmt ], "buffer", sLen )
        StrPut( textData, ObjGetAddress( objFormats[ uFmt ], "buffer" ), sLen, "CP0" )
        objFormats[ uFmt ].size := sLen
        return this._compileClipData( clipData, objFormats )
    }

    iAppendText( textData )
    {
        if ( textData = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    AppendText( textData )
    {
        if ( textData = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    SetHTML( html, source := "" )
    {
        if ( html = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._setHTML( clipData, clipSize, html, source ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    iSetHTML( html, source := "" )
    {
        if ( html = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._setHTML( clipData, clipSize, html, source ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }

    _calcHTMLLen( num )
    {
        while ( StrLen( num ) < 10 )
            num := "0" . num
        return num
    }

    _setHTML( ByRef clipData, clipSize, htmlData, source )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := WinClipAPI.RegisterClipboardFormat( "HTML Format" )
        objFormats[ uFmt ] := object()
        encoding := "UTF-8"
        htmlLen := StrPut( htmlData, encoding ) - 1   ;substract null
        srcLen := 2 + 10 + StrPut( source, encoding ) - 1      ;substract null
        StartHTML := this._calcHTMLLen( 105 + srcLen )
        EndHTML := this._calcHTMLLen( StartHTML + htmlLen + 76 )
        StartFragment := this._calcHTMLLen( StartHTML + 38 )
        EndFragment := this._calcHTMLLen( StartFragment + htmlLen )

        ;replace literal continuation section to make cross-compatible with v1 and v2
        html := "Version:0.9`r`n"
        html .= "StartHTML:" . StartHTML . "`r`n"
        html .= "EndHTML:" . EndHTML . "`r`n"
        html .= "StartFragment:" . StartFragment . "`r`n"
        html .= "EndFragment:" . EndFragment . "`r`n"
        html .= "SourceURL:" . source . "`r`n"
        html .= "<html>`r`n"
        html .= "<body>`r`n"
        html .= "<!--StartFragment-->`r`n"
        html .= htmlData . "`r`n"
        html .= "<!--EndFragment-->`r`n"
        html .= "</body>`r`n"
        html .= "</html>`r`n"

        sLen := StrPut( html, encoding )
        ObjSetCapacity( objFormats[ uFmt ], "buffer", sLen )
        StrPut( html, ObjGetAddress( objFormats[ uFmt ], "buffer" ), sLen, encoding )
        objFormats[ uFmt ].size := sLen
        return this._compileClipData( clipData, objFormats )
    }
    
    _appendText( ByRef clipData, clipSize, textData, IsSet := 0 )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := this.ClipboardFormats.CF_UNICODETEXT
        str := ""
        if ( objFormats.haskey( uFmt ) && !IsSet )
            str := strget( ObjGetAddress( objFormats[ uFmt ],  "buffer" ), "UTF-16" )
        else
            objFormats[ uFmt ] := object()
        str .= textData
        sLen := ( StrLen( str ) + 1 ) * 2
        ObjSetCapacity( objFormats[ uFmt ], "buffer", sLen )
        StrPut( str, ObjGetAddress( objFormats[ uFmt ], "buffer" ), sLen, "UTF-16" )
        objFormats[ uFmt ].size := sLen
        return this._compileClipData( clipData, objFormats )
    }
    
    _getFiles( pDROPFILES )
    {
        fWide := numget( pDROPFILES + 0, 16, "uchar" ) ;getting fWide value from DROPFILES struct
        pFiles := numget( pDROPFILES + 0, 0, "UInt" ) + pDROPFILES  ;getting address of files list
        list := ""
        while numget( pFiles + 0, 0, fWide ? "UShort" : "UChar" )
        {
            lastPath := strget( pFiles+0, fWide ? "UTF-16" : "CP0" )
            list .= ( list ? "`n" : "" ) lastPath
            pFiles += ( StrLen( lastPath ) + 1 ) * ( fWide ? 2 : 1 )
        }
        return list
    }
    
    _setFiles( ByRef clipData, clipSize, files, append := 0, isCut := 0 )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := this.ClipboardFormats.CF_HDROP
        if ( append && objFormats.haskey( uFmt ) )
            prevList := this._getFiles( ObjGetAddress( objFormats[ uFmt ], "buffer" ) ) "`n"
        objFiles := WinClipAPI.StrSplit( prevList . files, "`n", A_Space A_Tab )
        objFiles := WinClipAPI.RemoveDubls( objFiles )
        if !objFiles.MaxIndex()
            return 0
        objFormats[ uFmt ] := object()
        DROP_size := 20 + 2
        for i,str in objFiles
            DROP_size += ( StrLen( str ) + 1 ) * 2
        VarSetCapacity( DROPFILES, DROP_size, 0 )
        NumPut( 20, DROPFILES, 0, "UInt" )  ;offset
        NumPut( 1, DROPFILES, 16, "uchar" ) ;NumPut( 20, DROPFILES, 0, "UInt" )
        offset := &DROPFILES + 20
        for i,str in objFiles
        {
            StrPut( str, offset, "UTF-16" )
            offset += ( StrLen( str ) + 1 ) * 2
        }
        ObjSetCapacity( objFormats[ uFmt ], "buffer", DROP_size )
        WinClipAPI.memcopy( ObjGetAddress( objFormats[ uFmt ], "buffer" ), &DROPFILES, DROP_size )
        objFormats[ uFmt ].size := DROP_size
        prefFmt := WinClipAPI.RegisterClipboardFormat( "Preferred DropEffect" )
        objFormats[ prefFmt ] := { size : 4 }
        ObjSetCapacity( objFormats[ prefFmt ], "buffer", 4 )
        NumPut( isCut ? 2 : 5, ObjGetAddress( objFormats[ prefFmt ], "buffer" ), 0, "UInt" )
        return this._compileClipData( clipData, objFormats )
    }
    
    SetFiles( files, isCut := 0 )
    {
        if ( files = "" )
            return 0
        clipSize := this._fromclipboard( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 0, isCut ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }
    
    iSetFiles( files, isCut := 0 )
    {
        this._IsInstance( A_ThisFunc )
        if ( files = "" )
            return 0
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 0, isCut ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    AppendFiles( files, isCut := 0 )
    {
        if ( files = "" )
            return 0
        clipSize := this._fromclipboard( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 1, isCut ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }
    
    iAppendFiles( files, isCut := 0 )
    {
        this._IsInstance( A_ThisFunc )
        if ( files = "" )
            return 0
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 1, isCut ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    GetFiles()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_HDROP ) )
            return ""
        return this._getFiles( &out_data )
    }
    
    iGetFiles()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_HDROP ) )
            return ""
        return this._getFiles( &out_data )
    }
    
    _getFormatData( ByRef out_data, ByRef data, size, needleFormat )
    {
        needleFormat := (WinClipAPI.IsInteger( needleFormat ) ? needleFormat : WinClipAPI.RegisterClipboardFormat( needleFormat ))
        if !needleFormat
            return 0
        offset := 0
        while ( offset < size )
        {
            if !( fmt := NumGet( data, offset, "UInt" ) )
                break
            offset += 4
            if !( dataSize := NumGet( data, offset, "UInt" ) )
                break
            offset += 4
            if ( fmt == needleFormat )
            {
                VarSetCapacity( out_data, dataSize, 0 )
                WinClipAPI.memcopy( &out_data, &data + offset, dataSize )
                return dataSize
            }
            offset += dataSize
        }
        return 0
    }
    
    _DIBtoHBITMAP( ByRef dibData )
    {
        ;http://ebersys.blogspot.com/2009/06/how-to-convert-dib-to-bitmap.html
        pPix := WinClipAPI.GetPixelInfo( dibData )
        gdip_token := WinClipAPI.Gdip_Startup()
        DllCall("gdiplus\GdipCreateBitmapFromGdiDib", "Ptr", &dibData, "Ptr", pPix, "Ptr*", pBitmap )
        DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "Ptr*", hBitmap, "int", 0xffffffff )
        DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
        WinClipAPI.Gdip_Shutdown( gdip_token )
        return hBitmap
    }
    
    GetBitmap()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return ""
        return this._DIBtoHBITMAP( out_data )
    }
    
    iGetBitmap()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return ""
        return this._DIBtoHBITMAP( out_data )
    }
    
    _BITMAPtoDIB( bitmap, ByRef DIB )
    {
        if !bitmap
            return 0
        if !WinClipAPI.IsInteger( bitmap )
        {
            gdip_token := WinClipAPI.Gdip_Startup()
            DllCall("gdiplus\GdipCreateBitmapFromFileICM", "wstr", bitmap, "Ptr*", pBitmap )
            DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "Ptr*", hBitmap, "int", 0xffffffff )
            DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
            WinClipAPI.Gdip_Shutdown( gdip_token )
            bmMade := 1
        }
        else
            hBitmap := bitmap, bmMade := 0
        if !hBitmap
                return 0
        ;http://www.codeguru.com/Cpp/G-M/bitmap/article.php/c1765
        if !( hdc := DllCall( "GetDC", "Ptr", 0 ) )
            goto, _BITMAPtoDIB_cleanup
        hPal := DllCall( "GetStockObject", "UInt", 15 ) ;DEFAULT_PALLETE
        hPal := DllCall( "SelectPalette", "ptr", hdc, "ptr", hPal, "Uint", 0 )
        DllCall( "RealizePalette", "ptr", hdc )
        size := DllCall( "GetObject", "Ptr", hBitmap, "Uint", 0, "ptr", 0 )
        VarSetCapacity( bm, size, 0 )
        DllCall( "GetObject", "Ptr", hBitmap, "Uint", size, "ptr", &bm )
        biBitCount := NumGet( bm, 16, "UShort" )*NumGet( bm, 18, "UShort" )
        nColors := (1 << biBitCount)
    if ( nColors > 256 ) 
        nColors := 0
    bmiLen  := 40 + nColors * 4
        VarSetCapacity( bmi, bmiLen, 0 )
        ;BITMAPINFOHEADER initialization
        NumPut( 40, bmi, 0, "Uint" )
        NumPut( NumGet( bm, 4, "Uint" ), bmi, 4, "Uint" )   ;width
        NumPut( biHeight := NumGet( bm, 8, "Uint" ), bmi, 8, "Uint" ) ;height
        NumPut( 1, bmi, 12, "UShort" )
        NumPut( biBitCount, bmi, 14, "UShort" )
        NumPut( 0, bmi, 16, "UInt" ) ;compression must be BI_RGB

        ; Get BITMAPINFO. 
        if !DllCall("GetDIBits"
                            ,"ptr",hdc
                            ,"ptr",hBitmap
                            ,"uint",0 
                            ,"uint",biHeight
                            ,"ptr",0      ;lpvBits 
                            ,"ptr",&bmi  ;lpbi 
                            ,"uint",0)    ;DIB_RGB_COLORS
            goto, _BITMAPtoDIB_cleanup
        biSizeImage := NumGet( &bmi, 20, "UInt" )
        if ( biSizeImage = 0 )
        {
            biBitCount := numget( &bmi, 14, "UShort" )
            biWidth := numget( &bmi, 4, "UInt" )
            biHeight := numget( &bmi, 8, "UInt" )
            biSizeImage := (((( biWidth * biBitCount + 31 ) & ~31 ) >> 3 ) * biHeight )
            ;~ dwCompression := numget( bmi, 16, "UInt" )
            ;~ if ( dwCompression != 0 ) ;BI_RGB
                ;~ biSizeImage := ( biSizeImage * 3 ) / 2
            numput( biSizeImage, &bmi, 20, "UInt" )
        }
        DIBLen := bmiLen + biSizeImage
        VarSetCapacity( DIB, DIBLen, 0 )
        WinClipAPI.memcopy( &DIB, &bmi, bmiLen )
        if !DllCall("GetDIBits"
                            ,"ptr",hdc
                            ,"ptr",hBitmap
                            ,"uint",0 
                            ,"uint",biHeight
                            ,"ptr",&DIB + bmiLen     ;lpvBits 
                            ,"ptr",&DIB  ;lpbi 
                            ,"uint",0)    ;DIB_RGB_COLORS
            goto, _BITMAPtoDIB_cleanup
_BITMAPtoDIB_cleanup:
        if bmMade
            DllCall( "DeleteObject", "ptr", hBitmap )
        DllCall( "SelectPalette", "ptr", hdc, "ptr", hPal, "Uint", 0 )
        DllCall( "RealizePalette", "ptr", hdc )
        DllCall("ReleaseDC","ptr",hdc)
        if ( A_ThisLabel = "_BITMAPtoDIB_cleanup" )
            return 0
        return DIBLen
    }
    
    _setBitmap( ByRef DIB, DIBSize, ByRef clipData, clipSize )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := this.ClipboardFormats.CF_DIB
        objFormats[ uFmt ] := { size : DIBSize }
        ObjSetCapacity( objFormats[ uFmt ], "buffer", DIBSize )
        WinClipAPI.memcopy( ObjGetAddress( objFormats[ uFmt ], "buffer" ), &DIB, DIBSize )
        return this._compileClipData( clipData, objFormats )
    }
    
    SetBitmap( bitmap )
    {
        if ( DIBSize := this._BITMAPtoDIB( bitmap, DIB ) )
        {
            clipSize := this._fromclipboard( clipData )
            if ( clipSize := this._setBitmap( DIB, DIBSize, clipData, clipSize ) )
                return this._toclipboard( clipData, clipSize )
        }
        return 0
    }
    
    iSetBitmap( bitmap )
    {
        this._IsInstance( A_ThisFunc )
        if ( DIBSize := this._BITMAPtoDIB( bitmap, DIB ) )
        {
            clipSize := this._getClipData( clipData )
            if ( clipSize := this._setBitmap( DIB, DIBSize, clipData, clipSize ) )
                return this._setClipData( clipData, clipSize )
        }
        return 0
    }
    
    GetText()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_UNICODETEXT ) )
            return ""
        return strget( &out_data, out_size / 2, "UTF-16" )
    }

    iGetText()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_UNICODETEXT ) )
            return ""
        return strget( &out_data, out_size / 2, "UTF-16" )
    }
    
    GetHtml()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "HTML Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    iGetHtml()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "HTML Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    _getFormatName( iformat )
    {
        if this.formatByValue.HasKey( iformat )
            return this.formatByValue[ iformat ]
        else
            return WinClipAPI.GetClipboardFormatName( iformat )
    }
    
    iGetData( ByRef Data )
    {
        this._IsInstance( A_ThisFunc )
        return this._getClipData( Data )
    }
    
    iSetData( ByRef data )
    {
        this._IsInstance( A_ThisFunc )
        return this._setClipData( data, VarSetCapacity( data ) )
    }
    
    iGetSize()
    {
        this._IsInstance( A_ThisFunc )
        return ObjGetCapacity( this, "alldata" )
    }
    
    HasFormat( fmt )
    {
        if !fmt
            return 0
        return WinClipAPI.IsClipboardFormatAvailable( WinClipAPI.IsInteger( fmt ) ? fmt : WinClipAPI.RegisterClipboardFormat( fmt )  )
    }
    
    iHasFormat( fmt )
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        return this._hasFormat( clipData, clipSize, fmt )
    }

    _hasFormat( ByRef data, size, needleFormat )
    {
        needleFormat := WinClipAPI.IsInteger( needleFormat ) ? needleFormat : WinClipAPI.RegisterClipboardFormat( needleFormat )
        if !needleFormat
            return 0
        offset := 0
        while ( offset < size )
        {
            if !( fmt := NumGet( data, offset, "UInt" ) )
                break
            if ( fmt == needleFormat )
                return 1
            offset += 4
            if !( dataSize := NumGet( data, offset, "UInt" ) )
                break
            offset += 4 + dataSize
        }
        return 0
    }
    
    iSaveBitmap( filePath, format )
    {
        this._IsInstance( A_ThisFunc )
        if ( filePath = "" || format = "" )
            return 0
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        if !( DIBsize := this._getFormatData( DIB, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return 0
        gdip_token := WinClipAPI.Gdip_Startup()
        if !WinClipAPI.GetEncoderClsid( format, CLSID )
            return 0
        DllCall("gdiplus\GdipCreateBitmapFromGdiDib", "Ptr", &DIB, "Ptr", WinClipAPI.GetPixelInfo( DIB ), "Ptr*", pBitmap )
        DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "wstr", filePath, "Ptr", &CLSID, "Ptr", 0 )
        DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
        WinClipAPI.Gdip_Shutdown( gdip_token )
        return 1
    }
    
    SaveBitmap( filePath, format )
    {
        if ( filePath = "" || format = "" )
            return 0
        if !( clipSize := this._fromclipboard( clipData ) )
            return 0
        if !( DIBsize := this._getFormatData( DIB, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return 0
        gdip_token := WinClipAPI.Gdip_Startup()
        if !WinClipAPI.GetEncoderClsid( format, CLSID )
            return 0
        DllCall("gdiplus\GdipCreateBitmapFromGdiDib", "Ptr", &DIB, "Ptr", WinClipAPI.GetPixelInfo( DIB ), "Ptr*", pBitmap )
        DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "wstr", filePath, "Ptr", &CLSID, "Ptr", 0 )
        DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
        WinClipAPI.Gdip_Shutdown( gdip_token )
        return 1
    }
    
    static ClipboardFormats := { CF_BITMAP : 2 ;A handle to a bitmap (HBITMAP).
                                ,CF_DIB : 8  ;A memory object containing a BITMAPINFO structure followed by the bitmap bits.
                                ,CF_DIBV5 : 17 ;A memory object containing a BITMAPV5HEADER structure followed by the bitmap color space information and the bitmap bits.
                                ,CF_DIF : 5 ;Software Arts' Data Interchange Format.
                                ,CF_DSPBITMAP : 0x0082 ;Bitmap display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in bitmap format in lieu of the privately formatted data.
                                ,CF_DSPENHMETAFILE : 0x008E ;Enhanced metafile display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in enhanced metafile format in lieu of the privately formatted data.
                                ,CF_DSPMETAFILEPICT : 0x0083 ;Metafile-picture display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in metafile-picture format in lieu of the privately formatted data.
                                ,CF_DSPTEXT : 0x0081 ;Text display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in text format in lieu of the privately formatted data.
                                ,CF_ENHMETAFILE : 14 ;A handle to an enhanced metafile (HENHMETAFILE).
                                ,CF_GDIOBJFIRST : 0x0300 ;Start of a range of integer values for application-defined GDI object clipboard formats. The end of the range is CF_GDIOBJLAST.Handles associated with clipboard formats in this range are not automatically deleted using the GlobalFree function when the clipboard is emptied. Also, when using values in this range, the hMem parameter is not a handle to a GDI object, but is a handle allocated by the GlobalAlloc function with the GMEM_MOVEABLE flag.
                                ,CF_GDIOBJLAST : 0x03FF ;See CF_GDIOBJFIRST.
                                ,CF_HDROP : 15 ;A handle to type HDROP that identifies a list of files. An application can retrieve information about the files by passing the handle to the DragQueryFile function.
                                ,CF_LOCALE : 16 ;The data is a handle to the locale identifier associated with text in the clipboard. When you close the clipboard, if it contains CF_TEXT data but no CF_LOCALE data, the system automatically sets the CF_LOCALE format to the current input language. You can use the CF_LOCALE format to associate a different locale with the clipboard text. An application that pastes text from the clipboard can retrieve this format to determine which character set was used to generate the text. Note that the clipboard does not support plain text in multiple character sets. To achieve this, use a formatted text data type such as RTF instead. The system uses the code page associated with CF_LOCALE to implicitly convert from CF_TEXT to CF_UNICODETEXT. Therefore, the correct code page table is used for the conversion.
                                ,CF_METAFILEPICT : 3 ;Handle to a metafile picture format as defined by the METAFILEPICT structure. When passing a CF_METAFILEPICT handle by means of DDE, the application responsible for deleting hMem should also free the metafile referred to by the CF_METAFILEPICT handle.
                                ,CF_OEMTEXT : 7 ;Text format containing characters in the OEM character set. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data.
                                ,CF_OWNERDISPLAY : 0x0080 ;Owner-display format. The clipboard owner must display and update the clipboard viewer window, and receive the WM_ASKCBFORMATNAME, WM_HSCROLLCLIPBOARD, WM_PAINTCLIPBOARD, WM_SIZECLIPBOARD, and WM_VSCROLLCLIPBOARD messages. The hMem parameter must be NULL.
                                ,CF_PALETTE : 9 ;Handle to a color palette. Whenever an application places data in the clipboard that depends on or assumes a color palette, it should place the palette on the clipboard as well.If the clipboard contains data in the CF_PALETTE (logical color palette) format, the application should use the SelectPalette and RealizePalette functions to realize (compare) any other data in the clipboard against that logical palette.When displaying clipboard data, the clipboard always uses as its current palette any object on the clipboard that is in the CF_PALETTE format.
                                ,CF_PENDATA : 10 ;Data for the pen extensions to the Microsoft Windows for Pen Computing.
                                ,CF_PRIVATEFIRST : 0x0200 ;Start of a range of integer values for private clipboard formats. The range ends with CF_PRIVATELAST. Handles associated with private clipboard formats are not freed automatically; the clipboard owner must free such handles, typically in response to the WM_DESTROYCLIPBOARD message.
                                ,CF_PRIVATELAST : 0x02FF ;See CF_PRIVATEFIRST.
                                ,CF_RIFF : 11 ;Represents audio data more complex than can be represented in a CF_WAVE standard wave format.
                                ,CF_SYLK : 4 ;Microsoft Symbolic Link (SYLK) format.
                                ,CF_TEXT : 1 ;Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data. Use this format for ANSI text.
                                ,CF_TIFF : 6 ;Tagged-image file format.
                                ,CF_UNICODETEXT : 13 ;Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data.
                                ,CF_WAVE : 12 } ;Represents audio data in one of the standard wave formats, such as 11 kHz or 22 kHz PCM.
    
    static                       WM_COPY := 0x301
                                ,WM_CLEAR := 0x0303
                                ,WM_CUT := 0x0300
                                ,WM_PASTE := 0x0302
    
    static skipFormats := {      2      : 0 ;"CF_BITMAP"
                                ,17     : 0 ;"CF_DIBV5"
                                ,0x0082 : 0 ;"CF_DSPBITMAP"
                                ,0x008E : 0 ;"CF_DSPENHMETAFILE"
                                ,0x0083 : 0 ;"CF_DSPMETAFILEPICT"
                                ,0x0081 : 0 ;"CF_DSPTEXT"
                                ,0x0080 : 0 ;"CF_OWNERDISPLAY"
                                ,3      : 0 ;"CF_METAFILEPICT"
                                ,7      : 0 ;"CF_OEMTEXT"
                                ,1      : 0 } ;"CF_TEXT"
                                                            
    static formatByValue := {    2 : "CF_BITMAP"
                                ,8 : "CF_DIB"
                                ,17 : "CF_DIBV5"
                                ,5 : "CF_DIF"
                                ,0x0082 : "CF_DSPBITMAP"
                                ,0x008E : "CF_DSPENHMETAFILE"
                                ,0x0083 : "CF_DSPMETAFILEPICT"
                                ,0x0081 : "CF_DSPTEXT"
                                ,14 : "CF_ENHMETAFILE"
                                ,0x0300 : "CF_GDIOBJFIRST"
                                ,0x03FF : "CF_GDIOBJLAST"
                                ,15 : "CF_HDROP"
                                ,16 : "CF_LOCALE"
                                ,3 : "CF_METAFILEPICT"
                                ,7 : "CF_OEMTEXT"
                                ,0x0080 : "CF_OWNERDISPLAY"
                                ,9 : "CF_PALETTE"
                                ,10 : "CF_PENDATA"
                                ,0x0200 : "CF_PRIVATEFIRST"
                                ,0x02FF : "CF_PRIVATELAST"
                                ,11 : "CF_RIFF"
                                ,4 : "CF_SYLK"
                                ,1 : "CF_TEXT"
                                ,6 : "CF_TIFF"
                                ,13 : "CF_UNICODETEXT"
                                ,12 : "CF_WAVE" }
}
test.ahk (demo script)

Code: Select all

;#NoEnv  ; removed for AHKv2 script compatibility
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

#Include WinClipAPI.ahk
#Include WinClip.ahk
wc := new WinClip


msgbox copy some text on clipboard
msgbox % text := WinClip.GetText()


msgbox copy some SMALL text piece from browser
msgbox % html := WinClip.GetHTML()


msgbox copy some picture
hBitmap := WinClip.GetBitmap()
Gui, Add, Picture,% "hwndPicHwnd +" SS_BITMAP := 0xE
SendMessage,% STM_SETIMAGE := 0x0172,% IMAGE_BITMAP := 0,% hBitmap,, ahk_id %PicHwnd%
DllCall("DeleteObject", "Ptr", hBitmap )
Gui, Show, w1000 h700


msgbox copy few files from explorer window
msgbox % fileslist := WinClip.GetFiles()


inputbox, textData, Input Something,Enter text you want to put on clipboard
if textData
{
  WinClip.Clear()
  WinClip.SetText( textData )
}
msgbox % text := WinClip.GetText()


inputbox, textData, Input Something,Enter text you want to APPEND to the one currently on clipboard
if textData
  WinClip.AppendText( textData )
msgbox % text := WinClip.GetText()


inputbox, picPath, Input path to Picture,Enter path to the picture you want to place on clipboard ( without quotes )
if picPath
{
  WinClip.Clear()
  WinClip.SetBitmap( picPath )
  msgbox the picture should be on clipboard now
}


msgbox copy some data to clipboard
msgbox % "All clipboard data has been saved to clip.txt`nSize: " WinClip.Save( "clip.txt" )


WinClip.Clear()
msgbox clipboard should now be empty


bytes := WinClip.Load( "clip.txt" )
msgbox % bytes " bytes loaded from 'clip.txt' file to clipboard"


inputbox, html, Html to clipboard,Enter some text you want to place on clipboard as HTML`nfor example:`n<a href="www.hello.com">link</a>,,300,200
if html
{
  WinClip.Clear()
  WinClip.SetHTML( html )
  msgbox html data should be on clipboard
}

; see docs for other examples here:
; http://www.autohotkey.net/~Deo/index.html
Last edited by egocarib on 26 Sep 2019, 11:22, edited 1 time in total.
skrommel
Posts: 17
Joined: 15 Sep 2014, 06:15

Re: WinClip - AHKv2 Compatibility

18 May 2019, 18:11

How can I show Enhanced Meta Files? :?:

I've tried for hours now to display a copied EMF file in a GUI.
It should be possible using

Code: Select all

PlayEnhMetaFile
but no success.
https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-playenhmetafile


Skrommel
burque505
Posts: 1732
Joined: 22 Jan 2017, 19:37

Re: WinClip - AHKv2 Compatibility

19 May 2019, 18:44

@skrommel, I've been struggling with this issue for some time now myself. I don't know about V2 yet, but maybe this will help for V1. By the way, thank you very much for all the great scripts you've shared over the years.

If you've already got the EMF file on the clipboard, try what's in the Zip archive.
I used TpX to get the included EMF (Month_Calendar.emf) onto the clipboard as EMF only (I think :)).
The GIF below shows it being pasted into my version of @just me's Class_RichEdit sample. I think everything but TpX, which you probably don't need, is in the Zip file.
Attachments
for skrommel.zip
(54.86 KiB) Downloaded 249 times
for skrommel 2.gif
for skrommel 2.gif (114.82 KiB) Viewed 10312 times
burque505
Posts: 1732
Joined: 22 Jan 2017, 19:37

Re: WinClip - AHKv2 Compatibility

19 May 2019, 18:47

P.S. Here's another RichEdit GUI that may be quite a bit easier to adapt for your GUI purposes (I hope, anyway). In the archive should be RE4.0.ahk and RichEdit OleCallback.ahk. Other than an EMF on the clipboard, that might be all you need. Same EMF pasted here.
Attachments
skrommel 3.PNG
skrommel 3.PNG (62.15 KiB) Viewed 10312 times
EMF.zip
(34.74 KiB) Downloaded 267 times
skrommel
Posts: 17
Joined: 15 Sep 2014, 06:15

Re: WinClip - AHKv2 Compatibility

20 May 2019, 05:04

Thank you burque505, for your zip file and all! :)

I've been using the RichEdit-class by just me, but different programs present different RTF files.
But the one you use, RichEdit OleCallback by DigiDon and just me, works!
All I had to do to was add RE_SetOleCallback(hRichEdit.HWND) to my script! Thank you! :thumbup:

And the solution was there in the forum all this time... :facepalm:
https://www.autohotkey.com/boards/viewtopic.php?f=5&t=43427
burque505
Posts: 1732
Joined: 22 Jan 2017, 19:37

Re: WinClip - AHKv2 Compatibility

20 May 2019, 07:45

Glad to hear it worked, skrommel. Also, thanks for the attributions to DigiDon and just me for RichEdit OleCallback, which I should have included in the body of my post.
Regards,
burque505
skrommel
Posts: 17
Joined: 15 Sep 2014, 06:15

Re: WinClip - AHKv2 Compatibility

21 May 2019, 18:36

:?: Is it just my computer, or does text:=wc.iGetText(data) crash when copying a large chunk of text?
The text:=WinGet.GetText(data) crashes in the same manner.

The culprit is StrGet, I'm having to read smaller chunks to make ut work properly:

Code: Select all

  
  ;;;; text:=wc.iGetText(data) replacement ;;;;
  clipSize := wc._getClipData( clipData )
  out_size := wc._getFormatData( out_data, clipData, clipSize, wc.ClipboardFormats.CF_UNICODETEXT ) 
  text:=""
  size:=1000
  If (size>out_size)
    size:=out_size
  Loop,% Floor(out_size/size)
    text:=text . StrGet( &out_data+(A_Index-1)*size, size , "UTF-16" )


Skrommel
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

30 May 2019, 17:29

Hi, I could be wrong, but shouldn't there be a comma (see below - marked big and bold) in the 3rd to the last line of the _setFiles function?
(If I am right, it was already missing in the original.)

_setFiles( ByRef clipData, clipSize, files, append := 0, isCut := 0 )
{
[...code...]
ObjSetCapacity( objFormats[ prefFmt ], "buffer", 4 )
NumPut( isCut ? 2 : 5, ObjGetAddress( objFormats[ prefFmt ], "buffer" ), 0, "UInt" )
return this._compileClipData( clipData, objFormats )
}
Last edited by autocart on 06 Jun 2019, 18:44, edited 2 times in total.
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

31 May 2019, 17:35

Hi all,

I found another problem, of which I am sure this time that it is a bug in the code that needs to be fixed.

The big and bold part must be inserted, otherwise it leads to problems whenever the _fromclipboard method is applied to an empty clipboard, which is the case e.g. when calling WinClip.Snap(data) on an emptly clipboard, but also other methods are affected by _fromclipboard:

_fromclipboard( ByRef clipData )
{
if !WinClipAPI.OpenClipboard()
return 0
[...code...]
structSize := formatsNum*( 4 + 4 ) + clipSize ;allocating 4 bytes for format ID and 4 for data size
if !structSize
{
WinClipAPI.CloseClipboard()

return 0
}
VarSetCapacity( clipData, structSize, 0 )
[...code...]
WinClipAPI.CloseClipboard()
return structSize
}
skrommel
Posts: 17
Joined: 15 Sep 2014, 06:15

Re: WinClip - AHKv2 Compatibility

05 Jun 2019, 08:54

I can confirm the missing CloseClipboard-error, I've been putting the command everywhere in my code to keep it from crashing. :)
I'm impressed you found the origion of the problem, autocart!

Skrommel
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

06 Jun 2019, 18:40

skrommel wrote:
05 Jun 2019, 08:54
...
I'm impressed you found the origion of the problem, autocart!

Skrommel
Thank you very much, Skrommel. :angel:
It took me a few hours and some stressed out sweat, but it is very satisfying once you find such a bug. :D
Regards, autocart
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

20 Jun 2019, 04:26

skrommel wrote:
21 May 2019, 18:36
:?: Is it just my computer, or does text:=wc.iGetText(data) crash when copying a large chunk of text?
The text:=WinGet.GetText(data) crashes in the same manner.

The culprit is StrGet, I'm having to read smaller chunks to make ut work properly:

Code: Select all

  
  ;;;; text:=wc.iGetText(data) replacement ;;;;
  clipSize := wc._getClipData( clipData )
  out_size := wc._getFormatData( out_data, clipData, clipSize, wc.ClipboardFormats.CF_UNICODETEXT ) 
  text:=""
  size:=1000
  If (size>out_size)
    size:=out_size
  Loop,% Floor(out_size/size)
    text:=text . StrGet( &out_data+(A_Index-1)*size, size , "UTF-16" )


Skrommel
Hi Skrommel,
I have a question about this code of yours: Let's say, if out_size was 1500, then the loop would be executed 1 time and the remaining 500 (Bytes, I assume?) are not read at all. Am I correct or do I overlook something?

BTW, what size are "large" chunks of text in this context?
Last edited by autocart on 20 Jun 2019, 13:31, edited 1 time in total.
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

20 Jun 2019, 10:31

autocart wrote:
20 Jun 2019, 04:26
Let's say, if out_size was 1500, then the loop would be executed 1 time and the remaining 500 (Bytes, I assume?) are not read at all.
Never mind, I found the answer to this question as well as your original question:

The answer to your original question, Skrommel, which was:
skrommel wrote:
21 May 2019, 18:36
Is it just my computer, or does text:=wc.iGetText(data) crash when copying a large chunk of text?"
is: No, it is also other computeres as well. The original WinClip-code is wrong. The syntax of StrGet is StrGet(Address , Length , Encoding := None) (details found here: https://www.autohotkey.com/docs/commands/StrPutGet.htm), where Length is number of characters, however, the original coder is handing over a variable with number of bytes. If now, as in this case, "UTF-16" is used, then number of characters = number of bytes/2 (because in UTF-16 one char needs 2 bytes). Often, this does not matter, because the help says, that Length is the maximum number of characters. So, if it is (a little) more than needed, the function is not bothered. Obviously, though, there is an undocumented limit of how much too much is really too much more, therefore, if coded in this "dirty" way, it crashes with huge strings (when Length then results in a number which is "twice huge"). So, instead of strget( &out_data, out_size, "UTF-16" ) the correct code snippet inside GetText() and iGetText() would be:

Code: Select all

strget( &out_data, out_size / 2, "UTF-16" )
Then it also works with huge strings and does not crash, as far as I (quickly) tested.
BTW, iGetText() does not need a parameter (since you had written iGetText(data)).

NOTE: I noticed that the original coder is using StrGet and StrPut with "UTF-16" in a few more locations as well, besides GetText() and iGetText(). If I am not too tired I will look at it and might repost the original code with all of my corrections (incl. the other posts) worked in.


Regarding my question about your code, Skrommel; sorry, but your code is buggy:

(1) It is true, that the loop theorectically runs one time too few. The output with short strings being < 2000 bytes or < 1000 characters seems to work, because if they are < 2000 bytes, then the loop runs only once because of the wrong calculation in the loop header but still reads twice as many bytes than chars being defined (because of the "UTF-16"), meaning it still reads all of these up to 1998 bytes (or 999 chars).

(2) I gets really buggy, though, if the string is at least 2000 bytes or 1000 chars long. Then your code will first read 1000 chars, then the loop will run at least once more and then read the string again starting at character 501 and reading again 1000 characters, and so on. So, if the string is long enough, it will first read chars 1-1000, then 501-1500, then 1001-2000 and so on and concatenate them all together. Thus, a lot of text is repeated in the output and that in a confusing way. But, of course with so many chars, it might not be visible at first glance. So, you might want to recheck the output which you already created with this code. (I am only writing it bold and big and red, so you don't overlook it, since it might be important. No harm meant.) :wave:

Regards, S.
Last edited by autocart on 20 Jun 2019, 13:31, edited 1 time in total.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: WinClip - AHKv2 Compatibility

20 Jun 2019, 12:57

Btw a recent change to AHK v2:
AutoHotkey v2 alpha (UPDATES) - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=2120&p=276644#p276644
Changed StrPut to return byte count rather than character count.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

20 Jun 2019, 14:51

[EDIT:] Thx for the update, jeeswg! [EDIT OFF]
autocart wrote:
20 Jun 2019, 10:31
NOTE: I noticed that the original coder is using StrGet and StrPut with "UTF-16" in a few more locations as well, besides GetText() and iGetText(). If I am not too tired I will look at it and might repost the original code with all of my corrections (incl. the other posts) worked in.
Ok, I checked the other occurances and did not find (according to my skills) any other StrPut/StrGet problems.

Here is the WinClip.ahk code with the mentioned corrections:
(1) { WinClipAPI.CloseClipboard() ... } in _fromclipboard( ByRef clipData )
(2) The comma in _setFiles( ByRef clipData, clipSize, files, append := 0, isCut := 0 )
(3) strget( &out_data, out_size / 2, "UTF-16" ) in GetText() and iGetText()

I did not change the WinClipAPI.ahk and test.ahk (demo script), which are still there the first post.

Code: Select all

class WinClip extends WinClip_base
{
    __New()
    {
        this.isinstance := 1
        this.allData := ""
    }
 
    _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
    }
    
    _fromclipboard( ByRef clipData )
    {
        if !WinClipAPI.OpenClipboard()
            return 0
        nextformat := 0
        objFormats := object()
        clipSize := 0
        formatsNum := 0
        while ( nextformat := WinClipAPI.EnumClipboardFormats( nextformat ) )
        {
            if this.skipFormats.hasKey( nextformat )
                continue
            if ( dataHandle := WinClipAPI.GetClipboardData( nextformat ) )
            {
                pObjPtr := 0, nObjSize := 0
                if ( nextformat == this.ClipboardFormats.CF_ENHMETAFILE )
                {
                    if ( bufSize := WinClipAPI.GetEnhMetaFileBits( dataHandle, hemfBuf ) )
                        pObjPtr := &hemfBuf, nObjSize := bufSize
                }
                else if ( nSize := WinClipAPI.GlobalSize( WinClipAPI.GlobalLock( dataHandle ) ) )
                    pObjPtr := dataHandle, nObjSize := nSize
                else
                    continue
                if !( pObjPtr && nObjSize )
                    continue
                objFormats[ nextformat ] := { handle : pObjPtr, size : nObjSize }
                clipSize += nObjSize
                formatsNum++
            }
        }
        structSize := formatsNum*( 4 + 4 ) + clipSize  ;allocating 4 bytes for format ID and 4 for data size
        if !structSize
        {
            WinClipAPI.CloseClipboard()
            return 0
        }
        VarSetCapacity( clipData, structSize, 0 )
        ; array in form of:
        ; format   UInt
        ; dataSize UInt
        ; data     Byte[]
        offset := 0
        for fmt, params in objFormats
        {
            NumPut( fmt, &clipData, offset, "UInt" )
            offset += 4
            NumPut( params.size, &clipData, offset, "UInt" )
            offset += 4
            WinClipAPI.memcopy( &clipData + offset, params.handle, params.size )
            offset += params.size
            WinClipAPI.GlobalUnlock( params.handle )
        }
        WinClipAPI.CloseClipboard()
        return structSize
    }
    
    _IsInstance( funcName )
    {
        if !this.isinstance
        {
            throw Exception( "Error in '" funcName "':`nInstantiate the object first to use this method!", -1 )
            return 0
        }
        return 1
    }
    
    _loadFile( filePath, ByRef Data )
    {
        f := FileOpen( filePath, "r","CP0" )
        if !IsObject( f )
            return 0
        f.Pos := 0
        dataSize := f.RawRead( Data, f.Length )
        f.close()
        return dataSize
    }

    _saveFile( filepath, byRef data, size )
    {
        f := FileOpen( filepath, "w","CP0" )
        bytes := f.RawWrite( &data, size )
        f.close()
        return bytes
    }

    _setClipData( ByRef data, size )
    {
        if !size
            return 0
        if !ObjSetCapacity( this, "allData", size )
            return 0
        if !( pData := ObjGetAddress( this, "allData" ) )
            return 0
        WinClipAPI.memcopy( pData, &data, size )
        return size
    }
    
    _getClipData( ByRef data )
    {
        if !( clipSize := ObjGetCapacity( this, "allData" ) )
            return 0
        if !( pData := ObjGetAddress( this, "allData" ) )
            return 0
        VarSetCapacity( data, clipSize, 0 )
        WinClipAPI.memcopy( &data, pData, clipSize )
        return clipSize
    }
    
    __Delete()
    {
        ObjSetCapacity( this, "allData", 0 )
        return
    }
    
    _parseClipboardData( ByRef data, size )
    {
        offset := 0
        formats := object()
        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
            params := { name : this._getFormatName( fmt ), size : dataSize }
            ObjSetCapacity( params, "buffer", dataSize )
            pBuf := ObjGetAddress( params, "buffer" )
            WinClipAPI.memcopy( pBuf, &data + offset, dataSize )
            formats[ fmt ] := params
            offset += dataSize
        }
        return formats
    }
    
    _compileClipData( ByRef out_data, objClip )
    {
        if !IsObject( objClip )
            return 0
        ;calculating required data size
        clipSize := 0
        for fmt, params in objClip
            clipSize += 8 + params.size
        VarSetCapacity( out_data, clipSize, 0 )
        offset := 0
        for fmt, params in objClip
        {
            NumPut( fmt, out_data, offset, "UInt" )
            offset += 4
            NumPut( params.size, out_data, offset, "UInt" )
            offset += 4
            WinClipAPI.memcopy( &out_data + offset, ObjGetAddress( params, "buffer" ), params.size )
            offset += params.size
        }
        return clipSize
    }
    
    GetFormats()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return 0
        return this._parseClipboardData( clipData, clipSize )
    }
    
    iGetFormats()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        return this._parseClipboardData( clipData, clipSize )
    }
    
    Snap( ByRef data )
    {
        return this._fromclipboard( data )
    }
    
    iSnap()
    {
        this._IsInstance( A_ThisFunc )
        if !( dataSize := this._fromclipboard( clipData ) )
            return 0
        return this._setClipData( clipData, dataSize )
    }

    Restore( ByRef clipData )
    {
        clipSize := VarSetCapacity( clipData )
        return this._toclipboard( clipData, clipSize )
    }

    iRestore()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    Save( filePath )
    {
        if !( size := this._fromclipboard( data ) )
            return 0
        return this._saveFile( filePath, data, size )
    }
    
    iSave( filePath )
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
                    return 0
        return this._saveFile( filePath, clipData, clipSize )
    }

    Load( filePath )
    {
        if !( dataSize := this._loadFile( filePath, dataBuf ) )
            return 0
        return this._toclipboard( dataBuf, dataSize )
    }

    iLoad( filePath )
    {
        this._IsInstance( A_ThisFunc )
        if !( dataSize := this._loadFile( filePath, dataBuf ) )
            return 0
        return this._setClipData( dataBuf, dataSize )
    }

    Clear()
    {
        if !WinClipAPI.OpenClipboard()
            return 0
        WinClipAPI.EmptyClipboard()
        WinClipAPI.CloseClipboard()
        return 1
    }
    
    iClear()
    {
        this._IsInstance( A_ThisFunc )
        ObjSetCapacity( this, "allData", 0 )
    }
    
    Copy( timeout := 1, method := 1 )
    {
        this.Snap( data )
        this.Clear()    ;clearing the clipboard
        if( method = 1 )
            SendInput, ^{Ins}
        else
            SendInput, ^{vk43sc02E} ;ctrl+c
        ClipWait,% timeout, 1
        if ( ret := this._isClipEmpty() )
            this.Restore( data )
        return !ret
    }
    
    iCopy( timeout := 1, method := 1 )
    {
        this._IsInstance( A_ThisFunc )
        this.Snap( data )
        this.Clear()    ;clearing the clipboard
        if( method = 1 )
            SendInput, ^{Ins}
        else
            SendInput, ^{vk43sc02E} ;ctrl+c
        ClipWait,% timeout, 1
        bytesCopied := 0
        if !this._isClipEmpty()
        {
            this.iClear()   ;clearing the variable containing the clipboard data
            bytesCopied := this.iSnap()
        }
        this.Restore( data )
        return bytesCopied
    }
    
    Paste( plainText := "", method := 1 )
    {
        ret := 0
        if ( plainText != "" )
        {
            this.Snap( data )
            this.Clear()
            ret := this.SetText( plainText )
        }
        if( method = 1 )
            SendInput, +{Ins}
        else
            SendInput, ^{vk56sc02F} ;ctrl+v
        this._waitClipReady( 3000 )
        if ( plainText != "" )
        {
            this.Restore( data )
        }
        else
            ret := !this._isClipEmpty()
        return ret
    }
    
    iPaste( method := 1 )
    {
        this._IsInstance( A_ThisFunc )
        this.Snap( data )
        if !( bytesRestored := this.iRestore() )
            return 0
        if( method = 1 )
            SendInput, +{Ins}
        else
            SendInput, ^{vk56sc02F} ;ctrl+v
        this._waitClipReady( 3000 )
        this.Restore( data )
        return bytesRestored
    }
    
    IsEmpty()
    {
        return this._isClipEmpty()
    }
    
    iIsEmpty()
    {
        return !this.iGetSize()
    }
    
    _isClipEmpty()
    {
        return !WinClipAPI.CountClipboardFormats()
    }
    
    _waitClipReady( timeout := 10000 )
    {
        start_time := A_TickCount
        sleep 100
        while ( WinClipAPI.GetOpenClipboardWindow() && ( A_TickCount - start_time < timeout ) )
            sleep 100
    }

    iSetText( textData )
    {
        if ( textData = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData, 1 ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    SetText( textData )
    {
        if ( textData = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData, 1 ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    GetRTF()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "Rich Text Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    iGetRTF()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "Rich Text Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    SetRTF( textData )
    {
        if ( textData = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._setRTF( clipData, clipSize, textData ) )
                    return 0
        return this._toclipboard( clipData, clipSize )
    }
    
    iSetRTF( textData )
    {
        if ( textData = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize :=  this._getClipData( clipData )
        if !( clipSize := this._setRTF( clipData, clipSize, textData ) )
                    return 0
        return this._setClipData( clipData, clipSize )
    }

    _setRTF( ByRef clipData, clipSize, textData )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := WinClipAPI.RegisterClipboardFormat( "Rich Text Format" )
        objFormats[ uFmt ] := object()
        sLen := StrLen( textData )
        ObjSetCapacity( objFormats[ uFmt ], "buffer", sLen )
        StrPut( textData, ObjGetAddress( objFormats[ uFmt ], "buffer" ), sLen, "CP0" )
        objFormats[ uFmt ].size := sLen
        return this._compileClipData( clipData, objFormats )
    }

    iAppendText( textData )
    {
        if ( textData = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    AppendText( textData )
    {
        if ( textData = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._appendText( clipData, clipSize, textData ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    SetHTML( html, source := "" )
    {
        if ( html = "" )
            return 0
        clipSize :=  this._fromclipboard( clipData )
        if !( clipSize := this._setHTML( clipData, clipSize, html, source ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }

    iSetHTML( html, source := "" )
    {
        if ( html = "" )
            return 0
        this._IsInstance( A_ThisFunc )
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._setHTML( clipData, clipSize, html, source ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }

    _calcHTMLLen( num )
    {
        while ( StrLen( num ) < 10 )
            num := "0" . num
        return num
    }

    _setHTML( ByRef clipData, clipSize, htmlData, source )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := WinClipAPI.RegisterClipboardFormat( "HTML Format" )
        objFormats[ uFmt ] := object()
        encoding := "UTF-8"
        htmlLen := StrPut( htmlData, encoding ) - 1   ;substract null
        srcLen := 2 + 10 + StrPut( source, encoding ) - 1      ;substract null
        StartHTML := this._calcHTMLLen( 105 + srcLen )
        EndHTML := this._calcHTMLLen( StartHTML + htmlLen + 76 )
        StartFragment := this._calcHTMLLen( StartHTML + 38 )
        EndFragment := this._calcHTMLLen( StartFragment + htmlLen )

        ;replace literal continuation section to make cross-compatible with v1 and v2
        html := "Version:0.9`r`n"
        html .= "StartHTML:" . StartHTML . "`r`n"
        html .= "EndHTML:" . EndHTML . "`r`n"
        html .= "StartFragment:" . StartFragment . "`r`n"
        html .= "EndFragment:" . EndFragment . "`r`n"
        html .= "SourceURL:" . source . "`r`n"
        html .= "<html>`r`n"
        html .= "<body>`r`n"
        html .= "<!--StartFragment-->`r`n"
        html .= htmlData . "`r`n"
        html .= "<!--EndFragment-->`r`n"
        html .= "</body>`r`n"
        html .= "</html>`r`n"

        sLen := StrPut( html, encoding )
        ObjSetCapacity( objFormats[ uFmt ], "buffer", sLen )
        StrPut( html, ObjGetAddress( objFormats[ uFmt ], "buffer" ), sLen, encoding )
        objFormats[ uFmt ].size := sLen
        return this._compileClipData( clipData, objFormats )
    }
    
    _appendText( ByRef clipData, clipSize, textData, IsSet := 0 )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := this.ClipboardFormats.CF_UNICODETEXT
        str := ""
        if ( objFormats.haskey( uFmt ) && !IsSet )
            str := strget( ObjGetAddress( objFormats[ uFmt ],  "buffer" ), "UTF-16" )
        else
            objFormats[ uFmt ] := object()
        str .= textData
        sLen := ( StrLen( str ) + 1 ) * 2
        ObjSetCapacity( objFormats[ uFmt ], "buffer", sLen )
        StrPut( str, ObjGetAddress( objFormats[ uFmt ], "buffer" ), sLen, "UTF-16" )
        objFormats[ uFmt ].size := sLen
        return this._compileClipData( clipData, objFormats )
    }
    
    _getFiles( pDROPFILES )
    {
        fWide := numget( pDROPFILES + 0, 16, "uchar" ) ;getting fWide value from DROPFILES struct
        pFiles := numget( pDROPFILES + 0, 0, "UInt" ) + pDROPFILES  ;getting address of files list
        list := ""
        while numget( pFiles + 0, 0, fWide ? "UShort" : "UChar" )
        {
            lastPath := strget( pFiles+0, fWide ? "UTF-16" : "CP0" )
            list .= ( list ? "`n" : "" ) lastPath
            pFiles += ( StrLen( lastPath ) + 1 ) * ( fWide ? 2 : 1 )
        }
        return list
    }
    
    _setFiles( ByRef clipData, clipSize, files, append := 0, isCut := 0 )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := this.ClipboardFormats.CF_HDROP
        if ( append && objFormats.haskey( uFmt ) )
            prevList := this._getFiles( ObjGetAddress( objFormats[ uFmt ], "buffer" ) ) "`n"
        objFiles := WinClipAPI.StrSplit( prevList . files, "`n", A_Space A_Tab )
        objFiles := WinClipAPI.RemoveDubls( objFiles )
        if !objFiles.MaxIndex()
            return 0
        objFormats[ uFmt ] := object()
        DROP_size := 20 + 2
        for i,str in objFiles
            DROP_size += ( StrLen( str ) + 1 ) * 2
        VarSetCapacity( DROPFILES, DROP_size, 0 )
        NumPut( 20, DROPFILES, 0, "UInt" )  ;offset
        NumPut( 1, DROPFILES, 16, "uchar" ) ;NumPut( 20, DROPFILES, 0, "UInt" )
        offset := &DROPFILES + 20
        for i,str in objFiles
        {
            StrPut( str, offset, "UTF-16" )
            offset += ( StrLen( str ) + 1 ) * 2
        }
        ObjSetCapacity( objFormats[ uFmt ], "buffer", DROP_size )
        WinClipAPI.memcopy( ObjGetAddress( objFormats[ uFmt ], "buffer" ), &DROPFILES, DROP_size )
        objFormats[ uFmt ].size := DROP_size
        prefFmt := WinClipAPI.RegisterClipboardFormat( "Preferred DropEffect" )
        objFormats[ prefFmt ] := { size : 4 }
        ObjSetCapacity( objFormats[ prefFmt ], "buffer", 4 )
        NumPut( isCut ? 2 : 5, ObjGetAddress( objFormats[ prefFmt ], "buffer" ), 0, "UInt" )
        return this._compileClipData( clipData, objFormats )
    }
    
    SetFiles( files, isCut := 0 )
    {
        if ( files = "" )
            return 0
        clipSize := this._fromclipboard( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 0, isCut ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }
    
    iSetFiles( files, isCut := 0 )
    {
        this._IsInstance( A_ThisFunc )
        if ( files = "" )
            return 0
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 0, isCut ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    AppendFiles( files, isCut := 0 )
    {
        if ( files = "" )
            return 0
        clipSize := this._fromclipboard( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 1, isCut ) )
            return 0
        return this._toclipboard( clipData, clipSize )
    }
    
    iAppendFiles( files, isCut := 0 )
    {
        this._IsInstance( A_ThisFunc )
        if ( files = "" )
            return 0
        clipSize := this._getClipData( clipData )
        if !( clipSize := this._setFiles( clipData, clipSize, files, 1, isCut ) )
            return 0
        return this._setClipData( clipData, clipSize )
    }
    
    GetFiles()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_HDROP ) )
            return ""
        return this._getFiles( &out_data )
    }
    
    iGetFiles()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_HDROP ) )
            return ""
        return this._getFiles( &out_data )
    }
    
    _getFormatData( ByRef out_data, ByRef data, size, needleFormat )
    {
        needleFormat := (WinClipAPI.IsInteger( needleFormat ) ? needleFormat : WinClipAPI.RegisterClipboardFormat( needleFormat ))
        if !needleFormat
            return 0
        offset := 0
        while ( offset < size )
        {
            if !( fmt := NumGet( data, offset, "UInt" ) )
                break
            offset += 4
            if !( dataSize := NumGet( data, offset, "UInt" ) )
                break
            offset += 4
            if ( fmt == needleFormat )
            {
                VarSetCapacity( out_data, dataSize, 0 )
                WinClipAPI.memcopy( &out_data, &data + offset, dataSize )
                return dataSize
            }
            offset += dataSize
        }
        return 0
    }
    
    _DIBtoHBITMAP( ByRef dibData )
    {
        ;http://ebersys.blogspot.com/2009/06/how-to-convert-dib-to-bitmap.html
        pPix := WinClipAPI.GetPixelInfo( dibData )
        gdip_token := WinClipAPI.Gdip_Startup()
        DllCall("gdiplus\GdipCreateBitmapFromGdiDib", "Ptr", &dibData, "Ptr", pPix, "Ptr*", pBitmap )
        DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "Ptr*", hBitmap, "int", 0xffffffff )
        DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
        WinClipAPI.Gdip_Shutdown( gdip_token )
        return hBitmap
    }
    
    GetBitmap()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return ""
        return this._DIBtoHBITMAP( out_data )
    }
    
    iGetBitmap()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return ""
        return this._DIBtoHBITMAP( out_data )
    }
    
    _BITMAPtoDIB( bitmap, ByRef DIB )
    {
        if !bitmap
            return 0
        if !WinClipAPI.IsInteger( bitmap )
        {
            gdip_token := WinClipAPI.Gdip_Startup()
            DllCall("gdiplus\GdipCreateBitmapFromFileICM", "wstr", bitmap, "Ptr*", pBitmap )
            DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "Ptr*", hBitmap, "int", 0xffffffff )
            DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
            WinClipAPI.Gdip_Shutdown( gdip_token )
            bmMade := 1
        }
        else
            hBitmap := bitmap, bmMade := 0
        if !hBitmap
                return 0
        ;http://www.codeguru.com/Cpp/G-M/bitmap/article.php/c1765
        if !( hdc := DllCall( "GetDC", "Ptr", 0 ) )
            goto, _BITMAPtoDIB_cleanup
        hPal := DllCall( "GetStockObject", "UInt", 15 ) ;DEFAULT_PALLETE
        hPal := DllCall( "SelectPalette", "ptr", hdc, "ptr", hPal, "Uint", 0 )
        DllCall( "RealizePalette", "ptr", hdc )
        size := DllCall( "GetObject", "Ptr", hBitmap, "Uint", 0, "ptr", 0 )
        VarSetCapacity( bm, size, 0 )
        DllCall( "GetObject", "Ptr", hBitmap, "Uint", size, "ptr", &bm )
        biBitCount := NumGet( bm, 16, "UShort" )*NumGet( bm, 18, "UShort" )
        nColors := (1 << biBitCount)
    if ( nColors > 256 ) 
        nColors := 0
    bmiLen  := 40 + nColors * 4
        VarSetCapacity( bmi, bmiLen, 0 )
        ;BITMAPINFOHEADER initialization
        NumPut( 40, bmi, 0, "Uint" )
        NumPut( NumGet( bm, 4, "Uint" ), bmi, 4, "Uint" )   ;width
        NumPut( biHeight := NumGet( bm, 8, "Uint" ), bmi, 8, "Uint" ) ;height
        NumPut( 1, bmi, 12, "UShort" )
        NumPut( biBitCount, bmi, 14, "UShort" )
        NumPut( 0, bmi, 16, "UInt" ) ;compression must be BI_RGB

        ; Get BITMAPINFO. 
        if !DllCall("GetDIBits"
                            ,"ptr",hdc
                            ,"ptr",hBitmap
                            ,"uint",0 
                            ,"uint",biHeight
                            ,"ptr",0      ;lpvBits 
                            ,"ptr",&bmi  ;lpbi 
                            ,"uint",0)    ;DIB_RGB_COLORS
            goto, _BITMAPtoDIB_cleanup
        biSizeImage := NumGet( &bmi, 20, "UInt" )
        if ( biSizeImage = 0 )
        {
            biBitCount := numget( &bmi, 14, "UShort" )
            biWidth := numget( &bmi, 4, "UInt" )
            biHeight := numget( &bmi, 8, "UInt" )
            biSizeImage := (((( biWidth * biBitCount + 31 ) & ~31 ) >> 3 ) * biHeight )
            ;~ dwCompression := numget( bmi, 16, "UInt" )
            ;~ if ( dwCompression != 0 ) ;BI_RGB
                ;~ biSizeImage := ( biSizeImage * 3 ) / 2
            numput( biSizeImage, &bmi, 20, "UInt" )
        }
        DIBLen := bmiLen + biSizeImage
        VarSetCapacity( DIB, DIBLen, 0 )
        WinClipAPI.memcopy( &DIB, &bmi, bmiLen )
        if !DllCall("GetDIBits"
                            ,"ptr",hdc
                            ,"ptr",hBitmap
                            ,"uint",0 
                            ,"uint",biHeight
                            ,"ptr",&DIB + bmiLen     ;lpvBits 
                            ,"ptr",&DIB  ;lpbi 
                            ,"uint",0)    ;DIB_RGB_COLORS
            goto, _BITMAPtoDIB_cleanup
_BITMAPtoDIB_cleanup:
        if bmMade
            DllCall( "DeleteObject", "ptr", hBitmap )
        DllCall( "SelectPalette", "ptr", hdc, "ptr", hPal, "Uint", 0 )
        DllCall( "RealizePalette", "ptr", hdc )
        DllCall("ReleaseDC","ptr",hdc)
        if ( A_ThisLabel = "_BITMAPtoDIB_cleanup" )
            return 0
        return DIBLen
    }
    
    _setBitmap( ByRef DIB, DIBSize, ByRef clipData, clipSize )
    {
        objFormats := this._parseClipboardData( clipData, clipSize )
        uFmt := this.ClipboardFormats.CF_DIB
        objFormats[ uFmt ] := { size : DIBSize }
        ObjSetCapacity( objFormats[ uFmt ], "buffer", DIBSize )
        WinClipAPI.memcopy( ObjGetAddress( objFormats[ uFmt ], "buffer" ), &DIB, DIBSize )
        return this._compileClipData( clipData, objFormats )
    }
    
    SetBitmap( bitmap )
    {
        if ( DIBSize := this._BITMAPtoDIB( bitmap, DIB ) )
        {
            clipSize := this._fromclipboard( clipData )
            if ( clipSize := this._setBitmap( DIB, DIBSize, clipData, clipSize ) )
                return this._toclipboard( clipData, clipSize )
        }
        return 0
    }
    
    iSetBitmap( bitmap )
    {
        this._IsInstance( A_ThisFunc )
        if ( DIBSize := this._BITMAPtoDIB( bitmap, DIB ) )
        {
            clipSize := this._getClipData( clipData )
            if ( clipSize := this._setBitmap( DIB, DIBSize, clipData, clipSize ) )
                return this._setClipData( clipData, clipSize )
        }
        return 0
    }
    
    GetText()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_UNICODETEXT ) )
            return ""
        return strget( &out_data, out_size / 2, "UTF-16" )
    }

    iGetText()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, this.ClipboardFormats.CF_UNICODETEXT ) )
            return ""
        return strget( &out_data, out_size / 2, "UTF-16" )
    }
    
    GetHtml()
    {
        if !( clipSize := this._fromclipboard( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "HTML Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    iGetHtml()
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return ""
        if !( out_size := this._getFormatData( out_data, clipData, clipSize, "HTML Format" ) )
            return ""
        return strget( &out_data, out_size, "CP0" )
    }
    
    _getFormatName( iformat )
    {
        if this.formatByValue.HasKey( iformat )
            return this.formatByValue[ iformat ]
        else
            return WinClipAPI.GetClipboardFormatName( iformat )
    }
    
    iGetData( ByRef Data )
    {
        this._IsInstance( A_ThisFunc )
        return this._getClipData( Data )
    }
    
    iSetData( ByRef data )
    {
        this._IsInstance( A_ThisFunc )
        return this._setClipData( data, VarSetCapacity( data ) )
    }
    
    iGetSize()
    {
        this._IsInstance( A_ThisFunc )
        return ObjGetCapacity( this, "alldata" )
    }
    
    HasFormat( fmt )
    {
        if !fmt
            return 0
        return WinClipAPI.IsClipboardFormatAvailable( WinClipAPI.IsInteger( fmt ) ? fmt : WinClipAPI.RegisterClipboardFormat( fmt )  )
    }
    
    iHasFormat( fmt )
    {
        this._IsInstance( A_ThisFunc )
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        return this._hasFormat( clipData, clipSize, fmt )
    }

    _hasFormat( ByRef data, size, needleFormat )
    {
        needleFormat := WinClipAPI.IsInteger( needleFormat ) ? needleFormat : WinClipAPI.RegisterClipboardFormat( needleFormat )
        if !needleFormat
            return 0
        offset := 0
        while ( offset < size )
        {
            if !( fmt := NumGet( data, offset, "UInt" ) )
                break
            if ( fmt == needleFormat )
                return 1
            offset += 4
            if !( dataSize := NumGet( data, offset, "UInt" ) )
                break
            offset += 4 + dataSize
        }
        return 0
    }
    
    iSaveBitmap( filePath, format )
    {
        this._IsInstance( A_ThisFunc )
        if ( filePath = "" || format = "" )
            return 0
        if !( clipSize := this._getClipData( clipData ) )
            return 0
        if !( DIBsize := this._getFormatData( DIB, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return 0
        gdip_token := WinClipAPI.Gdip_Startup()
        if !WinClipAPI.GetEncoderClsid( format, CLSID )
            return 0
        DllCall("gdiplus\GdipCreateBitmapFromGdiDib", "Ptr", &DIB, "Ptr", WinClipAPI.GetPixelInfo( DIB ), "Ptr*", pBitmap )
        DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "wstr", filePath, "Ptr", &CLSID, "Ptr", 0 )
        DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
        WinClipAPI.Gdip_Shutdown( gdip_token )
        return 1
    }
    
    SaveBitmap( filePath, format )
    {
        if ( filePath = "" || format = "" )
            return 0
        if !( clipSize := this._fromclipboard( clipData ) )
            return 0
        if !( DIBsize := this._getFormatData( DIB, clipData, clipSize, this.ClipboardFormats.CF_DIB ) )
            return 0
        gdip_token := WinClipAPI.Gdip_Startup()
        if !WinClipAPI.GetEncoderClsid( format, CLSID )
            return 0
        DllCall("gdiplus\GdipCreateBitmapFromGdiDib", "Ptr", &DIB, "Ptr", WinClipAPI.GetPixelInfo( DIB ), "Ptr*", pBitmap )
        DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "wstr", filePath, "Ptr", &CLSID, "Ptr", 0 )
        DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
        WinClipAPI.Gdip_Shutdown( gdip_token )
        return 1
    }
    
    static ClipboardFormats := { CF_BITMAP : 2 ;A handle to a bitmap (HBITMAP).
                                ,CF_DIB : 8  ;A memory object containing a BITMAPINFO structure followed by the bitmap bits.
                                ,CF_DIBV5 : 17 ;A memory object containing a BITMAPV5HEADER structure followed by the bitmap color space information and the bitmap bits.
                                ,CF_DIF : 5 ;Software Arts' Data Interchange Format.
                                ,CF_DSPBITMAP : 0x0082 ;Bitmap display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in bitmap format in lieu of the privately formatted data.
                                ,CF_DSPENHMETAFILE : 0x008E ;Enhanced metafile display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in enhanced metafile format in lieu of the privately formatted data.
                                ,CF_DSPMETAFILEPICT : 0x0083 ;Metafile-picture display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in metafile-picture format in lieu of the privately formatted data.
                                ,CF_DSPTEXT : 0x0081 ;Text display format associated with a private format. The hMem parameter must be a handle to data that can be displayed in text format in lieu of the privately formatted data.
                                ,CF_ENHMETAFILE : 14 ;A handle to an enhanced metafile (HENHMETAFILE).
                                ,CF_GDIOBJFIRST : 0x0300 ;Start of a range of integer values for application-defined GDI object clipboard formats. The end of the range is CF_GDIOBJLAST.Handles associated with clipboard formats in this range are not automatically deleted using the GlobalFree function when the clipboard is emptied. Also, when using values in this range, the hMem parameter is not a handle to a GDI object, but is a handle allocated by the GlobalAlloc function with the GMEM_MOVEABLE flag.
                                ,CF_GDIOBJLAST : 0x03FF ;See CF_GDIOBJFIRST.
                                ,CF_HDROP : 15 ;A handle to type HDROP that identifies a list of files. An application can retrieve information about the files by passing the handle to the DragQueryFile function.
                                ,CF_LOCALE : 16 ;The data is a handle to the locale identifier associated with text in the clipboard. When you close the clipboard, if it contains CF_TEXT data but no CF_LOCALE data, the system automatically sets the CF_LOCALE format to the current input language. You can use the CF_LOCALE format to associate a different locale with the clipboard text. An application that pastes text from the clipboard can retrieve this format to determine which character set was used to generate the text. Note that the clipboard does not support plain text in multiple character sets. To achieve this, use a formatted text data type such as RTF instead. The system uses the code page associated with CF_LOCALE to implicitly convert from CF_TEXT to CF_UNICODETEXT. Therefore, the correct code page table is used for the conversion.
                                ,CF_METAFILEPICT : 3 ;Handle to a metafile picture format as defined by the METAFILEPICT structure. When passing a CF_METAFILEPICT handle by means of DDE, the application responsible for deleting hMem should also free the metafile referred to by the CF_METAFILEPICT handle.
                                ,CF_OEMTEXT : 7 ;Text format containing characters in the OEM character set. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data.
                                ,CF_OWNERDISPLAY : 0x0080 ;Owner-display format. The clipboard owner must display and update the clipboard viewer window, and receive the WM_ASKCBFORMATNAME, WM_HSCROLLCLIPBOARD, WM_PAINTCLIPBOARD, WM_SIZECLIPBOARD, and WM_VSCROLLCLIPBOARD messages. The hMem parameter must be NULL.
                                ,CF_PALETTE : 9 ;Handle to a color palette. Whenever an application places data in the clipboard that depends on or assumes a color palette, it should place the palette on the clipboard as well.If the clipboard contains data in the CF_PALETTE (logical color palette) format, the application should use the SelectPalette and RealizePalette functions to realize (compare) any other data in the clipboard against that logical palette.When displaying clipboard data, the clipboard always uses as its current palette any object on the clipboard that is in the CF_PALETTE format.
                                ,CF_PENDATA : 10 ;Data for the pen extensions to the Microsoft Windows for Pen Computing.
                                ,CF_PRIVATEFIRST : 0x0200 ;Start of a range of integer values for private clipboard formats. The range ends with CF_PRIVATELAST. Handles associated with private clipboard formats are not freed automatically; the clipboard owner must free such handles, typically in response to the WM_DESTROYCLIPBOARD message.
                                ,CF_PRIVATELAST : 0x02FF ;See CF_PRIVATEFIRST.
                                ,CF_RIFF : 11 ;Represents audio data more complex than can be represented in a CF_WAVE standard wave format.
                                ,CF_SYLK : 4 ;Microsoft Symbolic Link (SYLK) format.
                                ,CF_TEXT : 1 ;Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data. Use this format for ANSI text.
                                ,CF_TIFF : 6 ;Tagged-image file format.
                                ,CF_UNICODETEXT : 13 ;Unicode text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data.
                                ,CF_WAVE : 12 } ;Represents audio data in one of the standard wave formats, such as 11 kHz or 22 kHz PCM.
    
    static                       WM_COPY := 0x301
                                ,WM_CLEAR := 0x0303
                                ,WM_CUT := 0x0300
                                ,WM_PASTE := 0x0302
    
    static skipFormats := {      2      : 0 ;"CF_BITMAP"
                                ,17     : 0 ;"CF_DIBV5"
                                ,0x0082 : 0 ;"CF_DSPBITMAP"
                                ,0x008E : 0 ;"CF_DSPENHMETAFILE"
                                ,0x0083 : 0 ;"CF_DSPMETAFILEPICT"
                                ,0x0081 : 0 ;"CF_DSPTEXT"
                                ,0x0080 : 0 ;"CF_OWNERDISPLAY"
                                ,3      : 0 ;"CF_METAFILEPICT"
                                ,7      : 0 ;"CF_OEMTEXT"
                                ,1      : 0 } ;"CF_TEXT"
                                                            
    static formatByValue := {    2 : "CF_BITMAP"
                                ,8 : "CF_DIB"
                                ,17 : "CF_DIBV5"
                                ,5 : "CF_DIF"
                                ,0x0082 : "CF_DSPBITMAP"
                                ,0x008E : "CF_DSPENHMETAFILE"
                                ,0x0083 : "CF_DSPMETAFILEPICT"
                                ,0x0081 : "CF_DSPTEXT"
                                ,14 : "CF_ENHMETAFILE"
                                ,0x0300 : "CF_GDIOBJFIRST"
                                ,0x03FF : "CF_GDIOBJLAST"
                                ,15 : "CF_HDROP"
                                ,16 : "CF_LOCALE"
                                ,3 : "CF_METAFILEPICT"
                                ,7 : "CF_OEMTEXT"
                                ,0x0080 : "CF_OWNERDISPLAY"
                                ,9 : "CF_PALETTE"
                                ,10 : "CF_PENDATA"
                                ,0x0200 : "CF_PRIVATEFIRST"
                                ,0x02FF : "CF_PRIVATELAST"
                                ,11 : "CF_RIFF"
                                ,4 : "CF_SYLK"
                                ,1 : "CF_TEXT"
                                ,6 : "CF_TIFF"
                                ,13 : "CF_UNICODETEXT"
                                ,12 : "CF_WAVE" }
}
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

20 Jun 2019, 16:24

If someone is interested, I posted this related question here:
WinClip.Snap(clipData) lost, when saving clipData to an array  (https://www.autohotkey.com/boards/viewtopic.php?f=76&t=65568)

(Did not want to unnecessarily clutter this thread, therefore opened a new thread for that question.)
autocart
Posts: 214
Joined: 12 May 2014, 07:42

Re: WinClip - AHKv2 Compatibility

22 Jun 2019, 12:19

One addition to the WinClip.ahk library:

I am not sure, whether the text in the Rich Text Format clipboard format should be null-terminated. (This site https://www.codeproject.com/Reference/1091137/Windows-Clipboard-Formats might indicate that IMO, but I could be wrong.) Still, as far as I could see it, the code does not null-terminate the RTF clipboard format when using SetRTF( textData ).

Even if RTF clipboard data should be null-terminated, it seems no big problem that SetRTF( textData ) does not do it, because when it is retrieved with GetRTF() it does not rely on that null-terminator and still returns valid data. But I don't know how other programs handle it, when they read RTF data from the clipboard. So, if someone runs into problems with SetRTF or GetRTF in connection with other programs accessing that data on the clipboard, then one might want to try the following addition to _setRTF( ByRef clipData, clipSize, textData ):

_setRTF( ByRef clipData, clipSize, textData )
{
objFormats := this._parseClipboardData( clipData, clipSize )
uFmt := WinClipAPI.RegisterClipboardFormat( "Rich Text Format" )
objFormats[ uFmt ] := object()
sLen := StrLen( textData ) + 1
ObjSetCapacity( objFormats[ uFmt ], "buffer", sLen )
StrPut( textData, ObjGetAddress( objFormats[ uFmt ], "buffer" ), sLen, "CP0" )
objFormats[ uFmt ].size := sLen
return this._compileClipData( clipData, objFormats )
}
egocarib
Posts: 100
Joined: 21 May 2015, 18:21

Re: WinClip - AHKv2 Compatibility

26 Sep 2019, 11:23

Thanks for the fixes and improvements, @autocart. I've incorporated them into the original post.

(For now, I didn't include the +1 change in _setRTF, since i'm not 100% sure about that either.)
dereklim78
Posts: 1
Joined: 29 Dec 2015, 02:55

Re: WinClip - AHKv2 Compatibility

03 Nov 2019, 22:12

Hi all,

I previously used WinClip (setHTML) to copy the contents of an .html file into the clipboard and then use Ctrl-V to paste.

All this worked in Win7, but there in Win10 there seems to be a glitch now - it does copy into the clipboard, but when I Ctrl-V into the client program (an electronic medical record), it doesn't paste anything, whereas Ctrl-V in Ms-Word works fine.

Does anyone have any suggestions about where to look to trouble shoot? I'm using v1.1.30.03. I'm also using the updated WinClip library.

Thank you so much,

Derek Lim

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: JoeWinograd and 130 guests