t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA スクリーンショットを撮るたびに自動でシートに張り付けるマクロ

私は仕事でサーバー管理をしているが、作業を行う際にスクリーンショットで証跡を残す必要がある。

Alt+PrintScreenでカレントウインドウをキャプチャ―し、Excelに切り替えてCtrl+Vで張り付ける。
作業終了まで延々とこれを繰り返す。

WinShotのようなツールを使って撮りためた画像を後からマクロで取り込むということも検討したが、あいにくフリーソフトの類は勝手にインストールすることはできず、なんとか楽にする方法は無いかと模索していた。

今日たまたまこれを実現するアイデアが浮かび、実際にコードを書いてみたらうまく動いたので自分用のメモも兼ねてここに記しておこうと思う。

画像サイズや張り付け位置の調整は後からコードに組み込めば良いので、今回のサンプルでは考慮していない。

Declare Function OpenClipboard Lib "user32" (Optional ByVal hwnd As Long = 0) As Long
Declare Function CloseClipboard Lib "user32" () As Long
Declare Function EmptyClipboard Lib "user32" () As Long

Sub AutoCapture()
    MsgBox "AutoCaptureを開始します。" & vbNewLine & _
        "終了するには任意のシートのA1セルにExitと入力してください。", vbInformation
    Dim CB As Variant
    Do While True
        CB = Application.ClipboardFormats
        If StrConv(ActiveSheet.Cells(1, 1).Value, vbUpperCase) = "EXIT" Then GoTo Quit
        If CB(1) <> -1 Then
            For i = 1 To UBound(CB)
                If CB(i) = xlClipboardFormatBitmap Then
                    ActiveSheet.Paste Destination:=Range("A5").Offset(OffsetY, 0)
                    OffsetY = OffsetY + 10
                    
                    'クリップボードを空にする。
                    OpenClipboard
                    EmptyClipboard
                    CloseClipboard
                End If
            Next i
        End If
        DoEvents
    Loop
    
Quit:
    MsgBox "AutoCaptureを停止しました。", vbInformation
    ActiveSheet.Cells(1, 1).ClearContents
End Sub

AutoCaptureを実行するとDo文で無限ループに入り、常時クリップボードの中身を監視する。もし画像が入ってきたらセルに張り付け、その後クリップボードを空にする。そして再び画像が入ってくるまで無限ループする。

マクロを終了する条件は、ActiveSheetのA1セルにexitと入力すること。
無限ループでは、ループの最後に入れているDoEventsが非常に重要で、これが無いとすぐにExcelはフリーズする。
(本当はスリープも挟んだほうが良いかもしれない。)

マクロ実行中もExcelは操作でき、他のマクロの実行もできるようだ(Windows10 Excel2013で検証済)。

余談

マクロ自体のアイデアは1年ほど前からあったのだが、当初はキーフックでPrintScreenが押されたことを検知してイベントで処理できないかと考えていた。
また、無限ループで回したりしたら、同じ画像が無数に張り付いてしまうと思い込んでおり、「さっきと同じ画像かどうか」をどうやって判定させようか悩んでいた。

今日たまたまクリップボードに別のテキストをセットして画像を消したら良いということに気がついてコードを書いたらうまくいったのだが、考えてみればクリップボードの中身を消してしまえば良いわけで、なぜ最初にこれを思いつかなかったのか未だに理解しがたい。

クリップボードを空にする処理は以下のサイトを参考にした。
www7b.biglobe.ne.jp


ちなみに、無限ループを使うことを思いついたのは、この本のおかげ。

Excel VBA アクションゲーム作成入門 Excel 2007/2003/2002 対応

Excel VBA アクションゲーム作成入門 Excel 2007/2003/2002 対応

この本では無限ループではなく、「恒常ループ」と呼んでいる。

ビジネスアプリケーションのプログラミングを習ってきた私にとって無限ループは一種のバグである。終了条件を間違えて無限ループに陥ったコードはアプリのフリーズを引き起こし、強制終了させられる。
しかし、ゲームのように常に動きつづけるプログラムにとって、恒常ループは基本中の基本のようだ。

結局ゲームは作っていないが、頭の片隅に「恒常ループ」というキーワードがあったからこそ、今回のマクロを作ることができた。
やはり何事も経験である。分野の違うことでも少しかじっておくと、そこから広がる世界もあるのだと思った。

2015/11/29追記

改良版を作成したので、こちらの記事もどうぞ。
thom.hateblo.jp

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。