VC6.0 怎么释放资源
一、概述
GDI+的应用使得平面图形图象编程变的更加容易,本文以一个基于对话框的时钟程序为例,在VC6.0中调用*.png图片实现半透明渐变窗口,该程序实现了指针式和数字式两种时钟显示方式。窗口实现了半透明渐变窗口、窗口拖动无移动矩形框、隐藏了任务栏窗体按钮等。效果图如下:
图一?程序执行后与WindowXP桌面背景效果图
二、准备工作
1、图片资源准备工作。首先在Photoshop中编辑好时钟的背景、时针、分针以及数字时钟显示方式的所有图片,如下图:将这些图片保存成为带透明通道的.png格式(GDI+调用显示时能够透明调背景)。这样程序中图片资源就准备好了。
2、下面开始做好在VC6.0下展开此项工作的基本准备工作。
(1)、下载gdiplus?forVC6.0的SDK,(总***两兆多)
(2)、在C盘建立文件夹“GDI+”将开发包拷贝在里面,亦即建立如下路径,以便例子代码顺利编译(当然你可以放到任意你喜欢的地方,只要在你的Project中正确包含路径即可!)。
C:\GDI+\Includes
C:\GDI+\Lib
C:\GDI+\gdiplus.dll
(3)在stdAfx.h中添加对GDI+环境的设置
#define?UNICODE
#ifndef?ULONG_PTR
#define?ULONG_PTR?unsigned?long*
#endif
#include?"c:\gdi+\includes\gdiplus.h"?////请修改为你的头文件路径
using?namespace?Gdiplus;?
#pragma?comment(lib,?"c:\\gdi+\\lib\\gdiplus.lib")?////请修改为你的.lib文件路径
(4)在GDIPClock.cpp中编辑app的InitInstance()中添加如下代码进行GDI+的初始化工作
GdiplusStartupInput?gdiplusStartupInput;
ULONG_PTR?gdiplusToken;
GdiplusStartup(&gdiplusToken,?&gdiplusStartupInput,?NULL);
......
//在对话框程序结束后
//关闭gdiplus的环境
GdiplusShutdown(gdiplusToken);
三、程序的实现全过程
1、建立一个基于对话框的Project,这里的名称为GDIPClock
2、在GDIPClockDlg.h中定义所有类成员变量,包括所有图片的指针和图片的长宽尺寸信息。
Image?*m_pImageClock;
Image?*m_pImageClock1;
Image?*m_pImageHHour;
Image?*m_pImageHMinu;
Image?*m_pImageHSec;
Image?*m_pImageNum;
int?m_BakWidth?,?m_BakHeight?;
int?m_HourWidth,?m_HourHeight;
int?m_MinuWidth?,?m_MinuHeight;
int?m_SecWidth?,?m_SecHeight?;
HINSTANCE?hFuncInst?;
Typedef?BOOL?(WINAPI*MYFUNC)(HWND,HDC,POINT*,SIZE*,HDC,POINT*,
COLORREF,BLENDFUNCTION*,DWORD);?
MYFUNC?UpdateLayeredWindow;
在这一步中需要特别说明的是,在创建透明窗口式需要调用一个Windows?API函数UpdateLayeredWindow(),该函数在.net以上的版本的SDK中有申明,但是在VC6.0下要调用要么下载200多兆的高版本SDK,要么从动态链接库“User32.dll”中调用,这里选择从“User32.dll”中调用。以上定义中后三项就是为此作准备的。?3、在对话框的OnCreate()中添加如下代码:对2的函数和成员变量进行初始化!(其中ImageFromIDResource()函数为从资源中载入Png图像的一个方法!)
int?CGDIPClockDlg::OnCreate(LPCREATESTRUCT?lpCreateStruct)?
{
if?(CDialog::OnCreate(lpCreateStruct)?==?-1)
return?-1;hFuncInst?=?LoadLibrary("User32.DLL");?
BOOL?bRet=FALSE;
if(hFuncInst)?
UpdateLayeredWindow=(MYFUNC)GetProcAddress(hFuncInst,?"UpdateLayeredWindow");else
{
AfxMessageBox("User32.dll?ERROR!"); exit(0);}
//初始化gdiplus的环境
//?Initialize?GDI+.
m_Blend.BlendOp=0;?//theonlyBlendOpdefinedinWindows2000
m_Blend.BlendFlags=0;?//nothingelseisspecial...
m_Blend.AlphaFormat=1;?//...
m_Blend.SourceConstantAlpha=255;//AC_SRC_ALPHA
//?png图片添加到资源中了在"PNG"下:所以这里可以从资源中调用,
//?这里Image没有提供字节调用资源中图像的函数,
//?ImageFromIDResource()是通过资源名称"PNG"和资源ID号将图像
//?的Image指针传递给指针应用。来完成的。
ImageFromIDResource(IDR_PNGBAK1,"PNG",m_pImageClock1);
ImageFromIDResource(IDR_PNGNUM,"PNG",m_pImageNum);
ImageFromIDResource(IDR_PNGBAK,"PNG",m_pImageClock);
ImageFromIDResource(IDR_PNGHOUR,"PNG",m_pImageHHour);
ImageFromIDResource(IDR_PNGMIN,"PNG",m_pImageHMinu);
ImageFromIDResource(IDR_PNGSEC,"PNG",m_pImageHSec);
m_BakWidth?=m_pImageClock->GetWidth();
m_BakHeight?=m_pImageClock->GetHeight();
m_HourWidth?=m_pImageHHour->GetWidth();
m_HourHeight=m_pImageHHour->GetHeight();
m_MinuWidth?=m_pImageHMinu->GetWidth();
m_MinuHeight=m_pImageHMinu->GetHeight();
m_SecWidth?=m_pImageHSec->GetWidth();
m_SecHeight?=m_pImageHSec->GetHeight();
::SetWindowPos(m_hWnd,?HWND_TOPMOST,0,0,m_BakWidth,m_BakHeight,SWP_NOSIZE|SWP_NOMOVE);
return?0;
}
4.在OnInitDialog()种添加如下代码对调用透明窗体初始化和设置时钟进行刷新,代码意义有注解:
//启动后立刻更新窗口样式为透明窗体
UpdateClockDisplay();
SetTimer(1,500,NULL);
//去除任务栏窗口对应按钮
ModifyStyleEx?(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW?);
void?CGDIPClockDlg::OnTimer(UINT?nIDEvent)?
{
//?TODO:?Add?your?message?handler?code?here?and/or?call?default
UpdateClockDisplay();
CDialog::OnTimer(nIDEvent);
}
5、透明窗体创建于刷新,均调用以下函数完成,函数的参数表示整个窗体的透明度
在该函数中包括了GDI+中对Image.DrawImage()函数的集中重载方式的使用,还有在GDI+中图像变换矩阵的使用初步研究。
BOOL?CGDIPClockDlg::UpdateClockDisplay(int?Transparent)
{
HDC?hdcTemp=GetDC()->m_hDC;
m_hdcMemory=CreateCompatibleDC(hdcTemp);
HBITMAP?hBitMap=CreateCompatibleBitmap(hdcTemp,m_BakWidth,m_BakHeight);
SelectObject(m_hdcMemory,hBitMap);
if(Transparent<0||Transparent>100)?Transparent=100;
m_Blend.SourceConstantAlpha=int(Transparent*2.55);
HDC?hdcScreen=::GetDC?(m_hWnd);
RECT?rct;
GetWindowRect(&rct);
POINT?ptWinPos={rct.left,rct.top};
Graphics?graph(m_hdcMemory);
Point?points[]?=?{?Point(0,?0),?
Point(m_BakWidth,?0),? Point(0,?m_BakHeight)};
static?bool?bFly=false;
bFly?graph.DrawImage(m_pImageClock,?points,?3):?graph.DrawImage(m_pImageClock1,?points,?3);?
bFly=!bFly;
int?OxyX=140;//m_BakWidth/2+8;
int?OxyY=90;//m_BakHeight/2+10;
SYSTEMTIME?SystemTime;//?address?of?system?time?structure
GetLocalTime(&SystemTime);
//?定义一个单位矩阵,坐标原点在表盘中央
Matrix?matrixH(1,0,0,1,OxyX,OxyY);?
//?时针旋转的角度度
matrixH.Rotate(SystemTime.wHour*30+SystemTime.wMinute/2.0-180);?
Point?pointsH[]?=?{?Point(0,?0),Point(m_HourWidth,?0),Point(0,?m_HourHeight)};
matrixH.Translate(-m_HourWidth/2,-m_HourHeight/6);
//?用该矩阵转换points
matrixH.TransformPoints(?pointsH,?3);?
graph.DrawImage?(m_pImageHHour,pointsH,?3);
//?定义一个单位矩阵,坐标原点在表盘中央
Matrix?matrixM(1,0,0,1,OxyX,OxyY);?
//?分针旋转的角度度
matrixM.Rotate(SystemTime.wMinute*6-180);?
Point?pointsM[]?=?{?Point(0,?0),Point(m_MinuWidth,?0),Point(0,?m_MinuHeight)};
matrixM.Translate(-m_MinuWidth/2,-m_MinuHeight/6);
//?用该矩阵转换pointsM
matrixM.TransformPoints(?pointsM,?3);?
graph.DrawImage?(m_pImageHMinu,pointsM,?3);
//?定义一个单位矩阵,坐标原点在表盘中央
Matrix?matrix(1,0,0,1,OxyX,OxyY);?
//?秒针旋转的角度度
matrix.Rotate(SystemTime.wSecond*6-180);?
Point?pointsS[]?=?{?Point(0,?0),Point(?m_SecWidth,0),Point(0,m_SecHeight?)};
matrix.Translate(-m_SecWidth/2,-m_SecHeight/7);
//?用该矩阵转换pointsS
matrix.TransformPoints(?pointsS,?3);?
graph.DrawImage?(m_pImageHSec,pointsS,?3);
//HH:MM:SS
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
graph.DrawImage(m_pImageNum,0,?0,?14*(SystemTime.wHour/10),?0,14,23,UnitPixel);?
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
graph.DrawImage(m_pImageNum,20,0,?14*(SystemTime.wHour%10),?0,14,23,UnitPixel);?
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置 graph.DrawImage(m_pImageNum,20*2,0,?140,?0,14,23,UnitPixel);?//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
graph.DrawImage(m_pImageNum,20*3,?0,?14*(SystemTime.wMinute/10),?0,14,23,UnitPixel);?
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
graph.DrawImage(m_pImageNum,20*4,0,?14*(SystemTime.wMinute%10),?0,14,23,UnitPixel);?
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
graph.DrawImage(m_pImageNum,20*5,0,?140,?0,14,23,UnitPixel);?
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
graph.DrawImage(m_pImageNum,20*6,?0,?14*(SystemTime.wSecond/10),?0,14,23,UnitPixel);?
//该函数从m_pImageClock中剪切指定rect中的像素draw到指定位置
graph.DrawImage(m_pImageNum,20*7,0,?14*(SystemTime.wSecond%10),?0,14,23,UnitPixel);?
SIZE?sizeWindow={m_BakWidth,m_BakHeight};
POINT?ptSrc={0,0};
DWORD?dwExStyle=GetWindowLong(m_hWnd,GWL_EXSTYLE);
if((dwExStyle&0x80000)!=0x80000)
SetWindowLong(m_hWnd,GWL_EXSTYLE,dwExStyle^0x80000);BOOL?bRet=FALSE;
bRet=?UpdateLayeredWindow(?m_hWnd,hdcScreen,&ptWinPos,
&sizeWindow,m_hdcMemory,&ptSrc,0,&m_Blend,2);
graph.ReleaseHDC(m_hdcMemory);
::ReleaseDC(m_hWnd,hdcScreen);
hdcScreen=NULL;
::ReleaseDC(m_hWnd,hdcTemp);
hdcTemp=NULL;
DeleteObject(hBitMap);
DeleteDC(m_hdcMemory);
m_hdcMemory=NULL;
return?bRet;
}
BOOL?CGDIPClockDlg::ImageFromIDResource(UINT?nID,?LPCTSTR?sTR,Image?*?&pImg)
{
HINSTANCE?hInst?=?AfxGetResourceHandle();
HRSRC?hRsrc?=?::FindResource?(hInst,MAKEINTRESOURCE(nID),sTR);?//?type
if?(!hRsrc)
return?FALSE;//?load?resource?into?memory
DWORD?len?=?SizeofResource(hInst,?hRsrc);
BYTE*?lpRsrc?=?(BYTE*)LoadResource(hInst,?hRsrc);
if?(!lpRsrc)
return?FALSE;//?Allocate?global?memory?on?which?to?create?stream
HGLOBAL?m_hMem?=?GlobalAlloc(GMEM_FIXED,?len);
BYTE*?pmem?=?(BYTE*)GlobalLock(m_hMem);
memcpy(pmem,lpRsrc,len);
IStream*?pstm;
CreateStreamOnHGlobal(m_hMem,FALSE,&pstm);
//?load?from?stream
pImg=Gdiplus::Image::FromStream(pstm);
//?free/release?stuff
GlobalUnlock(m_hMem);
pstm->Release();
FreeResource(lpRsrc);
}
void?CGDIPClockDlg::OnLButtonDown(UINT?nFlags,?CPoint?point)?
{
//禁止显示移动矩形窗体框 ::SystemParametersInfo(SPI_SETDRAGFULLWINDOWS,TRUE,NULL,0); //非标题栏移动整个窗口 SendMessage(WM_SYSCOMMAND,0xF012,0);? //?PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));? CDialog::OnLButtonDown(nFlags,?point);}
详细实现过程请参考源代码!
四、结束语
编写该程序的主要动力来自于对GDI+图像、图形功能的好奇,网上好多例子和文章都是关于C#或delphi等语言的。本人一直以来习惯于使用VC6.0。希望通过此文能增进与大家交流。仅供参考,请自借鉴
希望对您有帮助