作者:万立中 时间:2011-04-7 转载:请注明www.wanlizhong.com
本文以VC++6.0为开发环境,采用Windows SDK方式实现一个实际可用的小图片更换屏保程序。程序中使用了20张微软Bing背景图片,每张图片的大小为335×179,当然你也可以选择自己更感兴趣的图片。如果需要本程序的图片资源,可以点击这里下载。
由于采用Windows SDK方式进行代码的编写,主窗口的生成、显示及窗口消息处理需要由自己编写代码来完成。作为屏幕保护程序,该主窗口必须全屏幕方式显示,而且窗口需要每隔一段时间自动更换画面,这个工作可以采用定时器来完成(关于主窗口的生成及显示以及定时器的使用,可以参见本站的《VC++定时器的使用(Windows SDK方式)》一文)。另外,屏幕保护程序的退出方式也不同于一般程序,典型的处理方式及时按下键盘上的任意键或移动鼠标。为此,需要专门处理WM_MOUSEMOVE消息以及WM_KEYDOWN消息。
下面简单阐述一下整个的实现过程:
1. 首先在VC++6.0中创建一个空的Win32 Application,工程名自定义;
2. 然后为工程添加ImgSaver.h、ImgSaver.cpp以及SaverApp.cpp三个文件,其中SaverApp.cpp文件中实现主窗口的生成、显示及窗口消息处理,ImgSaver.h和ImgSaver.cpp文件用来声明和定义一个简单的CImgSaver图像处理类,实现图片资源的加载、绘制;
3. 为了使程序能够独立运行,所有的图片要以资源的形式添加到工程中,并且编译到exe文件中。当图片加入到工程时,需要保持这些图片资源的标识号是连续的,最重要的是要记住第一个图片资源的标识号,加载图片资源是就可以以此类推了;
4. 在SaverApp.cpp文件的主窗口回调函数中,需要处理的消息及说明如下:
1) WM_CREATE消息:初始化CImgSaver对象;
2) WM_TIMER消息:处理定时任务,即画面更新;
3) WM_PAINT消息:处理画面绘制,其中要实现双缓冲绘图,避免屏幕闪烁;
4) WM_MOUSEMOVE消息:判断鼠标位置,处理是否退出程序;
5) WM_KEYDOWN消息:判断是否按下键盘,处理是否退出程序;
6) WM_DESTROY:程序销毁处理。
5. 代码完成并通过编译后,将.exe扩展名修改为.scr,拷贝至Windows目录的System32目录中,然后在系统的屏幕保护程序的选项中就可以看到你的程序,选择后就可以正式运行了。
以下是本程序的具体实现代码:
//===================================================
//ImgSaver.h文件
//===================================================
#pragma once
#include <windows.h>
#include <time.h>
#include <iostream>//srand, rand
using namespace std;
class CImgSaver
{
private:
int update_speed;
int total_num;
HBITMAP* hBitmap;
HWND h_wnd;
public:
CImgSaver(HWND hwnd, int Speed, int Num);
~CImgSaver();
HBITMAP LoadSrcBmp(WORD srcID);
PaintImage(HDC hdc);
};
//===================================================
//ImgSaver.cpp文件
//===================================================
#include "ImgSaver.h"
#include "resource.h"
CImgSaver::CImgSaver(HWND hwnd, int Speed, int Num)
{
h_wnd = hwnd;
update_speed = Speed;
total_num = Num;
hBitmap = new HBITMAP[Num];
for(int i=0; i<Num; i++)
{
int N = rand()%Num;
N = N + 1;
hBitmap[i] = LoadSrcBmp((120+N));//(120+N)为资源标号
}
//设置定时器
SetTimer(h_wnd, 1, Speed, (TIMERPROC)NULL);
}
CImgSaver::~CImgSaver()
{
delete[] hBitmap;
}
HBITMAP CImgSaver::LoadSrcBmp(WORD srcID)
{
HBITMAP hBmp =
(HBITMAP)LoadImage(GetModuleHandle(0),
MAKEINTRESOURCE(srcID),
IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION);
if (!hBmp) return NULL;
return hBmp;
}
CImgSaver::PaintImage(HDC hdc)
{
HPEN linePen = CreatePen(PS_SOLID, 2, RGB(255, 255, 0));
SelectObject(hdc, linePen);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
RECT WinRect;
GetClientRect(h_wnd, &WinRect);
int Col = WinRect.right/335;
int Row = WinRect.bottom/179;
int num;
for(int i=0; i<Col+1; i++)
{
for(int k=0; k<Row+1; k++)
{
RECT CellRect;
CellRect.left = i*335;
CellRect.top = k*179;
CellRect.bottom = CellRect.top+179;
CellRect.right = CellRect.left+335;
num = rand()%20;
HDC FrameDC = CreateCompatibleDC(hdc);
HBITMAP hOldBitmap =
(HBITMAP)SelectObject(FrameDC, hBitmap[num]);
StretchBlt(hdc, i*335, k*179, 335, 179,
FrameDC, 0, 0, 335, 179, SRCCOPY);
Rectangle(hdc, CellRect.left, CellRect.top,
CellRect.right, CellRect.bottom);
SelectObject(FrameDC, hOldBitmap);
DeleteDC(FrameDC);
}
}
DeleteObject(linePen);
}
//===================================================
//SaverApp.cpp文件
//===================================================
#include <windows.h>
#include "ImgSaver.h"
//声明回调函数
LONG CALLBACK MyWndProc(HWND, UINT, WPARAM, LPARAM);
CImgSaver* iSaver; //声明CImgSaver类对象的指针
POINT mXY; //记录鼠标的位置
//========================================================
//WinMain函数
//========================================================
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HWND hWnd;
WNDCLASSEX wnd;
wnd.cbSize = sizeof(WNDCLASSEX);
wnd.style = CS_HREDRAW | CS_VREDRAW;
wnd.lpfnWndProc = (WNDPROC)MyWndProc;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hInstance = hInstance;
wnd.hIcon = NULL;
wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
wnd.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wnd.lpszMenuName = NULL;
wnd.lpszClassName = "ImageSaver";
wnd.hIconSm = NULL;
RegisterClassEx(&wnd);
hWnd = CreateWindow( "ImageSaver", "ImageSaver",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 800, 600,
NULL, NULL, hInstance, NULL);
//==========================================================
//全屏幕显示
HWND hDesktop;
RECT rc;
hDesktop = GetDesktopWindow();
GetWindowRect(hDesktop, &rc);
SetWindowLong(hWnd, GWL_EXSTYLE, WS_EX_WINDOWEDGE);
SetWindowLong(hWnd, GWL_STYLE, WS_BORDER);
SetWindowPos(hWnd, HWND_TOP, 0, 0, rc.right, rc.bottom, SWP_SHOWWINDOW);
//==========================================================
if (!hWnd) return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//========================================================
//回调函数
//========================================================
LONG CALLBACK MyWndProc (HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
HDC hdc, memDC;
RECT winRect;
GetClientRect(hWnd, &winRect);
HBITMAP memBmp;
HBRUSH brush;
switch (message) {
case WM_CREATE:
iSaver = new CImgSaver(hWnd, 1000, 20);
return 0;
case WM_TIMER:
//调用WM_PAINT重绘, 不擦除背景
InvalidateRect(hWnd, &winRect, FALSE);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
memDC = CreateCompatibleDC(hdc);
memBmp = CreateCompatibleBitmap(hdc, winRect.right, winRect.bottom);
SelectObject(memDC, memBmp);
brush = CreateSolidBrush(RGB(255, 255, 255));
FillRect(memDC, &winRect, brush);
iSaver->PaintImage(memDC);
BitBlt(hdc, 0, 0, winRect.right, winRect.bottom, memDC, 0, 0, SRCCOPY);
DeleteDC(memDC);
DeleteObject(memBmp);
EndPaint(hWnd,&ps);
return 0;
case WM_MOUSEMOVE:
if(mXY.x == 0 && mXY.y == 0)
{
//记录窗口创建时的鼠标位置
mXY.x = LOWORD(lParam);
mXY.y = HIWORD(lParam);
}
if(mXY.x != LOWORD(lParam) && mXY.y != HIWORD(lParam))
{
KillTimer(hWnd, 1);
PostQuitMessage(0);
}
return 0;
case WM_KEYDOWN:
KillTimer(hWnd, 1);
PostQuitMessage(0);
return 0;
case WM_DESTROY:
KillTimer(hWnd, 1);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
上下翻半天都没找到用来记录鼠标位置坐标的 “mXY” 变量 的初始化代码,
难道POINT定义实例对象会自动初始化“x”,“y”成员为0值吗?但是翻一下手册,找到定义POINT结构的代码,却也没发现有这样的初始化为0的代码。。
老师..,这是何故?
呃..还有一个就是把资源添加到工程中,以图片ID形式调用,好像会造成图片色度的下降,因为工程好像只支持256色的图片,那么还有其它方法,使图片在质量不被损坏的情况,依然能被编译到程序中吗??
POINT结构体是两个LONG型值成员, 如果没有赋值, 系统默认赋0; 图片资源的色度不会下降, 引入图片时出现提示的意思是多于256色的图片不能在VC的位图编辑器中直接进行编辑而已, 编译后图片的色深并没有改变, 这个你看屏幕的图片就知道了, 如果是256色的话, 很多像素应该是看不到的, 现在看到的效果显然是真彩的
哦~懂了..谢谢老师!
老师我想问下,我载入不同大小的图片,怎么样才能让整个屏保跟这个图片契合呢,