クリップボード関数

AHKクリップボード操作しててExcelが固まったり不正エラーで落ちることがよくある。
どうもクリップボードEXCELからコピーした内容が入ってる時にClipboardAllするといかんらしい。
試しにファイルに書出してみたら 4,5文字の1セルのはずなのになんと9MBもあった。
リンク情報とか色々入れてるらしいがそれにしてもあんまりだ。
ClipboardAllする前にクリップボードの容量をチェックできないかと調べてみたがよくわからん。
結局クリップボードオーナーをチェックしてEXCELがオーナーのときはテキストだけ待避するようにして回避することにした。

    hWnd := DllCall("GetClipboardOwner",Int)
    h := A_DetectHiddenWindows
    DetectHiddenWindows, On
    WinGet,ClipOwner,ProcessName,ahk_id %hWnd%
    ifNotEqual,h,ON,    DetectHiddenWindows,%h%
    ifEqual ClipOwner,EXCEL.EXE,    SetEnv,clipsave,%Clipboard%
    else                            SetEnv,clipsave,%ClipboardALL%



色々テストしてた過程で作ったクリップボード系の関数も貼っときます。
クリップボード内容のチェックにはCLCLのビューアを、制御方法については同ソースコードを参考にさせて貰いました。お手本があるというのは本当にありがたいことですな。

SetClipData(format, hMem, hWnd=0)
クリップボードにフォーマットを指定してデータを設定
GetClipData(byref to_mem, format, hWnd=0)
クリップボード内のデータをフォーマットを指定して取得
GetClipboardFormats(byref formats, getUsrFmtName=0, hWnd=0)
現在クリップボード内にあるフォーマットを列挙
GetClipFormatName(format)
クリップボードフォーマットIDからフォーマット名を取得
GetClipFormatID(FormatName)
クリップボードフォーマット名からフォーマットIDを取得
    ;--- コピペ用 ---
    ;最後にクリップボードにデータを設定したウィンドウのハンドルを取得
    hOunerWnd := DllCall("GetClipboardOwner",UInt)

    ;指定のフォーマットのデータがクリップボード内にあるか調べる
    ; 1:あり 0:なし
    ret := DllCall("IsClipboardFormatAvailable", Int,format,Int)
/*
  (参考) 標準クリップボードフォーマットID
          CF_TEXT               = 1     ; TEXT
          CF_BITMAP             = 2     ; BITMAP
          CF_METAFILEPICT       = 3     ; METAFILE PICTURE
          CF_SYLK               = 4     ; SYLK
          CF_DIF                = 5     ; DIF
          CF_TIFF               = 6     ; TIFF
          CF_OEMTEXT            = 7     ; OEM TEXT
          CF_DIB                = 8     ; DIB
          CF_PALETTE            = 9     ; PALETTE
          CF_PENDATA            = 10    ; PEN DATA
          CF_RIFF               = 11    ; RIFF
          CF_WAVE               = 12    ; WAVE DATA
          CF_UNICODETEXT        = 13    ; UNICODE TEXT
          CF_ENHMETAFILE        = 14    ; ENHANCED METAFILE
          CF_HDROP              = 15    ; DROP FILE LIST
          CF_LOCALE             = 16    ; LOCALE
          CF_MAX                = 17    ; MAX
          CF_OWNERDISPLAY       = 128   ; 0x80  OWNER DISPLAY
          CF_DSPTEXT            = 129   ; 0x81  PRIVATE TEXT
          CF_DSPBITMAP          = 130   ; 0x82  PRIVATE BITMAP
          CF_DSPMETAFILEPICT    = 131   ; 0x83  PRIVATE METAFILE PICTURE
          CF_DSPENHMETAFILE     = 142   ; 0x8E  PRIVATE ENHANCED METAFILE
          CF_PRIVATEFIRST       = 512   ; 0x200 PRIVATE FIRST
          CF_PRIVATELAST        = 767   ; 0x2FF PRIVATE LAST
          CF_GDIOBJFIRST        = 768   ; 0x300 GDI OBJECT FIRST
          CF_GDIOBJLAST         = 1023  ; 0x3FF GDI OBJECT LAST
 */

クリップボードにフォーマットを指定してデータを設定する

SetClipData(format, hMem, hWnd=0)
;----------------------------------------------------------
;   クリップボードにフォーマットを指定してデータを設定する
;       対象: AHK v1.0.34以降       2005.09.20
;   in  format  フォーマットID
;       hMem    データのハンドル(or データアドレス)
;       hWnd    クリップボードをオープンするウィンドウのハンドル
;   戻り値      データのハンドル (0:エラー)
;----------------------------------------------------------
{
    ; クリップボードの初期化
    If (!DllCall("OpenClipboard", UInt,hWnd, Unt))  ;Open
        return
    if (DllCall("EmptyClipboard"))                  ;データ削除
    {
        ; クリップボードにデータを設定する
        ; ※ Bitmapなんかの場合はハンドルを指定して下さい。
        ;    バイナリアドレス渡してもうまく書き戻せません。
        ret := DllCall("SetClipboardData", Int,Format, Int,hMem, Int)
    }
    else ret = 0

    ;クリップボードのクローズ
    DllCall("CloseClipboard",Int)
    return ret
}

クリップボード内のデータをフォーマットを指定して取得

GetClipData(byref to_mem, format, hWnd=0)
;----------------------------------------------------------
;  クリップボード内のデータをフォーマットを指定して取得
;       対象: AHK v1.0.34以降       2005.09.20
;   out to_mem  クリップボードデータ(バイナリ)の先頭アドレス
;   in  format  フォーマットID
;       hWnd    クリップボードをオープンするウィンドウのハンドル
;   戻り値      データサイズ(バイト数)
;----------------------------------------------------------
{
    ;指定のフォーマットのデータがクリップボード内にあるか調べる
    If (!DllCall("IsClipboardFormatAvailable", Int,format,Int))
        return
    If (!DllCall("OpenClipboard", UInt,hWnd, Unt))  ;Open
        return

    ;--- クリップボードから指定された形式のデータを取得 ---
    ;※ フォーマット非考慮、まるごとバイナリで返すので上位でどうにかして下さい
    ;※ 尚、Bitmapなんかの場合はここで得た内容をそのままSetClipboardDataしても
    ;   クリップボードには書き戻せませんので悪しからず。
    hClip := DllCall("GetClipboardData", UInt,format,UInt)
    ret_size := DllCall("GlobalSize", Uint,hClip,Uint)  ;サイズ取得
    from_mem := DllCall("GlobalLock",Uint,hClip,UInt)   ;コピー元ロック

    ;コピー先確保 (wFlags: GHND)
    hTo := DllCall("GlobalAlloc",Int,0x42, Uint,ret_size,Uint)
    to_mem := DllCall("GlobalLock",Uint,hTo, UInt)      ;コピー先ロック

    ;コピー (MoveMemory使用)
    DllCall("RtlMoveMemory", Uint,to_mem, Uint,from_mem, Int,ret_size)

    ;グローバルメモリ領域のロック解除
    DllCall("GlobalUnlock",Uint,hTo, Int)
    DllCall("GlobalUnlock",Uint,hClip, Int)
    DllCall("CloseClipboard",Int)               ;Close

    return ret_size
}

現在クリップボード内にあるフォーマットを列挙

GetClipboardFormats(byref formats, getUsrFmtName=0, hWnd=0)
;------------------------------------------------------------------------
;   現在クリップボード内にあるフォーマットを列挙
;       対象: AHK v1.0.34以降       2005.09.20
;   out formats         フォーマットIDをCRで区切って列挙したもの
;                         ID (TAB UserFormatName)`n ID…
;
;   in  getUsrFmtName   1:非標準フォーマットの場合はフォーマット名も取得する
;                          (フォーマットIDの後にTABで追加)
;       hWnd            クリップボードをオープンするウィンドウのハンドル
;   戻り値              フォーマット数カウント
;------------------------------------------------------------------------
{
    If (!DllCall("OpenClipboard", UInt,hWnd, Unt))  ;Open
        return

    ;現在クリップボード内にあるフォーマットを列挙する
    format = 0
    Loop
    {
        ; Declare Function EnumClipboardFormats Lib "user32.dll" (ByVal format As Long) As Long
        format := DllCall("EnumClipboardFormats", UInt,format, Unt)
        if (!format)
        {
            cnt := A_Index-1
            break
        }
        formats := formats . "`n" . format

        ;ユーザー定義のクリップボードフォーマット名を取得
        if (getUsrFmtName)
        {
            max := VarSetCapacity(s, 256)
            if(DllCall("GetClipboardFormatName",Int,format, Str,s, Int,max,Int))
                formats := formats . A_TAB . s
        }
    }
    DllCall("CloseClipboard",Int)   ;Close

    StringTrimLeft, formats, formats, 1 ;最初のカンマ除去
    return cnt
}

クリップボードフォーマットIDからフォーマット名を取得

GetClipFormatName(format)
;---------------------------------------------------------
;   クリップボードフォーマットIDからフォーマット名を取得
;       対象: AHK v1.0.34以降              2005.09.20
;   in  format  (int)フォーマットID
;   戻り値      (str)フォーマット名
;---------------------------------------------------------
{
    ;--- 標準フォーマット名チェック ---
     cf_tbl := "1,CF_TEXT`n2,CF_BITMAP`n3,CF_METAFILEPICT`n4,CF_SYLK`n5,CF_DIF`n6,CF_TIFF`n"
    cf_tbl := cf_tbl . "7,CF_OEMTEXT`n8,CF_DIB`n9,CF_PALETTE`n10,CF_PENDATA`n11,CF_RIFF`n"
    cf_tbl := cf_tbl . "12,CF_WAVE`n13,CF_UNICODETEXT`n14,CF_ENHMETAFILE`n15,CF_HDROP`n"
    cf_tbl := cf_tbl . "16,CF_LOCALE`n17,CF_MAX`n128,CF_OWNERDISPLAY`n129,CF_DSPTEXT`n"
    cf_tbl := cf_tbl . "130,CF_DSPBITMAP`n131,CF_DSPMETAFILEPICT`n142,CF_DSPENHMETAFILE`n"
    cf_tbl := cf_tbl . "512,CF_PRIVATEFIRST`n767,CF_PRIVATELAST`n768,CF_GDIOBJFIRST`n"
    cf_tbl := cf_tbl . "1023,CF_GDIOBJLAST`n"

    Loop,parse,cf_tbl, `n
    {
        StringSplit, cf, A_LoopField, `,
        ifEqual format,%cf1%,      return cf2
    }

    ;--- 標準じゃない場合はユーザー定義フォーマット名を取得 ---
    ; Declare Function GetClipboardFormatName Lib "user32.dll" Alias "GetClipboardFormatNameA" 
    ;(ByVal format As Long, ByVal lpszFormatName As String, ByVal cchMaxCount As Long) As Long
    max := VarSetCapacity(s, 256)
    DllCall("GetClipboardFormatName",Int,format, Str,s, Int,max, Int)
    return s
}

クリップボードフォーマット名からフォーマットIDを取得

;---------------------------------------------------------
;   クリップボードフォーマット名からフォーマットIDを取得
;   (存在しないフォーマット名の場合は新たに登録される)
;       対象: AHK v1.0.34以降              2005.09.20
;   in  FormatName  (str)フォーマット名
;   戻り値          (int) フォーマットID
;---------------------------------------------------------
{   
    ;--- 標準フォーマット名チェック ---
    cf_tbl := "1,CF_TEXT`n2,CF_BITMAP`n3,CF_METAFILEPICT`n4,CF_SYLK`n5,CF_DIF`n6,CF_TIFF`n"
    cf_tbl := cf_tbl . "7,CF_OEMTEXT`n8,CF_DIB`n9,CF_PALETTE`n10,CF_PENDATA`n11,CF_RIFF`n"
    cf_tbl := cf_tbl . "12,CF_WAVE`n13,CF_UNICODETEXT`n14,CF_ENHMETAFILE`n15,CF_HDROP`n"
    cf_tbl := cf_tbl . "16,CF_LOCALE`n17,CF_MAX`n128,CF_OWNERDISPLAY`n129,CF_DSPTEXT`n"
    cf_tbl := cf_tbl . "130,CF_DSPBITMAP`n131,CF_DSPMETAFILEPICT`n142,CF_DSPENHMETAFILE`n"
    cf_tbl := cf_tbl . "512,CF_PRIVATEFIRST`n767,CF_PRIVATELAST`n768,CF_GDIOBJFIRST`n"
    cf_tbl := cf_tbl . "1023,CF_GDIOBJLAST`n"

    Loop,parse,cf_tbl, `n
    {
        StringSplit, cf, A_LoopField, `,
        ifEqual FormatName,%cf2%,      return cf1
    }

    ;新しいクリップボードフォーマットを登録する
    ;すでに存在していた場合は既存のフォーマットIDが戻る。
    return DllCall("RegisterClipboardFormat", Str,FormatName, Int)
}