【VBA×WindowsAPI】クリップボードにビットマップ画像をセットする
VBAを使っていてクリップボードを操作したいという場面に出くわすことがしばしばあります。
クリップボードを操作する方法はいくつか存在しますが、ここではWindows APIを使ってクリップボードに指定の画像ファイルをセットする方法を解説していきます。Windows APIを利用することで(Application.ClipboardFormatsやActiveSheet.Pasteなどのような)“Excel VBAだけ”といったアプリケーションにとらわれない処理を行うことができます。
Windows APIを使ってクリップボードにビットマップ画像をセットするコードは、基本的にほとんど決まりきったものです。そのためパッと使いたい程度であればサンプルコードをコピペすればそのまま利用できます。ただし、しっかりと扱わないとVBA外にも影響の出る恐れのあるコードのため、コード解説の「終了処理」の項目だけは軽く目を通しておくことをオススメします。
VBAでクリップボードに画像をセットする方法
VBAでクリップボードに任意の画像データをセットするには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
OpenClipboard関数: クリップボードを開く
EmptyClipboard関数: クリップボードを空にする
CloseClipboard関数: クリップボードを閉じる
SetClipboardData関数: クリップボードにデータ(画像)をセットする
LoadImage関数: メモリ上にビットマップをロードする
DeleteObject関数: メモリ上のビットマップを削除しシステムリソースを解放する
「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。
サンプルコード
クリップボードにビットマップ画像をセットするためのサンプルコードは下記の通りです。
コード内の「”画像ファイルパス.bmp”」部分を任意のビットマップファイルのフルパスに書き換えてから実行すると、指定した画像ファイルがクリップボードにセットされます。
指定パスがビットマップでなかったり、ファイル自体が存在しなかったりしてうまく処理ができなかった場合はクリップボード内が空になります。しっかりとデータのセットができたかを確認したい場合は”Callで関数を呼び出す”のではなく戻り値での判定処理を追加する必要があります。各関数の戻り値はそれぞれの関数別ページで解説しているので合わせて参照してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
Option Explicit Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long Declare PtrSafe Function EmptyClipboard Lib "user32" () As Long Declare PtrSafe Function CloseClipboard Lib "user32" () As Long Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As LongPtr) As LongPtr Declare PtrSafe Function LoadImage Lib "user32" Alias "LoadImageA" (ByVal hInst As LongPtr, ByVal lpsz As String, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As LongPtr Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As LongPtr) As Long Const IMAGE_BITMAP As Long = 0 Const CF_BITMAP As Long = 2 Const LR_LOADFROMFILE As Long = &H10 Sub main() Dim sPathImage As String Dim hBitmap As LongPtr Dim lRet As Long sPathImage = "画像ファイルパス.bmp" 'Bmpファイルをメモリ上に読み込む hBitmap = LoadImage(0, sPathImage, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE) 'クリップボードを開く If OpenClipboard(0) Then 'クリップボードを空にする(初期化) Call EmptyClipboard '読み込んだBmpファイルをクリップボードにセットする Call SetClipboardData(CF_BITMAP, hBitmap) 'クリップボードを閉じる Call CloseClipboard End If 'Bmpファイルのメモリを解放 lRet = DeleteObject(hBitmap) End Sub |
コード解説
Bmpファイルをメモリ上に読み込む
クリップボード内に画像をセットするにはまず、画像をメモリ上に読み込む必要があります。
画像をメモリ上に読み込ませるためにはLoadImage関数を使い下記のように書きます。
hBitmap = LoadImage(0, sPathImage, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)
LoadImage関数はアイコン、カーソル、ビットマップをメモリ上に読み込むための関数で、今回のようなビットマップ画像データを読み込む場合は第3引数に「IMAGE_BITMAP」を入力します。また、その読み込ませるビットマップ画像データとしてローカルにある画像ファイル(.bmp)を読み込ませる場合は第6引数に「LR_LOADFROMFILE」を入力します。
「IMAGE_BITMAP」「LR_LOADFROMFILE」は該当のヘッダーファイル内に定義されていますが、VBAでは関数の呼び出しを行っているだけなのでこれら定数は定義されていません。そのため「Const IMAGE_BITMAP As Long = 0」「Const LR_LOADFROMFILE As Long = &H10」というかたちでコードの初めに定義しています。(引数として直接「0」「&H10」を入力しても同じ結果となります)
上記の引数と合わせて実際にメモリ上に読み込ませるビットマップ画像のファイルパス(フルパス)を第2引数に入力することで、指定の画像ファイルをメモリ上に読み込ませることができます。戻り値の「hBitmap」にはメモリ上に読み込まれたビットマップ画像へのハンドルが返されます。
クリップボードを開く & 初期化
クリップボード内のデータ操作を行う際は、OpenClipboard関数を使ってクリップボードを”開く”、つまりはプログラム(VBA)からクリップボードにアクセスできるようにする必要があります。
分岐処理の「If OpenClipboard(0) Then」は”クリップボードが正常に開かれた場合”を意味しています。基本的にOpenClipboard関数が失敗することはありませんが、VBAとは別で稼働しているアプリケーション等でクリップボード操作の処理が行なわれている場合に失敗する可能性があります。
クリップボードが正常に開けたらクリップボード内に既にあるデータをクリアして、クリップボード内を空の状態にします。クリップボード内のデータを空にするにはEmptyClipboard関数を呼び出すだけです。(※この関数は事前にOpenClipboard関数でクリップボードを開いておく必要があります)
読み込んだBmpファイルをクリップボードにセットする
クリップボードを開くことが出来たらをSetClipboardData関数を使ってメモリ上に読み込んだビットマップ画像をクリップボードにセットします。
Call SetClipboardData(CF_BITMAP, hBitmap)
クリップボードにセットするデータがビットマップの場合は第1引数に「CF_BITMAP」を入力します。これもLoadImage関数の時と同様にヘッダーファイルで定義された定数で値は「2」です。
第2引数には読み込ませたビットマップ画像へのハンドル(LoadImage関数の戻り値)を入力します。これでクリップボード内に画像データをセットすることができます。サンプルコードでは正常に処理ができる前提で戻り値の受け取りはしていませんが、よりしっかりと書くのであれば戻り値を受け取って正常に処理ができているかを判定したほうがよいです。
終了処理
クリップボードの操作が終わったらCloseClipboard関数でクリップボードを閉じる必要があります。
これを行わないと外部のアプリケーションおよび手作業でのクリップボードの操作が行えなくなるので、OpenClipboard関数を使った後は必ずCloseClipboard関数でクリップボードを閉じる処理を通るようなコードにしておく必要があります。
またLoadImage関数でメモリ上に読み込ませた画像データはVBAが終わってもメモリ上に残り続けてしまいます。つまり実行すればするほどメモリ上に画像データが読み込まれ、不要なメモリが蓄積されていってしまいます。この現象をメモリリークといい、これを起こさないようにするためには使わなくなった不要な画像データをメモリ上から除去する必要があります。メモリ上の画像データを削除するにはDeleteObject関数を使います。使い方は引数に読み込ませたビットマップ画像へのハンドル(LoadImage関数の戻り値)を入力するだけです。
関連情報
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:クリップボードの使用 – Win32 apps