2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

アプリのスクリーンショットをpngファイルにする

Last updated at Posted at 2019-03-23

概要

アプリのスクリーンショットをpngファイルにするマクロ。
事前にC#をビルドする必要あり。

screenshot_to_png.gif

  • 画像ではマクロをショートカットに登録しています。

背景

アプリのスクリーンショット画像をセルに書いてあるファイル名で保存したい。
「スクリーンショットを取ってExcelに貼り付ける」記事は調べると出てくる。

が、ちょっと違う。コレジャナイ。

嵌ったポイント1 - Excelへの貼り付け用の例しか見つからない

Excelだけでクリップボードからpngファイル化する例はほとんど見つけられませんでした。
近いのは・・・ペイント(mspaint.exe), ADODB, SendKeysを駆使した

です。

C#でクリップボード→pngファイルを組んでしまったほうが安定するかな・・・と考えて, C#でコンソールぽく実装します(作り方はフォームアプリ)。

嵌ったポイント2 - アプリが最小化されているとうまく取得できない

最小化されている場合, AppActivateだけではアプリ画面が最前面化されないようです。

そこで, Word Taskオブジェクトを使って, ウィンドウの状態を標準に戻してからアクティブにしています。

' ターゲットアプリのタイトルバー文字列
Const APP_TITLE = "ターゲット"
・・・
    '****
    ' 1. ターゲットアプリをアクティブにする
    '****
    
    ' 最小化されている場合, AppActivateしても画面が出てこない.
    ' WordのTaskオブジェクトでwindowStateを操作して, 標準の状態に戻したあと,
    ' アクティブにする
    
    If objWord.Tasks(APP_TITLE).WindowState = 2 Then
        objWord.Tasks(APP_TITLE).WindowState = 0
    End If
    
    Dim AppFullName
    AppFullName = objWord.Tasks(APP_TITLE).Name
    
    AppActivate AppFullName, False

よく考えると, **objWord.Tasks()にActivateメソッドがある**んだった・・・以下でよかったかな。。。

' ターゲットアプリのタイトルバー文字列
Const APP_TITLE = "ターゲット"
・・・
    '****
    ' 1. ターゲットアプリをアクティブにする
    '****
    
    ' 最小化されている場合, AppActivateしても画面が出てこない.
    ' WordのTaskオブジェクトでwindowStateを操作して, 標準の状態に戻したあと,
    ' アクティブにする
    
    If objWord.Tasks(APP_TITLE).WindowState = 2 Then
        objWord.Tasks(APP_TITLE).WindowState = 0
    End If
    objWord.Tasks(APP_TITLE).Activate

嵌ったポイント3 - Windowsのアニメーション効果が有効だとタイミングが合わない

Windows10のアニメーション効果が有効だと, AppActivate後に少しWaitを入れないとダメなようです。
「アニメーション後」を取る方法がなさそうだったのでSleepでアクティブ待ちをしています。

    '****
    ' 2. ターゲットアプリのアクティブ待ち
    '****
    ' Windows10のアニメーション効果が有効な場合, AppActivateのあと少しウェイトを入れる必要がある
    Call Sleep(300) 

嵌ったポイント4 - AppActivateは完全なタイトルバーの文字列でないといけない

objWord.Tasks(APP_TITLE)は部分一致でいける一方でAppActivateは完全なタイトルバーの文字列(プロセスのフレンドリ名?)でないとダメなようです。

objWord.Tasks(APP_TITLE).Nameで取得したものAppActivateに渡します。

    Dim AppFullName
    AppFullName = objWord.Tasks(APP_TITLE).Name

    AppActivate AppFullName, False

コード

VBA - 主体となる部分

Option Explicit

' win32api
Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

'仮想キーコード
Const VK_SNAPSHOT = &H2C ' Printscreen
Const VK_MENU = &H12 ' Alt

' ターゲットアプリのタイトルバー文字列
Const APP_TITLE = "ターゲット"


' ターゲットアプリのスクリーンショット画像(.png)を取得する.
' 出力ファイル名は選択したセルの文字列を使う
Public Sub GetScreenshotPng()

    Dim objWsh
    Set objWsh = CreateObject("WScript.Shell")
    
    Dim objFso
    Set objFso = CreateObject("Scripting.FileSystemObject")

    Dim objWord
    Set objWord = CreateObject("Word.Application")


    '****
    ' 0. エラーチェック
    '****
    ' ターゲットアプリの存在確認
    If Not objWord.Tasks.Exists(APP_TITLE) Then
        MsgBox APP_TITLE & "が見つかりません"
        GoTo ReleaseObjects
    End If

    ' ファイル名の確認
    Dim outFileName
    outFileName = Selection.Value
    If outFileName = "" Then
        MsgBox "出力ファイル名が空白です"
        GoTo ReleaseObjects
    End If
    
    
    '****
    ' 1. ターゲットアプリをアクティブにする
    '****
    
    ' 最小化されている場合, AppActivateしても画面が出てこない.
    ' WordのTaskオブジェクトでwindowStateを操作して, 標準の状態に戻したあと,
    ' アクティブにする
    
    If objWord.Tasks(APP_TITLE).WindowState = 2 Then
        objWord.Tasks(APP_TITLE).WindowState = 0
    End If
    
    Dim AppFullName
    AppFullName = objWord.Tasks(APP_TITLE).Name
    
    AppActivate AppFullName, False
    
    
    '****
    ' 2. ターゲットアプリのアクティブ待ち
    '****
    ' Windows10のアニメーション効果が有効な場合, AppActivateのあと少しウェイトを入れる必要がある
    Call Sleep(300)
    

    '****
    ' 3. Alt + PrintScreenを押下する
    '****
    'Alt + PrintScreen
    Call keybd_event(VK_MENU, 0, 1, 0) 'Alt押下
    Call keybd_event(VK_SNAPSHOT, 0, 1, 0) 'PrintScreen押下
    Call keybd_event(VK_SNAPSHOT, 0, 3, 0) 'PrintScreen離し
    Call keybd_event(VK_MENU, 0, 3, 0) 'Alt離し


    '****
    ' 4. クリップボードの画像をpngファイル化する
    '****
    
    ' get_clipboard_image.exeのパスを作成(.xlsmと同じフォルダにあることを期待)
    Dim cmd
    cmd = """" & objFso.BuildPath(ThisWorkbook.Path, "get_clipboard_image.exe") & """"
    
    ' get_clipboard_image.exeの引数(出力ファイル名)を作成(.xlsmと同じフォルダに出力することを期待)
    Dim arg
    arg = """" & objFso.BuildPath(ThisWorkbook.Path, Selection.Value) & """"
    
    ' 「get_clipboard_image.exe 出力ファイル名」を実行
    Call objWsh.Run(cmd & " " & arg, 0, True)

    
ReleaseObjects:
    objWord.Quit
    Set objWord = Nothing
    Set objFso = Nothing
    Set objWsh = Nothing
End Sub

注1

xlsmと同じフォルダに「get_clipboard_image.exe」が置かれることを決めうちした実装になっています。

png出力先も「xlsmと同じフォルダ」の決めうちです。ファイル名だけ選択したセルからとってきています。

注2

' ターゲットアプリのタイトルバー文字列
Const APP_TITLE = "ターゲット"

を変更することでスクリーンショットを取るアプリを変更できます。

この記事ではHTAで作成したダミーアプリをターゲットにしています。

注3

簡単化のため, マクロ呼び出しごとに各種オブジェクトを生成+破棄してますが、 実際使うときはWorkBookを開いたときに生成してそれを使いまわす形がいいです。
※ 概要の動画gifのように特に最初のスクリーンショットが重くなる。

注4

objWord.Tasks(APP_TITLE)は部分一致で判定しているので, 似ているタイトルのアプリがいるとうまく動作しない可能性があります。objWord.Tasks(APP_TITLE)をFor Eachでまわして, フルネームを判定に使えばもう少しマシになると思います。

フルネーム
    Dim AppFullName
    AppFullName = objWord.Tasks(APP_TITLE).Name

C# - クリップボードの画像をpngファイル化するアプリ

コンソール画面が出ると気になるので「何もフォームを作らないフォームアプリ」を作成します。
プロジェクト作成時は「Windowsフォームアプリケーション」を指定し,

image.png

プロジェクト名を「get_clipboard_image」とします。
image.png

ソリューションエクスプローラでProgram.csを開きます。
image.png

これを以下のコードで置き換えます。

Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;//追加
using System.Drawing.Imaging;//追加

namespace get_clipboard_image
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static int Main(string[] args)
        {
            //Application.EnableVisualStyles();
            //Application.SetCompatibleTextRenderingDefault(false);
            //Application.Run(new Form1());

            if(args.Length == 1)
            {
                var fname = args[0];

                var clip = Clipboard.GetDataObject();
                // 画像ファイルのみ取り込み
                var bmp = clip.GetData(typeof(Bitmap)) as Bitmap;
                if (bmp != null)
                {
                    bmp.Save(fname, ImageFormat.Png);
                    Clipboard.Clear();
                    return 0;
                }
                else
                {
                    MessageBox.Show("クリップボードに画像が見つかりませんでした。");
                    return 1;
                }
            }
            else
            {
                MessageBox.Show("使い方:get_clipboard_image.exe [ファイル名]");
                return 2;
            }
        }
    }
}

「Release」「Any CPU」でビルドすればget_clipboard_image.exeができます。
xlsmと同じフォルダにおいてください。

image.png

html - ターゲットアプリ

ダミーのターゲットアプリ。
なんでもよかったので古の技術HTA
テキストエディタに貼り付けて「target.hta」で保存すれば終わりです。

ダミーのターゲットアプリは、「<html><head><title>ターゲット</title></head></html>」の一行だけでもいいですし, 「ターゲット.txt」をメモ帳で開いても代替できます。

target.hta
<html>
<head>
<title>ターゲット</title>
</head>
<script type="text/javascript">
window.onload = function myCheck(){
    window.resizeTo(400,120);
    start=new Date();
    setInterval("DispTime()",1);
}

function DispTime(){	
    now = new Date();
    document.getElementById("display").innerText = now.getTime() - start.getTime()
}	
</script>
<body>
<font size=7><div id="display"></div></font>
</body>
</html>

参考

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?