想用C#制作一個windows屏保,網上的文章大都泛泛而談,語焉不詳,特別是涉及到windows屏保設置的時候,怎樣在屏保預覽窗口顯示屏保,沒有找到詳細的說明,胡亂測試了一通,還真做出來了,分享一下。

畫面是這樣的,就是簡單的隨機畫點線條,代碼都是別人寫現成的,本身也簡單。關鍵是怎么轉為屏保。
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace 屏幕保護程序
{
public partial class Form1 : Form
{
public PictureBox pictureBox;
Timer timer;
Graphics graphics;
Random random;
Point start = new Point(0, 0);
public Form1()
{
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None; //窗體運行后無邊界
this.ShowInTaskbar = false; //程序運行后不顯示在任務欄上
this.WindowState = FormWindowState.Maximized; //窗體運行后,最大化,充滿整個屏幕
pictureBox = new PictureBox();
pictureBox.Parent = this;
pictureBox.Dock = DockStyle.Fill;
pictureBox.BackColor = Color.Black;
this.KeyDown += new KeyEventHandler(this.Form1_KeyDown);
this.MouseMove += new MouseEventHandler(this.Conctrol_MouseMove); // 控件充滿窗體時沒用
pictureBox.MouseMove += new MouseEventHandler(Conctrol_MouseMove);
}
private void Form1_Load(object sender, EventArgs e)
{
graphics = pictureBox.CreateGraphics(); // 在構造函數中使用時畫圖區域為窗體初始大小
random = new Random();
timer = new Timer();
timer.Interval = 20;
timer.Enabled = true;
timer.Tick += new EventHandler(Timer_Tick);
Cursor.Hide();
}
private Pen GetPen()
{
// make a random color
int A = random.Next(0, 256);
int R = random.Next(0, 256);
int G = random.Next(0, 256);
int B = random.Next(0, 256);
Color color = Color.FromArgb(A, R, G, B);
// make pen out of color
int width = random.Next(2, 8);
return new Pen(color, width);
}
public void DrawShape(Graphics graphics) // 畫隨機線
{
Pen pen = GetPen();
int x1 = random.Next(0, (int)graphics.VisibleClipBounds.Width);
int y1 = random.Next(0, (int)graphics.VisibleClipBounds.Height);
int x2 = random.Next(0, (int)graphics.VisibleClipBounds.Width);
int y2 = random.Next(0, (int)graphics.VisibleClipBounds.Height);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
graphics.DrawLine(pen, x1, y1, x2, y2);
}
private void Timer_Tick(object source, EventArgs e)
{
DrawShape(graphics);
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
graphics.Dispose();
timer.Dispose();
}
private void Form1_KeyDown(object sender, KeyEventArgs e) // 檢測有按鍵退出屏保
{
Cursor.Show();
Application.Exit();
}
private void Conctrol_MouseMove(object sender, MouseEventArgs e) // 控件充滿窗體時窗體檢測不到鼠標移動
{
Debug.WriteLine("MouseMove: {0}", e.Location); // 窗體打開時鼠標即使不動也會不停觸發MouseMove事件
if (start == new Point(0, 0)) // 程序開始運行后,把鼠標當前的位置記錄下來
{
start = e.Location;
return;
}
else if (start != e.Location) // 判斷自程序運行后,鼠標的位置是否變動
{
Cursor.Show();
Application.Exit();
};
}
}
}
主要輸出畫面的代碼根據注釋一看就明白了,稍微麻煩的是在windows里怎么設置,如下:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace 屏幕保護程序
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args != null && args.Length != 0)
{
//for (int i = 0; i < args.Length; i++) // 測試scr文件右鍵以及系統的屏幕保護程序設置使用的調用參數
//{
// MessageBox.Show(i + ": " + args[i], "系統提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
//}
if (args[0].StartsWith("/c") || args[0].StartsWith("/C") || args[0].StartsWith("-c") || args[0].StartsWith("-C"))
{
MessageBox.Show("此屏幕保護沒有可供設置的選項!", "系統提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (args[0].Equals("/p") || args[0].Equals("/P") || args[0].Equals("-p") || args[0].Equals("-P"))
{
if (args.Length < 2) return;
if (!int.TryParse(args[1], out int result)) return; // 參數/p之后有緊隨另一個整數參數(句柄)
IntPtr handle = new IntPtr(result);
Form1 form1 = new Form1(); // 必須有此句預覽窗口才有顯示輸出(到底起了什么作用?很奇怪!)
//form1.Show(); // 正常顯示窗體,會蓋住"屏幕保護程序設置"窗口
Graphics graphics = Graphics.FromHwnd(handle);
while (IsWindowVisible(handle)) // 在系統的"屏幕保護程序設置"的預覽窗口顯示輸出
{
//form1.DrawShape(graphics); // 這樣調用(無論是實例化還是靜態)死活不行
DrawShape(graphics);
System.Threading.Thread.Sleep(100); // 間隔一定時間形成動畫效果
}
form1.Close();
return;
}
}
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr handle);
static Random random = new Random();
static private Pen GetPen()
{
// make a random color
int A = random.Next(0, 256);
int R = random.Next(0, 256);
int G = random.Next(0, 256);
int B = random.Next(0, 256);
Color color = Color.FromArgb(A, R, G, B);
// make pen out of color
int width = random.Next(2, 8);
return new Pen(color, width);
}
static public void DrawShape(Graphics graphics) // 畫隨機線
{
Pen pen = GetPen();
int x1 = random.Next(0, (int)graphics.VisibleClipBounds.Width);
int y1 = random.Next(0, (int)graphics.VisibleClipBounds.Height);
int x2 = random.Next(0, (int)graphics.VisibleClipBounds.Width);
int y2 = random.Next(0, (int)graphics.VisibleClipBounds.Height);
graphics.DrawLine(pen, x1, y1, x2, y2);
}
}
}

生成Windows屏幕保護程序,正常編譯得到exe文件,把"exe"改名為"scr",可拷貝到Windows的"System32"目錄中
scr文件右鍵對應的菜單傳遞的參數:Test為/s,安裝為/p和句柄參數,配置為空(不是/c)
系統的"屏幕保護程序設置":下圖的按鈕"設置"和"預覽"點擊后分別被系統調用了兩次,
"設置"第一次對應 (/c加一個整數句柄)參數調用(一個參數) ,第二次對應 (/p)和(句柄參數)參數調用(兩個參數)
"預覽"第一次對應(/s)參數調用 ,第二次對應 (/p)和(句柄參數)參數調用(兩個參數)
"設置"點擊后根據句柄參數可以打開設置窗體,"預覽"點擊后在主屏幕上預覽,兩者完畢后都在"預覽"窗口顯示預覽(實際上是以預覽參數又一次調用我們的屏保程序)
存在的疑問是:怎么在屏保預覽窗口顯示屏保,如下

這是我偶然試出來的,不明白為啥。就是必須在預覽參數的代碼段里實例化屏保窗體但卻不使用:Form1 form1 = new Form1(); // 必須有此句預覽窗口才有顯示輸出(到底起了什么作用?很奇怪!)
最開始確實是要實例化窗體的,因為要重復使用窗體類畫圖的代碼,結果不行,沒辦法才在Main方法里重新搞了一遍,結果無意中可以預覽了,真是神奇。
