は?何言ってんの?と思うかもしれませんが、真面目な話です。作成したアプリの動作を画面録画してYouTubeにアップしていますが、これはAG-デスクトップレコーダーというフリーのソフトを使っています。
AG-デスクトップレコーダー – k本的に無料ソフト・フリーソフト
ところがこれだと録画中に録画する場所を変更することができません。そこで自作することにしました。その結果、できあがったのが以下の動画です。
画面録画するにはどうすればいいのか
画面録画するにはどうすればいいのでしょうか? ここを参考にしました。
AForge.NETというライブラリを使用します。そしてリンク先の記事を参考に以下のようなコードを書いてみました。
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 |
AForge.Video.VFW.AVIWriter aviWriter = new AForge.Video.VFW.AVIWriter(); int width = 640; int height = 480; Bitmap bitmap = new Bitmap(width, height); Graphics g = Graphics.FromImage(bitmap); aviWriter.FrameRate = 10; aviWriter.Open("test.avi", width, height); // 最長30秒。あまり長くするとファイルサイズが巨大になりすぎる for (int i = 0; i < 30 * 10; i++) { g.CopyFromScreen(new Point(0, 0), new Point(0, 0), bitmap.Size); aviWriter.AddFrame(bitmap); await Task.Delay(100); } bitmap.Dispose(); g.Dispose(); aviWriter.Close(); MessageBox.Show("録画が完了しました。"); |
これで座標(0,0)を起点に640 × 480ピクセルで画面録画することができます。座標を変えれば動作中に録画する場所を変更することができそうです。録画する場所を簡単に変更するのであればフォームに穴をあけてそこから見える部分を録画するという仕様にしたいのですがどうでしょうか?
フォームに穴をあける
ではフォームに穴をあけるにはどうすればいいのでしょうか?
Form.TransparencyKeyプロパティを使えば簡単にできます。Form.TransparencyKeyプロパティにセットした色が透明になるのです。
以下のコードでは赤い矩形を描画することでその部分を透明にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { Rectangle HoleRectangle = new Rectangle(10, 50, 480, 360); public Form1() { InitializeComponent(); // 穴にフォームのクライアントサイズを合わせる this.ClientSize = new Size(HoleRectangle.Width + 20, HoleRectangle.Height + 70); this.TransparencyKey = Color.Red; } protected override void OnPaint(PaintEventArgs e) { e.Graphics.FillRectangle(Brushes.Red, HoleRectangle); base.OnPaint(e); } } |
フォームの穴から画面録画
フォームに穴をあけることができたら、この部分を画面録画できるようにします。
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 |
public partial class Form1 : Form { private async void button1_Click(object sender, EventArgs e) { AForge.Video.VFW.AVIWriter aviWriter = new AForge.Video.VFW.AVIWriter(); int width = HoleRectangle.Width; int height = HoleRectangle.Height; Bitmap bitmap = new Bitmap(width, height); Graphics g = Graphics.FromImage(bitmap); // aviWriter.Openに渡す幅と高さは2の倍数にすること。さもないと例外が発生する aviWriter.FrameRate = 10; aviWriter.Open("test.avi", width, height); // 0.1秒おき300回(30秒間)録画する for (int i = 0; i < 30 * 10; i++) { Point pt = this.PointToScreen(new Point(HoleRectangle.X, HoleRectangle.Y)); g.CopyFromScreen(pt, new Point(0, 0), bitmap.Size); aviWriter.AddFrame(bitmap); await Task.Delay(100); } bitmap.Dispose(); g.Dispose(); aviWriter.Close(); MessageBox.Show("録画が完了しました。"); } } |
フォームを自前で移動させる
これで完成といいたいのですが、実際に使ってみると問題点があることがわかりました。ウィンドウをドラッグして移動するとき、ドロップで移動先が確定するまで移動先が描画されます。この部分が録画されてしまうのです。
動画の29秒あたりから画面録画をした動画を再生しているのですが、フォームを移動させようとすると枠のようなものが写り込んでいることがわかります。
そこでウィンドウをドラッグしたら移動がすぐにおこなわれるようにして線が入るのを防ぎます。フォームを移動させるときはタイトルバーをドラッグするのではなくクライアント領域をドラッグするようにします。
マウスボタンが押されたらForm.Locationをフィールド変数に保存します。そしてどれだけ移動するかはイベントハンドラの引数を利用すれば算出できるのですが、このときe.Xとかe.Yで取得できる座標はクライアント座標です。フォームが移動すれば同じ座標でもクライアント座標はかわります。そこでスクリーン座標として保存し評価しなければなりません。
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 |
public partial class Form1 : Form { Point MouseDownPoint = Point.Empty; Point MouseDownLocation = Point.Empty; protected override void OnMouseDown(MouseEventArgs e) { // Form.Locationをフィールド変数に保存 MouseDownLocation = this.Location; // マウスボタンが押された座標をスクリーン座標として保存 MouseDownPoint = this.PointToScreen(new Point(e.X, e.Y)); this.Capture = true; base.OnMouseDown(e); } protected override void OnMouseMove(MouseEventArgs e) { if (this.Capture) { // マウスカーソルがある位置をスクリーン座標として取得する Point mousePoint = this.PointToScreen(new Point(e.X, e.Y)); // マウスボタンが押された位置からどれだけ移動させるべきかを計算する int dx = mousePoint.X - MouseDownPoint.X; int dy = mousePoint.Y - MouseDownPoint.Y; // 計算して得られた量だけ移動させる this.Location = new Point(MouseDownLocation.X + dx, MouseDownLocation.Y + dy); } base.OnMouseMove(e); } } |
これでクライアント領域をドラッグすればフォームを移動させることができるのですが、タイトルバーをドラッグしたときも同様に移動できるようにしてしまいましょう。
クライアント領域以外の部分がクリックされたらWM_NCLBUTTONDOWNメッセージが発生します。またWParamがHTCAPTIONのときがタイトルバーがクリックされたときです。そこでそのときは以下の処理をおこないます。これでフォームを移動させることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { protected override void WndProc(ref Message m) { int WM_NCLBUTTONDOWN = 0x00A1; int HTCAPTION = 2; if (m.Msg == WM_NCLBUTTONDOWN && m.WParam.ToInt32() == HTCAPTION) { MouseDownPoint = Control.MousePosition; MouseDownLocation = this.Location; this.Capture = true; m.Result = IntPtr.Zero; return; } base.WndProc(ref m); } } |
初めまして、てんのすけと申します。
こちらの記事に関して伺いたい点があり、コメントさせていただきました。
Formを作成してボタンのイベントに[画面録画するにはどうすればいいのか]の部分にあるコードをコピーさせていただいて実行してみたところ、以下のようなエラーが発生してしまいました。
—————————–
ユーザーが処理していない例外
System.OverflowException: ‘Arithmetic operation resulted in an overflow.’
—————————–
このような場合、どう対処すればいいのかご存じであれば、大変恐縮ですがご教示いただけませんでしょうか?
当方C#に関してまだあまり詳しくなく、非常に初歩的な内容で失礼に当たるような内容でしたらご容赦いただけますと幸いです。
何卒よろしくお願いいたします。
まず https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/aforge/AForge.NET%20Framework-2.2.5-(libs%20only).zip からzipファイルをダウンロードして解凍します。必要なのはReleaseフォルダです。このなかにあるAForge.Video.VFW.dllをプロジェクトに追加してください。このときにAForge.Video.VFW.dllだけでなくReleaseフォルダのなかにあるファイルが必要です。AForge.Video.VFW.dllだけ別フォルダに移動してしまうとビルドはできますが、実行しようとすると例外が発生します。
それからソースコードをGitHubで公開しました。⇒ https://github.com/mi3w2a1/RecordScreenFormHole
ご確認ください。
ご返信ありがとうございます。
GitHubもありがとうございました。
コメントとGitのファイルを拝見させていただいて試してみたところ、無事に実行することができました。
ご指摘いただいた通り、AForge.Video.VFW.dllは同フォルダに入れていたのですが、他のファイルを入れていなかったことが原因だったようです。
非常に参考になり、とても助かりました。
ご教示いただきありがとうございました。