第一百一十一个SizeofResource获取一个资源的大小
函数定义:DWORD SizeofResource(
HMODULE hModule,//模块句柄,同FindResource,LoadResource的模块句柄一样
HRSRC hResInfo//资源位置句柄,是FindResource函数返回的句柄
);
例子:自定义资源,EXE文件包含DLL,双击该EXE文件释放里面的DLL文件到当前目录
首先我们新建一个控件台工程,你也可以新建一个WIN32工程,由于这里建的不是MFC工程,所以还得往工程里添加资源文件,步骤是文件->新建,在弹出的对话框中选择“文件”选项标签,然后选中“资源脚本”,我们要建的就是这个文件,取名、确定、完成。
接着我们进入资源视图(ResourceView),右击资源文件,选择引入(记得把文件类型选所有),选择一个DLL文件,点引入。这时候会弹出一个对话框,提示你给自定义资源类型取类型名,记住这个类型名对应着FindResource函数的第三个参数lpType,取名为DLL,确定,接着我们只要查看一下这个自定义资源的ID号,就可以去敲代码了。方法是右击资源文件,选择"资源符号",这里我查看的对应ID号是101。好了进入代码编辑区,键入如下代码:
#include
int main()
{
HRSRC hRsrc=FindResource(NULL,MAKEINTRESOURCE(101),"DLL");//查找资源位置
DWORD dllSize=SizeofResource(NULL,hRsrc);//获取资源大小
HGLOBAL hGlobal=LoadResource(NULL,hRsrc);//加载资源到内存
LPVOID pBuffer=LockResource(hGlobal);//获取内存首地址
char pathName[100];
GetModuleFileName(NULL,pathName,100);//获得应用程序完整路径名
int i=0;
while(pathName[i]!='\0') i++;//计算路径名字符串最后的位置
//把exe改成dll,然后创建文件
pathName[i-1]='l';
pathName[i-2]='l';
pathName[i-3]='d';
HANDLE hFile=CreateFile(pathName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);//创建文件
DWORD dwSize;
WriteFile(hFile,pBuffer,dllSize,&dwSize,NULL);//写入数据
return 0;
}
运行,然后把生成的EXE程序随便复制到一个目录下,双击运行一下。看不是在同一目录下释放出了一个DLL文件。
这可以应用到任何文件上,像可以让一个EXE文件里包含两个EXE文件,当执行这个EXE文件后,会释放出两个EXE文件,然后删除自身。
第一百一十二个SetLayeredWindowAttributes设置窗口透明度
函数定义:BOOL SetLayeredWindowAttributes(
HWND hwnd,//要设置的窗口句柄
COLORREF crKey,//如果窗口一个像素颜色值等于crKey,那么这个像素将是透明的
BYTE bAlpha,//取值范围0到255,0表示全透明,255表示不透明
DWORD dwFlags//指定crKey还是bAlpha值有效,LWA_COLORKEY(0x1)指定crKey值有效,bAlpha值无效,
//LWA_ALPHA(0x2)表 示 bAlpah值有效,crKey值无效。LWA_COLORKEY|LWA_ALPHA表示两者都有效
);
想要调用SetLayeredWindowAttributes设置窗口透明度,必须让窗口具有WS_EX_LAYERED(0x80000)扩展样式,可用SetWindowLong函数设置,该函数用法参考API 常用函数第九十八个。而SetLayeredWindowAttributes函数在动态链接库user32.dll里面,关于调用动态链接库里的函数请参考之前的例子,本例采用第二种方法用LoadLibrary,这个函数的用法不懂的可以参考 API常用函数第四十九个。
这里的窗口依旧以无标题记事本为例
#include
int main()
{
HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
SetWindowLong(wnd,GWL_EXSTYLE,GetWindowLong(wnd,GWL_EXSTYLE)|0x80000);
typedef BOOL (WINAPI *FSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
FSetLayeredWindowAttributes SetLayeredWindowAttributes ;
HINSTANCE hInst = LoadLibrary("User32.DLL");
SetLayeredWindowAttributes = (FSetLayeredWindowAttributes)GetProcAddress(hInst,"SetLayeredWindowAttributes");
int value=0;
while(1)
{
if(value==256) value=0;
SetLayeredWindowAttributes(wnd,RGB(0,0,0),value,0x2);//0x2 让第二个参数无效。
value++;
Sleep(50);
}
return 0;
}
第一百一十三个SetROP2设置DC画图模式
通常情况下,如果不调用SetROP2函数设置画图模式,那么默认的DC画图模式就是R2_COPYPEN(直接用源值覆盖),以Rectangle函数来举例,选入一个红色画刷,如:
HBRUSH brush=CreateSolidBrush(RGB(255,0,0));
SelectObject(hdc,brush);那么调用Rectangle在一个窗口里画一个0,0,100,100的矩形就是
Rectangle(hdc,0,0,100,100);
如果没调用Rectangle之前,窗口这部分区域全是绿色,
如下图:
(为了方便,把这个称为dest)
那么调用Rectangle之后,这部分区域应该是这样的:
(这个就称为src)
这两个区域每一个像素都有一个RGB颜色值,如0,255,0和255,0,0,而Rectangle所要做的就是合并这样两个区域(dest和src),由于采用R2_COPYPEN画图模式,直接用src覆盖掉dest,那么最终在窗口里显示的结果应该全是红色,也就是每个像素值都是红色。
又如R2_MASKPEN模式,这个是采用按位与(AND)合并两幅图片。两张图片的对应像素颜色值进行按位与运算。
也就是RGB(0,255,0)&RGB(255,0,0)的结果就是窗口最终显示的像素颜色。
那么如果用SetROP2设置画图模式为R2_MASKPEN,再调用Rectangle,窗口显示的应该是这样:
红蓝按位与合并后的结果。RGB(0,255,0)&RGB(255,0,0)的结果是0,也就是颜色值RGB(0,0,0)黑色。
类似的,还有按位或,按位异或,取反等运算,现给出所有模式及运算解释(来自Programming Windows with MFC)
R2_NOPdest = destR2_NOTdest = NOT destR2_BLACK dest = BLACKR2_WHITEdest = WHITER2_COPYPEN dest = srcR2_NOTCOPYPEN dest = NOT srcR2_MERGEPENNOT dest = (NOT dest) OR srcR2_MASKPENNOT dest = (NOT dest) AND srcR2_MERGENOTPENdest = (NOT src) OR destR2_MASKNOTPEN dest = (NOT src) AND destR2_MERGEPEN dest = dest OR srcR2_NOTMERGEPEN dest = NOT (dest OR src)R2_MASKPEN dest = dest AND srcR2_NOTMASKPENdest = NOT (dest AND src)R2_XORPENdest = src XOR destR2_NOTXORPENdest = NOT (src XOR dest)
例子:以R2_MASKPEN 画图模式在窗口里画一个矩形
#include
int main()
{
HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
HDC dc=GetWindowDC(wnd);
HBRUSH brush=CreateSolidBrush(RGB(100,255,255));
SelectObject(dc,brush);
SetROP2(dc,R2_MASKPEN);
while(1)
{
Rectangle(dc,0,0,200,200);
Sleep(200);
}
return 0;
}
运行效果:
用另外几种模式试一下,看看结果又会有什么不同。
第一百一十四个SetMapMode设置DC映射模式
默认的模式是MM_TEXT,一个单位等于一个像素,也就是当你调用Rectangle等函数画图时,参数的单位是像素,如Rectangle(hdc,0,0,100,100);此时的0,0是窗口的左上角,一个宽300,高300像素的矩形.
而如果把模式换一下的话,结果就不是这样了,像MM_LOMETRIC模式的单位就是0.1毫米,那么Rectangle(hdc,0,0,100,100)所画的矩形应该是100*0.1mm宽,100*0.1mm高,也就是1厘米宽高,但真设置NM_LOMETRIC模式,再调用Rectangle(hdc,0,0,100,100)后,并不会在窗口里显示一个矩形,这是为什么呢,窗口所对应的坐标不同.
NM_TEXT模式下窗口左上角对应坐标点0,0,往右往下都是递增,如下图:
而除了自定义模式,和NM_TEXT模式,窗口的坐标都是这样的:
那么如果窗口坐标处在这种模式下,想在窗口里画矩形,Y坐标必须为是负数,所以上面的Rectangle调用应该是这样Rectangle(0,0,100,-100);而这里的单位是0.1mm
下面给出各种模式及解释
MM_TEXT 1 pixel
MM_LOMETRIC 0.1 mm
MM_HIMETRIC 0.01 mm
MM_LOENGLISH 0.01 in.
MM_HIENGLISH 0.001 in.
MM_TWIPS 1/1440 in. (0.0007 in.)
MM_ISOTROPIC User-defined (x and y scale identically) User-defined
MM_ANISOTROPIC User-defined (x and y scale independently) User-defined
in.是英寸的缩写.
例子在一个窗口画一根5厘米长的直线,映射模式为MM_LOMETRIC
#include
int main()
{
HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
HDC dc=GetDC(wnd);
::SetMapMode(dc,MM_LOMETRIC);//设置映射模式
while(1)
{
MoveToEx(dc,0,-50,NULL);
LineTo(dc,500,-50);//500不是像素,单位是0.1mm
Sleep(200);
}
return 0;
}
//用尺子去量一下那根线,看是不是五厘米,没尺子?没关系,用PS建一个
//五厘米的宽的图像,对比一下。
第一百一十五个DPtoLP将设备坐标转换成逻辑坐标
其实就是将MM_TEXT模式(以像素为单位)坐标点转换成当前模式下的坐标,比如在MM_TEXT模式下150,150这个坐标点位置在MM_LOMETRIC模式里是在哪里。也就是说50*0.1mm是多少像素。
先来看下面这个例子,将150,150这个在MM_TEXT模式下的坐标点转换成MM_LOMETRIC模式下对应的坐标点.
#include
#include
int main()
{
HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
HDC dc=GetDC(wnd);
POINT pt;
pt.x=pt.y=150;
//设置当前模式,针对当前模式而转换,如果不设置,默认是MM_TEXT,MM_TEXT转MM_TEXT结果还是一样
SetMapMode(dc,MM_LOMETRIC);
DPtoLP(dc,&pt,1);//dc是设备上下文,第二个参数是一个POINT指针,第三个指明数组数量。用于多个转换
//如:POINT pt[2]={{100,100},{200,200}};DPtoLP(dc,pt,2);
cout<
}
我这儿的输出结果是 469,-469
具体依据电脑设置而定,也就是说在MM_LOMETRIC模式下469,-469这个坐标点和MM_TEXT模式下100,100这个坐标点的位置是相同的。
例子:在一个窗口里的两种模式下画同一长度的线
#include
int main()
{
HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
HDC dc=GetDC(wnd);
POINT ptText[2]={{0,50},{200,50}};//MM_TEXT模式下直线两点
POINT ptLOME[2]={{0,100},{200,100}};//MM_LOMETRIC模式下直线两点
SetMapMode(dc,MM_LOMETRIC);//设置模式
DPtoLP(dc,ptLOME,2);//转换成MM_LOMETRIC对应的坐标点
while(1)
{
MoveToEx(dc,ptLOME[0].x,ptLOME[1].y,NULL);
LineTo(dc,ptLOME[1].x,ptLOME[1].y);
SetMapMode(dc,MM_TEXT);//模式改回来
MoveToEx(dc,ptText[0].x,ptText[1].y,NULL);
LineTo(dc,ptText[1].x,ptText[1].y);
Sleep(300);
SetMapMode(dc,MM_LOMETRIC);//设置模式
}
return 0;
}
既然可以转过去,也当然可以转回来
DPtoLP用于把像素单位转换成其它模式单位,它的参数是以像素为单位的,转换后值的单位就是当前模式对应的单位,面LPtoDP正好相反,它参数的单位是以当前模式为准的,用于把其它模式下的坐标点转换成MM_TEXT模式下的。如模式MM_LOMETRIC,把上面的469,-469转换成对应像素单位就是POINT pt={469,-469};LPtoDP(dc,&pt,1);//记得要设置模式。