【VBA×WindowsAPI】クリップボード内のテキストデータ(文字列)を取得する

VBAでクリップボードの値を取得したりセットしたりするには「DataObject」オブジェクトを使うことが一般的ですが、ここではWindows APIの関数を使ってクリップボード内の文字列を取得する方法を解説していきます。

「DataObject」オブジェクトはWindowsが8,10あたりになり正常に動作しなくなるという事象が発生していますが(主にクリップボードに文字列をセットする際)、Windows APIを使えばそういった現象を避けることができます。「DataObject」オブジェクト使用時に比べてコードの量が非常に多くなりますが、その分クリップボード内部のデータの動きをしっかりと理解することができます。

クリップボード内の文字列を取得する

Windows APIの関数を使うことでクリップボード内のデータにアクセスして、テキストデータをString型の変数に格納することが可能になります。クリップボード内には様々なデータを格納することができますが、データの取得においてはテキストデータが最も単純といっても過言ではありません。

VBAでクリップボード内の文字列を取得するには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。

icon-check-square OpenClipboard関数                    :クリップボードを開く 
icon-check-square CloseClipboard関数                    :クリップボードを閉じる
icon-check-square IsClipboardFormatAvailable関数:クリップボードに指定形式のデータがあるか判定する
icon-check-square GetClipboardData関数        :指定した形式でクリップボードからデータを取得する
icon-check-square GlobalLock関数                          :メモリ領域の最初のバイトへのポインタを取得する
icon-check-square GlobalUnlock関数                     :指定のメモリ領域のロックを解除する
icon-check-square GlobalSize関数                           :指定のメモリ領域のサイズをバイト数で取得する
icon-check-square MoveMemory関数                      :指定のメモリ領域をある場所から別の場所に移動する

「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。
また、その他のクリップボードの操作に関しては下記も併せて参照下さい。

クリップボード内にテキストデータ(文字列)をセットする方法
クリップボードにビットマップ画像をセットする方法

サンプルコード

クリップボード内の文字列を取得する方法は下記の通りです。
標準モジュールにコピーペーストして「main」を実行することで、クリップボード内の文字列をイミディエイトウィンドウに出力することができます。クリップボード内のデータがテキスト以外の場合はクリップボード内のデータがテキストデータでないことをイミディエイトウィンドウに出力します。

コード解説

Windows APIを使ってクリップボード内の文字列を取得するには下記の手順を行います。
以下ではこれら処理の中でも核となるメモリ操作部分(3~8)の内容を解説していきます。

1. OpenClipboard関数でクリップボードを開く
2. IsClipboardFormatAvailable関数でクリップボード内のデータが文字列かを確認
3. GetClipboardData関数でクリップボード内のテキストデータへのメモリハンドルを取得
4. GlobalLock関数を使って取得したハンドルが示すメモリ領域の先頭のポインタを取得
5. GlobalSize関数でテキストのデータサイズを取得
6. 取得したデータサイズ分の空のテキストデータを用意
7. MoveMemory関数でクリップボード内のデータを用意した空データ部分に移動
8. GlobalUnlock関数でハンドルが示すメモリ領域のロックを解除する
9. CloseClipboard関数でクリップボードを閉じる

 

icon-edit クリップボード内の文字列を受け取る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関数で実際のサイズが取得できていた場合)
  

icon-edit 用意した変数(固定長文字列)にクリップボード内の文字列を移動

固定長文字列の変数(sTmp)が用意出来たら、クリップボード内の文字列データを変数(sTmp)のメモリ部分に移動させます。メモリ間のデータ移動を行うにはMoveMemory関数を使います。

MoveMemory関数を使うには「移動前のメモリ領域の先頭へのポインタ」と「移動後のメモリ領域の先頭へのポインタ」、「移動するデータのサイズ」が必要になります。

「移動前のメモリ領域の先頭へのポインタ」はクリップボード内の文字列データへのハンドル(hMem)を引数としてGlobalLock関数を使うことで取得することができます。「移動後のメモリ領域の先頭へのポインタ」は用意した変数(sTmp)へのポインタなので、StrPtr関数を使うことで取得が可能です。また「移動するデータのサイズ」は事前に変数のサイズを調整しておいたのでLenB関数で変数のサイズを取得すればデータのサイズと等しくなります。

これらを引数としてMoveMemory関数を使うことで、クリップボード内の文字列データを用意しておいた変数(sTmp)内に移動することができます。これでクリップボード内の文字列の取得は完了ですが、GlobalSize関数は実際のサイズより大きいサイズで取得されている場合があるので、取得した文字列内に余計な空文字が含まれてしまうことがあります。そのため最後にLeft関数やMid関数などで空文字部分を除去して体裁を整えてあげる必要があります。

※GlobalLock関数の使用後はGlobalUnlock関数を使ってメモリのロックを解除する必要があります。

関連情報

icon-share-square VBA×WindowsAPIまとめページ

その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。

icon-share-square 参考

Microsoft公式:クリップボードから情報を取得する – VBA

2023年8月15日Excel, VBA, Windows API

Posted by Lic