御影 功の日記

自作ゲームは↑のものおきに置いてあります。
2004 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2005 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 |
2010 | 03 | 09 |
2012 | 01 | 02 |
2013 | 02 | 05 |

2013-05-18 (Sat)

[][]画面キャプチャツールその4

前回で完成と言ったな?あれは嘘だ。

・保存フォルダをフォルダ選択ダイアログで選択するためのボタンとエクスプローラでフォルダを開くボタンを追加

 このスクリプトpowershellコマンドで実行するときに-Staオプションをつけないとフォルダ選択ダイアログを開くときにエラーを吐いて動かない。シングルスレッドアパートメントがどうのとかいう理由らしいけどよくわからん。

 powershellコマンドのデフォルトはマルチスレッドアパートメントっぽいけど、powershell_ise上で実行するとデフォルトでシングルスレッドアパートメントになってるっぽい。powershell_iseは重いし前回実行時の変数残ってるし実行時の挙動は違うしで開発環境としては欠陥品もいいところなんだがなんでこんなことになってしまったのか…

####################################################################################################
# 前準備
####################################################################################################

# スクリプト実行環境を設定
$ErrorActionPreference = "Stop"    # エラー発生時は実行中断
Set-StrictMode -Version Latest     # 文法チェックをオン

# .NETアセンブリをインポート
Add-Type -AssemblyName System.Windows.Forms

# ホットキー登録用WindowsAPI関数をインポート
Add-Type -Namespace Win32APIs -Name HotKey -MemberDefinition @"
    [ DllImport( "user32.dll" ) ] public static extern int RegisterHotKey( IntPtr hWnd, int id, int modKey, int key );    // ホットキー追加関数
    [ DllImport( "user32.dll" ) ] public static extern int UnregisterHotKey( IntPtr hWnd, int id );                       // ホットキー削除関数
    public const int MOD_ALT     = 0x01;    // Altキーコード
    public const int MOD_CONTROL = 0x02;    // Ctrlキーコード
    public const int MOD_SHIFT   = 0x04;    // Shiftキーコード
    public const int VK_F12      = 0x7B;    // F12キーコード
"@

# ホットキーイベント対応Buttonクラスを宣言
Add-Type -ReferencedAssemblies System.Windows.Forms -TypeDefinition @"
    using System;
    using System.Windows.Forms;
    public class ButtonWithHotKey : Button {
        public const int WM_HOTKEY = 0x0312;    // ホットキーイベントコード(publicメンバをひとつも宣言しないとなぜか警告が出るのでpublicにしておく)
        protected override void WndProc( ref Message m ){
            if( m.Msg == WM_HOTKEY ) OnClick( new EventArgs() );    // ホットキーが押されたらクリックイベントを発生させる
            base.WndProc( ref m );
        }
    }
"@

# アクティブフォーム取得用WindowsAPI関数をインポート
Add-Type -Namespace Win32APIs -Name ActiveWindow -MemberDefinition @"
    public struct Rect{ public int left, top, right, bottom; }                                               // 四角座標構造体を宣言
    [ DllImport( "user32.dll" ) ] public static extern IntPtr GetForegroundWindow();                         // アクティブウィンドウハンドル取得API関数をインポート
    [ DllImport( "user32.dll" ) ] public static extern int GetWindowRect( IntPtr hwnd, out Rect lpRect );    // ウィンドウ位置サイズ取得API関数をインポート
    
    // アクティブウィンドウ位置サイズ取得関数を宣言
    public static Rect GetForegroundWindowRect(){
        Rect rect = new Rect();
        GetWindowRect( GetForegroundWindow(), out rect );
        return rect;
    }
"@



####################################################################################################
# イベントハンドラ用スクリプトブロック群
####################################################################################################

# フォームのパネルの保存フォルダパネルの"保存フォルダ参照"ボタンのクリックイベントハンドラ
$Form_Panel_FolderPanel_SelectButton_Click = {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    
    # フォルダ参照ダイアログを生成
    $FolderSelectDialog              = New-Object System.Windows.Forms.FolderBrowserDialog
    $FolderSelectDialog.Description  = "保存フォルダを選択してください"
    $FolderSelectDialog.RootFolder   = [System.Environment+SpecialFolder]::MyComputer

    # フォルダ参照ダイアログのデフォルト選択フォルダを設定
    $FolderSelectDialog.SelectedPath = $Form_Panel_FolderPanel_PathTextBox.Text
    
    # フォルダ参照ダイアログを表示
    if( $FolderSelectDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK )
    {
        $Form_Panel_FolderPanel_PathTextBox.Text = $FolderSelectDialog.SelectedPath
    }
    
    # フォルダ参照ダイアログを破棄
    $FolderSelectDialog.Dispose()
}



# フォームのパネルの保存フォルダパネルの"保存フォルダ開く"ボタンのクリックイベントハンドラ
$Form_Panel_FolderPanel_OpenButton_Click = {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    
    # 保存フォルダ有無をチェック
    if( $Form_Panel_FolderPanel_PathTextBox.Text -eq "" -or -not $( Test-Path -PathType Container -LiteralPath $Form_Panel_FolderPanel_PathTextBox.Text ) )
    {
        $Icon.ShowBalloonTip( 1, $Null, "保存フォルダが見つからないよー!", [System.Windows.Forms.ToolTipIcon]::None )
        return
    }
    
    # 保存フォルダをエクスプローラで開く
    explorer $Form_Panel_FolderPanel_PathTextBox.Text
}



# フォームのパネルの"キャプチャホットキー"チェックボックスのチェック状態変更イベントハンドラ
$Form_Panel_HotKeyCheckBox_CheckedChanged = {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    
    if( $Sender.Checked )
    {
        # ホットキーを登録
        $Result1 = [Win32APIs.HotKey]::RegisterHotKey( $Form_Panel_ScreenCaptureButton.Handle, 1, [Win32APIs.HotKey]::MOD_CONTROL -bor [Win32APIs.HotKey]::MOD_SHIFT,                                  [Win32APIs.HotKey]::VK_F12 )
        $Result2 = [Win32APIs.HotKey]::RegisterHotKey( $Form_Panel_WindowCaptureButton.Handle, 1, [Win32APIs.HotKey]::MOD_CONTROL -bor [Win32APIs.HotKey]::MOD_SHIFT -bor [Win32APIs.HotKey]::MOD_ALT, [Win32APIs.HotKey]::VK_F12 )
        
        # 成否メッセージを表示(そんなに失敗しないと思うので成否判定は適当)
        if( $Result1 -ne 0 -and $Result2 -ne 0 ){ $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えるようにしたよー!",         [System.Windows.Forms.ToolTipIcon]::None ) }
        else                                    { $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えるようにできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None ) }
    }
    else
    {
        # ホットキーを解除
        $Result1 = [Win32APIs.HotKey]::UnregisterHotKey( $Form_Panel_ScreenCaptureButton.Handle, 1 )
        $Result2 = [Win32APIs.HotKey]::UnregisterHotKey( $Form_Panel_WindowCaptureButton.Handle, 1 )
        
        # 成否メッセージを表示(そんなに失敗しないと思うので成否判定は適当)
        if( $Result1 -ne 0 -and $Result2 -ne 0 ){ $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えなくしたよー!",         [System.Windows.Forms.ToolTipIcon]::None ) }
        else                                    { $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えなくできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None ) }
    }
}



# フォームのパネルの"画面キャプチャ"ボタンのクリックイベントハンドラ
$Form_Panel_ScreenCaptureButton_Click = {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    
    # 保存フォルダ有無チェック
    if( $Form_Panel_FolderPanel_PathTextBox.Text -eq "" -or -not $( Test-Path -PathType Container -LiteralPath $Form_Panel_FolderPanel_PathTextBox.Text ) )
    {
        $Icon.ShowBalloonTip( 1, $Null, "保存フォルダが見つからないよー!", [System.Windows.Forms.ToolTipIcon]::None )
        return
    }
    
    # 状態復元のためにフォームの表示状態を保存
    $ShowedForm = $Form.Visible

    # フォームと通知領域アイコンを隠す
    if( $ShowedForm )
    {
        $Form.Hide()
        Sleep -Milliseconds 200    # フォームが隠れるのを待つ(待たないとキャプチャでフォームが写ってしまう)
    }
    $Icon.Visible = $False
    
    # 画面キャプチャ取得
    $FilePath = Join-Path $Form_Panel_FolderPanel_PathTextBox.Text $( Get-Date -UFormat "capture_%Y%m%d_%H%M%S.png" )
    $Bitmap   = New-Object System.Drawing.Bitmap -ArgumentList $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width ), $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height )
    $Graphics = [System.Drawing.Graphics]::FromImage( $Bitmap )
    $Graphics.CopyFromScreen( 0, 0, 0, 0, $Bitmap.Size )
    $Bitmap.Save( $FilePath )
    
    # フォームと通知領域アイコンを戻す
    if( $ShowedForm ){ $Form.Show() }
    $Icon.Visible = $True
    
    # 結果メッセージ表示
    $Icon.ShowBalloonTip( 1, $Null, "ウィンドウキャプチャしたよー!`n" + $FilePath, [System.Windows.Forms.ToolTipIcon]::None )
    
    # 変数後始末
    $Graphics.Dispose()
    $Bitmap.Dispose()
}



# フォームのパネルの"ウィンドウキャプチャ"ボタンのクリックイベントハンドラ
$Form_Panel_WindowCaptureButton_Click = {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    
    # 保存フォルダ有無チェック
    if( $Form_Panel_FolderPanel_PathTextBox.Text -eq "" -or -not $( Test-Path -PathType Container -LiteralPath $Form_Panel_FolderPanel_PathTextBox.Text ) )
    {
        $Icon.ShowBalloonTip( 1, $Null, "保存フォルダが見つからないよー!", [System.Windows.Forms.ToolTipIcon]::None )
        return
    }
    
    # 状態復元のためにフォームの表示状態を保存
    $ShowedForm = $Form.Visible

    # フォームと通知領域アイコンを隠す
    if( $ShowedForm )
    {
        $Form.Hide()
        Sleep -Milliseconds 200    # フォームが隠れるのを待つ(待たないとキャプチャでフォームが写ってしまう)
    }
    $Icon.Visible = $False
    
    # ウィンドウキャプチャ取得
    $FilePath = Join-Path $Form_Panel_FolderPanel_PathTextBox.Text $( Get-Date -UFormat "capture_%Y%m%d_%H%M%S.png" )
    $Rect     = [Win32APIs.ActiveWindow]::GetForegroundWindowRect();
    $Bitmap   = New-Object System.Drawing.Bitmap -ArgumentList $( $Rect.right - $Rect.left ), $( $Rect.bottom - $Rect.top )
    $Graphics = [System.Drawing.Graphics]::FromImage( $Bitmap )
    $Graphics.CopyFromScreen( $Rect.left, $Rect.top, 0, 0, $Bitmap.Size )
    $Bitmap.Save( $FilePath )
    
    # フォームと通知領域アイコンを戻す
    if( $ShowedForm ){ $Form.Show() }
    $Icon.Visible = $True
    
    # 結果メッセージ表示
    $Icon.ShowBalloonTip( 1, $Null, "ウィンドウキャプチャしたよー!`n" + $FilePath, [System.Windows.Forms.ToolTipIcon]::None )
    
    # 変数後始末
    $Graphics.Dispose()
    $Bitmap.Dispose()
}



# フォームのクローズ中イベントハンドラを登録
$Form_Closing = {
    param( [System.Object] $Sender, [System.ComponentModel.CancelEventArgs] $E )
    
    # フォームクローズをキャンセルして隠すだけにする
    $E.Cancel = $True
    $Sender.Hide()
}



# 通知領域アイコンのコンテキストメニューの"終了"項目のクリックイベントハンドラ
$Icon_Menu_QuitItem_Click = {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    
    # アプリケーションを終了
    [System.Windows.Forms.Application]::Exit()
}



# 通知領域アイコンのクリックイベントハンドラ
$Icon_MouseClick = {
    param( [System.Object] $Sender, [System.Windows.Forms.MouseEventArgs] $E )
    
    # 右クリック処理
    if( $E.Button -eq [System.Windows.Forms.MouseButtons]::Left )
    {
        # フォームの表示/非表示を切り替え
        if( $Form.Visible )
        {
            $Form.Hide()
        }
        else
        {
            $Form.Show()
            $Form.Activate()
        }
    }
}



####################################################################################################
# フォームを生成
####################################################################################################

# フォームのパネルの保存フォルダパネルの"保存フォルダパス"テキストボックスを生成
$Form_Panel_FolderPanel_PathTextBox      = New-Object System.Windows.Forms.TextBox
$Form_Panel_FolderPanel_PathTextBox.Dock = [System.Windows.Forms.DockStyle]::Fill
$Form_Panel_FolderPanel_PathTextBox.Text = "保存フォルダパス"

# フォームのパネルの保存フォルダパネルの"保存フォルダ参照"ボタンを生成
$Form_Panel_FolderPanel_SelectButton              = New-Object System.Windows.Forms.Button
$Form_Panel_FolderPanel_SelectButton.AutoSize     = $True
$Form_Panel_FolderPanel_SelectButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_FolderPanel_SelectButton.Text         = "参照"
$Form_Panel_FolderPanel_SelectButton.add_Click( $Form_Panel_FolderPanel_SelectButton_Click )

# フォームのパネルの保存フォルダパネルの"保存フォルダ開く"ボタンを生成
$Form_Panel_FolderPanel_OpenButton              = New-Object System.Windows.Forms.Button
$Form_Panel_FolderPanel_OpenButton.AutoSize     = $True
$Form_Panel_FolderPanel_OpenButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_FolderPanel_OpenButton.Text         = "開く"
$Form_Panel_FolderPanel_OpenButton.add_Click( $Form_Panel_FolderPanel_OpenButton_Click )

# フォームのパネルの保存フォルダパネルを生成
$Form_Panel_FolderPanel               = New-Object System.Windows.Forms.FlowLayoutPanel
$Form_Panel_FolderPanel.AutoSize      = $True
$Form_Panel_FolderPanel.AutoSizeMode  = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_FolderPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::LeftToRight
$Form_Panel_FolderPanel.SuspendLayout()
$Form_Panel_FolderPanel.Controls.Add( $Form_Panel_FolderPanel_PathTextBox )
$Form_Panel_FolderPanel.Controls.Add( $Form_Panel_FolderPanel_SelectButton )
$Form_Panel_FolderPanel.Controls.Add( $Form_Panel_FolderPanel_OpenButton )
$Form_Panel_FolderPanel.ResumeLayout()

# フォームのパネルの"キャプチャホットキー"チェックボックスを生成
$Form_Panel_HotKeyCheckBox          = New-Object System.Windows.Forms.CheckBox
$Form_Panel_HotKeyCheckBox.AutoSize = $True
$Form_Panel_HotKeyCheckBox.Text     = "ホットキーを有効化"
$Form_Panel_HotKeyCheckBox.add_CheckedChanged( $Form_Panel_HotKeyCheckBox_CheckedChanged )

# フォームのパネルの"画面キャプチャ"ボタンを生成
$Form_Panel_ScreenCaptureButton              = New-Object ButtonWithHotKey
$Form_Panel_ScreenCaptureButton.AutoSize     = $True
$Form_Panel_ScreenCaptureButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_ScreenCaptureButton.Text         = "画面キャプチャ (Ctrl+Shift+F12)"
$Form_Panel_ScreenCaptureButton.add_Click( $Form_Panel_ScreenCaptureButton_Click )

# フォームのパネルの"ウィンドウキャプチャ"ボタンを生成
$Form_Panel_WindowCaptureButton              = New-Object ButtonWithHotKey
$Form_Panel_WindowCaptureButton.AutoSize     = $True
$Form_Panel_WindowCaptureButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_WindowCaptureButton.Text         = "ウィンドウキャプチャ (Ctrl+Shift+Alt+F12)"
$Form_Panel_WindowCaptureButton.add_Click( $Form_Panel_WindowCaptureButton_Click )

# フォームのパネルを生成
$Form_Panel               = New-Object System.Windows.Forms.FlowLayoutPanel
$Form_Panel.AutoSize      = $True
$Form_Panel.AutoSizeMode  = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
$Form_Panel.SuspendLayout()
$Form_Panel.Controls.Add( $Form_Panel_FolderPanel )
$Form_Panel.Controls.Add( $Form_Panel_HotKeyCheckBox )
$Form_Panel.Controls.Add( $Form_Panel_ScreenCaptureButton )
$Form_Panel.Controls.Add( $Form_Panel_WindowCaptureButton )
$Form_Panel.ResumeLayout()

# フォームを生成
$Form                 = New-Object System.Windows.Forms.Form
$Form.AutoSize        = $True
$Form.AutoSizeMode    = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form.MaximizeBox     = $False
$Form.MinimizeBox     = $False
$Form.Text            = "画面キャプチャツール"
$Form.SuspendLayout()
$Form.Controls.Add( $Form_Panel )
$Form.ResumeLayout()
$Form.add_Closing( $Form_Closing )



####################################################################################################
# 通知領域アイコンを生成
####################################################################################################

# 通知領域アイコンのコンテキストメニューの"終了"項目を生成
$Icon_Menu_QuitItem      = New-Object System.Windows.Forms.ToolStripMenuItem
$Icon_Menu_QuitItem.Text = "終了"
$Icon_Menu_QuitItem.add_Click( $Icon_Menu_QuitItem_Click )

# 通知領域アイコンのコンテキストメニューを生成
$Icon_Menu = New-Object System.Windows.Forms.ContextMenuStrip
$Icon_Menu.SuspendLayout()
$Icon_Menu.Items.Add( $Icon_Menu_QuitItem ) | Out-Null
$Icon_Menu.ResumeLayout()

# 通知領域アイコンを生成
$Icon                  = New-Object System.Windows.Forms.NotifyIcon
$Icon.ContextMenuStrip = $Icon_Menu
$Icon.Icon             = $Form.Icon
$Icon.Text             = $Form.Text
$Icon.add_MouseClick( $Icon_MouseClick )



####################################################################################################
# 処理実行
####################################################################################################

# 表示
$Form.Show()
$Icon.Visible = $True
$Icon.ShowBalloonTip( 1, $Null, "おはよーございます!", [System.Windows.Forms.ToolTipIcon]::None )

# イベントループ
[System.Windows.Forms.Application]::Run()



####################################################################################################
# 後始末
####################################################################################################

$Form.Dispose()
$Icon.Dispose()

miffymiffy 2014/04/26 21:55 大変参考になりました。
利用させて頂きます。

トラックバック - http://d.hatena.ne.jp/KouMikage/20130518

2013-02-12 (Tue)

[][]画面キャプチャツールその3

やっぱりウィンドウ単位でもキャプチャ取れないとだめだよねーということで更に機能追加。これで完成。

・ウィンドウキャプチャボタン追加。Alt+PrintScreenキーと同じ範囲をキャプチャする。

・ウィンドウキャプチャのホットキーも追加。Ctrl+Shift+Alt+F12キーでウィンドウキャプチャが動く。

・実は$Graphicsの生成辺りで例外が発生すると「if( $Graphics -ne $Null ){ $Graphics.Dispose() }」で例外を吐いて止まるので、事前に$Graphicsと$BitmapをNullで初期化(初期化というか変数宣言の代替)

 Set-StrictModeしてる状態だと未定義変数はNullと比較すると変数宣言してない例外がでるらしい。

####################################################################################################
# 前準備
####################################################################################################

# スクリプト実行環境を設定
$ErrorActionPreference = "Stop"    # エラー発生時は実行中断
Set-StrictMode -Version Latest     # 文法チェックをオン

# .NETアセンブリをインポート
Add-Type -AssemblyName System.Windows.Forms

# ホットキー登録用WindowsAPI関数をインポート
Add-Type -Namespace Win32APIs -Name HotKey -MemberDefinition @"
    [ DllImport( "user32.dll" ) ] public static extern int RegisterHotKey( IntPtr hWnd, int id, int modKey, int key );    // ホットキー追加関数
    [ DllImport( "user32.dll" ) ] public static extern int UnregisterHotKey( IntPtr hWnd, int id );                       // ホットキー削除関数
    public const int MOD_ALT     = 0x01;    // Altキーコード
    public const int MOD_CONTROL = 0x02;    // Ctrlキーコード
    public const int MOD_SHIFT   = 0x04;    // Shiftキーコード
    public const int VK_F12      = 0x7B;    // F12キーコード
"@

# ホットキーイベント対応Buttonクラスを宣言
Add-Type -ReferencedAssemblies System.Windows.Forms -TypeDefinition @"
    using System;
    using System.Windows.Forms;
    public class ButtonWithHotKey : Button {
        public const int WM_HOTKEY = 0x0312;    // ホットキーイベントコード(publicメンバをひとつも宣言しないとなぜか警告が出るのでpublicにしておく)
        protected override void WndProc( ref Message m ){
            if( m.Msg == WM_HOTKEY ) OnClick( new EventArgs() );    // ホットキーが押されたらクリックイベントを発生させる
            base.WndProc( ref m );
        }
    }
"@

# アクティブフォーム取得用WindowsAPI関数をインポート
Add-Type -Namespace Win32APIs -Name ActiveWindow -MemberDefinition @"
    public struct Rect{ public int left, top, right, bottom; }                                               // 四角座標構造体を宣言
    [ DllImport( "user32.dll" ) ] public static extern IntPtr GetForegroundWindow();                         // アクティブウィンドウハンドル取得API関数をインポート
    [ DllImport( "user32.dll" ) ] public static extern int GetWindowRect( IntPtr hwnd, out Rect lpRect );    // ウィンドウ位置サイズ取得API関数をインポート
    
    // アクティブウィンドウ位置サイズ取得関数を宣言
    public static Rect GetForegroundWindowRect(){
        Rect rect = new Rect();
        GetWindowRect( GetForegroundWindow(), out rect );
        return rect;
    }
"@



####################################################################################################
# フォームのパネルを生成
####################################################################################################

# フォームのパネルの"保存フォルダ"テキストボックスを生成
$Form_Panel_SaveFolderTextBox      = New-Object System.Windows.Forms.TextBox
$Form_Panel_SaveFolderTextBox.Dock = [System.Windows.Forms.DockStyle]::Fill
$Form_Panel_SaveFolderTextBox.Text = "保存フォルダパス"

# フォームのパネルの"キャプチャホットキー"チェックボックスを生成
$Form_Panel_HotKeyCheckBox          = New-Object System.Windows.Forms.CheckBox
$Form_Panel_HotKeyCheckBox.AutoSize = $True
$Form_Panel_HotKeyCheckBox.Text     = "ホットキーを有効化"

# フォームのパネルの"画面キャプチャ"ボタンを生成
$Form_Panel_ScreenCaptureButton              = New-Object ButtonWithHotKey
$Form_Panel_ScreenCaptureButton.AutoSize     = $True
$Form_Panel_ScreenCaptureButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_ScreenCaptureButton.Text         = "画面キャプチャ (Ctrl+Shift+F12)"

# フォームのパネルの"ウィンドウキャプチャ"ボタンを生成
$Form_Panel_WindowCaptureButton              = New-Object ButtonWithHotKey
$Form_Panel_WindowCaptureButton.AutoSize     = $True
$Form_Panel_WindowCaptureButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_WindowCaptureButton.Text         = "ウィンドウキャプチャ (Ctrl+Shift+Alt+F12)"

# フォームのパネルを生成
$Form_Panel               = New-Object System.Windows.Forms.FlowLayoutPanel
$Form_Panel.AutoSize      = $True
$Form_Panel.AutoSizeMode  = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
$Form_Panel.SuspendLayout()
$Form_Panel.Controls.Add( $Form_Panel_SaveFolderTextBox )
$Form_Panel.Controls.Add( $Form_Panel_HotKeyCheckBox )
$Form_Panel.Controls.Add( $Form_Panel_ScreenCaptureButton )
$Form_Panel.Controls.Add( $Form_Panel_WindowCaptureButton )
$Form_Panel.ResumeLayout()

# フォームのパネルの"キャプチャホットキー"チェックボックスのチェック状態変更イベントハンドラを登録
$Form_Panel_HotKeyCheckBox.add_CheckedChanged( {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    if( $Sender.Checked ){
        $Result1 = [Win32APIs.HotKey]::RegisterHotKey( $Form_Panel_ScreenCaptureButton.Handle, 1, [Win32APIs.HotKey]::MOD_CONTROL -bor [Win32APIs.HotKey]::MOD_SHIFT,                                  [Win32APIs.HotKey]::VK_F12 )
        $Result2 = [Win32APIs.HotKey]::RegisterHotKey( $Form_Panel_WindowCaptureButton.Handle, 1, [Win32APIs.HotKey]::MOD_CONTROL -bor [Win32APIs.HotKey]::MOD_SHIFT -bor [Win32APIs.HotKey]::MOD_ALT, [Win32APIs.HotKey]::VK_F12 )
        if( $Result1 -ne 0 -and $Result2 -ne 0 ){ $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えるようにしたよー!",         [System.Windows.Forms.ToolTipIcon]::None ) }    # そんなに失敗しないと思うので成否判定は適当
        else                                    { $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えるようにできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None ) }
    }else{
        $Result1 = [Win32APIs.HotKey]::UnregisterHotKey( $Form_Panel_ScreenCaptureButton.Handle, 1 )
        $Result2 = [Win32APIs.HotKey]::UnregisterHotKey( $Form_Panel_WindowCaptureButton.Handle, 1 )
        if( $Result1 -ne 0 -and $Result2 -ne 0 ){ $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えなくしたよー!",         [System.Windows.Forms.ToolTipIcon]::None ) }    # そんなに失敗しないと思うので成否判定は適当
        else                                    { $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えなくできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None ) }
    }
} )

# フォームのパネルの"画面キャプチャ"ボタンのクリックイベントハンドラを登録
$Form_Panel_ScreenCaptureButton.add_Click( {
    param( [System.Object] $Sender, [System.EventArgs] $E )

    $ShowedForm = $Form.Visible    # 状態復元のためにFormの表示状態を保存
    if( $ShowedForm ){
        $Form.Hide()
        Sleep -Milliseconds 200    # Formが隠れるのを待つ(待たないと画面キャプチャにFormが写ってしまう)
    }
    $Icon.Visible = $False
    
    $Bitmap   = $Null    # 成否チェックのためにNull初期化(やらないと例外発生時に未宣言の変数になってしまう)
    $Graphics = $Null    # 成否チェックのためにNull初期化(やらないと例外発生時に未宣言の変数になってしまう)

    try{
        $FilePath = Join-Path $Form_Panel_SaveFolderTextBox.Text $( Get-Date -UFormat "capture_%Y%m%d_%H%M%S.png" )
        $Bitmap   = New-Object System.Drawing.Bitmap -ArgumentList $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width ), $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height )
        $Graphics = [System.Drawing.Graphics]::FromImage( $Bitmap )
        $Graphics.CopyFromScreen( 0, 0, 0, 0, $Bitmap.Size )
        $Bitmap.Save( $FilePath )
        
        if( $ShowedForm ){ $Form.Show() }
        $Icon.Visible = $True
        $Icon.ShowBalloonTip( 1, $Null, "画面キャプチャしたよー!`n" + $FilePath, [System.Windows.Forms.ToolTipIcon]::None )
    }
    catch{
        if( $ShowedForm ){ $Form.Show() }
        $Icon.Visible = $True
        $Icon.ShowBalloonTip( 1, $Null, "画面キャプチャできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None )
    }
    
    if( $Graphics -ne $Null ){ $Graphics.Dispose() }
    if( $Bitmap -ne $Null ){ $Bitmap.Dispose() }
} )

# フォームのパネルの"ウィンドウキャプチャ"ボタンのクリックイベントハンドラを登録
$Form_Panel_WindowCaptureButton.add_Click( {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    
    $ShowedForm = $Form.Visible    # 状態復元のためにFormの表示状態を保存
    if( $ShowedForm ){
        $Form.Hide()
        Sleep -Milliseconds 200    # Formが隠れるのを待つ(待たないと画面キャプチャにFormが写ってしまう)
    }
    $Icon.Visible = $False
    
    $Bitmap   = $Null    # 成否チェックのためにNull初期化(やらないと例外発生時に未宣言の変数になってしまう)
    $Graphics = $Null    # 成否チェックのためにNull初期化(やらないと例外発生時に未宣言の変数になってしまう)

    try{
        $FilePath = Join-Path $Form_Panel_SaveFolderTextBox.Text $( Get-Date -UFormat "capture_%Y%m%d_%H%M%S.png" )
        $Rect     = [Win32APIs.ActiveWindow]::GetForegroundWindowRect();
        $Bitmap   = New-Object System.Drawing.Bitmap -ArgumentList $( $Rect.right - $Rect.left ), $( $Rect.bottom - $Rect.top )
        $Graphics = [System.Drawing.Graphics]::FromImage( $Bitmap )
        $Graphics.CopyFromScreen( $Rect.left, $Rect.top, 0, 0, $Bitmap.Size )
        $Bitmap.Save( $FilePath )
        
        if( $ShowedForm ){ $Form.Show() }
        $Icon.Visible = $True
        $Icon.ShowBalloonTip( 1, $Null, "ウィンドウキャプチャしたよー!`n" + $FilePath, [System.Windows.Forms.ToolTipIcon]::None )
    }
    catch{
        if( $ShowedForm ){ $Form.Show() }
        $Icon.Visible = $True
        $Icon.ShowBalloonTip( 1, $Null, "ウィンドウキャプチャできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None )
    }
    
    if( $Graphics -ne $Null ){ $Graphics.Dispose() }
    if( $Bitmap -ne $Null ){ $Bitmap.Dispose() }
} )



####################################################################################################
# フォームを生成
####################################################################################################

# フォームを生成
$Form                 = New-Object System.Windows.Forms.Form
$Form.AutoSize        = $True
$Form.AutoSizeMode    = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form.MaximizeBox     = $False
$Form.MinimizeBox     = $False
$Form.Text            = "画面キャプチャツール"
$Form.SuspendLayout()
$Form.Controls.Add( $Form_Panel )
$Form.ResumeLayout()

# フォームのクローズ中イベントハンドラを登録
$Form.add_Closing( {
    param( [System.Object] $Sender, [System.ComponentModel.CancelEventArgs] $E )
    $E.Cancel = $True
    $Sender.Hide()
} )



####################################################################################################
# 通知領域アイコンのコンテキストメニューを生成
####################################################################################################

# 通知領域アイコンのコンテキストメニューの"終了"項目を生成
$Icon_Menu_QuitItem      = New-Object System.Windows.Forms.ToolStripMenuItem
$Icon_Menu_QuitItem.Text = "終了"

# 通知領域アイコンのコンテキストメニューを生成
$Icon_Menu = New-Object System.Windows.Forms.ContextMenuStrip
$Icon_Menu.SuspendLayout()
$Icon_Menu.Items.Add( $Icon_Menu_QuitItem ) | Out-Null
$Icon_Menu.ResumeLayout()

# 通知領域アイコンのコンテキストメニューの"終了"項目のクリックイベントハンドラを登録
$Icon_Menu_QuitItem.add_Click( {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    [System.Windows.Forms.Application]::Exit()
} )



####################################################################################################
# 通知領域アイコンを生成
####################################################################################################

# 通知領域アイコンを生成
$Icon                  = New-Object System.Windows.Forms.NotifyIcon
$Icon.ContextMenuStrip = $Icon_Menu
$Icon.Icon             = $Form.Icon
$Icon.Text             = $Form.Text

# 通知領域アイコンのクリックイベントハンドラを登録
$Icon.add_MouseClick( {
    param( [System.Object] $Sender, [System.Windows.Forms.MouseEventArgs] $E )
    if( $E.Button -eq [System.Windows.Forms.MouseButtons]::Left ){
        if( $Form.Visible ){
            $Form.Hide()
        }else{
            $Form.Show()
            $Form.Activate()
        }
    }
} )



####################################################################################################
# 処理実行
####################################################################################################

# 表示
$Form.Show()
$Icon.Visible = $True
$Icon.ShowBalloonTip( 1, $Null, "おはよーございます!", [System.Windows.Forms.ToolTipIcon]::None )

# イベントループ
[System.Windows.Forms.Application]::Run()



####################################################################################################
# 後始末
####################################################################################################

$Form.Dispose()
$Icon.Dispose()

.NETは自アプリケーションの外側見れない制限があるみたいなのでWindowsAPI叩きまくり。

せっかく.NETで動いてるのにあんまりアンマネージドなところ触りたくないのだけど…

トラックバック - http://d.hatena.ne.jp/KouMikage/20130212

2013-02-09 (Sat)

[][]画面キャプチャツールその2

前回を踏まえて改良。

・画面キャプチャするのにフォームでボタン押さないといけないのはアレなのでホットキー対応。チェックボックスにチェックを入れておけばフォーム隠してる状態でもキャプチャ取れるようになった。

・常駐するプログラムタスクバーに居座ってると気になってしょうがないので×ボタン押したら完全に消えるようにした。通知領域アイコンを叩くと復活する。

・×ボタンでプログラム終了しないようにしたので、通知領域アイコンの右クリックメニューに終了コマンドを追加。

・ついでに常に手前に表示コマンドも追加。

####################################################################################################
# 前準備
####################################################################################################

# スクリプト実行環境を設定
$ErrorActionPreference = "Stop"    # エラー発生時は実行中断
Set-StrictMode -Version Latest     # 文法チェックをオン

# .NETアセンブリをインポート
Add-Type -AssemblyName System.Windows.Forms

# WindowsAPI関数をインポート
Add-Type -Namespace Win32 -Name APIs -MemberDefinition @"
    [ DllImport( "user32.dll" ) ] public static extern int RegisterHotKey( IntPtr hWnd, int id, int modKey, int key );    // ホットキー追加関数
    [ DllImport( "user32.dll" ) ] public static extern int UnregisterHotKey( IntPtr hWnd, int id );                       // ホットキー削除関数
    public const int MOD_CONTROL = 0x02;    // Ctrlキーコード
    public const int MOD_SHIFT   = 0x04;    // Shiftキーコード
    public const int VK_F12      = 0x7B;    // F12キーコード
"@

# ホットキーイベント対応Buttonクラスを宣言
Add-Type -ReferencedAssemblies System.Windows.Forms -TypeDefinition @"
    using System;
    using System.Windows.Forms;
    public class ButtonWithHotKey : Button {
        public const int WM_HOTKEY = 0x0312;    // ホットキーイベントコード(publicメンバをひとつも宣言しないとなぜか警告が出るのでpublicにしておく)
        protected override void WndProc( ref Message m ){
            if( m.Msg == WM_HOTKEY ) OnClick( new EventArgs() );    // ホットキーが押されたらクリックイベントを発生させる
            base.WndProc( ref m );
        }
    }
"@



####################################################################################################
# フォームのパネルを生成
####################################################################################################

# フォームのパネルの"保存フォルダ"テキストボックスを生成
$Form_Panel_SaveFolderTextBox      = New-Object System.Windows.Forms.TextBox
$Form_Panel_SaveFolderTextBox.Dock = [System.Windows.Forms.DockStyle]::Fill
$Form_Panel_SaveFolderTextBox.Text = "保存フォルダパス"

# フォームのパネルの"ホットキー"チェックボックスを生成
$Form_Panel_HotKeyCheckBox          = New-Object System.Windows.Forms.CheckBox
$Form_Panel_HotKeyCheckBox.AutoSize = $True
$Form_Panel_HotKeyCheckBox.Text     = "Ctrl+Shift+F12で画面キャプチャ"

# フォームのパネルの"画面キャプチャ"ボタンを生成
$Form_Panel_CaptureButton              = New-Object ButtonWithHotKey
$Form_Panel_CaptureButton.AutoSize     = $True
$Form_Panel_CaptureButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_CaptureButton.Text         = "画面キャプチャ"

# フォームのパネルを生成
$Form_Panel               = New-Object System.Windows.Forms.FlowLayoutPanel
$Form_Panel.AutoSize      = $True
$Form_Panel.AutoSizeMode  = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
$Form_Panel.SuspendLayout()
$Form_Panel.Controls.Add( $Form_Panel_SaveFolderTextBox )
$Form_Panel.Controls.Add( $Form_Panel_HotKeyCheckBox )
$Form_Panel.Controls.Add( $Form_Panel_CaptureButton )
$Form_Panel.ResumeLayout()

# フォームのパネルの"ホットキー"チェックボックスのチェック状態変更イベントハンドラを登録
$Form_Panel_HotKeyCheckBox.add_CheckedChanged( {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    if( $Sender.Checked ){
        $Result = [Win32.APIs]::RegisterHotKey( $Form_Panel_CaptureButton.Handle, 1, [Win32.APIs]::MOD_CONTROL -bor [Win32.APIs]::MOD_SHIFT, [Win32.APIs]::VK_F12 )
        if( $Result -ne 0 ){ $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えるようにしたよー!",         [System.Windows.Forms.ToolTipIcon]::None ) }
        else               { $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えるようにできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None ) }
    }else{
        $Result = [Win32.APIs]::UnregisterHotKey( $Form_Panel_CaptureButton.Handle, 1 )
        if( $Result -ne 0 ){ $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えないようにしたよー!",         [System.Windows.Forms.ToolTipIcon]::None ) }
        else               { $Icon.ShowBalloonTip( 1, $Null, "ホットキー使えないようにできなかったよー!", [System.Windows.Forms.ToolTipIcon]::None ) }
    }
} )

# フォームのパネルの"画面キャプチャ"ボタンのクリックイベントハンドラを登録
$Form_Panel_CaptureButton.add_Click( {
    param( [System.Object] $Sender, [System.EventArgs] $E )

    $ShowedForm = $Form.Visible    # 状態復元のためにFormの表示状態を保存
    if( $ShowedForm ){
        $Form.Hide()
        Sleep -Milliseconds 200    # Formが隠れるのを待つ(待たないと画面キャプチャにFormが写ってしまう)
    }
    $Icon.Visible = $False
    
    try{
        $FilePath = Join-Path $Form_Panel_SaveFolderTextBox.Text $( Get-Date -UFormat "capture_%Y%m%d_%H%M%S.png" )
        $Bitmap   = New-Object System.Drawing.Bitmap -ArgumentList $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width ), $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height )
        $Graphics = [System.Drawing.Graphics]::FromImage( $Bitmap )
        $Graphics.CopyFromScreen( 0, 0, 0, 0, $Bitmap.Size )
        $Bitmap.Save( $FilePath )
        
        if( $ShowedForm ){ $Form.Show() }
        $Icon.Visible = $True
        $Icon.ShowBalloonTip( 1, $Null, "画面とったよー!`n" + $FilePath, [System.Windows.Forms.ToolTipIcon]::None )
    }
    catch{
        if( $ShowedForm ){ $Form.Show() }
        $Icon.Visible = $True
        $Icon.ShowBalloonTip( 1, $Null, "画面とれなかったよー!", [System.Windows.Forms.ToolTipIcon]::None )
    }
    
    if( $Graphics -ne $Null ){ $Graphics.Dispose() }
    if( $Bitmap -ne $Null ){ $Bitmap.Dispose() }
} )



####################################################################################################
# フォームを生成
####################################################################################################

# フォームを生成
$Form                 = New-Object System.Windows.Forms.Form
$Form.AutoSize        = $True
$Form.AutoSizeMode    = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form.MaximizeBox     = $False
$Form.MinimizeBox     = $False
$Form.Text            = "画面キャプチャツール"
$Form.SuspendLayout()
$Form.Controls.Add( $Form_Panel )
$Form.ResumeLayout()

# フォームのクローズ中イベントハンドラを登録
$Form.add_Closing( {
    param( [System.Object] $Sender, [System.ComponentModel.CancelEventArgs] $E )
    $E.Cancel = $True
    $Sender.Hide()
} )



####################################################################################################
# 通知領域アイコンのコンテキストメニューを生成
####################################################################################################

# 通知領域アイコンのコンテキストメニューの"終了"項目を生成
$Icon_Menu_QuitItem      = New-Object System.Windows.Forms.ToolStripMenuItem
$Icon_Menu_QuitItem.Text = "終了"

# 通知領域アイコンのコンテキストメニューを生成
$Icon_Menu = New-Object System.Windows.Forms.ContextMenuStrip
$Icon_Menu.SuspendLayout()
$Icon_Menu.Items.Add( $Icon_Menu_QuitItem ) | Out-Null
$Icon_Menu.ResumeLayout()

# 通知領域アイコンのコンテキストメニューの"終了"項目のクリックイベントハンドラを登録
$Icon_Menu_QuitItem.add_Click( {
    param( [System.Object] $Sender, [System.EventArgs] $E )
    [System.Windows.Forms.Application]::Exit()
} )



####################################################################################################
# 通知領域アイコンを生成
####################################################################################################

# 通知領域アイコンを生成
$Icon                  = New-Object System.Windows.Forms.NotifyIcon
$Icon.ContextMenuStrip = $Icon_Menu
$Icon.Icon             = $Form.Icon
$Icon.Text             = $Form.Text

# 通知領域アイコンのクリックイベントハンドラを登録
$Icon.add_MouseClick( {
    param( [System.Object] $Sender, [System.Windows.Forms.MouseEventArgs] $E )
    if( $E.Button -eq [System.Windows.Forms.MouseButtons]::Left ){
        if( $Form.Visible ){
            $Form.Hide()
        }else{
            $Form.Show()
            $Form.Activate()
        }
    }
} )



####################################################################################################
# 処理実行
####################################################################################################

# 表示
$Form.Show()
$Icon.Visible = $True
$Icon.ShowBalloonTip( 1, $Null, "おはよーございます!", [System.Windows.Forms.ToolTipIcon]::None )

# イベントループ
[System.Windows.Forms.Application]::Run()



####################################################################################################
# 後始末
####################################################################################################

$Form.Dispose()
$Icon.Dispose()


ホットキー対応のために、WindowsAPIを触る必要があるのとButtonクラスのWndProcメソッドをオーバーライドする必要があって出来るんかいなと思ったけどなんとかなった。

powershellでAdd-Typeコマンドレットを使うと、C#だろうがVB.NETだろうがソースコードをその場でコンパイルしてpowershell上から使えるようにしてくれる。

(変な制約はあるが。標準で読み込まれてないアセンブリC#コード内のusingだけじゃインポートできなくて、Add-Typeコマンドレットの-ReferencedAssembliesで指定しなきゃいけないとか)

ということで、C#でWindowsAPIをインポートするコードを書くことに。

でもこういうことするんだったらpowershellじゃなくて全部C#で書いたほうが早くね…?

トラックバック - http://d.hatena.ne.jp/KouMikage/20130209

2013-02-06 (Wed)

もう一年経つじゃないか…

[][]画面キャプチャツール

powershellを触る機会があったのでちょっとツールを作ってみた。

小さいフォームを出して、フォームのボタンを押すと画面キャプチャをとってファイルに保存するツール。

f:id:KouMikage:20130206235303p:image

####################################################################################################
# 前準備
####################################################################################################

$ErrorActionPreference = "Stop"                 # エラー発生時は実行中断
Set-StrictMode -Version Latest                  # 文法チェックをオン

Add-Type -AssemblyName "System.Windows.Forms"   # .NETアセンブリをロード



####################################################################################################
# フォームを生成
####################################################################################################

# フォームのパネルの"保存フォルダ"テキストボックスを生成
$Form_Panel_SaveFolderTextBox          = New-Object System.Windows.Forms.TextBox
$Form_Panel_SaveFolderTextBox.AutoSize = $True
$Form_Panel_SaveFolderTextBox.Text     = "保存フォルダ"

# フォームのパネルの"画面キャプチャ"ボタンを生成
$Form_Panel_CaptureButton              = New-Object System.Windows.Forms.button
$Form_Panel_CaptureButton.AutoSize     = $True
$Form_Panel_CaptureButton.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel_CaptureButton.Text         = "画面キャプチャ"

# フォームのパネルを生成
$Form_Panel               = New-Object System.Windows.Forms.FlowLayoutPanel
$Form_Panel.AutoSize      = $True
$Form_Panel.AutoSizeMode  = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form_Panel.FlowDirection = [System.Windows.Forms.FlowDirection]::TopDown
$Form_Panel.SuspendLayout()
$Form_Panel.Controls.Add( $Form_Panel_SaveFolderTextBox )
$Form_Panel.Controls.Add( $Form_Panel_CaptureButton )
$Form_Panel.ResumeLayout()

# フォームを生成
$Form                 = New-Object System.Windows.Forms.Form
$Form.Text            = "画面キャプチャツール"
$Form.AutoSize        = $True
$Form.AutoSizeMode    = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$Form.MaximizeBox     = $False
$Form.MinimizeBox     = $False
$Form.SuspendLayout()
$Form.Controls.Add( $Form_Panel )
$Form.ResumeLayout()

# フォームのパネルの"画面キャプチャ"ボタンのクリックイベントハンドラを登録
$Form_Panel_CaptureButton.add_Click( {
    param( [System.Object] $Sender, [System.EventArgs] $E )

    # FormとIconが画面キャプチャに入らないように隠す
    $Form.Hide()
    $Icon.Visible = $False
    Sleep -Milliseconds 200    # Formが隠れるのを待つ
    
    try{
        # 画面キャプチャを取得してファイルに保存
        $FilePath = Join-Path $Form_Panel_SaveFolderTextBox.Text $( Get-Date -UFormat "capture_%Y%m%d_%H%M%S.png" )
        $Bitmap   = New-Object System.Drawing.Bitmap -ArgumentList $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width ), $( [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height )
        $Graphics = [System.Drawing.Graphics]::FromImage( $Bitmap )
        $Graphics.CopyFromScreen( 0, 0, 0, 0, $Bitmap.Size )
        $Bitmap.Save( $FilePath )
        
        # FormとIconを再表示
        $Form.Show()
        $Icon.Visible = $True
        
        # 結果通知
        $Icon.ShowBalloonTip( 1, $Null, "画面とったよー!`n" + $FilePath, [System.Windows.Forms.ToolTipIcon]::None )
    }
    catch{
        # FormとIconを再表示
        $Form.Show()
        $Icon.Visible = $True
        
        # 結果通知
        $Icon.ShowBalloonTip( 1, $Null, "画面とれなかったよー!", [System.Windows.Forms.ToolTipIcon]::None )
    }
    
    # 後始末
    if( $Graphics -ne $Null ){ $Graphics.Dispose() }
    if( $Bitmap -ne $Null ){ $Bitmap.Dispose() }
} )



####################################################################################################
# 通知領域アイコンを生成
####################################################################################################

# 通知領域アイコンを生成
$Icon      = New-Object System.Windows.Forms.NotifyIcon
$Icon.Text = $Form.Text
$Icon.Icon = $Form.Icon



####################################################################################################
# 処理実行
####################################################################################################

$Icon.Visible = $True
$Icon.ShowBalloonTip( 1, $Null, "おはよーございます!", [System.Windows.Forms.ToolTipIcon]::None )
$Form.ShowDialog()



####################################################################################################
# 後始末
####################################################################################################

$Form.Dispose()
$Icon.Dispose()

powershellからは.Net Frameworkが触れるのでこんな感じでGUIのツールも作れる。

powershellのコマンドの実行結果が扱いにくすぎるのを考えなければかなり強い。

GUI作ってる分には実行結果全部投げ捨てられるのでなんとか耐えられる。

(いや、コマンドひとつ実行するだけでも結果が省略表示(...になる)されてまともに確認できないのは酷すぎると思うんですよ。helpにも結果の型書いてないし。コマンドひとつごとにパイプでFormatコマンドに繋げるの死ぬほど面倒です…)


実行方法。

1.上のスクリプトを適当にテキストファイルに貼り付ける。ファイルの拡張子は"ps1"にする。

2.powershellスクリプトデフォルトだとなぜか実行できない設定になっているので、コマンドプロンプトで以下のコマンドを実行してスクリプト実行制限の設定を緩くする。

Set-ExecutionPolicy RemoteSigned

3.ps1ファイルを右クリックして、メニューの「PowerShell で実行」から実行する。ps1ファイルは直接powershellに関連付けされているわけではないので、ps1ファイルをダブルクリックでは実行できない。なぜかメモ帳が開く。


ちなみに。

2をやるのが嫌なら、コマンドプロンプトとかから以下のコマンドでスクリプト実行制限の設定を指定しつつps1ファイルを実行できる。

(やっておいて何だが、セキュリティのためにデフォルトスクリプト実行できなくなっているのに引数ごときで権限指定できるのはおかしいよなぁ…)

powershell -ExecutionPolicy RemoteSigned -WindowStyle Hidden -File <スクリプトファイル名>

vbsファイルに上のコマンドを実行するように書いて、wscriptコマンドでvbsファイルを実行すると無駄なコマンドプロンプト画面が出なくなるのでおすすめ。

トラックバック - http://d.hatena.ne.jp/KouMikage/20130206

2012-02-22 (Wed)

[][]コンパイルタイムコンパイラコンパイラその8

構文解析した結果のトークンツリーの整理処理を手書きでやってたら、意外と量がきついうえに整理の順序を考えるのが面倒なことになった。ということで解析結果の出し方も文法内で指定できるようにしてみた。

DMDはv2.058を使用。

http://red.ribbon.to/~koumikage/ccc.zip

まずPEG記法で文法を書く。各定義の先頭に解析結果の出し方(@xxxx)を指定する。

"@mute"はトークンを出力しない。

"@token"は一つのトークンとして出力する。

"@flat"は出力いじらない。

"@infix"は偶数番目のトークンの両側のトークンを偶数番目のトークンの子トークンに組み換えて出力する。

"@entry"は文法内で一番最初に使う定義。エントリーポイント。

# test.peg

@mute  spaces      <- ( " " / "\t" )*

@mute  end         <- spaces ( !. )

@token operand     <- spaces ( "0" / [1-9][0-9]* )

@mute  bracket_l   <- spaces ( "(" )
@mute  bracket_r   <- spaces ( ")" )

@token add         <- spaces ( "+" )
@token sub         <- spaces ( "-" )
@token mul         <- spaces ( "*" )
@token div         <- spaces ( "/" )

@flat  exp_bracket <- bracket_l exp_addsub bracket_r / operand
@infix exp_muldiv  <- exp_bracket ( ( mul / div ) exp_bracket )*
@infix exp_addsub  <- exp_muldiv  ( ( add / sub ) exp_muldiv  )*

@entry root        <- exp_addsub end

実行コードを以下のような感じで書く。

import std.stdio;

import niboshi.ccc.token;
import niboshi.ccc.peg;

// パーサ宣言
alias PEG!( import( "test.peg" ) ) Parser;

void main()
{
	// test.pegから生成したパーサコードを表示
	writefln( "パーサコード ----->\n%s", Parser.toString() );
	
	// test.pegから生成したパーサコードで構文解析実行
	auto tokens = Parser( "100+1 + 2 * (3 - 4)" );
	writefln( "構文解析結果 ----->\n%s", dumpToken( tokens ) );
}

実行結果はこんな感じ。

パーサコード ----->
static Result peg_spaces( Input input ){ return mute!( many!( or!( lex!( " " ), lex!( "\t" ) ) ) )( input ); }
static Result peg_end( Input input ){ return mute!( and!( peg_spaces, not!( lex!() ) ) )( input ); }
static Result peg_operand( Input input ){ return token!( "operand", and!( peg_spaces, or!( lex!( "0" ), and!( or!( lex!( '1', '9' ) ), many!( or!( lex!( '0', '9' ) ) ) ) ) ) )( input ); }
static Result peg_bracket_l( Input input ){ return mute!( and!( peg_spaces, lex!( "(" ) ) )( input ); }
static Result peg_bracket_r( Input input ){ return mute!( and!( peg_spaces, lex!( ")" ) ) )( input ); }
static Result peg_add( Input input ){ return token!( "add", and!( peg_spaces, lex!( "+" ) ) )( input ); }
static Result peg_sub( Input input ){ return token!( "sub", and!( peg_spaces, lex!( "-" ) ) )( input ); }
static Result peg_mul( Input input ){ return token!( "mul", and!( peg_spaces, lex!( "*" ) ) )( input ); }
static Result peg_div( Input input ){ return token!( "div", and!( peg_spaces, lex!( "/" ) ) )( input ); }
static Result peg_exp_bracket( Input input ){ return or!( and!( and!( peg_bracket_l, peg_exp_addsub ), peg_bracket_r ), peg_operand )( input ); }
static Result peg_exp_muldiv( Input input ){ return infix!( and!( peg_exp_bracket, many!( and!( or!( peg_mul, peg_div ), peg_exp_bracket ) ) ) )( input ); }
static Result peg_exp_addsub( Input input ){ return infix!( and!( peg_exp_muldiv, many!( and!( or!( peg_add, peg_sub ), peg_exp_muldiv ) ) ) )( input ); }
static Token[] opCall( string source ){ return parse!( peg_root )( source ); }
static Result peg_root( Input input ){ return and!( peg_exp_addsub, peg_end )( input ); }
構文解析結果 ----->
line:[1] column:[6] kind:[add] data:[+]
	line:[1] column:[4] kind:[add] data:[+]
		line:[1] column:[1] kind:[operand] data:[100]
		line:[1] column:[5] kind:[operand] data:[1]
	line:[1] column:[10] kind:[mul] data:[*]
		line:[1] column:[8] kind:[operand] data:[2]
		line:[1] column:[15] kind:[sub] data:[-]
			line:[1] column:[14] kind:[operand] data:[3]
			line:[1] column:[17] kind:[operand] data:[4]

だいぶ手間が減った気がする。

トラックバック - http://d.hatena.ne.jp/KouMikage/20120222

2012-01-22 (Sun)

[][]コンパイルタイムコンパイラコンパイラその7

ちょっと見ないうちにDさんが超パワーアップしていたようなので、前作っていたコンパイルタイムコンパイラコンパイラを最新DMD(v2.057)対応してみた。

http://red.ribbon.to/~koumikage/ccc.zip

PEG記法で記述された文法データをコンパイル時に解析して構文解析関数群コードに変換します。構文解析関数群コードはPEG構造体の内部でmixinされ、PEG.opCall()で呼び出せるようになります。プログラムソースをPEG.opCall()に突っ込んで実行すればトークンツリーが出てきます。お手軽…というほどではないですがどうですかね。

ちなみに、内部処理的には高速化とかまったく考えてないので遅いでしょう。

(とはいえ爆速DMDさんは簡単なものなら5秒とかからんのですが…DMDさんまじぱねぇっす)

使い方は以下。

まずPEG記法で文法を書く。

# test.peg

spaces      <- ( " " / "\t" )*

end         <- spaces ( !. )

operand     <- spaces ( "0" / [1-9][0-9]* )

bracket_l   <- spaces ( "(" )
bracket_r   <- spaces ( ")" )

add         <- spaces ( "+" )
sub         <- spaces ( "-" )
mul         <- spaces ( "*" )
div         <- spaces ( "/" )

exp_bracket <- bracket_l exp_addsub bracket_r / operand
exp_muldiv  <- exp_bracket ( ( mul / div ) exp_bracket )*
exp_addsub  <- exp_muldiv  ( ( add / sub ) exp_muldiv  )*

root        <- exp_addsub end

実行コードを以下のような感じで書く。


import std.stdio;

import niboshi.ccc.token;
import niboshi.ccc.peg;

// パーサ宣言
alias PEG!( import( "test.peg" ), "root" ) Parser;

void main()
{
	Token[] tokens;
	
	// test.pegから生成したパーサコードを表示
	writefln( "パーサコード ----->\n%s", Parser.toString() );
	
	// test.pegから生成したパーサコードで構文解析実行
	tokens = Parser( "100+1 + 2 * (3 - 4)" );
	writefln( "構文解析結果(生) --->\n%s", dumpToken( tokens ) );
	
	// 構文解析結果がそのままじゃ使いにくいので整理
	tokens = omitToken!(   "spaces"      )( tokens );
	tokens = omitToken!(   "end"         )( tokens );
	tokens = omitToken!(   "bracket_l"   )( tokens );
	tokens = omitToken!(   "bracket_r"   )( tokens );
	
	tokens = uniteToken!(  "operand"     )( tokens );
	tokens = uniteToken!(  "add"         )( tokens );
	tokens = uniteToken!(  "sub"         )( tokens );
	tokens = uniteToken!(  "mul"         )( tokens );
	tokens = uniteToken!(  "div"         )( tokens );
	
	tokens = infixToken!(  "add"         )( tokens );
	tokens = infixToken!(  "sub"         )( tokens );
	tokens = infixToken!(  "mul"         )( tokens );
	tokens = infixToken!(  "div"         )( tokens );
	
	tokens = removeToken!( "exp_bracket" )( tokens );
	tokens = removeToken!( "exp_muldiv"  )( tokens );
	tokens = removeToken!( "exp_addsub"  )( tokens );
	tokens = removeToken!( "root"        )( tokens );

	writefln( "構文解析結果(整理後) --->\n%s", dumpToken( tokens ) );
}

実行結果はこんな感じ。

パーサコード ----->
static Result peg_spaces( Input input ){ return tree!( "spaces", many!( or!( lex!( " " ), lex!( "\t" ) ) ) )( input ); }
static Result peg_end( Input input ){ return tree!( "end", and!( peg_spaces, not!( lex!() ) ) )( input ); }
static Result peg_operand( Input input ){ return tree!( "operand", and!( peg_spaces, or!( lex!( "0" ), and!( or!( lex!( '1', '9' ) ), many!( or!( lex!( '0', '9' ) ) ) ) ) ) )( input ); }
static Result peg_bracket_l( Input input ){ return tree!( "bracket_l", and!( peg_spaces, lex!( "(" ) ) )( input ); }
static Result peg_bracket_r( Input input ){ return tree!( "bracket_r", and!( peg_spaces, lex!( ")" ) ) )( input ); }
static Result peg_add( Input input ){ return tree!( "add", and!( peg_spaces, lex!( "+" ) ) )( input ); }
static Result peg_sub( Input input ){ return tree!( "sub", and!( peg_spaces, lex!( "-" ) ) )( input ); }
static Result peg_mul( Input input ){ return tree!( "mul", and!( peg_spaces, lex!( "*" ) ) )( input ); }
static Result peg_div( Input input ){ return tree!( "div", and!( peg_spaces, lex!( "/" ) ) )( input ); }
static Result peg_exp_bracket( Input input ){ return tree!( "exp_bracket", or!( and!( peg_bracket_l, peg_exp_addsub, peg_bracket_r ), peg_operand ) )( input ); }
static Result peg_exp_muldiv( Input input ){ return tree!( "exp_muldiv", and!( peg_exp_bracket, many!( and!( or!( peg_mul, peg_div ), peg_exp_bracket ) ) ) )( input ); }
static Result peg_exp_addsub( Input input ){ return tree!( "exp_addsub", and!( peg_exp_muldiv, many!( and!( or!( peg_add, peg_sub ), peg_exp_muldiv ) ) ) )( input ); }
static Result peg_root( Input input ){ return tree!( "root", and!( peg_exp_addsub, peg_end ) )( input ); }

構文解析結果(生) --->
line:[1] column:[1] kind:[root] data:[]
	line:[1] column:[1] kind:[exp_addsub] data:[]
		line:[1] column:[1] kind:[exp_muldiv] data:[]
			line:[1] column:[1] kind:[exp_bracket] data:[]
				line:[1] column:[1] kind:[operand] data:[]
					line:[1] column:[1] kind:[spaces] data:[]
					line:[1] column:[1] kind:[] data:[1]
					line:[1] column:[2] kind:[] data:[0]
					line:[1] column:[3] kind:[] data:[0]
		line:[1] column:[4] kind:[add] data:[]
			line:[1] column:[4] kind:[spaces] data:[]
			line:[1] column:[4] kind:[] data:[+]
		line:[1] column:[5] kind:[exp_muldiv] data:[]
			line:[1] column:[5] kind:[exp_bracket] data:[]
				line:[1] column:[5] kind:[operand] data:[]
					line:[1] column:[5] kind:[spaces] data:[]
					line:[1] column:[5] kind:[] data:[1]
		line:[1] column:[6] kind:[add] data:[]
			line:[1] column:[6] kind:[spaces] data:[]
				line:[1] column:[6] kind:[] data:[ ]
			line:[1] column:[7] kind:[] data:[+]
		line:[1] column:[8] kind:[exp_muldiv] data:[]
			line:[1] column:[8] kind:[exp_bracket] data:[]
				line:[1] column:[8] kind:[operand] data:[]
					line:[1] column:[8] kind:[spaces] data:[]
						line:[1] column:[8] kind:[] data:[ ]
					line:[1] column:[9] kind:[] data:[2]
			line:[1] column:[10] kind:[mul] data:[]
				line:[1] column:[10] kind:[spaces] data:[]
					line:[1] column:[10] kind:[] data:[ ]
				line:[1] column:[11] kind:[] data:[*]
			line:[1] column:[12] kind:[exp_bracket] data:[]
				line:[1] column:[12] kind:[bracket_l] data:[]
					line:[1] column:[12] kind:[spaces] data:[]
						line:[1] column:[12] kind:[] data:[ ]
					line:[1] column:[13] kind:[] data:[(]
				line:[1] column:[14] kind:[exp_addsub] data:[]
					line:[1] column:[14] kind:[exp_muldiv] data:[]
						line:[1] column:[14] kind:[exp_bracket] data:[]
							line:[1] column:[14] kind:[operand] data:[]
								line:[1] column:[14] kind:[spaces] data:[]
								line:[1] column:[14] kind:[] data:[3]
					line:[1] column:[15] kind:[sub] data:[]
						line:[1] column:[15] kind:[spaces] data:[]
							line:[1] column:[15] kind:[] data:[ ]
						line:[1] column:[16] kind:[] data:[-]
					line:[1] column:[17] kind:[exp_muldiv] data:[]
						line:[1] column:[17] kind:[exp_bracket] data:[]
							line:[1] column:[17] kind:[operand] data:[]
								line:[1] column:[17] kind:[spaces] data:[]
									line:[1] column:[17] kind:[] data:[ ]
								line:[1] column:[18] kind:[] data:[4]
				line:[1] column:[19] kind:[bracket_r] data:[]
					line:[1] column:[19] kind:[spaces] data:[]
					line:[1] column:[19] kind:[] data:[)]
	line:[1] column:[20] kind:[end] data:[]
		line:[1] column:[20] kind:[spaces] data:[]

構文解析結果(整理後) --->
line:[1] column:[6] kind:[add] data:[+]
	line:[1] column:[4] kind:[add] data:[+]
		line:[1] column:[1] kind:[operand] data:[100]
		line:[1] column:[5] kind:[operand] data:[1]
	line:[1] column:[10] kind:[mul] data:[*]
		line:[1] column:[8] kind:[operand] data:[2]
		line:[1] column:[15] kind:[sub] data:[-]
			line:[1] column:[14] kind:[operand] data:[3]
			line:[1] column:[17] kind:[operand] data:[4]

あとは適当にコード生成とかするだけ!

トラックバック - http://d.hatena.ne.jp/KouMikage/20120122

2010-09-05 (Sun)

[][]40時間ゲームプログラミングコンテスト

つくらぐさんの所で「40時間ゲームプログラミングコンテスト」なるものに参加させて頂きました。

http://game.tsukuba-linux.org/

金曜日の18:00ぐらいから日曜日(今日)の12:00ぐらいまでに規定のリソースでゲームを一本作るというもの。

ひさしぶりに気合を入れて取り組んだので良い経験になったと思います。

…できたものはアレでしたがね。いろいろと自分に足りないものが良く分かったとです。

[]ついったはじめました

アカウントはKouMikageです。

トラックバック - http://d.hatena.ne.jp/KouMikage/20100905