【VBA×WindowsAPI】クリップボード内のテキストデータ(文字列)を取得する
VBAでクリップボードの値を取得したりセットしたりするには「DataObject」オブジェクトを使うことが一般的ですが、ここではWindows APIの関数を使ってクリップボード内の文字列を取得する方法を解説していきます。
「DataObject」オブジェクトはWindowsが8,10あたりになり正常に動作しなくなるという事象が発生していますが(主にクリップボードに文字列をセットする際)、Windows APIを使えばそういった現象を避けることができます。「DataObject」オブジェクト使用時に比べてコードの量が非常に多くなりますが、その分クリップボード内部のデータの動きをしっかりと理解することができます。
クリップボード内の文字列を取得する
Windows APIの関数を使うことでクリップボード内のデータにアクセスして、テキストデータをString型の変数に格納することが可能になります。クリップボード内には様々なデータを格納することができますが、データの取得においてはテキストデータが最も単純といっても過言ではありません。
VBAでクリップボード内の文字列を取得するには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
OpenClipboard関数 :クリップボードを開く
CloseClipboard関数 :クリップボードを閉じる
IsClipboardFormatAvailable関数:クリップボードに指定形式のデータがあるか判定する
GetClipboardData関数 :指定した形式でクリップボードからデータを取得する
GlobalLock関数 :メモリ領域の最初のバイトへのポインタを取得する
GlobalUnlock関数 :指定のメモリ領域のロックを解除する
GlobalSize関数 :指定のメモリ領域のサイズをバイト数で取得する
MoveMemory関数 :指定のメモリ領域をある場所から別の場所に移動する
「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。
また、その他のクリップボードの操作に関しては下記も併せて参照下さい。
・クリップボード内にテキストデータ(文字列)をセットする方法
・クリップボードにビットマップ画像をセットする方法
サンプルコード
クリップボード内の文字列を取得する方法は下記の通りです。
標準モジュールにコピーペーストして「main」を実行することで、クリップボード内の文字列をイミディエイトウィンドウに出力することができます。クリップボード内のデータがテキスト以外の場合はクリップボード内のデータがテキストデータでないことをイミディエイトウィンドウに出力します。
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
Option Explicit Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long Private Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Long) As Long Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As LongPtr Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As Long Private Declare PtrSafe Function GlobalSize Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr Private Declare PtrSafe Sub MoveMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As LongPtr) Const CF_TEXT As Long = 1 Const CF_UNICODETEXT As Long = 13 '---------------------------------------------------------------- ' メイン処理 '---------------------------------------------------------------- Sub main() 'クリップボードの文字列を取得 Dim sData As String sData = GetClipboardText '取得した文字列をイミディエイトウィンドウに出力 Debug.Print "クリップボード内の文字列 : " & sData End Sub '---------------------------------------------------------------- ' クリップボードの中身を取得する ' 戻り値:クリップボード内にある文字列 '---------------------------------------------------------------- Public Function GetClipboardText() As String Dim hMem As LongPtr Dim lLen As LongPtr Dim hPtr As LongPtr Dim sTmp As String Dim sUniText As String 'クリップボードを開く If OpenClipboard(0&) <> 0 Then 'クリップボード内のデータが文字列かを確認 If IsClipboardFormatAvailable(CF_TEXT) <> 0 Then 'クリップボード内テキストのメモリハンドルをUnicode形式で取得 hMem = GetClipboardData(CF_UNICODETEXT) If hMem <> 0 Then 'クリップボード内のデータサイズが収まる大きさの空データを用意 lLen = GlobalSize(hMem) sTmp = String(CLng(lLen) \ 2 , vbNullChar) 'メモリハンドルの先頭へのポインタを取得(メモリ領域のロック) hPtr = GlobalLock(hMem) '確保した空データ部分にクリップボード内の文字列データを移動する Call MoveMemory(StrPtr(sTmp), hPtr, LenB(sTmp)) '不要なバッファが存在する場合は削除 If InStr(sTmp, vbNullChar) <> 0 Then sUniText = Left(sTmp, InStr(sTmp, vbNullChar) - 1) Else sUniText = sTmp End If 'メモリ領域のロックを解除 Call GlobalUnlock(hMem) End If '戻り値設定 GetClipboardText = sUniText Else Debug.Print "クリップボード内のデータがテキストではありません" End If 'クリップボードを閉じる Call CloseClipboard End If End Function |
コード解説
Windows APIを使ってクリップボード内の文字列を取得するには下記の手順を行います。
以下ではこれら処理の中でも核となるメモリ操作部分(3~8)の内容を解説していきます。
2. IsClipboardFormatAvailable関数でクリップボード内のデータが文字列かを確認
3. GetClipboardData関数でクリップボード内のテキストデータへのメモリハンドルを取得
4. GlobalLock関数を使って取得したハンドルが示すメモリ領域の先頭のポインタを取得
5. GlobalSize関数でテキストのデータサイズを取得
6. 取得したデータサイズ分の空のテキストデータを用意
7. MoveMemory関数でクリップボード内のデータを用意した空データ部分に移動
8. GlobalUnlock関数でハンドルが示すメモリ領域のロックを解除する
9. CloseClipboard関数でクリップボードを閉じる
クリップボード内の文字列を受け取るString型変数を用意する
VBAでクリップボード内の文字列を取得するということは、最終的には用意しておいたString型の変数にその文字列が格納されるということを意味しています。
Windows APIで文字列のデータのやり取りをする場合は“固定長文字列”というものを扱う必要があります。通常、VBAで利用される「Dim sText As String」と宣言している文字列は“可変長文字列”と呼ばれるもので、入力される文字列に合わせて変数内に格納されるデータのメモリサイズを変化させることができます。対して固定長文字列は文字通りメモリサイズは常に一定なものとなります。
クリップボード内の文字列の大きさは常に変化するものであるため、固定長文字列を用意する前にクリップボード内の文字列の大きさを調べておく必要があります。まずはGetClipboadData関数を使ってクリップボード内の文字列データへのハンドル(hMem)を取得します。このときVBAのString型の文字コードであるUnicodeに合わせて引数は「CF_UNICODETEXT」を渡します。
クリップボード内の文字列データへのハンドル(hMem)が取得できたら、それを引数にGlobalSize関数を使うことでクリップボード内の文字列のデータサイズを調べることができます。このとき取得されるサイズは実際のサイズより大きくなる場合もあるので注意が必要です。
あとは取得したサイズと同じ大きさの固定長文字列の変数を用意するだけです。
「Dim sTmp As String」と可変長文字列を宣言しておき、String関数で取得したデータサイズ分だけ「vbNullChar」で敷き詰めた固定長文字列を代入します。このときGlobalSize関数で取得されるサイズはバイト単位で、String型の文字コードであるUnicodeは1文字あたり2バイトなので「÷2」をして調整してあげる必要があります。
これにより「sTmp」というクリップボード内の文字列と同じメモリサイズの空文字を持った変数を用意することができます。(※GlobalSize関数で実際のサイズが取得できていた場合)
用意した変数(固定長文字列)にクリップボード内の文字列を移動
固定長文字列の変数(sTmp)が用意出来たら、クリップボード内の文字列データを変数(sTmp)のメモリ部分に移動させます。メモリ間のデータ移動を行うにはMoveMemory関数を使います。
MoveMemory関数を使うには「移動前のメモリ領域の先頭へのポインタ」と「移動後のメモリ領域の先頭へのポインタ」、「移動するデータのサイズ」が必要になります。
「移動前のメモリ領域の先頭へのポインタ」はクリップボード内の文字列データへのハンドル(hMem)を引数としてGlobalLock関数を使うことで取得することができます。「移動後のメモリ領域の先頭へのポインタ」は用意した変数(sTmp)へのポインタなので、StrPtr関数を使うことで取得が可能です。また「移動するデータのサイズ」は事前に変数のサイズを調整しておいたのでLenB関数で変数のサイズを取得すればデータのサイズと等しくなります。
これらを引数としてMoveMemory関数を使うことで、クリップボード内の文字列データを用意しておいた変数(sTmp)内に移動することができます。これでクリップボード内の文字列の取得は完了ですが、GlobalSize関数は実際のサイズより大きいサイズで取得されている場合があるので、取得した文字列内に余計な空文字が含まれてしまうことがあります。そのため最後にLeft関数やMid関数などで空文字部分を除去して体裁を整えてあげる必要があります。
※GlobalLock関数の使用後はGlobalUnlock関数を使ってメモリのロックを解除する必要があります。
関連情報
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:クリップボードから情報を取得する – VBA