VC的一个屏保例子

用清明上河图做屏保程序

参考http://it.sohu.com/2004/03/15/29/article219442988.shtml

前几天得到清明上河图的图片,感觉它太长,于是不容易欣赏,希望能慢慢的自动的移动,最好加点音乐,有这个想法之后就开始做了一个看清明上河图的小软件,可以放大、缩小、移动速度增快或是变慢、暂停、再开始、或是反方向移动等功能。之后就想到不如用其作为屏保,于是查找了怎么制作屏保程序。参考了上面链接地址的方法。不过我认为我这种方法要简单得多,按照它上面的方法我没有成功。不过挺感谢他的。
其实屏保程序就是一个执行文件,将后缀名改为.scr,然后放到C:\windows\system32\下就可以了,最后再在桌面的属性中选择自己的屏保程序就可以了,简单吧,哈哈。
在这里我要说的是用VC来做清明上河图的屏保程序,图片是清明上河图,自己慢慢移动显示的,我还加了音乐,这更加有意思了。


准备工作:
1、清明上河图的图片,将其转化成.bmp格式。
2、准备一个wav格式的歌曲,命名为“清明上河图屏保音乐.wav”其他格式的可以用千千静听转化。(不需要背景音乐的话这个就不需要了)
3、就是编程环境VC,我的程序是在Visual C++ 6.0下面编译的。


在这里,我是假设你没有学过VC,一点都不懂VC的情况下写的,对于会用VC的朋友有的可以不用看,主要看代码部分。


方法如下:
一、单击VC中上菜单“file”,选择“new”,出来一个对话框,选择“Projects”——>“MFC appWizard[exe]”,然后在右边的Project name上输入工程名称“清明上河图屏保”。在下面Location中选择要保存工程的地方。然后单击“OK”。(如图1)

(图1)

二、现在出来了第二个对话框,我们只需要建立一个基于对话框的就可以了,这样方便省事。沟选“Dialog based”,单击“next”。(如图2)

(图2)

三、出来第三个对话框,取消掉已构选的“Aboat box”,沟选也可以,只是对于次程序没有用。单击“finish”。(如图3)

(图3)

四、好了,工程文件建立完了,出来了设计窗口,(如图4)将对话框上的控件删除掉(用鼠标选择,然后按“Delete”键就可以删除掉)。(如图5)

(图4)

(图5)

五、用MFC写的程序有的时候在别的机器上不能用,于是要做如下设置:选择菜单“Project”——>“Setting...”(如图6)。之后出来一个对话框,选择“General”,然后在“Microsoft Foundation Classes:”下选择“Use MFC in a Static Library”,最后单击“OK”就可以了。(如图7)

(图6)

(图7)

六、载入我们的清明上河图图片,就是之前准备的bmp格式的图片,只能是这个格式才可以这样载入图片。鼠标右击“Icon”选择“Import...”(如图8)。之后出来一个文件对话框,你将“文件类型”改为“所有文件(*.*)”。你找到你之前准备的清明上河图图片,然后单击“Impot”,之后会弹出一个窗口,单击“确定”就行了。

(图8)

对于一些基础的设置就完成了,之后的步骤就是添加代码了。

七、找到左中位置的“Class view”(或者是Class...) ,单击小窗口中的图标展开类结构(如图9)。双击“CMyDlg”,添加如下代码到如下地方(如图10中的黑色部分)
private:
UINT s_showH;//这是显示屏的高度
UINT s_showW;//这是显示屏的宽度度
UINT w_showX;//图像要显示的位置(从左到右的坐标)
CDC* w_pdcmem;//位图内存,也就是和清明上河图关联的内存,
CBitmap w_bitmap;//清明上河图位图
CPoint w_point;       //记录鼠标的位置
void DrawBitmap();//画图的函数,这里手动添加了。这样方便快捷
(说明一下,这里添加的这个函数DrawBitmap(),是将图片画处理的函数,这里直接这样声明了。后面手动添加代码。)

(图9)

(图10)

八、添加消息响应函数,这里我们通过鼠标单击来添加,如图11,右键单击“CMyDlg”,选择“Add Windows Message Handler...”,这是出来一个添加Windows 消息的对话框,如图12,在右边找到如下项,分别双击,之后就添加完成了,都添加完成之后,单击右边的“OK”,就完成了消息的处理函数。
WM_TIMER       //计时器消息
WM_KEYDOWN       //键盘消息
WM_LBUTTONDOWN       //鼠标左键按下
WM_MBUTTONDOWN       //鼠标中键按下
WM_MOUSEMOVE       //鼠标移动
WM_RBUTTONDOWN       //鼠标右键按下
WM_SYSKEYDOWN       //系统键消息
WM_DESTROY       //销毁消息
WM_ACTIVEVATEAPP //只能允许一个程序在运行。

(图11)

(图12)

九、完成消息的处理。
1.如图13,只要双击图中画圈的函数名称,就可以进入到函数体中。分别对坐边画上圈的函数添加一行代码 PostMessage(WM_CLOSE); 这是关闭窗口,退出程序运行的操作。操作后代码如下:
void CMyDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CMyDlg::OnLButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnLButtonDown(nFlags, point);
}

void CMyDlg::OnMButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnMButtonDown(nFlags, point);
}

void CMyDlg::OnRButtonDown(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnRButtonDown(nFlags, point);
}

void CMyDlg::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
// TODO: Add your message handler code here and/or call default
PostMessage(WM_CLOSE);
CMyDlg::OnSysKeyDown(nChar, nRepCnt, nFlags);
}

(图13)

2.对OnMouseMove(UINT nFlags, CPoint point)要做处理,如下:表示要等鼠标移动200个象素之后才退出屏保
void CMyDlg::OnMouseMove(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
if(abs(point.x-w_point.x)>=200 ||
       abs(point.y-w_point.y)>=200)
{
       PostMessage(WM_CLOSE);
}
}


3.对OnActivateApp(BOOL bActive, HTASK hTask) 函数补充,加入代码后如下:
void CMyDlg::OnActivateApp(BOOL bActive, HTASK hTask) 
{
CWnd::OnActivateApp(bActive, hTask);
// TODO: Add your message handler code here
if (!bActive) //is being deactivated
       PostMessage(WM_CLOSE); 
}
4.对OnDestroy()函数补充,加入代码后如下:
void CMyDlg::OnDestroy() 
{
CMyDlg::OnDestroy();

// TODO: Add your message handler code here
delete w_pdcmem;
KillTimer(1);//程序中用到了计时器。这里关掉计时器
}
4.对OnTimer(UINT nIDEvent),这里主要是时间片到的时候就调用DrawBitmap();重画一次屏幕,这样来实现图片沿着一个方向移动。
void CMyDlg::OnTimer(UINT nIDEvent) 
{
// TODO: Add your message handler code here and/or call default
w_showX += 1;
if(w_showX>=BITMAP_WEIGHT){
       w_showX = 0;

DrawBitmap();

CDialog::OnTimer(nIDEvent);
}
解释一下代码:
w_showX += 1;w_showX变量是对清明上河图要显示区域的左位置。没重画一次,w_showX加1,也就是一个象素的距离,这样使得图片移动时更流畅,逼真。
if(w_showX>=BITMAP_WEIGHT){这是当显示到结尾处的时候再次从开始位置处显示。
w_showX = 0;

DrawBitmap();调用这个函数重画。

十、之前对消息处理完成了,现在我们来处理界面,因为我们是创建的对话框,而屏保是全屏显示的,所以我们要做一下处理。添加如下代码到CMyDlg::OnInitDialog()。(如图14)
//设置全屏窗口
CRect m_rcMain;
GetWindowRect(&m_rcMain);//restore        the        src        screen's        size;   
         //删除窗口的标题栏
LONG        style        =        GetWindowLong(m_hWnd,GWL_STYLE);   
style &= ~WS_CAPTION;   
SetWindowLong(m_hWnd,GWL_STYLE,style); //设置显示窗口状态  
//获得显示器屏幕的宽度  
s_showW        =        GetSystemMetrics(SM_CXSCREEN);   
s_showH        =        GetSystemMetrics(SM_CYSCREEN);   
//show        window   
SetWindowPos(NULL,-23,-3,s_showW+27,s_showH+6,SWP_NOZORDER);

//获得鼠标的位置
::GetCursorPos(&w_point);
//隐藏鼠标
ShowCursor(FALSE);
完成的代码如下:
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Set the icon for this dialog.       The framework does this automatically
//       when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);        // Set big icon
SetIcon(m_hIcon, FALSE);       // Set small icon

// TODO: Add extra initialization here

//设置全屏窗口
CRect m_rcMain;
GetWindowRect(&m_rcMain);//restore        the        src        screen's        size;   
         //删除窗口的标题栏
LONG        style        =        GetWindowLong(m_hWnd,GWL_STYLE);   
style &= ~WS_CAPTION;   
SetWindowLong(m_hWnd,GWL_STYLE,style); //设置显示窗口状态  
//获得显示器屏幕的宽度  
s_showW        =        GetSystemMetrics(SM_CXSCREEN);   
s_showH        =        GetSystemMetrics(SM_CYSCREEN);   
//show        window   
SetWindowPos(NULL,-23,-3,s_showW+27,s_showH+6,SWP_NOZORDER);
//获得鼠标的位置
::GetCursorPos(&w_point);
//隐藏鼠标
ShowCursor(FALSE);

return TRUE;       // return TRUE       unless you set the focus to a control
}

(图14)

十一、界面,消息都处理完成了,现在我们就来开始在屏幕上画图了。不过先别急,先到CMyDlg::OnPaint() 添加一些初始画数据的代码。完成之后如下:
void CMyDlg::OnPaint() 
{
if (IsIconic())
{
       CPaintDC dc(this); // device context for painting

       SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

       // Center icon in client rectangle
       int cxIcon = GetSystemMetrics(SM_CXICON);
       int cyIcon = GetSystemMetrics(SM_CYICON);
       CRect rect;
       GetClientRect(&rect);
       int x = (rect.Width() - cxIcon + 1) / 2;
       int y = (rect.Height() - cyIcon + 1) / 2;

       // Draw the icon
       dc.DrawIcon(x, y, m_hIcon);
}
else
{
       CPaintDC dc(this); 
       w_pdcmem = new CDC;
       w_bitmap.LoadBitmap(IDB_BITMAP1);
       w_pdcmem->CreateCompatibleDC(&dc);       //建立与dc兼容的
       w_pdcmem->SelectObject(&w_bitmap);

       sndPlaySound( "清明上河图屏保音乐.wav" , SND_ASYNC | SND_LOOP );
       w_showX = 0;
       DrawBitmap();

       SetTimer(1,1,NULL);
       CDialog::OnPaint();
}
}
说明一下:if段里面的都是编译器生成的。else里面的是添加的代码,
CPaintDC dc(this);
获得屏幕画图DC
w_pdcmem = new CDC;
开辟一个内存区,用来和清明上河图位图相关联。这样直接从内存中拷贝到屏幕就可以了, 
w_bitmap.LoadBitmap(IDB_BITMAP1);
载入图片,其中IDB_BITMAP1是我们刚开始载入图片时图片的ID
w_pdcmem->CreateCompatibleDC(&dc); 
建立与屏幕dc兼容的
w_pdcmem->SelectObject(&w_bitmap);
图片与内存关联
sndPlaySound( "清明上河图屏保音乐.wav" , SND_ASYNC | SND_LOOP );
这是播放声音的函数,是系统函数。加入你需要播放声音的话,只是这个还不能实现,还需要在现在编辑的文件的最上方加入如下代码:(如图15)
#include "mmsystem.h"
#pragma comment( lib , "winmm.lib" )

w_showX = 0;
显示的时候从开始位置开始显示,你可以该为其他数据,但这里必须是在图片长度范围之内,
DrawBitmap();
开始的时候就画一次。
SetTimer(1,1,NULL);
设置计时器,第一个参数1是计时器的ID号,第二个是时间(单位毫秒),这里我设置为1毫秒,可以根据自己的需要来设定。表示的是没格这个时间段后自动调用OnTimer(UINT nIDEvent)函数。

(图15)

十二、对最后一项的处理了。就是画屏保的函数,还记得第七步中有个DrawBitmap()函数吗,这就是画图用的,现在来写代码,
现在这里说明一下:要补充一点,之前忘记了,我们必须要知道该清明上河图的长度和宽度,可以在用鼠标放在图片上,之后出来的详细信息(如图16左边部分),或直接右击该图查看属性得知(如图16右边部分),记下该数字,如图15中的地方定义两个宏:
#define BITMAP_HEIGHT 300 //原图像高度
#define BITMAP_WEIGHT 9937 //原图像宽度

(图16)

1.在这里函数里画屏幕,我们要得到屏幕DC,可以用CClientDC dc(this);来实现。
为了绘制图片的时候看起来是浏览的,我们先将要画在屏幕上图片的部分画在内存里,之后直接从内存里拷贝到屏幕上来就不会出现闪烁情况,于是我们要定义一个内存变量来和dc兼容,在为这块内存设置大小,拷贝过来的时候才知道拷贝到什么地方。这里声明一个位图对象,这位图就作为在内存里画的对象:
CDC dcmem;
CBitmap bitmap;
//创建位图s_showW,s_showH为屏幕的宽度和高度
bitmap.CreateCompatibleBitmap(&dc,s_showW,s_showH);
dcmem.CreateCompatibleDC(&dc);
dcmem.SelectObject(&bitmap);
设置背景为黑色:
dcmem.FillRect(&CRect(0,0,s_showW,s_showH),&CBrush(RGB(0,0,0)));
2.将要显示的图片部分拷贝到内存。由于清明上河图很长,我们只能在屏幕上显示一部分,我们将清明上河图显示到屏幕的中间,可以这样来计算:(屏幕的高度-图像的高度)/2就为要显示图片的重坐标。代码如下:
UINT y = (s_showH-BITMAP_HEIGHT)/2;
dcmem.StretchBlt(
       0,y,s_showW, BITMAP_HEIGHT, 
       w_pdcmem,
       w_showX,0,s_showW,BITMAP_HEIGHT-12,
       SRCCOPY);

3.只是显示图片,是不是有点太傻了,输出几个汉字?在VC中用TextOut就可以输出汉字,我这里来动态的输出汉字,也就是汉字随着图片会移动,移动完之后又再次出来。先设置输出的字体
CFont font;
font.CreateFont(20,10,//nHeight,nWidth
       0,       //int nEscapement,
       60,       //int nOrientation, 
       30,       //int nWeight,
       FALSE, //BYTE bItalic,
       FALSE,       //BYTE bUnderline,
       FALSE,       //BYTE cStrikeOut,
       DEFAULT_CHARSET, //BYTE nCharSet,
       OUT_CHARACTER_PRECIS, //BYTE nOutPrecision,
       CLIP_DEFAULT_PRECIS, //BYTE nClipPrecision,
       DEFAULT_QUALITY,       //BYTE nQuality,
       FIXED_PITCH|FF_MODERN,        //BYTE nPitchAndFamily,
       "隶书"
       );
dcmem.SelectObject(&font);
再次定义静态变量,记录开始输出汉字的坐标,然后变化该变量,实现如下:
static long l = s_showW;
--l;
if(l>-1020){
       dcmem.SetTextColor(RGB(0,100,255));
       dcmem.TextOut(l,20,"制作:汪建友");
       dcmem.TextOut(l+200,20,"QQ:421068480");
       dcmem.TextOut(l+400,20,"E-mail:neuwjyou@163.com");
       dcmem.TextOut(l+720,20,"空间:http://hi.baidu.com/neujyou");  
}
else{ 
       l = s_showW;
}
说明:1020是所有汉字的宽度,可以根据自己的需要修改,TextOut中的参数,第一个是横坐标,第二个是纵坐标,第三个为要输出的字符串。当最后一个字符串显示完成之后再次设置l = s_showW;,又可以再次现实。

4.将图片绘制在屏幕上。
dc.BitBlt(20,0,s_showW,s_showH,&dcmem,0,0,SRCCOPY);//显示在屏幕上

实现代码如下:
void CMyDlg::DrawBitmap()

CClientDC dc(this);
CDC dcmem;
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc,s_showW,s_showH);

dcmem.CreateCompatibleDC(&dc);
dcmem.SelectObject(&bitmap);
dcmem.SetBkMode(0);

dcmem.FillRect(&CRect(0,0,s_showW,s_showH),
          &CBrush(RGB(0,0,0))
       );
UINT y = (s_showH-BITMAP_HEIGHT)/2;
dcmem.StretchBlt(
       0,y,s_showW, BITMAP_HEIGHT, 
       w_pdcmem,
       w_showX,0,s_showW,BITMAP_HEIGHT-12,
       SRCCOPY);

CFont font;
font.CreateFont(20,10,//nHeight,nWidth
       0,       //int nEscapement,
       60,       //int nOrientation, 
       30,       //int nWeight,
       FALSE, //BYTE bItalic,
       FALSE,       //BYTE bUnderline,
       FALSE,       //BYTE cStrikeOut,
       DEFAULT_CHARSET, //BYTE nCharSet,
       OUT_CHARACTER_PRECIS, //BYTE nOutPrecision,
       CLIP_DEFAULT_PRECIS, //BYTE nClipPrecision,
       DEFAULT_QUALITY,       //BYTE nQuality,
       FIXED_PITCH|FF_MODERN,        //BYTE nPitchAndFamily,
       "隶书"
       );
  
dcmem.SelectObject(&font);

static long l = s_showW;
--l;
if(l>-1020){
       dcmem.SetTextColor(RGB(0,100,255));
       dcmem.TextOut(l,20,"制作:汪建友");
       dcmem.TextOut(l+200,20,"QQ:421068480");
       dcmem.TextOut(l+400,20,"E-mail:neuwjyou@163.com");
       dcmem.TextOut(l+720,20,"空间:http://hi.baidu.com/neujyou");  
}
else
       l = s_showW;

dc.BitBlt(20,0,s_showW,s_showH,&dcmem,0,0,SRCCOPY);//显示在屏幕上

bitmap.DeleteObject();
}


所有代码完成了,按一下键盘上的“F7”编译,看看有有没有问题,没有问题的话,就按键“Ctrl+F5”运行查看结果,(如图17)。没有音乐,对吗?将之前准备的“清明上河图音乐.wav”拷贝放到你的这个程序工程的目录下面,再运行看看...

(图17)


程序是完成了,不过现在还不是屏保,也就是我们要将它设置为屏保。要做最后的工作了,
1、要使在桌面属性对话框中选择此屏保时不运行(这里不是说不让它运行),要加最后一行代码。回到VC编程界面,进入CMyApp::InitInstance()中编辑,(如图18)
修改后代码如下:
BOOL CMyApp::InitInstance()

       AfxEnableControlContainer();

       LPTSTR lpszArgv = __argv[1];
       if(lpszArgv[0]=='/')
        lpszArgv++;
       if(lstrcmpi(lpszArgv,_T("s"))==0){ 
        CMyDlg dlg;
        m_pMainWnd = &dlg;
        int nResponse = dlg.DoModal();
        return true;
       }
       return FALSE;
}

(图18)


2.在你建立的工程目录下找到“debug”——>“清明上河图屏保.exe”,将“清明上河图屏保.exe”重命名为“清明上河图屏保.scr”,scr是windows系统的屏保文件。
3.将“清明上河图屏保.scr”和“清明上河图屏保音乐.wav”文件一起拷贝到“C:”文件夹下面。
4.回到桌面,单击鼠标右键,选择“属性”——>“屏幕保护程序”,
         在屏幕保护程序的下拉框中选择“清明上河图屏保”,最后单击“应用”——>“确定”,就完成了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值