【VBA×WindowsAPI】クリップボード内にテキストデータ(文字列)をセットする
VBAでクリップボードの値を取得したりセットしたりするには「DataObject」オブジェクトを使うことが一般的ですが、ここではWindows APIの関数を使ってVBAでクリップボード内に任意の文字列をセットする方法を解説していきます。
「DataObject」オブジェクトはWindowsが8,10あたりになり正常に動作しなくなるという事象が発生していますが(主にクリップボードに文字列をセットする際)、Windows APIを使えばそういった現象を避けることができます。「DataObject」オブジェクト使用時に比べてコードの量が非常に多くなりますが、その分クリップボード内部のデータの動きをしっかりと理解することができます。
クリップボード内の文字列を取得する
Windows APIの関数を使うことでクリップボード内にアクセスして、指定の文字列データ(String型)をセットすることが可能になります。メモリやポインタ、ハンドルといった概念が出てくるためVBAというよりはC++言語に近い内容でしっかりと理解するにはそちらの知識も多少必要になってきます。
VBAでクリップボード内の文字列を取得するには下記のWindows APIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
OpenClipboard関数 :クリップボードを開く
CloseClipboard関数 :クリップボードを閉じる
EmptyClipboard関数 :クリップボード内のデータを空にする
SetClipboardData関数 :指定した形式でクリップボードにデータをセットする
GlobalAlloc関数 :指定したサイズのメモリ領域を確保する
GlobalLock関数 :指定のメモリ領域の最初のバイトへのポインタを取得する
GlobalUnlock関数 :指定のメモリ領域のロックを解除する
MoveMemory関数 :指定のメモリ領域をある場所から別の場所に移動する
「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。
また、その他のクリップボードの操作に関しては下記も併せて参照下さい。
・クリップボード内のテキストデータ(文字列)を取得する方法
・クリップボードにビットマップ画像をセットする方法
サンプルコード
クリップボード内に指定の文字列をセットする方法は下記の通りです。
標準モジュールにコピーペーストして「main」を実行することで、SetClipboardText関数の引数として入力した文字列をクリップボード内にセットすることができます。
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 |
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 EmptyClipboard Lib "user32" () As Long Private Declare PtrSafe Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hMem As LongPtr) As LongPtr Private Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As LongPtr) 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 Sub MoveMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As LongPtr) Const CF_UNICODETEXT As Long = 13 Const GMEM_MOVEABLE As Long = &H2 Const GMEM_ZEROINIT As Long = &H40 Const GHND As Long = (GMEM_MOVEABLE Or GMEM_ZEROINIT) '---------------------------------------------------------------- ' メイン処理 '---------------------------------------------------------------- Sub main() Call SetClipboardText("Text") End Sub '---------------------------------------------------------------- ' 入力した文字列をクリップボードに設定する ' sUniText:クリップボードに貼り付ける文字列 '---------------------------------------------------------------- Public Sub SetClipboardText(sUniText As String) Dim hMem As LongPtr Dim lLen As LongPtr Dim hPtr As LongPtr '入力引数のバイト数取得 lLen = LenB(sUniText) '引数のバイト数分のメモリの確保(メモリハンドルを取得) hMem = GlobalAlloc(GHND, lLen + 2) If hMem <> 0 Then 'メモリハンドルの先頭へのポインタを取得 hPtr = GlobalLock(hMem) '確保したメモリ部分に入力された文字列データを移動する Call MoveMemory(hPtr, StrPtr(sUniText), lLen) 'メモリのロックを解除 Call GlobalUnlock(hMem) 'クリップボードを開く If OpenClipboard(0&) <> 0 Then 'クリップボード初期化 Call EmptyClipboard 'クリップボードにデータをセット Call SetClipboardData(CF_UNICODETEXT, hMem) 'クリップボードを閉じる Call CloseClipboard End If End If End Sub |
コード解説
Windows APIを使ってクリップボード内に文字列をセットするには下記の手順を行います。
以下ではこれら処理の中でも核となるメモリ操作部分(1~5)の内容を解説していきます。
2. GlobalAlloc関数で取得したサイズのメモリ領域を確保しそのメモリハンドルを取得
3. GlobalLock関数を使って確保したメモリ領域の先頭のポインタを取得
4. MoveMemory関数で確保したメモリ領域に入力された文字列データを移動する
5. GlobalUnlock関数でハンドルが示すメモリ領域のロックを解除する
6. OpenClipboard関数でクリップボードを開く
7. EmptyClipboard関数でクリップボード内のデータをクリア
8. SetClipboardData関数でクリップボードに確保したメモリ領域内のデータをセットする
9. CloseClipboard関数でクリップボードを閉じる
クリップボード内にセットする文字列を格納するためのメモリ領域を用意する
VBAでクリップボード内に文字列をセットするにはWindows APIのSetClipboardData関数を使います。このときこの関数の引数として、クリップボード内にセットするデータが格納されたメモリ領域へのハンドルを用意しておく必要があります。
メモリ領域を用意するにはGlobalAlloc関数を使います。どれくらいのサイズの領域を確保するのかを指定する必要があるため、文字列の入った変数(sUniText)のバイト数をLenB関数で求めてGlobalAlloc関数に引数として渡します。このとき、メモリ上で文字の終わりを表すためのNULL終端文字分を含めたサイズで指定する必要があるため「求めた値+2バイト」のサイズで指定します。これにより確保したメモリ領域へのハンドル(hMem)を取得することができます。
確保したメモリ領域に変数内の文字列を移動
SetClipboardData関数に引数として渡すためのクリップボード内にセットするデータが格納されたメモリ領域へのハンドル(hMem)は用意できましたが、この時点ではセットするデータが空なので、文字列の入った変数(sUniText)の値をこのメモリ領域に移動させる必要があります。
メモリ間のデータ移動を行うにはMoveMemory関数を使います。
MoveMemory関数を使うには「移動前のメモリ領域の先頭へのポインタ」と「移動後のメモリ領域の先頭へのポインタ」、「移動するデータのサイズ」が必要になります。
「移動前のメモリ領域の先頭へのポインタ」は変数(sUniText)へのポインタなので、StrPtr関数を使うことで取得が可能です。「移動後のメモリ領域の先頭へのポインタ」は確保したメモリ領域へのハンドル(hMem)を引数としてGlobalLock関数を使うことで取得することができます。「移動するデータのサイズ」はLenB関数で求めた変数のサイズをそのまま利用します。
これらを引数としてMoveMemory関数を使うことで、変数(sUniText)内の文字列データを確保したメモリ領域に移動させることができます。あとは確保したメモリ領域へのハンドル(hMem)をSetClipboardData関数の引数として入力すれば変数(sUniText)内の文字列をクリップボードにセットしたことになります。(※実際は確保したメモリ領域内のデータをセットしている)
※GlobalLock関数の使用後はGlobalUnlock関数を使ってメモリのロックを解除する必要があります。
関連情報
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:クリップボードから情報を取得する – VBA