黑客帝国经典屏保源码
聊一个经典老梗:电影《黑客帝国》里那满屏绿色数字雨的屏保效果,当年是多少人对着电脑屏幕发呆时的标配回忆。虽然现在系统自带的屏保已经不错,但亲手写代码、编译成.scr文件,再设置到桌面上,那种成就感完全不一样。下面把整个流程拆开讲,从代码到最终设置,一步不落。
前期准备
开发环境用Visual Studio 2010或更高版本就行。打开VS,新建项目,选择Visual C++下的Win32控制台应用程序,项目名称填写"hacker"——名字随意,但后面需要对着这个文件名操作。
粘贴代码
项目创建好后,把下面这段源码复制到主源文件里(通常是"hacker.cpp")。这段代码是一位叫Wicrecend的程序员写的数字流星雨效果,核心思路是用双向链表维护每一列字符的滚动状态,通过定时器逐帧刷新屏幕,配合随机字符和颜色渐变,实现经典的矩阵雨特效。
//数字流星雨 作者:Wicrecend
#include "stdafx.h"
#include
#define ID_TIMER 1
#define STRMAXLEN 25 //一个显示列的最大长度
#define STRMINLEN 8//一个显示列的最小长度
#pragma comment ( linker, "/subsystem:windows /entry:mainCRTStartup" ) //去除启动或退出程序时黑框
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
typedef struct tagCharChain //整个当作屏幕的一个显示列,这是个双向列表
{
struct tagCharChain *prev; //链表的前个元素
TCHAR ch;//一个显示列中的一个字符
struct tagCharChain *next; //链表的后个元素
}CharChain, *pCharChain;
typedef struct tagCharColumn
{
CharChain *head, *current, *point;
int x, y, iStrLen; //显示列的开始显示的x,y坐标,iStrLen是这个列的长度
int iStopTimes, iMustStopTimes; //已经停滞的次数和必须停滞的次数,必须停滞的次数是随机的
}CharColumn, *pCharColumn;
int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("matrix") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style= CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc= WndProc ;
wndclass.cbClsExtra= 0 ;
wndclass.cbWndExtra= 0 ;
wndclass.hInstance= hInstance ;
wndclass.hIcon= LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor= LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground= (HBRUSH) GetStockObject (BLACK_BRUSH) ;
wndclass.lpszMenuName= NULL ;
wndclass.lpszClassName= szAppName ;
if(!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("此程序必须运行在NT下!"), szAppName, MB_ICONERROR) ;
return 0;
}
hwnd = CreateWindow (szAppName, NULL, WS_DLGFRAME | WS_THICKFRAME | WS_POPUP,
0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, SW_SHOWMAXIMIZED) ; //最大化显示
UpdateWindow (hwnd) ;
ShowCursor(FALSE); //隐藏鼠标光标
srand ((int) GetCurrentTime ()) ; //初始化随机数发生器
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
ShowCursor(TRUE); //显示鼠标光标
return msg.wParam ;
}
TCHAR randomChar() //随机字符产生函数
{
return (TCHAR)(rand()%(126-33) + 33); //33到126之间
}
int init(CharColumn *cc, int cyScreen, int x) //初始化
{
int j;
cc->iStrLen = rand()%(STRMAXLEN-STRMINLEN) + STRMINLEN; //显示列的长度
cc->x = x + 3 ;//显示列的开始显示的x坐标
cc->y =rand()%3?rand()%cyScreen:0; //显示列的开始显示的y坐标
cc->iMustStopTimes = rand()%6 ;
cc->iStopTimes= 0 ;
cc->head = cc->current = (pCharChain)calloc(cc->iStrLen, sizeof(CharChain)); //生成显示列
for(j=0; jiStrLen-1; j++ )
{
cc->current->prev = cc->point; //cc->point一个显示列的前个元素
cc->current->ch= '0';
cc->current->next = cc->current + 1; //cc->current + 1一个显示列的后个元素
cc->point= cc->current++ ; //cc->point = cc->current; cc->current++ ;
}
cc->current->prev = cc->point; //最后一个节点
cc->current->ch= '0';
cc->current->next = cc->head;
cc->head->prev= cc->current; //头节点的前一个为此链的最后一个元素
cc->current = cc->point = cc->head; //free掉申请的内存要用current当参数
cc->head->ch = randomChar(); // 对链表头的 元素填充
return 0;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
int i, j, temp, ctn;
static HDC hdcMem;
HFONT hFont;
static HBITMAP hBitmap;
static int cxScreen, cyScreen; //屏幕的宽度 高度.
static int iFontWidth=10, iFontHeight=15, iColumnCount; //字体的宽度 高度, 列数
static CharColumn *ccChain;
switch (message)
{
case WM_CREATE:
cxScreen = GetSystemMetrics(SM_CXSCREEN) ; //屏幕宽度
cyScreen = GetSystemMetrics(SM_CYSCREEN) ;
SetTimer (hwnd, ID_TIMER, 10, NULL) ;
hdc = GetDC(hwnd);
hdcMem = CreateCompatibleDC(hdc);
hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
SelectObject(hdcMem, hBitmap);
ReleaseDC(hwnd, hdc);
//创建字体
hFont = CreateFont(iFontHeight, iFontWidth-5, 0, 0, FW_BOLD, 0, 0, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DRAFT_QUALITY, FIXED_PITCH | FF_SWISS, TEXT("Fixedsys"));
SelectObject(hdcMem, hFont);
DeleteObject (hFont) ;
SetBkMode(hdcMem, TRANSPARENT); //设置背景模式为 透明
iColumnCount = cxScreen/(iFontWidth*3/2); //屏幕所显示字母雨的列数
ccChain = (pCharColumn)calloc(iColumnCount, sizeof(CharColumn));
for(i=0; iiStopTimes ++ > (ccChain+i)->iMustStopTimes;
// (ccChain+i)->point = (ccChain+i)->head;
//point用于遍历整个显示列
//第一个字符显示为 白色
SetTextColor(hdcMem, RGB(255, 255, 255));
TextOut(hdcMem, (ccChain+i)->x, (ccChain+i)->y,
&((ccChain+i)->point->ch), 1);
j = (ccChain+i)->y;
(ccChain+i)->point = (ccChain+i)->point->next;
//遍历整个显示列,将这个显示列里的字符从下往上显示
temp = 0 ; //temp绿色过度到黑色之用
while((ccChain+i)->point != (ccChain+i)->head && (ccChain+i)->point->ch)
{
SetTextColor(hdcMem, RGB(0, 255-(255*(temp++)/(ccChain+i)->iStrLen), 0));
TextOut(hdcMem, (ccChain+i)->x, j-=iFontHeight,
&((ccChain+i)->point->ch), 1);
(ccChain+i)->point = (ccChain+i)->point->next;
}
if(ctn)
(ccChain+i)->iStopTimes = 0 ;
else
continue;
(ccChain+i)->y += iFontHeight; //下次开始显示的y坐标 为当前的y坐标加上 一个字符的高度
//如果开始显示的y坐标减去 整个显示列的长度超过了屏幕的高度
if( (ccChain+i)->y-(ccChain+i)->iStrLen*iFontHeight > cyScreen)
{
free( (ccChain+i)->current );
init(ccChain+i, cyScreen, (iFontWidth*3/2)*i);
}
//链表的头 为此链表的前个元素,因为下次开始显示的时候 就相当与在整个显示列的开头添加个元素,然后在开始往上显示
(ccChain+i)->head = (ccChain+i)->head->prev;
(ccChain+i)->head->ch = randomChar();
}
BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hdc);
return 0;
case WM_RBUTTONDOWN:
KillTimer (hwnd, ID_TIMER) ;
return 0;
case WM_RBUTTONUP:
SetTimer (hwnd, ID_TIMER, 10, NULL) ;
return 0;
//处理善后工作
case WM_KEYDOWN:
case WM_LBUTTONDOWN:
case WM_DESTROY:
KillTimer (hwnd, ID_TIMER) ;
DeleteObject(hBitmap);
DeleteDC(hdcMem);
for(i=0; icurrent );
}
free(ccChain);
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
编译与链接
代码粘贴进去后,直接按F7(或者点击生成→生成解决方案),确保编译没有错误。如果提示找不到"stdafx.h",检查一下项目属性里是否设置了预编译头,或者直接把那行#include注释掉也可以(因为代码里其实并没有用到预编译头中的内容)。
转换为屏保文件
编译成功之后,在项目输出目录(比如Debug或Release文件夹)中找到"hacker.exe"。把这个文件重命名,将后缀.exe改为.scr,变成"hacker.scr"。这一步是Windows屏保的硬性要求——屏保本质上就是一个可执行文件,只不过后缀必须为.scr。
安装到系统目录
将改好名的"hacker.scr"文件复制到C:\Windows\System32目录下(如果是64位系统且屏保是32位,也可以放在C:\Windows\SysWOW64,但通常放在System32即可)。复制时可能需要管理员权限,确认授权就好。
设置屏保
接下来就是常规操作了:在桌面空白处右键→个性化→锁屏界面→屏幕保护程序设置(或者直接搜索“更改屏幕保护程序”)。在下拉菜单里应该就能找到"hacker"这个选项,选中它,预览一下效果,然后点击应用和确定。
大功告成。当你离开电脑几分钟,屏幕就会自动下起绿色的数字雨——而且是你亲手编译出来的雨。

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
批处理BAT入门教程第一篇
提供13个批处理实战技巧,覆盖全盘查找并删除文件夹或文件、拷贝移动文件、创建畸形文件夹及设置隐藏属性等场景,可一键完成系统维护与文件管理工作,极大提升自动化操作效率和便捷性。
从零开始批处理命令For循环详解与实战案例
批处理For命令支持 d、 l、 r、 f四个参数。 d仅列出当前目录下的目录名; r递归搜索指定路径及其子目录中的文件; l生成数值序列; f可解析文件、字符串或命令输出,通过delims、tokens、skip、eol等选项灵活处理内容。
批评你的人是你生命中的贵人
批评你的人往往最值得珍惜,因为他们关注你、助你成长。面对批评应包容反思,用行动改进而非辩解。接受批评是自我完善的过程,能让人少走弯路,避免重复犯错。这样的人正是生命中的贵人,值得感恩与珍惜。
测试人员角色定位与职责详解
测试人员角色经历了从找问题、保证质量到分析风险的转变,最终核心职责是提供关键信息,协助团队创造优秀产品。这包括识别问题、评估风险及帮助团队了解项目状态,而非单纯把关或追求完美。
经营成功测试生涯的实用方法与策略
一、测试生涯的起点 1989年,我在田纳西大学攻读研究生时,意外地从软件开发人员转行成为一名软件测试工程师。这并非我主动选择,说起来还有些戏剧性——某个早晨,教授质问我为何缺席那么多开发会议,我解释说这些会议总是安排在周末早上,对我这个第一次离家、刚入学的学生来说实在不便。结果呢?等待我的不是解聘通
- 日榜
- 周榜
- 月榜
相关攻略
2026-07-03 16:15
2026-07-03 16:14
2026-07-03 16:14
2026-07-03 16:14
2026-07-03 16:14
2026-07-03 16:14
2026-07-03 16:13
2026-07-03 16:13
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

