ウィンドウをキャプチャしてアニメーションGIFを作る (.NET)
.NET Framework v3.5で動作確認。(v2.0.50727でも動くと思う)
使ったもの
- Windows
- Visual Basic 2008 express edition
- 時間
ウィンドウのキャプチャ
' Imports System.Runtime.InteropServices ''' <summary> ''' GetWindowRectで使う構造体 ''' </summary> <StructLayout(LayoutKind.Sequential)> _ Structure RECT Public left As Integer Public top As Integer Public right As Integer Public bottom As Integer End Structure ''' <summary> ''' 画像を別のコンテキストに送る ''' 参考: http://yokohama.cool.ne.jp/chokuto/urawaza/api/BitBlt.html ''' </summary> <DllImport("gdi32.dll")> _ Private Shared Function BitBlt(ByVal hDestDC As IntPtr, _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal nWidth As Integer, _ ByVal nHeight As Integer, _ ByVal hSrcDC As IntPtr, _ ByVal xSrc As Integer, _ ByVal ySrc As Integer, _ ByVal dwRop As Integer) As Integer End Function ''' <summary> ''' ウィンドウを最前面に出す ''' </summary> <DllImport("user32.dll")> _ Private Shared Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean End Function ''' <summary> ''' ウィンドウの座標を取得 ''' </summary> <DllImport("user32.dll")> _ Private Shared Function GetWindowRect(ByVal hwnd As IntPtr, ByRef rect As RECT) As Boolean End Function ''' <summary> ''' ウィンドウの座標を取得 ''' </summary> Private Shared Function GetWindowRect(ByVal hwnd As IntPtr) As RECT Dim rec As RECT GetWindowRect(hwnd, rec) Return rec End Function ''' <summary> ''' ウィンドウのハンドルをタイトル(正規表現)から取得 ''' </summary> Private Function GetHwndByTitle(ByVal title As String) As IntPtr Dim hwnd As IntPtr = Nothing Dim processes() As Process = Diagnostics.Process.GetProcesses() For Each p In processes If System.Text.RegularExpressions.Regex.IsMatch(p.MainWindowTitle, title) Then hwnd = p.MainWindowHandle Exit For End If Next Return hwnd End Function ''' <summary> ''' ウィンドウの画像を取得 ''' </summary> Private Function GetBitmapByHwnd(ByVal hwnd As IntPtr) As Bitmap ' ウィンドウが後ろに隠れてるとちゃんとキャプチャできませんので SetForegroundWindow(hwnd) System.Threading.Thread.Sleep(100) Dim rect As RECT = GetWindowRect(hwnd) ' GetWindowRectのサイズとGraphics.FromHwndのサイズが違うので適当に調節 Dim bmp As New Bitmap(rect.right - rect.left - 8, rect.bottom - rect.top - 50) Dim image As Graphics = Graphics.FromImage(bmp) Dim window As Graphics = Graphics.FromHwnd(hwnd) Dim imageHdc As IntPtr = image.GetHdc Dim windowHdc As IntPtr = window.GetHdc BitBlt(imageHdc, 0, 0, bmp.Width, bmp.Height, windowHdc, 0, 0, 13369376) ' SRCCOPY: 13369376 image.ReleaseHdc(imageHdc) window.ReleaseHdc(windowHdc) Return bmp End Function
アニメーションGIFの生成
System.Drawing.BitmapにはSaveAddというメソッドがありますが騙されてはいけません。これで作れるのはマルチフレームなTIFFだけです。GDI+ではアニメーションGIFが作れないらしいので、バイナリに直接書き込んでいくしかなさそうです。今更GIFの仕様とか理解するのは面倒なので、その辺に転がってるライブラリを使います。
http://www.codeproject.com/KB/GDI-plus/NGif.aspx
ログインしてダウンロードしたら適当にコンパイル。
> c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc /target:library /out:NGif.dll /warn:0 /nologo /debug *.cs
Dim GifEncoder As New Gif.Components.AnimatedGifEncoder Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Timer1.Interval = 500 End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Dim bmp As Bitmap = Me.GetBitmapByHwnd(Me.GetHwndByTitle("メモ帳")) Me.GifEncoder.AddFrame(bmp) bmp.Dispose() End Sub ''' <summary> ''' キャプチャ開始 ''' </summary> Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click If SaveFileDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then Me.GifEncoder.Start(SaveFileDialog1.FileName) Me.GifEncoder.SetFrameRate(1.0! / (Timer1.Interval / 1000.0!)) Me.GifEncoder.SetRepeat(0) Timer1.Enabled = True End If End Sub ''' <summary> ''' キャプチャ終了 ''' </summary> Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click If Timer1.Enabled Then Timer1.Enabled = False Me.GifEncoder.Finish() End If End Sub
次回はAVIバージョン。