Windows api资源篇

来源:互联网 发布:58上好多开淘宝店 编辑:程序博客网 时间:2024/05/16 12:49

WIndows的资源有:

Icons
Cursors
Character strings
Custom resources
Menus
Keyboard accelerators
Dialog boxes
Bitmaps

资源:Resources是数据,保存在.EXE文件中,但是却不在程序段中,所以没办法像int a;这样引用它,Windows提供了函数把资源load到memory,我们才能用。

resource script (.rc)列出了程序所有资源,RESOURCE.h头文件让你的程序可以引用这些资源。

resource.h头文件允许C源文件使用与source script同名的identifiers

resource script 就是一个文本文件,它包含了能够表示问文本的资源(菜单,对话框)的文本表达式,包含了不能表示为文本的资源(图标,光标)的二进制文件的引用。



Icon:

resource script把ICONDEMO.ico图标文件与IDI_ICON等同起来
资源文件会被资源编译器RC.EXE编译,resource script转换成二进制文件,最后被编译了的.res文件在连接阶段与.OBJ and .LIB这些文件一样被指定连接形成.EXE

获取Icon句柄:
hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON)) ;
LoadIcon函数返回HICON,第一个参数是资源所在的实例句柄,第二个参数是指向字符串的指针,MAKEINTRESOURCE是把资源标示符由数字变成指向字符串的指针
Icon的ID可以是字符串(要用""括起来)那么.h文件就没有define语句把字符串定义成一个Int,就不用再MAKRINTRESOURCE,可以直接用ID的字符串。


GetSystemMetrics(SM_CXICON/SM_CYICON)一般返回都是32*32,客户区的Icon和桌面上的Icon都是32*32的
GetSystemMetrics(SM_CXSMSIZE/SM_CYSMSIZE)返回的是16*16,小型Icon的size,标题栏的和任务栏的图标都是这个size

SetClassLong (hwnd, GCL_HICON,LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ALTICON))) ;//动态改变窗口的icon


Cursor:

自定义cursor一般都是32*32的,同时不好忘记设置cursor的hot spot,VS2012有个设置作用点工具来设hot spot的
如果使用了子窗口,那么子窗口设计类的时候hCursor这项可以修改,如果是Windows定义好的类,可以利用SetClassLong来修改
SetClassLong (hwndChild, GCL_HCURSOR,LoadCursor (hInstance, TEXT ("childcursor")) ;

如果只是窗口的一个逻辑分区,可以利用SetCursor来修改,防止闪烁则把类的hCursor设为NULL就不会自动用类的光标去刷新了
SetCursor (hCursor) ;


String Resource:

把字符串作为资源使用,可能会令理解更费力
LoadString (hInstance, id, szBuffer, iMaxLength) ;
LoadString()函数用于加载字符串资源,id是字符串资源的id,szBuffer则是用来接收字符串的字符串数组,iMaxLength是szBuffer数组的最大长度,函数返回值是加载到的字符串的实际长度。

字符串的ID通常define在头文件中,所以编程的时候记得包含头文件


自定义资源:

VS2012插入自定义资源的方法是直接import,它没custom选的,接着要写自定义资源的type,接着resource script就多了如下的语句,IDR_BINTYPE1是数字标示符,可以用字符串代替
IDR_BINTYPE1 BINTYPE BINDATA.BIN

hResource = LoadResource (hInstance,FindResource (hInstance, TEXT ("BINTYPE"),MAKEINTRESOURCE (IDR_BINTYPE1))) ;
hResource是HGLOBAL类型的,是内存块的句柄
LoadResource并不真正把资源加载到内存上

pData = LockResource (hResource) ;
LockResource加载资源到内存,并且返回指向它的指针:

FreeResource (hResource) ;
完成对自定义资源的操作,可以free it from memory


Bitmap:

位图是一个二维数组,最基本的单位是像素,它的值是颜色。
2种贮存图像的方法:bitmap和metafile,区别是一个是点阵图,一个是矢量图
位图的缺点是依赖于设备的颜色,拉伸性差,容易失真,存储容量大

位图除了有空间尺寸之外还有颜色深度,每个pixel有几位组成,1位是单色图,4位则有16种色。
现实中的设备发展:
CGA,HGC单色--->EGA 4颜色位,16种色----->VGA  8颜色位,256种色----->现在真彩  24颜色位

BitBlt(hdcDst,x,y,cx,cy,hdcSrc,x,y,dwROP);
StretchBlt(hdcDst,x,y,cx,cy,hdcSrc,x,y,cx,cy,dwROP);
PatBlt(hdcDst,x,y,cx,cy,dwROP);
这3个函数都很相像,我们可以把video memory当做一个大的bitmap,这3个函数就是直接操作video memory的像素,进行各种各样的位操作。
Source:就是源部分的rect
Destination:就是位操作结果存放的rect
Pattern就是由hdcDst的hbrush形成的与Destination同样大小的rect
dwROP是raster operation:具体的是上面3部分的位操作
假设黑色:0,白色:1
dwROP:
BLACKNESS      Destination=0
WHITENESS      Destination=1
SRCCOPY          Destination=S
NOTSRCCOPY   Destination=~S
PATCOPY           Destination=P
PATPAINT          Destination=P | D | ~S
..... .........


Stretch-Mode:
DC的属性,当bitmap被拉伸收缩的时候会把一行或多行合并,或者一列或多列合并,合并之后的颜色如何决定?取决于Stretch-Mode
BLACKONWHITE:适合于白底黑色
WHITEONBLACK:适合于黑底白色
COLORONCOLOR:适合于彩色图
HALFTONE:会利用调色板取出合并区域的平均颜色

DDB:

设备相关位图DDB,位图的确是资源,像Icon,Cursor它们一样有外部的.ico    .cur这些文件,位图有.bmp,但是有点不同的是DDB并非对应于那个.bmp,外部文件.bmp对应的是设备无关位图DIB,DIB自备调色板信息,所以可以保存在外,DDB没有这个信息,在DDB位图的一个像素中例如存的是0x37(一个像素8位),但是对应的颜色却是不确定的,他要看系统调色板对应过去才知道应该显示什么颜色。.bmp文件会转化成DDB,DDB的位的数据只能存在内存之中,所以需要用到MemoryDC,并且只有MemoryDC才能选进bitmap。
获得HBITMAP的函数:
hBitmap = CreateBitmap (cx, cy, cPlanes, cBitsPixel, bits) ;
前面2个参数是位图的宽和高,第三个参数是颜色平面数,一般是1,第四个参数是一个像素的位数,也就是之前讲到的颜色深度,最后一个是个位的数组,存的就是这个位图的每个位的值。这里要注意的点是这个函数自由度很高,可以让你随便设cPlanes和cBitsPixel,但是得到的hBitmap却不一定能显示到出来,因为和当前显示器有关,例如当前显示器是一个像素32位的,但是你设置的时候一个像素是3位的,当然显示不出来了。

为了解决DDB与当前显示器不兼容的问题,可以调用这个函数:
hBitmap = CreateCompatibleBitmap (hdc, cx, cy) ;
创建兼容的位图,参数hdc,函数根据这个hdc参数获取当前显示器的各种信息,得到一个与显示器兼容的位图句柄。不过这个函数没办法指定位图的各个位的值,初始化就是全0,显示出来的效果是全黑。可以通过SetBitmapBits(hBitmap,cBytes,&bits);函数来设置位图的位

最后还有一个用的最多的函数:
hBitmap = LoadBitmap (hInstance, szBitmapName) ;
这个函数就是利用.bmp资源转化为DDB加载入内存,返回hBitmap,由于资源能在显示器显示,就表示该资源转化的DDB也和设备兼容了,并且位图的各个位的值由资源转化的过程中就得到了,不用自己设,也避开了CreateCompatibleBitmap()函数不能设置的问题。

上面的3个获取hBitmap的函数中只有LoadBitmap用到了资源,我的理解是资源是外部的文件,例如.ico  .cur  .bmp等,编译成二进制文件之后连入.exe中,再通过LoadXXX函数从.exe中加载到内存中,最后返回的句柄标示着加载到内存的资源,所以只有LoadBitmap用到了资源,其他2个函数都是直接在内存中创建一个位图。


得到标示位图的hBitmap之后如何把它显示在hdc相应的位置呢?
我们之前了解过BitBlt,StretchBlt等函数可以对位进行raster operation,目的地肯定就是hdc了,但是源hdcSrc呢。这里我们只有hBitmap,我们需要有一个中介HDC,这个HDC要能把hBitmap选进去,而能选hBitmap的HDC就只有MemoryDC了:
hdcMem=CreateCompatibleDC(hdc);//创建与hdc兼容的Memory DC
DeleteDC(hdcMem);//HDC必然是成对出现的
有了这个hdcMem就能选位图,并且把hdcMem作为hdcSrc;
MemoryDC具有一个display surface,它就像real  raster device,但是初始化是一个1像素宽,1像素高的位图,没什么用,所以要选进一个你自己的hBitmap才有意义,并且这个hBitmap要么是单色的位图,要么就是与CreateCompatibleDC(hdc)中的hdc兼容的位图才能被选进hdcMem。

最后提醒下hBitmap需要DeleteObject()的。


下面来个CreateBitmap的实例:

这张位图5bit高,20bit宽,黑色为0,白色为1有(记住位图分配内存的时候字节数一定是偶数,不足的时候补零,20位不足,至少要补到32位,即4个字节)
0 1 0 1 0 0 0 1 0 1 1 1 0 1 1 1 0 0 0 1 = 51 77 10 00
0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00
0 0 0 1 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 13 77 50 00
0 1 0 1 0 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 = 57 77 50 00
0 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 = 51 11 10 00
hdc=GetDC(hwnd);hdcMem=CreateCompatibleDC(hdc);byte bits[5][4]={0x51,0x77,0x10,0,0x57,0x77,0x50,0,0x13,0x77,0x50,0,0x57,0x77,0x50,0,0x51,0x11,0x10,0};hBitmap=CreateBitmap(20,5,1,1,bits);SelectObject(hdcMem,hBitmap);ReleaseDC(hwnd,hdc);


位图画刷:
画刷实际上是个小位图,8*8的大小,可以用CreatePatternBrush函数使位图变成画刷,函数会复制一个hBitmap副本,让画刷和位图分开,所以用完之后位图和画刷都得DeleteObject。

直接用GDI函数的时候,hdc指定为hdcMem,那么就在hdcMem的bitmap上绘图,利用这点可以把hdcMem作为hdcSrc来为BitBlt,StretchBlt,patBlt函数服务!


非矩形的Bitmap:
bitmap都是矩形的,想显示非矩形的位图有2种办法(以显示椭圆位图为例子):
(1)把椭圆外围不显示的部分涂成白色,这种办法只有当背景是白色才会有用。
(2)利用mask技术
mask其实就是一张与原位图同size的单色位图,椭圆部分为白色,值为1,外围是黑色,值为0
先把mask与hBitmap做SRCAND,与0与操作结果是0,与1与操作结果是原来的值,结果得到的是hBitmap一个椭圆形的原图被黑色外围包含着
再来就是mask与Window的背景做0x220326 raster operation,这个光栅操作no name,做的事情是D & ~S,mask作为Source,取反变为黑色椭圆,白色外围,与背景&操作,得到的结果就是背景有一个黑色的椭圆
最后就是Window背景和hBitmap进行SRCPAINT操作,这个光栅操作D | S,黑色值为0,或操作不会改变原来的值,背景黑色椭圆把hBitmap的椭圆图片显示出来,hBitmap的黑色外围不会影响到背景原来的内容。

这个过程对于处理图片与背景的协调相当重要!



DIB:

设备无关位图,与DDB不同的地方在于DDB的一个像素中的值代表什么颜色是根据兼容DC决定的,而DIB的颜色则是自带的,什么值对应什么颜色都保存起来了,所以比起DDB要多一点存储结构。
DIB文件包含的fileheader结构:
typedef struct tagBITMAPFILEHEADER // bmfh
{
WORD bfType ; // signature word "BM" or 0x4D42
DWORD bfSize ; // entire size of file,单位是byte
WORD bfReserved1 ; // must be zero,这2项如果位图是光标就记录hot sopt的坐标
WORD bfReserved2 ; // must be zero
DWORD bfOffsetBits ; // offset in file of DIB pixel bits,存储像素阵列对于文件地址的偏移地址
}
BITMAPFILEHEADER, * PBITMAPFILEHEADER ;

BITMAPFILEHEADER结构体之后是BITMAPINFOHEADER结构体:
typedef struct tagBITMAPINFOHEADER // bmih
{
DWORD biSize ; // size of the structure = 40
LONG biWidth ; // width of the image in pixels
LONG biHeight ; // height of the image in pixels
WORD biPlanes ; // = 1
WORD biBitCount ; // bits per pixel (1, 4, 8, 16, 24, or 32)
DWORD biCompression ; // compression code
DWORD biSizeImage ; // number of bytes in image
LONG biXPelsPerMeter ; // horizontal resolution,每米多少像素,显示真实世界中图像到底有多大
LONG biYPelsPerMeter ; // vertical resolution
DWORD biClrUsed ; // number of colors used,颜色列表有多少种颜色,值为0的时候表明2^一个像素的位数
DWORD biClrImportant ; // number of important colors
}
BITMAPINFOHEADER, * PBITMAPINFOHEADER ;

接着存color table:
typedef struct tagRGBQUAD // rgb
{
BYTE rgbBlue ; // blue level
BYTE rgbGreen ; // green level
BYTE rgbRed ; // red level
BYTE rgbReserved ; // = 0
}
RGBQUAD ;
RGBQUAD结构体数组。

最后存储的就是像素阵列,存储方式一开始存储的是图像的底端,即上下颠倒存储,但是水平方向还是从左到右存储。

到这里为止我们知道了一个.bmp文件内部到底是怎样的构造,我们可以通过代码来查看一下一个.bmp文件的信息,这里涉及到了文件的操作,因为你想要看BITMAPFILEHEADER这些结构体的信息,首先你得获得指向这些结构体的指针吧,这就需要用到文件操作了。
打开一个文件(只读的方式),这里只是照着Windows编程第5版的代码写,因为书本本身也没有介绍文件操作的,只是突然插入这样的代码,所以我也不是很懂
HANDLE hfile=CreateFile(TEXT("F:\\stand_down.bmp"),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);if(hfile==INVALID_HANDLE_VALUE)return 0;DWORD dwFileSize,dwFileHighSize;dwFileSize=GetFileSize(hfile,&dwFileHighSize);pbmfile=(byte*)malloc(dwFileSize);bool bSuccess=ReadFile(hfile,pbmfile,dwFileSize,&dwFileReadNum,NULL);CloseHandle(hfile);
开始文件F盘的stand_down.bmp,其他参数都是照着写的,然后调用GetFileSize函数获取这个文件的大小,应该是以字节为单位,然后再malloc函数申请同样大小的空间,把文件的内容全部读到申请的空间中来,那么我们的pbmfile就指向了文件的开头了,换句话说也就是指向了BITMAPFILEHEADER结构体。
我们也可利用MessageBox来输出这个结构体的内容:
BITMAPFILEHEADER *pbmih=(BITMAPFILEHEADER*)(pbmfile);char szBuf[1024];wsprintfA(szBuf,"%x %d",pbmih->bfType,pbmih->bfSize);MessageBoxA(NULL,szBuf,"Title",MB_OK);
输出的结果了0x4d42,就是bitmap类型,还有就是位图的大小
紧接着的是BITMAPINFOHEADER结构体:
BITMAPINFOHEADER *pbmih=(BITMAPINFOHEADER*)(pbmfile+14);char szBuf[1024];wsprintfA(szBuf,"%d %d %d %d ",pbmih->biWidth,pbmih->biHeight,pbmih->biBitCount,pbmih->biClrUsed);MessageBoxA(NULL,szBuf,"Title",MB_OK);
因为BITMAPFILEHEADER大小是14,所以加14就是BITMAPINFOHEADER结构体的开始了,打印出有关信息:
当前位图的宽高,颜色深度是24,即真彩图,一个像素24位,所以不具有color table。


知道了这些消息之后,显示DIB位图的函数的一些参数就好办了!
iLines = SetDIBitsToDevice (
hdc, // device context handle
xDst, // x destination coordinate
yDst, // y destination coordinate
cxSrc, // source rectangle width
cySrc, // source rectangle height
xSrc, // x source coordinate
ySrc, // y source coordinate
yScan, // first scan line to draw
cyScans, // number of scan lines to draw
pBits, // pointer to DIB pixel bits
pInfo, // pointer to DIB information
fClrUse) ; // color use flag

这个SetDiBitsToDevice函数就是用来显示DIB位图的,hdc,xDst,yDs这3个参数很好理解吧,cxSrc和cySrc这2个参数是你显示的位图多大,比DIB中信息的大小小就会截取部分显示,大的就用空白填充,换句话说就是在(xDst,yDst)的位置显示一张cxSrc宽,cySrc高的位图,xSrc和ySrc则是源位图的开始截图的位置,原点在位图的左下角,yScan是要在cxSrc,cySrc的矩形的哪里开始显示图片,也是从下面开始上来的,0在底行,cyScans是定死的了,就是等于pBits有多少行扫描行,pBits是指针,指向像素阵列,pInfo也是指针,指向BITMAPINFO结构体,pbmfh+sizeof(BITMAPFILEHEADER)就是它的位置了。


还有一个函数:
iLines = StretchDIBits (
hdc, // device context handle
xDst, // x destination coordinate
yDst, // y destination coordinate
cxDst, // destination rectangle width
cyDst, // destination rectangle height
xSrc, // x source coordinate
ySrc, // y source coordinate
cxSrc, // source rectangle width
cySrc, // source rectangle height
pBits, // pointer to DIB pixel bits
pInfo, // pointer to DIB information
fClrUse, // color use flag
dwRop) ; // raster operation
这个函数是可以拉伸DIB位图的,大部分参数与上面的函数类似,最后一个参数是光栅操作,BitBlt这些函数一样,可以是SRCCOPY吧。


DDB与DIB的转换:

首先从DIB转化成DDB的方法:最最基础的实现方法,弄一个与hwnd兼容的,与DIB同size的位图(用CreateComptibleBitmap函数来创建),把这个位图选进hdcMem中,然后调用SetDiBitsToDevice函数画到hdcMem中去,然后hdcMem中保存的hBitmap就是一张与DIB同样的DDB了。
又或者直接调用CreateDiBitmap函数,这个函数实际上的实现也是上面的思路:
hBitmap = CreateDIBitmap (
hdc, // device context handle
pInfoHdr, // pointer to DIB information header
fInit, // 0 or CBM_INIT(CBM_INIT才是把DIB照搬到hBitmap中,0是不做事的意思)
pBits, // pointer to DIB pixel bits
pInfo, // pointer to DIB information
fClrUse) ; // color use flag

DDB转化到DIB的方法就是用:
int WINAPI GetDIBits (
hdc, // device context handle
hBitmap, // bitmap handle
yScan, // first scan line to convert
cyScans, // number of scan lines to convert
pBits, // pointer to pixel bits (out)
pInfo, // pointer to DIB information (out)
fClrUse) ; // color use flag
这个函数略难用,主要思路就是要分别调用2次,当pBits这个参数是NULL的时候,函数会用hBitmap的维数和格式来填充你给的pInfo这个结构体,主要是填充宽高和颜色深度这3项,接着根据刚才填充的信息来申请空间,记得要符合DIB像素空间的特点,字节是4的倍数,接着把申请得到的空白空间作为pBits参数,函数就会把hBitmap的像素复制到你申请的空间中去,那么主要的DIB的几个结构体就有了。
这里附上一个博客例子(就这个转载下别人的例子)
http://blog.csdn.net/craigyang/article/details/4483311
0 0
原创粉丝点击