[VC2005] スクリーンセーバーを作る方法

◆概要

この資料は、Microsoft(R) Visual C# 2005で スクリーンセーバーを作る方法について記述しています。

このサンプルでは、ピクチャボックスに画像を表示し、ピクチャボックスをランダムに画面に表示します。 WindowsXpに付属のスクリーンセーバー「Window Xp」もどきです。

WindowsXpに付属のスクリーンセーバー「Window Xp」

WindowsXpに付属のスクリーンセーバー「Window Xp」

◆Contents


1.フォームの設計
2.画面いっぱいにフォームを最大化する
3.マウスがクリックされたスクリーンセーバーを終了させる
4.キーボードで何か入力されたらスクリーンセーバーを終了させる
5.マウスが動いたらスクリーンセーバーを終了させる
6.コントロールボックスを表示しないようにする
7.マウスポインタを非表示にする
8.フォームのバックカラーの設定
9.フォームを最前面に設定する
10.ピクチャボックスを動かす
11.起動時にコマンドライン引数によって処理を分岐させる
12.プレビュー画面への対応
13.拡張子の変更

◆1.フォームの設計

フォームにピクチャボックスを追加し、表示したい画像をピクチャボックスのImageプロパティで指定します。
ピクチャボックスのSizeModeプロパティを適切に設定します。ここでは、AutoSizeにしました。

ピクチャボックスのimageプロパティ ピクチャボックスのプロパティ
ピクチャボックスのImageプロパティを
指定し画像を表示
ピクチャボックスのSizeModeをAutoSizeに


▼ページトップへ

◆2.画面いっぱいにフォームを最大化する

画面いっぱいにフォームを最大化する方法としては、次の2つがあります。

  1. フォームのWindowStateプロパティをMaximizedにする
  2. フォームのロード時にBoundsプロパティで設定

上記1をコードで設定するには次のようにします。

        private void Form1_Load(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Maximized;
        }

ここでは、2番目のBoundsプロパティで設定します。コードは次のようになります。

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Bounds = Screen.PrimaryScreen.Bounds;
        }

▼ページトップへ

◆3.マウスがクリックされたらスクリーンセーバーを終了させる

次にマウスがクリックされたときに、フォームが閉じるようにマウスのクリックイベントハンドラにフォームを閉じるコードを記述します。

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {
            this.Close();
        }

▼ページトップへ

◆4.キーボードで何か入力されたらスクリーンセーバーを終了させる

ユーザーがキーボードを操作したら、スクリーンセーバーを終了させる必要があります。そのためには、FormのKeyDownイベントハンドラにフォームを閉じるコードを記述します。

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
         //キーボードが押されたときの処理
            this.Close();
        }

▼ページトップへ

◆5.マウスが動いたらスクリーンセーバーを終了させる

ユーザーがマウスを動かしたとき、スクリーンセーバーを終了させます。フォームが表示されるとマウスは1度は必ず動くため、ちょっとした工夫が必要です。

そのため、最初はマウスの位置を変数に記憶し、その位置と違うときにスクリーンセーバーを終了させます。


まず、マウスの位置を代入するための変数を準備します。

namespace ScreensaverSample
{
    public partial class Form1 : Form
    {
        //マウスの位置を代入するための変数
        private Point MouseXY;

次に、マウスが動いたときにスクリーンセーバーを終了させるコードを記述します。MousDownイベントとコードを共有するために、フォームのMouseMoveイベントプロパティにOnMouseMoveと入力してEnterキーを押します。

Form1のプロパティ
MouseMoveイベントプロパティにOnMouseMoveと入力してEnterキーを押す

OnMouseMoveイベントハンドラが表示されます。
表示されたOnMouseMoveイベントハンドラに次のコードを入力します。

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (!MouseXY.IsEmpty)
            {
                if (MouseXY != new Point(e.X, e.Y))
                    Close();
                if (e.Clicks > 0)
                    Close();
            }
            MouseXY = new Point(e.X, e.Y);
        }

MouseDownでも同じコードを利用するために、フォームのMouseDownイベントのところで、下図のようにOnMouseMoveを選択します。

Form1のプロパティ

さて、ここまで一度プロジェクトを実行してみましょう。


プロジェクトを実行するとフォームは最大化され、マウスでクリックしたり、マウスを動かしたり或いはキーボードで何か入力するとするとアプリケーションは終了します。
ただ、コントロールボックスが表示されていますので、これを表示しないようにしましょう。


▼ページトップへ

◆6.コントロールボックスを表示しないようにする

コントロールボックスを表示しないようにフォームのBorderStyleプロパティをNoneにします。
コードで設定するにはForm1クラスのInitializeComponent();の次の行にコードを追加します。

        public Form1()
        {
            InitializeComponent();

            //コントロールボックスを表示しないように
            //FormBorderStyleをNoneに設定
            this.FormBorderStyle = FormBorderStyle.None;
        }

▼ページトップへ

◆7.マウスポインタを非表示にする

スクリーンセーバーでは、マウスポインタを非表示にする必要があります。
マウスポインタを非表示にするには、CursorオブジェクトのHideメソッドを使います。フォームのロードイベントハンドラに次のコードを追加します。

          Cursor.Hide();
        

▼ページトップへ

◆8.フォームのバックカラーの設定

フォームのバックカラーを黒にします。フォームのBackColorプロパティで黒を設定してください。コードでフォームのバックカラーを黒にするには、次のようにをForm1クラスに記述します。

        public Form1()
        {
            InitializeComponent();

            //コントロールボックスを表示しないように
            //FormBorderStyleをNoneに設定
            this.FormBorderStyle = FormBorderStyle.None;
            //フォームのバックカラーを黒に設定
            this.BackColor = Color.Black;
        }

▼ページトップへ

◆9.フォームを最前面に設定する

スクリーンセーバーは他のアプリケーションより前に表示する必要がありますので、TopMostプロパティをtrueにします。コードで設定するにはフォームのロードイベントハンドラに次のように記述します。

TopMost = true;

ここまでで、フォームのロードイベントハンドラは次のようになります。

        private void Form1_Load(object sender, EventArgs e)
        {
            //画面いっぱいにフォームを表示する
            this.Bounds = Screen.PrimaryScreen.Bounds;

            //マウスポインタを非表示に設定
            Cursor.Hide();
            
            //フォームを最前面に設定
            TopMost = true;
        }

▼ページトップへ

◆10.ピクチャボックスを動かす

Windows Xpに付属のスクリーンセーバーのようにピクチャボックスを画面上にランダムに表示するようにします。
そのために、ピクチャボックスの移動範囲を格納するRecTangle型の変数 VisibleRect を準備します。
また、ランダムに表示位置を変えるための変数randも準備します。

    public partial class Form1 : Form
    {
        //マウスの位置を代入するための変数
        private Point MouseXY;
        //ピクチャボックスの移動範囲を格納する変数
        private Rectangle VisibleRect;
        //ランダムな数値を発生させる変数
        private Random rand;


        public Form1()
        {
            InitializeComponent();

            //コントロールボックスを表示しないように
            //FormBorderStyleをNoneに設定
            this.FormBorderStyle = FormBorderStyle.None;
            //フォームのバックカラーを黒に設定
            this.BackColor = Color.Black;
        }

▼ページトップへ

次にフォームのロードイベントハンドラでピクチャボックスの移動範囲を変数VisibleRectに代入します。
BoundsクラスのWidthプロパティで画面の横幅、Heightプロパティで画面の高さを取得しますが、ピクチャボックスの幅と高さを引いておきます。そうしないと、ピクチャボックスが画面からはみ出してしまいます。
 また、最初のピクチャボックスの位置をランダムに設定するためのコードも追加します。

        private void Form1_Load(object sender, EventArgs e)
        {
            //画面いっぱいにフォームを表示する
            this.Bounds = Screen.PrimaryScreen.Bounds;

            //ピクチャボックスの移動範囲を変数に代入
            VisibleRect = new Rectangle(
                0, 0, Bounds.Width - pictureBox1.Width,
                Bounds.Height - pictureBox1.Height);
            //ピクチャボックスの位置をランダムに設定
            rand = new Random();
            pictureBox1.Location =
                new Point(rand.Next(VisibleRect.Width)
                , rand.Next(VisibleRect.Height));

            //マウスポインタを非表示に設定
            Cursor.Hide();

            //フォームを最前面に設定
            TopMost = true;
        }

次にフォームにTimerをツールボックスからドラッグし、ドラッグしたTimer1をダブルクリックしてコードを記述します。

   
        private void timer1_Tick(object sender, EventArgs e)
        {
            //timer1で設定した時間がきたらピクチャボックスの位置を
            //ランダムに変更
            pictureBox1.Location =
                new Point(rand.Next(VisibleRect.Width)
                , rand.Next(VisibleRect.Height));
        }

なお、Timer1のプロパティで下図のようにEnableプロパティをtrueに、Intervalプロパティを10000(10秒)にします。

フォームのプロパティ

ここまでで、Form1に記述するコードは終了しました。コードは次のようになっているはずです。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace ScreensaverSample
{
    public partial class Form1 : Form
    {
        //マウスの位置を代入するための変数
        private Point MouseXY;

        //ピクチャボックスの移動範囲を格納する変数
        private Rectangle VisibleRect;
        //ランダムな数値を発生させる変数
        private Random rand;

        public Form1()
        {
            InitializeComponent();

            //コントロールボックスを表示しないように
            //FormBorderStyleをNoneに設定
            this.FormBorderStyle = FormBorderStyle.None;
            //フォームのバックカラーを黒に設定
            this.BackColor = Color.Black;

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //画面いっぱいにフォームを表示する
            this.Bounds = Screen.PrimaryScreen.Bounds;

            //ピクチャボックスの移動範囲を変数に代入
            VisibleRect = new Rectangle(
                0, 0, Bounds.Width - pictureBox1.Width,
                Bounds.Height - pictureBox1.Height);
            //ピクチャボックスの位置をランダムに設定
            rand = new Random();
            pictureBox1.Location =
                new Point(rand.Next(VisibleRect.Width)
                , rand.Next(VisibleRect.Height));

            //マウスポインタを非表示に設定
            Cursor.Hide();

            //フォームを最前面に設定
            TopMost = true;
        }

        private void Form1_MouseClick(object sender, MouseEventArgs e)
        {
            this.Close();
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            this.Close();
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (!MouseXY.IsEmpty)
            {
                if (MouseXY != new Point(e.X, e.Y))
                    Close();
                if (e.Clicks > 0)
                    Close();
            }
            MouseXY = new Point(e.X, e.Y);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            //timer1で設定した時間がきたらピクチャボックスの位置を
            //ランダムに変更
            pictureBox1.Location =
                new Point(rand.Next(VisibleRect.Width)
                , rand.Next(VisibleRect.Height));
        }
    }
}

▼ページトップへ

◆11.起動時にコマンドライン引数によって処理を分岐させる

スクリーンセーバーでは、コマンドライン引数によって、次のように動作させる必要があります。
なお、WindowsXP/Windows2000ではパスワードモードに対応するコードを記述する必要はありません。

コマンドライン引数 モード
p プレビュー
c オプション
a パスワード
s 通常(スクリーンセーバーを起動)

まず、コマンドライン引数を受け取れるようにMainメソッドに引数を与えます。
ソリューションエクスプローラで「Program.cs」をダブルクリックします。


Program.csが開いたら、static void Main()というメインメソッドの()内に引数を受け取るための変数を宣言します。

        static void Main(string[] args)

▼ページトップへ

次に受け取った引数により処理を分岐させるためMainメソッドを次のように修正します。

        static void Main(string[] args)
        {
                if (args.Length > 0)
                {
                    // コマンド ライン引数を取得します。                
                    if (args[0].ToLower().StartsWith("/c"))
                    {
                        // 設定モードのとき、オプションダイアログを表示
                        //ここではノンサポート
                        MessageBox.Show(
                            "このスクリーンセーバーには" +
                            "設定できるオプションはありません。",
                            "オプション", MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
                        Application.Exit();
                    }
                    else if (args[0].ToLower().StartsWith("/p"))
                    {
                        //プレビューモード
                        //ここでは、ノンサポート
                        Application.Exit();
                    }
                    else if (args[0].ToLower().StartsWith("/s"))
                    {
                        // スクリーン セーバーのフォームを表示します。
                        ShowScreenSaver();
                    }
                    else
                    {
                        //コマンドライン異常
                        MessageBox.Show("コマンド ライン引数が無効です",
                             "引数が無効", MessageBoxButtons.OK,
                             MessageBoxIcon.Error);
                        Application.Exit();
                    }
                }
                else
                {
                    // 渡される引数がない場合、これはユーザーがファイルを右クリックして
                    //「構成」を選んだときに発生します。オプションフォームを表示します。
                    //ここではノンサポート
                    MessageBox.Show(
                            "このスクリーンセーバーには" +
                            "設定できるオプションはありません。",
                            "オプション", MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
                    Application.Exit();
                }
        }
                    
        static void ShowScreenSaver()
        {
            Form1 SaverForm= new Form1();
            Application.Run(SaverForm);
        }

▼ページトップへ

◆12.プレビュー画面への対応

プレビューモードに対応するため、

                else if (args[0].ToLower().StartsWith("/p"))

のところを次のように修正します。

                else if (args[0].ToLower().StartsWith("/p"))
                {
                    //プレビューモード
                    //プレビューウィンドウを開き、親ハンドルを取得
                    IntPtr hWnd = (IntPtr)uint.Parse(args[2]);

                    if (IntPtr.Zero != hWnd)
                    {
                        //プレビューウィンドウに描画するハンドルの
                        //スレッドをスタートさせる
                        System.Threading.Thread thread =
                             new System.Threading.Thread(
                             new System.Threading.ThreadStart(
                             new PreviewScreenSaver(hWnd).Run
                                 )
                              );
                        thread.Start();
                        Application.Run();
                        thread.Join();
                    }
                }

▼ページトップへ

次に、メニューの[プロジェクト]-[クラスの追加]でクラスを追加します。このとき、クラスの名前を PreviewScreenSaver.cs にします。
PreviewScreenSaver.csクラスに記述するコードは次のとおり。
最後の行の「My ScreenSaver」がプレビュー画面に表示される文字です。

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Windows.Forms;

namespace ScreensaverSample
{
    internal class PreviewScreenSaver
    {
        IntPtr parentHWnd;

        internal PreviewScreenSaver(IntPtr parentHWnd)
        {
            this.parentHWnd = parentHWnd;
        }

        internal void Run()
        {

            while (Common.IsWindow(parentHWnd) && 
                !Common.IsWindowVisible(parentHWnd))
                Thread.Sleep(50);

            if (Common.IsWindow(parentHWnd))
            {
                Rectangle rect = new Rectangle();
                points[0] = new Point(0, 0);
                points[1] = new Point(rect.Width, 0);
                points[2] = new Point(rect.Width, rect.Height);
                points[3] = new Point(0, rect.Height);

                if (Common.GetWindowRect(parentHWnd, ref rect))
                {
                    using (Graphics g = Graphics.FromHwnd(parentHWnd))
                    {
                        while (Common.IsWindow(parentHWnd) && 
                            Common.IsWindowVisible(parentHWnd) 
                            && g != null)
                        {
                            PaintPreview(g, rect);
                            Thread.Sleep(250);
                            Application.DoEvents();
                        }
                    }
                }
            }

            //プレビューアプリケーションの終了
            Application.Exit();
        }
        Point[] points = new Point[4];
        Color[] colors = new Color[4] { Color.Navy, Color.Navy,
            Color.Navy, Color.Navy };
        private void PaintPreview(Graphics g, Rectangle rect)
        {
            //プレビューの背景色
            g.Clear(Color.Black);
            //プレビュー画面に表示
            g.DrawString("My ScreenSaver", new Font(
                "Times New Roman", 10, FontStyle.Bold), 
                Brushes.Yellow, 15, 50);
        }
    }

}
 

次に、メニューの[プロジェクト]-[クラスの追加]でもうひとつクラスを追加します。このとき、クラスの名前をCommon.csにします。Common.csクラスのコードを次のように記述します。

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ScreensaverSample
{
    class Common
    {

        public Common()
        {
        }
        //プレビューモードでの親ウィンドウサイズ取得のためのAPI宣言
        [DllImport("user32.dll")]
        internal static extern bool GetWindowRect(
            IntPtr hWnd, ref Rectangle rect);

        [DllImport("user32.dll")]
        internal static extern bool IsWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        internal static extern bool IsWindowVisible(IntPtr hWnd);
    }
}

これで、プレビュー画面では下図のように表示されます。

画面のプロパティ


▼ページトップへ

◆13.拡張子の変更

このアプリケーションはスクリーンセーバーとして動作させる必要がありますが、このままビルドするとxxx.exeができてしまいます。


拡張子exeをエクスプローラでscrに変更してもよいのですが、プロジェクトのビルドイベントの「ビルド後に実行するコマンドライン」でファイルをコピーする方法がスマートです。

  1. メニューから[プロジェクト]-[xxxのプロパティ]を選択し、[ビルドイベント]をクリックします。
  2. 下図のようにビルドイベントを記述します。
  3. F6キーを押して、プロジェクトをビルドします。

プロジェクトのプロパティ

これでプロジェクトの保存先(保存していない場合は先に保存してからビルドしてください)の\bin\Releaseに
xxx.scrというファイルが出来上がりますので、これをウィンドウズのシステムディレクトリ(Windows\System32)にコピーしてください。


なお、レジストリはいじっていませんので、上記のディレクトリから該当のファイルを削除すればアンインストールとなります。

動作確認:WindowsXp Professional/Windows 7 64bit
必要なもの:.NET Framework 3.5

◆ダウンロード
ダウンロードmyscreensaver2.zip(97KB)

▼ページトップへ