クリップボード関数
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) }