1.FindWindow 根据窗口类名或窗口标题名来获得窗口的句柄
该函数返回窗口的句柄
这个函数的定义是这样的
HWND WINAPI FindWindow(LPCSTR lpClassName,LPCSTR lpWindowName);
第一个参数填窗口的类名,第二个填窗口的标题名,其实是不需要同时填两个参数的,也就是说,你只要知道窗口的类名或窗口的标题就可以了,没有的那个就用NULL代替。比如现在有一个窗口名为"无标题.txt - 记事本"的记事本程序。那么我就可以用上面的函数获得这个窗口的句柄,那获得了这个窗口的句柄我可以干什么呢?作用可大了,因为很多操作窗口的函数,都需要窗口句柄作参数,如移动、改变窗口大小的MoveWindow函数,在这里举个例子,大家就更能体会到这个FindWindow的用法、用处。
FindWindow例子:已知一个窗口名称,写一个程序关闭该窗口,假设当前电脑正有一个窗口名为"无标题.txt - 记事本"的记事本程序运行
#include<windows.h>//API函数的头文件
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");//获得窗口名为"无标题.txt - 记事本"的窗口句柄
SendMessage(wnd,WM_CLOSE,0,0);//调用SendMessage函数,发送一个WM_CLOSE(关闭)消息给wnd窗口句柄。
return 0;
}
如果要根据窗口类名来获得窗口句柄话,只要给函数的第一个参数填类名,第二个参数窗口名填NULL,即可,用Spy++可查看窗口类名。
2.SendMessage 根据窗口句柄发送一个消息给窗口
函数定义:
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);
第一个参数是窗口句柄,第二参数个是消息类型,下面的消息表列举了所有消息,第三,四个参数是消息附带信息,解释依赖于消息类型,比如一个字符消息(WM_CHAR),那么第三个参数就储存有一个字符的ASCII码。
消息机制大家都应该知道吧,Windows是基于消息的系统,鼠标移动键盘按键都会产生消息。
接下来举一个例子,发送一个WM_CHAR消息给窗口,也就是模仿键盘按键,接收消息的窗口依旧以"无标题.txt - 记事本"为例:
// SendMessage例子:模仿键盘按键
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
while(1){
SendMessage(wnd,WM_CHAR,WPARAM('a'),0);
Sleep(300);
}
return 0;
}
呵呵上面的例子是不是没用,这是为什么呢,哪里出错了吗?错倒是没有错,只是窗口句柄有问题,消息发送给了主窗口。接收消息的窗口不对。记事本窗口界面有些有什么东西呢?菜单,编辑框,状态栏等控件,控件也是窗口,既然是窗口,那当然它们也有窗口句柄,而在记事本里是在哪里打字的?编辑框控件里打字的嘛!所以消息应该发送编辑框控件,那如何获得记事本里编辑框控件的窗口句柄呢?用FindWindow吗?不知道编辑框窗口标题名,类名也不知道,当然也有其它方法获取编辑框窗口标题名和窗口类名,如Spy++。关于如何获得编辑框句柄,将在以后的函数中会有介绍,这里我们就用WindowFromPoint这个函数来获取,这个函数获取窗口句柄的方法比较笨,(相对于我这个例子来说),这个函数是根据什么来获取窗口句柄的呢?根据屏幕坐标点,如屏幕坐标点20,20,当前是哪个窗口占有,就返回哪个窗口的句柄。有了这个函数,我们还需要一个函数GetCursorPos获取鼠标当前位置(针对于屏幕);
// 可行的例子:模仿键盘按键:
#include<windows.h>
int main(){
HWND hWnd;
POINT curpos;//一个可储存坐标点的结构体变量,x横坐标,y,纵坐标,如curpos.x curpos.y
while(1){
GetCursorPos(&curpos);//获取当前鼠标的位置,位置将储存在curpos里。
hWnd = WindowFromPoint(curpos);//根据curpos所指的坐标点获取窗口句柄
SendMessage(hWnd,WM_CHAR,WPARAM('g'),0);//发送一个字符(按键)消息g给当前鼠标所指向的窗口句柄
Sleep(300);//睡眠三百毫秒,相当于等待三分之一秒
}
}
这个程序一运行后,只要把鼠标指向要输入字符的窗口句柄,那么就相当于键盘每三分之一秒按了一个g键,试试吧!
如果这样觉得模仿键盘按键太麻烦的话,那么就用 keybd_event 这个函数,这个专门用于模仿键盘按键的,关于怎么用,自己百度一搜,就知道了。既然 SendMessage 能模仿键盘按键的话,那也能模仿鼠标左击,右击。而此时 SendMessage 函数第三,四个参数的解释就是储存有鼠标左击,右击时的位置。如模仿鼠标右击,想一想,一次鼠标右击有哪几步,分别是鼠标右键按下,鼠标右键松开,如果你按下鼠标右键不松开,那它是不是鼠标右击,不是的,直到你松开鼠标右键,才能算是一次完整的鼠标右击.鼠标右键按下的消息类型是“ WM_RBUTTONDOWN ”,右键松开的消息是“ WM_RBUTTONUP ”,那么一次完整的鼠标右击应该是:
SendMessage(wnd,WM_RBUTTONDOWN,0,0);//鼠标右键按下,第三,四个参数说明了鼠标按下时的位置
Sleep(100);//间隔100毫秒
SendMessage(wnd,WM_RBUTTONUP,0,0);//鼠标右键松开
同样,也有一个专门模仿鼠标动作的函数,mouse_event这个函数,可以模仿鼠标的移动,单击,双击等。以后会有专门介绍。
3.GetCursorPos 获取鼠标当前位置(屏幕)
// 这个函数在SendMessage函数有介绍,这里仅举一个例子,在界面里不停的输出鼠标当前位置。
#include<windows.h>
#include<stdio.h>
int main(){
POINT curpos;
while(1){
GetCursorPos(&curpos);
printf("x:%d,y:%d",curpos.x,curpos.y);
Sleep(300);
printf("\n");
}
}
4.WindowFromPoint 根据坐标点获得对应的窗口句柄
在SendMessage有解释,这里仅举一个例子,鼠标指向哪个窗口,就关闭哪个窗口。
#include<windows.h>
int main(){
HWND wnd;
POINT curpos;
Sleep(2500);//等待一会儿,用于把鼠标移到其它窗口上去,避免指向本身进程的窗口,关掉自己的窗口。
while(1){
GetCursorPos(&curpos);
wnd = WindowFromPoint(curpos);
SendMessage(wnd,WM_CLOSE,0,0);
Sleep(300);
}
return 0;
}
5.MoveWindow 根据窗口句柄移动窗口,改变窗口大小
函数定义:
BOOL MoveWindow( HWND hWnd, int X, int Y, intnWidth, int nHeight, BOOL bRepaint );
hWnd是要改变大小的窗口的句柄,x,y相对于屏幕的坐标,窗口左上角的位置与之相对应,nWidth和nHeight是窗口新的宽高,bRepaint指定窗口是否重画。
这里依旧以"无标题.txt - 记事本"为例子,改变这个窗口大小,并把窗口移到左上角去。
//改变这个窗口大小,并把窗口移到左上角去。
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
MoveWindow(wnd,0,0,220,120,NULL);
return 0;
}
6.ShowWindow 设置窗口显示状态,如隐藏,最大化,最小化
函数定义
BOOL ShowWinow(HWND hWnd,int nCmdShow);
SW_HIDE:隐藏窗口并激活其他窗口。第一个参数hWnd指明了窗口句柄,第二个参数指明了窗口的状态,现在给出第二个参数常用取值范围:
SW_MAXIMIZE:最大化指定的窗口。
SW_MINIMIZE:最小化指定的窗口并且激活在Z序中的下一个顶层窗口。
SW_RESTORE:激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置。在恢复最小化窗口时,应用程序应该指定这个标志。
SW_SHOW:在窗口原来的位置以原来的尺寸激活和显示窗口。
// ShowWindow例子:程序运行后,在桌面上隐藏一个指定的窗口,并在4秒后再将其显示
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
ShowWindow(wnd,SW_HIDE);
Sleep(5000);
ShowWindow(wnd,SW_SHOW);
return 0;
}
7.SetCursorPos 设置鼠标的位置、把鼠标移动到指定的位置
函数定义:
BOOL SetCursorPos(int x,int y);
这个函数的两个参数我想大家应该知道是什么意思吧,屏幕的坐标点。
直接看例子:
#include<windows.h>
int main(){
int sec = 0;
while(sec<200){
SetCursorPos(rand()%1024,rand()%768);//随机设置鼠标的位置
Sleep(20);
sec++;
}
return 0;
}
8.CopyFile 复制一个文件
如何复制一个文件,比如,我要把E盘的abb.txt的文本文件复制到d盘的zhengyong.txt,则调用以下语句即可。
CopyFile("e:\\abb.txt","d:\\zhengyong.txt",FALSE);
第三个参数有以下说明:
如果设为TRUE(非零),那么一旦目标文件已经存在,则函数调用会失败。否则目标文件会被覆盖掉。
9.DeleteFile 删除一个文件
如何删除一个文件,语句:DeleteFile(“e\abb.txt”);既是删除
如果目标为隐藏或只读,则无用。
10.CreateDirectory 创建一个文件夹(目录)
假如E盘下什么文件也没有
CreateDirectory("e:\\aaa\\bbb",NULL);
这样是错的,不能同时建两个文件,除非E盘下已经有了个aaa文件夹了。这样是对的:
CreateDirectory("e:\\aaa",NULL);
http://hi.baidu.com/3582077/item/f032b82bbbfb71c0ee10f1e3
11.GetClientRect 获得窗口大小(客户区)
看例子:
#include<windows.h>
#include<stdio.h>
int main(int argc, char* argv[]){
RECT rect;//专门用来存储窗口大小
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
while(1){
GetClientRect(wnd,&rect);//获取窗口大小
printf("%d,%d,%d,%d\n",rect.left,rect.top,rect.right,rect.bottom);//输出窗口大小,试着用鼠标改变窗口大小
Sleep(300);
}
}
12.GetWindowRect 获得窗口大小(相对屏幕)
例子:
#include<windows.h>
#include<stdio.h>
int main(int argc, char* argv[]){
HWND wnd;
RECT rect;//专门用来存储窗口大小
while(1){
wnd = FindWindow(NULL,"无标题.txt - 记事本");
GetWindowRect(wnd,&rect);//获取窗口大小
printf("%d,%d,%d,%d\n",rect.left,rect.top,rect.right,rect.bottom);//输出窗口大小,试着用鼠标改变窗口大小
Sleep(300);
}
}
13.FindFirstFile 寻找文件以及获得文件的信息
// 这里举一个例子吧,列举E盘第一目录下的所有文件,包括文件夹,结合FindNextFile
#include<windows.h>
#include<stdio.h>
int main(){
BOOL done = TRUE;
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile("e:\\*.*", &fd);//第一个参数是路径名,可以使用通配符,懂DOS的人应该知道吧!fd存储有文件的信息
while (done){
printf("%s\n",fd.cFileName);
done = FindNextFile(hFind, &fd); //返回的值如果为0则没有文件要寻了
}
return 0;
}
当然也可以直接找一个文件,不使用通配符,但这样有什么意义呢?,如
FindFirstFile("e:\\aaa.txt",&fd);
其实这个可以获取一个文件的信息,如文件是不是隐藏的,或者有没有只读属性等。
当然通过控制通配符,也可以寻找特定类型的文件,比如我只要找文本文件,那么就是这个语句FindFirstFile(“e:\*.txt”,&fd);就行了,关键看你自己灵活运用。
/前面说过fd里存储有文件的信息,那怎么根据fd里面的成员判断这个文件的属性,文件是否隐藏,是不是文件夹。
fd里的dwFileAttributes存储有文件的信息,如判断是否为文件夹,只要把这个变量和FILE_ATTRIBUTE_DIRECTORY进行按位与运算,如果不为0的话,表明为文夹件,如
if(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) printf("%s是文件夹\n",fd.cFileName);
其它判断也是一样,现在给出文件的属性(常用几个):
FILE_ATTRIBUTE_HIDDEN(隐藏)
FILE_ATTRIBUTE_READONLY(只读)
FILE_ATTRIBUTE_SYSTEM(系统)
第十四个FindNextFile寻找文件
参照FindFirstFile函数的例子!
14.MoveFile 移动文件
如把一个盘里的文本移到另一个盘里去:
MoveFile("e:\\a.txt","d:\\abc.txt");
即可,意思把e盘下的a.txt移到d盘下去,并改名为abc.txt
15.GetClassName 根据窗口句柄获得窗口类名
函数定义:
int GetClassName(HWND hWnd, LPTSTR IpClassName, intnMaxCount);
这种函数不需要再解释了吧,前面有太多类似的例子。
16.SetFileAttributes 设置文件属性
函数定义:
BOOL SetFileAttributes( LPCTSTRlpFileName,DWORDdwFileAttributes);
这个函数的第二个参数dwFileAttributes和前面讲过的WIN32_FIND_DATA结构里的dwFileAttributes成员相对应。假设E盘第一目录下有一个文本文件a.txt的正常文件,我要把它设为只读和隐藏那要如何做呢?在前面介绍过WIN32_FIND_DATA结构里dwFileAttributes成员的几个常用属性,根据这个我们知道隐藏是FILE_ATTRIBUTE_HIDDEN,只读是FILE_ATTRIBUTE_READONLY。
那么把E盘下文本文件的属性设为隐藏和只读的语句就是:
SetFileAttributes("e:\\a.txt",FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);
(明:这个函数同样也能设置文件夹属性)
虽然这个语句可以达到要求,但不建议用,因为会覆盖掉文件的原来属性,也就是说如果这个文件之前有系统属性(系统文件)的话,那么这个语句一旦执行后,文件就只有隐藏和只读属性了。
比如一个文件原先就有隐藏属性,依旧以a.txt为例子,那么我把它设为只读,是不是这个语句就可以呢?
SetFileAttributes("e:\\a.txt",FILE_ATTRIBUTE_READONLY);
这样的话,虽然文件有只读属性了,但隐藏属性却没有了。
那要如何在不覆盖掉原来的属性下,把文件设为只读呢,其实说了这么多的废话,总结起来就一句话:如何增加一个文件的属性!
前提是要获得这个文件的原有属性:获得文件的属性,在FindFirstFile函数讲过。好吧!直接看例子:
// 假设e盘的a.txt文件属性为隐藏,给它增加只读属性:
#include<windows.h>
int main(){
WIN32_FIND_DATA fd;
FindFirstFile("e:\\a.txt",&fd);
fd.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;//在原来的属性下增加只读属性
SetFileAttributes("e:\\a.txt",fd.dwFileAttributes);//设置文件的属性
return 0;
}
第二个例子:如何去掉一个文件的属性
(补习一下,懂的人直接跳过)
我想懂这里的按位或、按位与或者按位异或运算的人应该知道该如何去掉一个文件的属性。其实一个文件信息都是以二进制码说明的。
比如一个八位二进制码:10000010,这里的每一位是不是只有0和1取值,不是0,就是1,正好可以表示一个文件属性的有无,如这个文件是隐藏的吗?只有是和不是,这样我们规定把这八位二进制码的第一位用于确定文件是否具有隐藏属性,如果为1那便是隐藏,无则没有,以此类推第二位就代表文件的只读,第三位系统。。。但要如何判断呢,或者把某一位的值改变呢,用按位运算就可以,00000010,我要把第2位的值设为0,其它位上的值保持不变,用按位异或运算即可,与00000010进行按位异或运算,但这里并不是与它本身进行运算,不管任何八位二进制数的值是多少只要与00000010进行按位异或运算,那第二位都会变成0,而其它的位保持不变。这样为了方便,我们就把00000010进行宏定义,方便记忆,这个二进制数的十进制为2。宏定义#define FILE_ATTRIBUTE_READONLY 2
明白了这个我们就来清除一个文件的一种属性吧!
清除一个文件的隐藏属性,假设a.txt为隐藏文件:
#include<windows.h>
int main(){
WIN32_FIND_DATA fd;
FindFirstFile("e:\\a.txt",&fd);//获取文件信息
fd.dwFileAttributes ^= FILE_ATTRIBUTE_HIDDEN;//在原来的属性下删除隐藏属性
SetFileAttributes("e:\\a.txt",fd.dwFileAttributes);//设置文件的属性
return 0;
}
如果单单只针对文件的属性进行操作的话,可以用GetFileAttributes函数获取文件的属性,该函数只一个参数,那就是文件的路径,函数返回一个DWORD值,包含文件属性信息。
17.ShellExecute 运行一个程序
函数定义:
ShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTRlpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd);
第一个参数hwnd是父窗口的句柄,可以为NULL,
第二个参数lpOperation表示行为,
第三个参数lpFile是程序的路径名,
第四个参数lpParameters是给所打开程序的参数,可以为NULL,
第五个参数lpDirectory可以为NULL,
第六个参数nShowCmd跟ShowWindow函数的第二个参数一样,作用也一样,如果打开的程序有窗口的话,这个参数就指明了窗口如何显示.
例如打开一个记事本:
ShellExecute(NULL,"open","NOTEPAD.EXE",NULL,NULL,SW_SHOWNORMAL);
而且这个函数还可以指定程序打开一个属于程序本身类型的文件,假如e盘有一个a.txt文件;我调用函数运行记事本程序并打开这个文本文件.
ShellExecute(NULL,"open","NOTEPAD.EXE","e:\\a.txt",NULL,SW_SHOWNORMAL);
这里由于记事本程序属于系统本身自带的程序,所以没有绝对路径.
这个函数还可以打开一个网站:
ShellExecute(NULL,"open","http://www.baidu.com",NULL,NULL,SW_SHOWNORMAL);
ShellExecute(NULL,"open","C:",NULL,NULL,SW_SHOWNORMAL);
还可以根据文件后缀名选择相应的程序打开一个文件:
ShellExecute(NULL,"open","e:\\a.bmp",NULL,NULL,SW_SHOWNORMAL);
类似的函数还有WinExec,只有两个参数,它的最后一个参数跟ShellExecute函数的最后一个参数一样.
而第一个参数则是程序路径名.举个例子:
WinExec("NOTEPAD.EXE",SW_SHOWNORMAL);
这个函数也可以给程序传递一个文件名供要运行的程序打开,那要如何加进去呢,这里又没有第三个参数,
方法把路径名加在NOTPEPAD.EXE的后面,要以空格来分开如:
WinExec("NOTEPAD.EXE e:\\a.txt",SW_SHOWNORMAL);
18.PlaySound 播放一个WAV文件
函数定义:
BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORDfdwSound);
第一个参数是WAV文件的路径名,
第二个参数如果不是播放MFC里以资源ID命名的文件,则可以为空,
第三个参数,指明了以何种方式播放文件。注意这个函数只能播放100K以下的WAV文件。
假如E盘有个a.wav文件,下面这个例子播放这个文件:
#include<windows.h>
#include<mmsystem.h>//PlaySound函数的头文件
#pragma comment(lib, "winmm.lib")//链接库,PlaySound函数必须使用
int main(){
PlaySound("e:\\19.wav",NULL,SND_SYNC);
return 0;
}
19.GetModuleFileName 根据模块导入表获取程序的完整路径
函数定义:
DWORD GetModuleFileName( HMODULE hModule, LPTSTRlpFilename, DWORD nSize );
关于第一个参数,将在以后的动态链接库里会有介绍,这里我们只要获得程序本身的路径,那么第一个参数可以为空。
第二个参数用以存储路径,nSize指明字符数组大小。
这个举个例子,运行后,把自身程序移动到e盘下,并改名为a.exe;
#include<windows.h>
int main(){
char szAppName[128] = {0};
GetModuleFileName(NULL,szAppName,128);
MoveFile(szAppName,"e:\\a.exe");
return 0;
}
http://hi.baidu.com/3582077/item/fc3e11164b01c00dd0d66de5
20.CreateWindow 创建一个窗口
补习懂的人直接跳过
之前API函数的例子,都是针对DOS编程的,严格来说是在windows下的仿DOS(cmd)进行编程,编写控制台应用程序大家都知道,主函数是main,那针对windows编程的主函数也是main吗?不是的,windows下的主函数(入口函数)是WinMain。在定义main主函数的时候,可以给它带两个参数,也可以不带。而WinMain函数就不能这样了,它有固定的格式,它必须带四个参数。
现给出WinMain函数的固定格式:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCEhPrevInstance, LPSTR lpCmdLine, int nCmdShow);
大家如果有兴趣可以通过其它渠道了解一下各参数的意思,现在我们只需要知道WinMain函数就是这样定义的,不理解也没关系。
知道了这个我们就来编一个WINDOWS程序吧!
因为我们是针对windows编程,所以要建一个Win32Application工程,步骤是点击文件,然后选择新建,在弹出的对话框里选择工程,再选中Win32Application 接着在右边的工程名称下填工程名称,名字随便取。之后点确定。接着又弹出了一个对话框,这里为了方便,我们选择“一个简单的 Win32 程序”,点完成。接着双击WinMain弹出代码编辑窗口,做完这个我们就可以打代码了。
简单的例子如下:
#include "stdafx.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow){
while(1)
Sleep(100);
return 0;
}
怎么样够简单吧,是不是觉得奇怪,怎么没有窗口,因为窗口要自己创建,不像控制台程序,只要一运行便会有窗口。虽然没有窗口,但你创建了一个进程,打开任务管理器,可以找到你所创建的那个进程,其实也没什么奇怪的,像WINDOWS本身的一些系统服务,也是只有进程,没有窗口的像spoolsv.exe,svchost.exe。
那要如何创建一个窗口呢?要创建一个窗口,就必须要向系统提供窗口的信息,如你要创建的窗口名字叫什么,窗口图标是什么,窗口大小,窗口背景色等,不然,系统怎么给你创建窗口呢?所以为了方便,VC就定义了一个结构,专门用于存储窗口信息。
现给出这个结构的定义。
typedef struct _WNDCLASS {
UINT style; //描述类风格
WNDPROC lpfnWndProc; //窗口处理函数
int cbClsExtra; //表示窗口类结构之后分配的额外的字节数。系统将该值初始化为0
int cbWndExtra; //表示窗口实例之后分配的额外的字节数。系统将该值初始化为0
HINSTANCE hInstance;// 应用程序实例句柄由WinMain函数传进来
HICON hIcon; //窗口图标句柄
HCURSOR hCursor; //窗口光标句柄
HBRUSH hbrBackground; //画刷句柄
LPCTSTR lpszMenuName; //窗口菜单名
LPCTSTR lpszClassName; //窗口类名
} WNDCLASS, *PWNDCLASS;
好了,如果我们已经把窗口信息填好了,那我们要怎样把这个信息告诉系统呢,也就是把要创建窗口的信息传给系统。这里我们调用RegisterClass函数就能实现这个功能。注册完窗口,我们就要创建窗口,用CreateWindow函数就能实现,不要问为什么注册窗口后直接显示不就行了,还要搞什么创建窗口。这我也不知道,反正你只要记住这格式就行了,硬式规定的,你想创建一个窗口,就必须按这些步骤来。
好了,窗口创建了,我们就要调用ShowWindow函数显示窗口,然后用UpdateWindow函数刷新一下,确保窗口能立即显示。
以下详细实现代码:
#include "stdafx.h"
#include<windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow){
HWND hwnd; //用以存储CreateWindow函数所创建的窗口句柄
WNDCLASS wndcls; //定义一个存储窗口信息WNDCLASS变量
wndcls.cbClsExtra = 0; //默认为0
wndcls.cbWndExtra = 0; //默认为0
wndcls.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); //背景画刷
wndcls.hCursor = LoadCursor(NULL,IDC_CROSS); //十字光标
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR); //窗口图标
wndcls.hInstance = hInstance; //应用程序实例句柄由WinMain函数传进来
wndcls.lpfnWndProc = NULL; //窗口消息处理函数
wndcls.lpszClassName = "windowclass"; //窗口类名
wndcls.lpszMenuName = NULL; //窗口菜单名,没有菜单,为NULL
wndcls.style = CS_HREDRAW | CS_VREDRAW;//窗口类型,CS_HREDRAW和CS_VERDRAW 表明
//当窗口水平方向垂直方向的宽度变化时重绘整个窗口
RegisterClass(&wndcls); //把窗口信息提交给系统,注册窗口类
hwnd = CreateWindow("windowclass","first windows",
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//创建窗口
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口创建完了,显示它
UpdateWindow(hwnd); //更新窗口,让窗口毫无延迟的显示
return 0;
}
是不是出错了,内存不能读取,为什么了呢,因为你创建的窗口没有消息处理函数,windows系统当然不允许这样一个窗口存在,对按键,鼠标都没有反应,这样的窗口是没有实际意义的。
wndcls.lpfnWndProc = NULL;
窗口消息处理函数,就是前面这句,必须要填
窗口过程(消息)处理函数,那这个函数是怎样定义的呢,像WinMain一样,它也有固定的格式。
窗口过程处理函数的格式:
LRESULT CALLBACK WinSunProc(HWND wnd,UINTuMsg,WPARAM wParam,LPARAM lParam);
下面的这个是一个窗口创建的完整例子:
#include "stdafx.h"
#include<windows.h>
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
if(uMsg == WM_LBUTTONDOWN)
MessageBox(hwnd,"mouse clicked","message",0);//处理鼠标按下消息,弹出消息框
else if(uMsg == WM_CLOSE && IDYES == MessageBox(hwnd,"是否真的结束?","message",MB_YESNO))
DestroyWindow(hwnd); //销毁窗口,并发送WM_DESTROY消息给自身窗口)
return DefWindowProc(hwnd,uMsg,wParam,lParam); //未处理的消息通过DefWindowProc函数交给系统处理
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow){
MSG msg;//消息结构类型
HWND hwnd; //用以存储CreateWindow函数所创建的窗口句柄
WNDCLASS wndcls; //定义一个存储窗口信息WNDCLASS变量
wndcls.cbClsExtra = 0; //默认为0
wndcls.cbWndExtra = 0; //默认为0
wndcls.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); //背景画刷
wndcls.hCursor = LoadCursor(NULL,IDC_ARROW); //光标
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR); //窗口图标
wndcls.hInstance = hInstance; //应用程序实例句柄由WinMain函数传进来
wndcls.lpfnWndProc = WinSunProc; //窗口消息处理函数
wndcls.lpszClassName = "windowclass"; //窗口类名
wndcls.lpszMenuName = NULL; //窗口菜单名,没有菜单,为NULL
wndcls.style = CS_HREDRAW | CS_VREDRAW;//窗口类型,CS_HREDRAW和CS_VERDRAW 表明
//当窗口水平方向垂直方向的宽度变化时重绘整个窗口
RegisterClass(&wndcls); //把窗口信息提交给系统,注册窗口类
hwnd = CreateWindow("windowclass","first windows",
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//创建窗口
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口创建完了,显示它
UpdateWindow(hwnd); //更新窗口,让窗口毫无延迟的显示
while(GetMessage(&msg,NULL,0,0)){//获取消息
//TranslateMessage(&msg); //此函数用于把键盘消息(WM_KEYDOWN,WM_KEYUP)转换成字符消息WM_CHAR
DispatchMessage(&msg); //这个函数调用窗口过程处理函数,并把MSG里的信息处理后传给过程函数的四个参数
}
return 0;
}
WinSunProc函数的四个参数,分别对应着SendMessage函数四个参数,详情参见SendMessage函数参数解释。
MSG类型解释 :
结构定义:
typedef struct tagMSG {
HWND hwnd;//hwnd表示消息将要发送给的窗口句柄
UINT message;//消息类型,如WM_WMCLOSE,WM_CHAR,WM_LBUTTONDOWN,参见消息表
WPARAM wParam;//消息附带信息,取值的意思具体依据消息类型而定
LPARAM lParam;//消息附带信息,取值的意思具体依据消息类型而定
DWORD time;//消息的发送时间,不常用
POINT pt;//消息发送时,鼠标所在的位置,不常用
}MSG;
大家试着把上面的例子运行一遍,然后关掉窗口,再运行一遍,是不是出错了,因为前一个程序虽然窗口关闭了,但进程还在运行,还记得那个循环语句吗?
while(GetMessage(&msg,NULL,0,0))
就是这个。只要条件成立,进程就会一直运行下去。如何让这个循环结束呢?用
PostQuitMessage(0);
这个语句就行了,参数0表示给自身窗口发送一个退出消息,当GetMessage函数接到PostQuitMessage函数发出的消息后,就会返回0值。
如在窗口过程函数中处理窗口关闭WM_CLOSE消息:
if(uMsg == WM_CLOSE)
PostQuitMessage(0);
这样只要一关闭窗口,它的进程也会结束。
接下来解释一下CreateWindow函数参数的意思,函数定义
HWND CreateWindow(LPCTSTR lpClassName,//窗口类名,应与WNDCLASS结构里的成员lpszClassName一致
LPCTSTR lpWindowName,,//窗口标题名
DWORD dwStyle,//窗口的风格,取值参见表Style
int x,
int y,//x,y表示所创建窗口左上角位置
int nWidth,
int nHeight,//nWidth,nHeight表示窗口的宽高
HWND hWndParent,//父窗口句柄,如果不是子窗口,这里取值为NULL
HMENU hMenu,//菜单句柄,没菜单的话,取NULL值
HANDLE hlnstance,//对应着WinMain函数的第一个参数
LPVOID lpParam);//NULL
表Style:(参考:百度)
参数 | 说明 |
---|---|
WS_BORDER | 创建一个单边框的窗口。 |
WS_CAPTION | 创建一个有标题框的窗口(包括WS_BODER风格)。 |
WS_CHILD | 创建一个子窗口。这个风格不能与WS_POPUP风格合用。 |
WS_CHLDWINDOW | 与WS_CHILD相同。 |
WS_CLIPCHILDREN | 当在父窗口内绘图时,排除子窗口区域。在创建父窗口时使用这个风格。 |
WS_CLlPBLINGS | 排除子窗口之间的相对区域,也就是,当一个特定的窗口接收到WM_PAINT消息时,WS_CLIPSIBLINGS 风格将所有层叠窗口排除在绘图之外,只重绘指定的子窗口。如果未指定WS_CLIPSIBLINGS风格,并且子窗口是层叠的,则在重绘子窗口的客户区时,就会重绘邻近的子窗口。 |
WS_DISABLED | 创建一个初始状态为禁止的子窗口。一个禁止状态的窗口不能接受来自用户的输入信息。 |
WS_DLGFRAME | 创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题条。 |
WS_GROUP | 指定一组控制的第一个控制。这个控制组由第一个控制和随后定义的控制组成,自第二个控制开始每个控制,具有WS_GROUP风格,每个组的第一个控制带有WS_TABSTOP风格,从而使用户可以在组间移动。用户随后可以使用光标在组内的控制间改变键盘焦点。 |
WS_HSCROLL | 创建一个有水平滚动条的窗口。 |
WS_ICONIC | 创建一个初始状态为最小化状态的窗口。与WS_MINIMIZE风格相同。 |
WS_MAXIMIZE | 创建一个初始状态为最大化状态的窗口。 |
WS_MAXIMIZEBOX | 创建一个具有最大化按钮的窗口。该风格不能与WS_EX_CONTEXTHELP风格同时出现,同时必须指定WS_SYSMENU风格。 |
WS_OVERLAPPED | 产生一个层叠的窗口。一个层叠的窗口有一个标题条和一个边框。与WS_TILED风格相同。 |
WS_OVERLAPPEDWINDOW | 创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXIMIZEBOX风格的层叠窗口,与WS_TILEDWINDOW风格相同。 WS_POPUP;创建一个弹出式窗口。该风格不能与WS_CHLD风格同时使用。 |
WS_POPUWINDOW | 创建一个具有WS_BORDER,WS_POPUP,WS_SYSMENU风格的窗口,WS_CAPTION和WS_POPUPWINDOW必须同时设定才能使窗口某单可见。 |
WS_SIZEBOX | 创建一个可调边框的窗口,与WS_THICKFRAME风格相同。 |
WS_SYSMENU | 创建一个在标题条上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格。 |
WS_TABSTOP | 创建一个控制,这个控制在用户按下Tab键时可以获得键盘焦点。按下Tab键后使键盘焦点转移到下一具有WS_TABSTOP风格的控制。 |
WS_THICKFRAME | 创建一个具有可调边框的窗口,与WS_SIZEBOX风格相同。 |
WS_TILED | 产生一个层叠的窗口。一个层叠的窗口有一个标题和一个边框。与WS_OVERLAPPED风格相同。 |
WS_TILEDWINDOW | 创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU, WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXMIZEBOX风格的层叠窗口。与WS_OVERLAPPEDWINDOW风格相同。 |
WS_VISIBLE | 创建一个初始状态为可见的窗口。 |
WS_VSCROLL | 创建一个有垂直滚动条的窗口。 |
21.GetMessage 获取窗口消息
参照CreateWindow函数例子,以后的例子可能是在控制台下,也可能是Win32 Application,大家以后根据主函数判断该建什么工程。
22.RegisterClass 注册窗口类
参照CreateWindow
23.UpdateWindow 更新窗口
参照CreateWindow
24.DispatchMessage 更新窗口
参照CreateWindow
25.LoadCursorFromFile 从磁盘加载一个光标文件
函数返回该光标句柄
假设e盘下有一个名为a.cur的光标文件。
HCURSOR cursor;//定义一个光标句柄,用于存放LoadCursorFromFile函数返回的光标句柄
cursor = LoadCursorFromFile("e:\\a.cur");
获得了光标句柄有什么用呢?看一下窗口类WNDCLASS里的hCursor成员,这个成员也是一个光标句柄,明白了吧!
26.CreateSolidBrush创建一个画刷
函数返回画刷句柄
HBRUSH hbr = CreateSolidBrush(RGB(12,172,59));
三个数字分别表明RGB的颜色值,RGB根据三种颜色值返回一个COLORREF类型的值
27.LoadImage 装载位图、图标、光标函数
函数定义:
HANDLE LoadImage(HINSTANCE hinst,LPCTSTR lpszName,UINTuType,int cxDesired,int CyDesired,UINT fuLoad);
这里我们只要这个函数的几个简单功能:从磁盘加载位图,从磁盘加载图标,从磁盘加载光标。
所以第一个参数hinst我们不用管它,直接填NULL就行
第二个参数lpszName是图片文件所在路径名
第三个参数uType指明要加载的是什么类型的图片,
是位图(填IMAGE_BITMAP),还是光标(填IMAGE_CURSOR),还是图标(填IMAGE_ICON)。
第四个cxDesired和第五个参数CyDesired,指定要加载的图片的宽高(可以放大光标,或者缩小),如果加载的是位图的话,则两个参数必须为0
第六个参数fuLoad表示以何种方式加载文件,这里我们是从磁盘加载文件,所以填LR_LOADFROMFILE
好了,假设e盘下有一个c.cur和i.ico文件。
例子:设置窗口图标和光标,还有背景色
#include "stdafx.h"//这个头文件是编译器自动生成的,不是空工程,都会有,
//如果是直接建C++源文件,包含这个头文件,会出错
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WinSunProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam); // second message parameter//窗口过程函数声明
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow){ // show state
MSG msg; //定义消息结构体,开始消息循环
HWND hwnd;//创建窗口,定义一个变量用来保存成功创建窗口后返回的句柄
//设计一个窗口类
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = CreateSolidBrush(RGB(12,172,59));//画刷
wndcls.hCursor = (HCURSOR)LoadImage(NULL,"e:\\c.cur",IMAGE_CURSOR,24,24,LR_LOADFROMFILE);//加载光标
wndcls.hIcon = (HICON)LoadImage(NULL,"e:\\i.ico",IMAGE_ICON,48,48,LR_LOADFROMFILE);//加载图标
wndcls.hInstance = hInstance; //应用程序实例句柄由WinMain函数传进来
wndcls.lpfnWndProc = WinSunProc; //定义窗口处理函数
wndcls.lpszClassName = "windowclass";
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndcls);
hwnd = CreateWindow("windowclass","first window",
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);
//显示及刷新窗口
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//编写窗口过程函数
LRESULT CALLBACK WinSunProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam){ // second message parameter
char szChar[20];
switch(uMsg){
case WM_CHAR: //字符消息
sprintf(szChar,"char code is %c",wParam);
MessageBox(hwnd,szChar,"char",0);
break;
case WM_LBUTTONDOWN: //鼠标左键按下消息
MessageBox(hwnd,"mouse clicked","message",0);
break;
case WM_CLOSE:
if(IDYES == MessageBox(hwnd,"是否真的结束?","message",MB_YESNO))
DestroyWindow(hwnd); //销毁窗口,并发送WM_DESTROY消息给自身窗口
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
28.GetDC 根据窗口句柄获取设备上下文(DC)
返回DC句柄
得到了一个窗口的设备上下文,就可以进行画图操作了,像画圆,画正方形,显示图片等函数都是要设备上下文(DC)句柄做参数的。
HWND wnd = FindWindow(NULL,"无标题.txt- 记事本");//获取窗口句柄
HDC dc = GetDC(wnd)//定义一个DC句柄,并获取这个窗口的设备上下文
29.Rectnagle 在窗口中画一个矩形
// 以"无标题.txt - 记事本"窗口为例,在这个窗口简单的画一个矩形
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
HDC dc = GetDC(wnd);//获取窗口设备上下文(DC)
while(1){//用循环语句重复画,是为了确保不会被窗口刷新给刷掉
Rectangle(dc,50,50,200,200);//画一个矩形
Sleep(200);
}
return 0;
}
http://hi.baidu.com/3582077/item/535b4211faa5df8f88a956e5
30.CreateToolhelp32Snapshot 给当前进程拍一个照
HANDLEhProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
记住这种格式就行了,返回的句柄,存储有进程信息,可以用Process32Firs函数找出来。
31.Process32First 进程查找
根据CreateToolhelp32Snapshot函数返回的句柄获取进程信息
结合Process32Next函数使用,有点像文件寻找函数。
看完整例子:显示系统进程名,以及进程ID号
#include<windows.h>
#include<tlhelp32.h>//声明快照函数的头文件
#include<stdio.h>
int main(){
PROCESSENTRY32 pe32;//进程的信息将会存储在这个结构里
HANDLE hProcessSnap;
BOOL bMore;
//在使用这个结构之前,先设置它的大小
pe32.dwSize = sizeof(pe32);
//给系统内的所有进程拍一个快照
hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
bMore = ::Process32First(hProcessSnap,&pe32);//第一次查找
while(bMore){
printf("进程名称:%s\n",pe32.szExeFile);//szExeFile是进程名
printf("进程ID号:%u\n\n",pe32.th32ProcessID);//th32ProcessID是进程ID号
bMore = ::Process32Next(hProcessSnap,&pe32);//寻找下个进程,函数返回0,则没有进程可寻
}
return 0;
}
32.OpenProcess 根据进程ID号获得进程句柄
句柄通过函数返回
函数定义:
HANDLE OpenProcess( DWORD dwDesiredAccess,BOOL bInheritHandle, DWORD dwProcessId);
第一个参数不要管它,填PROCESS_ALL_ACCESS
第二个参数也一样,填FALSE
最后一个参数就是进程ID号。
33.TerminateProcess 结束一个进程(需进程句柄做参数)
该函数只有两个参数,第一个是进程句柄,第二个填0就行了。
现在给个例子:假设当前有一个进程名为abc.exe的进程正在运行,编一个程序结束它。
#include<windows.h>
#include<tlhelp32.h>//声明快照函数的头文件
int main(int argc,char *argv[]){
PROCESSENTRY32 pe32;
HANDLE hProcessSnap;
BOOL bMore;
//在使用这个结构之前,先设置它的大小
pe32.dwSize = sizeof(pe32);
//给系统内的所有进程拍一个快照
hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
//遍历进程快照,轮流显示每个进程的信息
bMore = ::Process32First(hProcessSnap,&pe32);
while(bMore){
if(strcmp("abc.exe",pe32.szExeFile) == 0){//如果找到进程名为abc.exe
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//获取句柄
::TerminateProcess(hProcess,0);//结束它
}
bMore = ::Process32Next(hProcessSnap,&pe32);//寻找下一个
}
return 0;
}
上面的这个例子,只能结束普通权限进程,如果为系统进程的话,则没有用,结束不了。在后面的提升权限函数,会有例子说明如何结束系统进程。
34.CreatePen 创建一个画笔
返回画笔句柄
函数定义:
BOOL CreatePen(int nPenStyle, int nWidth, COLORREFcrColor);
第一个参数,表示是什么类型的线,取值有以下:
参数 | 说明 |
---|---|
PS_SOLID | 画笔画出的是实线 |
PS_DASH | 画笔画出的是虚线(nWidth必须是1) |
PS_DOT | 画笔画出的是点线(nWidth必须是1) |
PS_DASHDOT | 画笔画出的是点划线(nWidth必须是1) |
PS_DASHDOTDOT | 画笔画出的是点-点-划线(nWidth必须是1) |
第二个参数是画笔的宽度,第三个参数是画笔的颜色,COLORREF类型可以RGB来获得如RGB(233,128,88);
分别是红绿蓝。
如创建一个画笔:
HPEN pen = CreatePen(PS_SOLID,3,RGB(255,78,99));
35.CreateSolidBrush 创建一个画刷
只有一个COLORREF类型的参数
HBRUSH brush = CreateSolidBrush(RGB(22,182,111));
36.SelectObject 把GDI对象选入相应的DC中
像画笔(句柄HPEN),画刷(HBURSH),位图(HBITMAP)等都是GID对象。因为画图函数,如画圆,画矩形,画直线,它们所画出图形,默认属性都是不变的,如线的宽度。那么想要改变画出来时线的宽度,比如我想画出来的图形它的线条宽度为5(像素),那么就要创建一个宽度为5的画笔,然后再通过SelectObject函数,给这个画笔选入,就可以了.
接下举个例子:SelectObject应用
#include<windows.h>
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
HDC hDC;
PAINTSTRUCT ps;
static HPEN pen = CreatePen(PS_SOLID,3,RGB(255,78,99));//创建画笔
static HBRUSH brush = CreateSolidBrush(RGB(22,182,111));//创建画刷
if(uMsg == WM_PAINT){//窗口需要重画的时候
hDC = BeginPaint(hwnd,&ps); //BeginPaint只能在响应WM_PAINT,不能用GetDC获取设备上下文
SelectObject(hDC,pen);//选入画笔
SelectObject(hDC,brush);//选入画刷
Rectangle(hDC,100,100,200,200);
EndPaint(hwnd,&ps);
}else if(uMsg == WM_CLOSE)//用户关闭了窗口
DestroyWindow(hwnd);//销毁窗口,并发送WM_DESTROY消息
else if(uMsg == WM_DESTROY)//如果窗口被销毁
PostQuitMessage(0);//让进程退出
return DefWindowProc(hwnd,uMsg,wParam,lParam); //未处理的消息通过DefWindowProc函数交给系统处理
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow){
MSG msg;//消息结构类型
HWND hwnd; //用以存储CreateWindow函数所创建的窗口句柄
WNDCLASS wndcls; //定义一个存储窗口信息WNDCLASS变量
wndcls.cbClsExtra = 0; //默认为0
wndcls.cbWndExtra = 0; //默认为0
wndcls.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); //背景画刷
wndcls.hCursor = LoadCursor(NULL,IDC_ARROW); //光标
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR); //窗口图标
wndcls.hInstance = hInstance; //应用程序实例句柄由WinMain函数传进来
wndcls.lpfnWndProc = WinSunProc; //窗口消息处理函数
wndcls.lpszClassName = "windowclass"; //窗口类名
wndcls.lpszMenuName = NULL; //窗口菜单名,没有菜单,为NULL
wndcls.style = CS_HREDRAW | CS_VREDRAW;//窗口类型,CS_HREDRAW和CS_VERDRAW 表明
//当窗口水平方向垂直方向的宽度变化时重绘整个窗口
RegisterClass(&wndcls); //把窗口信息提交给系统,注册窗口类
hwnd = CreateWindow("windowclass","first windows",
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//创建窗口
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口创建完了,显示它
UpdateWindow(hwnd); //更新窗口,让窗口毫无延迟的显示
while(GetMessage(&msg,NULL,0,0)){//获取消息
//TranslateMessage(&msg); //此函数用于把键盘消息(WM_KEYDOWN,WM_KEYUP)转换成字符消息WM_CHAR
DispatchMessage(&msg); //这个函数调用窗口过程处理函数,并把MSG里的信息处理后传给过程函数的四个参数
}
return 0;
}
37.ReadProcessMemory 根据进程句柄读取相应的一段内存
读其它进程里的内存
函数定义:
BOOL ReadProcessMemory(HANDLE hProcess,PVOIDpvAddressRemote,PVOID pvBufferLocal,DWORD dwSize,PDWORD pdwNumBytesRead);
总共四个参数
第一个参数hProcess是远程进程句柄,被读取者 。
第二个参数pvAddressRemote是远程进程中内存地址。 从具体何处读取
第三个参数pvBufferLocal是本地进程中内存地址. 函数将读取的内容写入此处,dwSize是要读取的字节数。要读取多少
第四个参数pdwNumBytesRead是实际读取的内容(函数执行后,实际读了多少字节,将存储在该变量里)
远程进程的内存地址是什么意思呢,比如我现在定义一个变量a,int a;就是了,大家知道int型是占四个字节的,也就是说如果a变量所占的内存起始地址是0x1234,那么变量a就占用0x1234,0x1235,0x1236,0x1237这四个字节,这四个字节的内容决定了a变量的值。
好了知道了这个,我们就来举个例子,读取另一个进程里一个变量的值:需设计两个程序,一个用于读(Read)一个用于被读(BeRead);
那么要如何获得另一个进程中一个变量的地址呢?这里我们用一个简单的方法,让另一个进程自己去获取,然后输出地址值。
被读的程序代码如下:假设该进程名为:BeRead.exe
#include<stdio.h>
int main(){
int a = 10;//要读取的变量。
printf("%x\n",&a);//输出这个变量的起始地址,假设输出为12ff7c
while(1)
Sleep(1000);
return 0;
}
必须先让这个程序运行,然后根据输出的地址值,才能在下面的程序填入地址值。
读取的程序代码如下:
#include<windows.h>
#include<stdio.h>
#include<tlhelp32.h>
int main(){
int *p = new int;
unsigned long size;
int *ReAddress = (int *)0x12ff7c;//要读取的内存的地址值
//先要获取进程句柄,如何获取,参照TerminateProcess函数,结束一个进程
BOOL bMore;
HANDLE ReProcess;
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
bMore = ::Process32First(hProcessSnap,&pe32);
while(bMore){
if(strcmp(pe32.szExeFile,"BeRead.exe") == 0){//如果是BeRead.exe
ReProcess = ::OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//获取该进程句柄
break;
}
bMore = Process32Next(hProcessSnap,&pe32);
}
ReadProcessMemory(ReProcess,ReAddress,p,4,&size);//读取BeRead进程的内存
printf("%d\n",*p);//输出读取来的值
return 0;
}
38.WriteProcessMemory 根据进程句柄写入相应的一段内存(写入其它进程里的内存)
这个函数里的参数跟ReadProcessMemory函数参数意思一样,只不过一个是写,一个是读。
下面直接举个例子,形式跟读内存函数的例子一样。
被写的程序代码如下:假设该进程名为:BeWrite.exe
#include<stdio.h>
int main(){
int a = 10;
printf("%x\n",&a);//假设输出为12ff7c
while(1){
printf("%d\n",a);//每隔一秒输出,查看值有没有改变
Sleep(1000);
}
return 0;
}
写入的代码如下:
#include<windows.h>
#include<stdio.h>
#include<tlhelp32.h>
int main(){
int *ReAddress = (int *)0x12ff7c;
unsigned long size;
int *p = new int;
BOOL bMore;
HANDLE ReProcess;
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
bMore = ::Process32First(hProcessSnap,&pe32);
while(bMore){
if(strcmp(pe32.szExeFile,"BeWrite.exe") == 0){
ReProcess = ::OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);
break;
}
bMore = Process32Next(hProcessSnap,&pe32);
}
*p = 300;
WriteProcessMemory(ReProcess,ReAddress,p,4,&size);
return 0;
}
39.CreateThread 创建一个线程(多线程)
线程是什么意思呢,代码是由线程来执行的,一个程序默认只有一个线程(主线程),打个比方,线程就好比一个人,而不同功能的代码或函数就好是一件件不同的事情,如洗碗,洗衣服,擦地。一个人要把这几种事情做完,可以有好几种方案,第一种就是,洗完碗,就去洗衣服,衣服洗完了,再去擦地。第二种就是:洗一分钟碗,再去洗一分钟衣服,再去擦一分钟,然后又去洗一分钟衣服…直到做完。好了,现在你可以再创造一个人帮你做事,创造这个人后,你就叫他洗衣服,而你就洗碗,这样两件事就可以同时被做了。而这里的创造一个人指的就是CreateThread函数。
函数定义:
HANDLE CreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
该函数有六个参数:
第一个参数不用管它,填NULL
第二个参数dwStackSize用于新线程的初始堆栈大小,默认为0
第三个lpStartAddress填函数名(指标),但这个函数必须是这种固定格式的DWORD _stdcall ThreadProc(LPVOID lpParameter)
,新的线程将会执行这个函数里面的代码,直到函数结束,线程死亡。
第四个lpParameter是一自定义参数,用户可以通过这个参数,传递需要的类型,这个参数与线程函数的参数相对应。
第五个dwCreationFlags填0表示立即执行,如果是CREATE_SUSPENDED表示挂起,直到用ResumeThread函数唤醒。
第六个lpThreadId填NULL就行了。
// 现举个例子,两个线程同时每隔一秒输出一个数字,也就是一秒会有两数字输出。
#include<windows.h>
#include<stdio.h>
DWORD _stdcall ThreadProc(LPVOID lpParameter){//线程执行函数
int si = 100;
while(si>0){
printf("子线程输出数字:%d\n",si--);
Sleep(1000);
}
return 0;
}
int main(){
int mi = 0;
CreateThread(NULL,0,ThreadProc,NULL,0,NULL);//创建一个线程,去执行ThreadProc函数
while(mi<100){
printf("主线程输出数字:%d\n",mi++);
Sleep(1000);
}
return 0;
}
40.GetCurrentProcessId 获得当前进程ID
DWORD currentPID;
currentPID = ::GetCurrentProcessId();//返回进程ID号
cout<<currentPID<<endl;
http://hi.baidu.com/3582077/item/1e77f39fbee5b1de1f4271e3
41.CreateCompatibleDC 创建一个兼容的内存设备上下文(DC)
根据DC创造一个兼容的内存DC,此时创造出来的内存DC仅仅是一些属性跟源DC一样,如DC的大小,以及对应窗口的宽高等。内存DC里的数据没有具体取值,需用其它函数给里面的数据赋值。
42.GetObject 获取一个对象信息(如位图,图标,光标)
函数定义:
int GetObject(HGDIOBJ hgdiobj, int cbBuffer, LPVOIDlpvObject);
第一个参数hgdiobj是对象句柄
第二个参数cbBuffer是待写入lpvObject指针指向缓存区数据大小
第三个参数lpvObject是一个指针,指向一个缓存区。
这里举一个获取位图的信息,获取位图的大小,假设E盘下有一个aa.bmp的位图文件,输出位图的宽高
#include<windows.h>
#include<stdio.h>
int main(){
BITMAP bmInfo;//这个结构存储位图信息
HBITMAP bmp;
bmp = (HBITMAP)LoadImage(NULL,"e:\\aa.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
GetObject(bmp,sizeof(BITMAP),&bmInfo);
printf("位图宽:%d,位图高:%d\n",bmInfo.bmWidth,bmInfo.bmHeight);
return 0;
}
43.BitBlt 在窗口输出一个位图
其实倒不如说这个BitBlt函数是拷贝一个设备上下文(DC),或者合并两个窗口,再延伸一下,合并两个图片?也并无不可,往大了说,窗口难道不是图片吗?用截屏软件,把窗口截成图片,这样窗口便成了图片。可能有点瞎说,大家还是按照标准来吧,反正,你只要掌握这个函数就行了,而且这个概念也不会有什么影响,那就足够了。
BitBlt的作用跟把两幅图片合在一起一样,合并两幅图片。可能两幅图片大小不一样也可以合并,但合并DC就不行了,必须两个信息一样的DC才可以合并,那要如何确保两个DC一样呢?这就要用到CreateCompatibleDC函数了。
函数定义:
BOOL BitBlt(HDC hdcDest,int nXDest,int nYDest,intnWidth,int nHeight,HDC hdcSrc,int nXSrc,int nYSrc,DWORD dwRop);
第一个参数hdcDest是原DC句柄,被覆盖的DC
nXdest,nYDest,nWidth,nHeight这四个参数,指明了一个矩形,覆盖原DC哪块区域。
第六个参数hdcSrc是覆盖的DC句柄,nXSrc,nYSrc参数指明从哪里开始覆盖。(覆盖DC的左上角),
第九个参数dwPop表示以何种方式覆盖。因为这里我们只要输出一个位图,所以用SRCCOPY,直接覆盖。
好了,直接举个例子,在窗口输出一副图片,假设e盘下有一个aa.bmp的位图。为了方便,我们直接在记事本窗口输出位图,先运行一个窗口名为"无标题.txt - 记事本"记事本窗口程序。
#include<windows.h>
#include<stdio.h>
int main(){
HWND wnd;
HDC hdc,memDC;
BITMAP bmInfo;//这个结构存储位图信息
HBITMAP bmp;
bmp = (HBITMAP)LoadImage(NULL,"e:\\aa.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
GetObject(bmp,sizeof(BITMAP),&bmInfo);//获取位图信息
wnd = FindWindow(NULL,"无标题.txt - 记事本");
hdc = GetDC(wnd);
memDC = ::CreateCompatibleDC(hdc);//创造兼容的DC
SelectObject(memDC,bmp);//选入位图
while(1){
BitBlt(hdc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,0,0,SRCCOPY);//输出位图
Sleep(200);
}
return 0;
}
下面介绍一下BitBlt函数最后一个参数的常用取值及意思。
参考(百度)
参数 | 说明 |
---|---|
BLACKNESS | 表示使用与物理调色板的索引0相关的色彩来填充目标矩形区域,(对缺省的物理调色板而言,该颜色为黑色)。 |
DSTINVERT | 表示使目标矩形区域颜色取反。 |
MERGECOPY | 表示使用布尔型的AND(与)操作符将源矩形区域的颜色与特定模式组合一起。 |
MERGEPAINT | 通过使用布尔型的OR(或)操作符将反向的源矩形区域的颜色与目标矩形区域的颜色合并。 |
NOTSRCCOPY | 将源矩形区域颜色取反,于拷贝到目标矩形区域。 |
NOTSRCERASE | 使用布尔类型的OR(或)操作符组合源和目标矩形区域的颜色值,然后将合成的颜色取反。 |
PATCOPY | 将特定的模式拷贝到目标位图上。 |
PATPAINT | 通过使用布尔OR(或)操作符将源矩形区域取反后的颜色值与特定模式的颜色合并。然后使用OR(或)操作符将该操作的结果与目标矩形区域内的颜色合并。 |
PATINVERT | 通过使用XOR(异或)操作符将源和目标矩形区域内的颜色合并。 |
SRCAND | 通过使用AND(与)操作符来将源和目标矩形区域内的颜色合并。 |
SRCCOPY | 将源矩形区域直接拷贝到目标矩形区域。 |
SRCERASE | 通过使用AND(与)操作符将目标矩形区域颜色取反后与源矩形区域的颜色值合并。 |
SRCINVERT | 通过使用布尔型的XOR(异或)操作符将源和目标矩形区域的颜色合并。 |
SRCPAINT | 通过使用布尔型的OR(或)操作符将源和目标矩形区域的颜色合并。 |
WHITENESS | 使用与物理调色板中索引1有关的颜色填充目标矩形区域。(对于缺省物理调色板来说,这个颜色就是白色) |
44.GetWindowText 根据窗口句柄获得窗口标题名
函数定义:
int GetWindowText(HWND hWnd,LPTSTR lpString,intnMaxCount);
第一个参数hWnd是要获取窗口标题名的窗口句柄
第二个lpString是个字符串,窗口标题名,将会存储在这里面
第三个参数nMaxCount指明了第二个参数字符数组的大小。
// 下面结合GetCursorPos和WindowFromPoint举个例子,鼠标指向哪个窗口,就在界面显示那窗口的标题名
#include<windows.h>
#include<stdio.h>
int main(){
HWND wnd;
POINT curpos;
char Text[256] = {0};
while(1){
GetCursorPos(&curpos);
wnd = WindowFromPoint(curpos);
GetWindowText(wnd,Text,256);
printf("%s\n",Text);
Sleep(300);
}
return 0;
}
45.SetWindowText 根据窗口句柄设置窗口标题名
这个函数有两个参数,一个是窗口句柄,一个是标题名,这里就不需要解释了吧,直接看例子,设置一个窗口标题名,依旧以
"无标题.txt - 记事本"为例。
#include<windows.h>
#include<stdio.h>
int main(int argc, char* argv[]){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");//获取窗口句柄
SetWindowText(wnd,"新的窗口标题");//设置窗口标题名
return 0;
}
46.GetCurrentProcess 获得当前线程句柄
没有参数,直接调用即可,该函数返回线程句柄
47.OpenProcessToken 获得一个进程的访问令牌句柄
获得一个进程的访问令牌有什么用呢?主要是为了修改它的权限,前面在介绍结束一个进程的时候说过了,无法结束系统进程,是什么原因呢,原因是调用OpenProcess函数失败,无法获取系统进程句柄而引起的,那为什么会失败呢,权限不够,普通程序的进程没有SeDeDebug权限,而一个进程的权限是与访问令牌相关的,这样我们只要获取一个进程的访问令牌句柄,再以这个句柄为参数调用相应的函数提升进程的权限为SeDeDebug就可以获取系统进程句柄,进而结束它。
函数定义:
BOOL OpenProcessToken(HANDLE ProcessHandle,DWORDDesiredAccess,PHANDLE TokenHandle);
第一个参数ProcessHandle待获取的进程句柄
第二个参数DesiredAccess操作类型,填TOKEN_ADJUST_PRIVILEGES就行了
第三个TokenHandle是访问令牌句柄的指针,该参数接收句柄。
如获得本进程的访问令牌句柄:
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);
48.LookupPrivilegeValue
函数查看对应系统权限的特权值,返回信息到一个LUID结构体里
上面讲过了,进程有权限一说,那么大家也能猜到,进程权限的信息也一定存储在一个结构体里,这个结构体描述了进程权限相关的一些信息。这个结构体在这里就不具体描述了,我们所要做的,只是把一个进程权限设置成SeDeDebug就行了,所以我们只要知道TOKEN_PRIVILEGES便是描述进程权限的结构体就可以了。而LookupPrivilegeValue函数是根据访问令牌句获取相应的权限信息吗?
不是的。TOKEN_PRIVILEGES结构里的Privileges[0].Luid跟这个函数所查询的东西相对应,也就是说,如果进程是SeDeDeBug权限,那Privileges[0].Luid的取值是怎样的呢?用LookupPrivilegeValue函数便可以获取其取值。
这个函数是这样定义的:
BOOL LookupPrivilegeValue(LPCTSTRlpSystemName,LPCTSTR lpName,PLUID lpLuid);
第一个参数lpSystemName通常都填NULL,本地系统调用
第二个参数lpName填要查询的权限名,如要查询的是SeDeDebug权限则取值是SE_DEBUG_NAME
第三个参数lpLuid接收其取值。如
LUID luid;
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid);
49.AdjustTokenPrivileges 调整一个进程的访问令牌信息(权限)
函数定义:
BOOL AdjustTokenPrivileges(HANDLE TokenHandle,BOOLDisableAllPrivileges,PTOKEN_PRIVILEGES NewState,DWORDBufferLength,PTOKEN_PRIVILEGES PreviousState,PDWORD ReturnLength);
第一个参数TokenHandle是令牌句柄
第二个是禁用所有权限标志,后面填FALSE就行了。
第三个NewState是待刷进令牌句柄的PTOKEN_PRIVILEGES结构信息指针
第四个BufferLength指明TOKEN_PRIVILEGES结构大小,第五,六个参数填NULL就行了。
那么结合上面两个函数,提升一个进程权限制,让它能够结束系统进程的代码就是:
HANDLE hToken;
OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
TOKEN_PRIVILEGES tp;
LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
tp.PrivilegeCount = 1;//tp里其它一些属性设置
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ),NULL, NULL );
只要把上面的代码,加入结束普通进程例子的前面,那么就能结束系统进程了。
50.LoadLibrary 加载动态链接库,返回动态链接库模块句柄
该函数只有一个参数,那就是动态链接库的名称,如user32.dll,函数返回HMOUDLE类型的模块句柄,获得了一个动态链接库的模块句柄,就可以调用GetProcAddress函数获得模块里面的函数地址,从而调用动态链接库里的函数。
51.GetProcAddress 根据模块句柄获取相应的函数地址
提到GetProcAddress函数,不得不讲一下怎么设计一个动态链接库,这里我们就以自己设计动态链接库作为GetProcAddress函数的例子。
// 动态链接库里的函数相对于头文件的函数有什么优势呢?更节省内存,相对于比较常用的函数而已。如果在一个程序里,调用一个头文件里的函数的话,那不管如何,函数的代码就会被复制一份到当前程序里,所以,当有十几个程序调用同一个函数的时候,这个函数在内存中所占用的空间,就会有十几份,分别在各自调用的进程内存空间里,而动态链接库的函数,只在内存中有一份空间(公用空间)如果哪个程序要用到这个函数的话,只要给这个函数的地址,进程就可以跑到这个空间执行函数,那么如何获取函数地址呢,用GetProcAddress函数就行了。
下面我们就自己设计一个动态链接库,点“文件->新建->工程",然后选中“Win32Dynamic-Link Library”,再在右边给工程取一个名,点确定。接着弹出了一个对话框,询问希望创建什么类型,我们选择第二个“一个简单的DLL工程”,点完成->确定.然后单击右边的“+”号,很小的一个,接着下面会出现一个Globals的"+“号,单击该加号,然后再双击DllMain函数,进入代码编辑区,在这里编写代码,这里已经有了一些代码了,编译器自动生成的。那个DllMain函数,便是动态链接库的主函数。在程序进程加载动态链接的时候,进程会自动调用DllMain函数,也就是说会自动执行DllMain函数里的代码,也就是说,如果哪程序执行了这个语句“LoadLibrar(“user32.dll”)”,那么执行这个语句的进程,便会自动执行user32.dll里的DllMain函数。如果是主线程加载动态库的话,那么该DllMain函数里的代码会被执行两次,分别是加载的时候执行一次,调用FreeLibrary函数释放或程序结束自动释放动态链接库的时候执行一次,至于是什么原因导致DllMain函数被调用,DllMain函数的第二个参数ul_reason_for_call说明了原因,它有四个取值,代表了四个原因。分别是:
参数 | 说明 |
---|---|
DLL_PROCESS_ATTACH | 进程加载 |
DLL_THREAD_ATTACH | 线程加载 |
DLL_THREAD_DETACH | 线程释放 |
DLL_PROCESS_DETACH | 进程释放 |
因为这里我们只要设计一个动态链接函数,所以便不用管DllMain函数,DllMain函数将会在介绍CreateRemoteThread(创建一个远程线程)函数的时候讲到,所以我们只要在DllMain函数外定义一个函数就行了。
那么在动态链接库是如何定义函数呢?如果函数不需要导出的话,则跟普通函数定义没什么两样,导出是什么意思,就是可以用GetProcAddress函数获取地址的函数。那导出的函数要如何定义呢?
只要在函数前面加上extern "C" __declspec(dllexport)
就行了,声明导出函数,防止函数重命名。那么接下来就举个例子。
动态链接里的代码:
#include "stdafx.h"
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved){
return TRUE;
}
extern "C" __declspec(dllexport) int Add(int a,int b){
return a+b;
}
点编译执行,然后就会弹出一个调试对话框,直接点取消,接着便生成了动态链接库DLL,然后到你的工程里把后缀名为dll的文件找到,
位置在MyProject"你的工程名"\Debug下。接着把这个文件复制到要调用的工程下,或者直接复制C:\windows\system32目录下。
假设这个文件名为"sss.dll",那么要调用里面的Add函数便是如下代码:
HMODULE hmod = ::LoadLibrary("sss.dll");//获取sss.dll的模块,加载sss.dll动态链接库
typedef int (*pAdd)(int a,int b);//定义一个对应的函数类型型,以便定义和转换
pAdd add = (pAdd)GetProcAddress(hmod,"Add");//获取hmod模块里的Add函数地址
int a = add(3,5);//调用模块里的Add函数
http://hi.baidu.com/3582077/item/9c29483b581f53c5392ffa19
52.SetWindowsHookEx 安装一个钩子
WINDOWS是基于消息的系统,鼠标移动,单击,键盘按键,窗口关闭等都会产生相应的消息,那么钩子是什么意思呢,它可以监控一个消息,比如在一个窗口里单击了一下,首先获得这个消息的,不是应用程序,而是系统,系统获取这个消息后,就去查看这个消息是在哪个窗口产生的,找到窗口后,再把消息投递到相应程序里的消息队列里,这之间有一个传递过程,那么钩子的作用就是在消息到达应用程序之前截获它,钩子可以关联一个函数(钩子处理函数),也就是说,如果对一个进程安装了一个钩子,进程再接收到相应在消息之前,会先去执行钩子所关联的函数,
先来看一下这个函数定义:
HHOOK WINAPI SetWindowsHookEx(int idHook,HOOKPROClpfn,HINSTANCE hmod,DWORD dwThreadId)
第一个参数idHook指明要安装的钩子类型,如WH_KEYBOARD(键盘钩子),WH_MOUSE(鼠标钩子),第二个参数是钩子处理函数的地址,该函数必须是这种固定的格式:
LRESULT WINAPI HookProc(int nCode,WPARAM wParam,LPARAM lParam);
第三个参数hmod是钩子函数所在模块的句柄
第四个参数dwThreadId是线程ID,待监视消息的ID,如果为0,则为全局钩子,监视所有消息好,接下来我们举一个例子,钩子类型为WH_KEYBOARD,全局钩子。截获键盘按键消息,并扔掉该消息,让键盘失灵。
由于是装的是全局钩子,所以钩子处理函数必须放在动态链接库里。那么我们就设计一个动态链接库吧。
现给出动态链接库的所有代码:(KeyDll.dll)
#include "stdafx.h"
#include<windows.h>
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved){
return TRUE;
}
HMODULE WINAPI ModuleFromAddress(PVOID pv){//该函数根据内存地址,获得其所在的模块句柄
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(pv,&mbi,sizeof(mbi));
return (HMODULE)mbi.AllocationBase;
}
LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam){
return TRUE;//返回真,扔掉该消息
}
extern "C" __declspec(dllexport) void SetHook(void){
SetWindowsHookEx(WH_KEYBOARD,HookKey,ModuleFromAddress(HookKey),0);
}
生成dll文件后,把它复制到相应的目录下去。
再新建一个工程,调用用动态链接库里的函数,代码如下:
#include<windows.h>
int main(){
typedef void(*pSetHook)(void);
HMODULE hMod = LoadLibrary("KeyDll.dll");
pSetHook SetHook = (pSetHook)GetProcAddress(hMod,"SetHook");
SetHook();
while(1)
Sleep(1000);//避免程序结束,自动释放动态链接库
return 0;
}
这样当按下了一个键后,接收该按键消息的进程,会先去执行钩子处理函数,然后再处理消息,而钩子处理函数的几个参数说明了按键的详细信息,如按了哪个键,是按下(KEYDOWN)还是松开(KEYUP)。如果有兴趣的话,把上面那钩子处理函数的代码换成下面这个
LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAMlParam){
char sz[25];
sprintf(sz,"%c",wParam);//这个函数头文件#include<stdio.h>
MessageBox(NULL,sz,sz,MB_OK);
return FALSE;
}
每按下一个键,就会弹出一个提示框,并输出所按下的键,只对字符键有用。
53.SHGetFileInfo 获取一个文件的各项信息(文件关联图标,属性等)
函数定义:
DWORD SHGetFileInfo(LPCSTR pszPath, DWORDdwFileAttributes, SHFILEINFOA FAR *psfi, UINT cbFileInfo, UINT uFlags);
pszPath是文件的路径,dwFileAttributes一般取0,如果想要获取文件夹信息的话,则取值为FILE_ATTRIBUTE_DIRECTORY,psfi是一个SHFILEINFO结构的指针,该结构存储文件信息,定义如下:
typedef struct _SHFILEINFOA{
HICON hIcon; // 文件关联图标句柄
int iIcon; // 系统图标列表索引
DWORD dwAttributes; // 文件的属性
CHAR szDisplayName[MAX_PATH]; // 文件的路径名
CHAR szTypeName[80]; // 文件的类型名,如是bmp文件,还是执行文件exe,或者其它
} SHFILEINFO;
第四个参数cbFileInfo指明SHFILEINFO结构的大小,填sizoef(SHFILEINFO);
最后一个参数uFlags指定获取文件的什么信息,可选取值如下:(对应着SHFILEINFO里的成员)
SHGFI_ICON; //获得图标
SHGFI_DISPLAYNAME; //获得显示名
SHGFI_TYPENAME; //获得类型名
SHGFI_USEFILEATTRIBUTES; //获得属性
SHGFI_LARGEICON; //获得大图标
SHGFI_SMALLICON; //获得小图标
SHGFI_PIDL; // pszPath是一个标识符
比如,我只要获取文件图标,那么参数填SHGFI_LARGEICON
就行了。如果又想获取文件关联的图标,又想获取文件类型名,那么就是:SHGFI_LARGEICON|SHGFI_TYPENAME;
函数例子:
SHFILEINFO sfi;
SHGetFileInfo("e:\\aa.bmp",0,&sfi,sizeof(sfi),SHGFI_ICON|SHGFI_LARGEICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);
接着可以用DrawIcon函数画出文件关联图标:该函数定义:
BOOLDrawIcon(HDC hDC,int X,int Y, HICON hlcon );
54.RegCreateKeyEx 在注册表里创建一个子键,或获取一个子键的句柄
在这里我们先来了解一下注册表的基本概念,打开运行对话框,输入regedit,然后回车,便打开了注册表编辑器,首先映入眼前的,便是五个根键
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USER
HKEY_CURRENT_CONFIG
在根键下面便是主键了,如HKEY_CURRENT_CONFIG根键下有两个主键,分别是Software和System(可能会不一样),那么主键下面是什么呢,对了,就是跟 RegCreateKeyEx函数相关的子键,子键下面就是具体的键值项了,但也可以又是子键。键值有五种可选类型,分别是:
字符串值(REG_SZ)
二进制值(REG_BINARY)
DWORD值(REG_DWORD)
多字符串值(REG_MULTI_SZ)
可扩充字符值(REG_EXPAND_SZ)
键值项还有其它信息,它的名称,数据。了解了上面这些东西,接着就来了解下RegCreateKeyEx函数的各个参数吧,先来看一下函数定义:
LONG RegCreateKeyEx (
HKEY hKey,//根键句柄,指明要在哪个根键下创建子键,填根键名既可
LPCSTR lpSubKey,//子键名,包含完整路径名
DWORD Reserved,.//一般取0
LPSTR lpClass,//一般取NULL
DWORD dwOptions,//创建子键时的选项,可选值REG_OPTION_NON_VOLATILE,REG_OPTION_VOLATILE,这里取0既可
REGSAM samDesired,//打开方式,填KEY_ALL_ACCESS,在任何情况都行。
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//指定继承性,还是取0
PHKEY phkResult,//子键对应句柄,待创建或打开的子键句柄将存储在该句柄里
LPDWORD lpdwDisposition//打开还是创建子键,对应REG_CREATED_NEW_KEY和REG_OPENED_EXISTING_KEY
);
在这里举一个例子,以便我们能更好的理解该函数。
// 在HKEY_CURRENT_CONFIG根键下的Software主键里创建一个名为MySelf的子键。
#include<windows.h>
int main(){
HKEY hroot;//子键句柄
DWORD dwDisposition;//对应着最后一个参数
RegCreateKeyEx(HKEY_CURRENT_CONFIG,"Software\\MySelf",0,NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
return 0;
}
55.RegSetValueEx 根据子键句柄在其下创建或修改一个键值
函数定义:
LONG RegSetValueEx(
HKEY hKey, // 子键句柄
LPCTSTR lpValueName, // 键值名称,如果提供的子键下没有该名称,则创建
DWORD Reserved, // 保留,填0
DWORD dwType, // 键值类型,
CONST BYTE *lpData, // 键值的数据
DWORD cbData // 键值的数据的大小
);
接着我们以增加开机自启动为例,来看一下函数是如何创建一个键值的,我们知道,像程序添加开机自启动一般都在
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run
下添加一个键值,键值类型为二进制(REG_SZ),而键值的数据就为要自启动程序的路径。
// 假设e盘下有一个AutoRun.exe的应用程序,让电脑开机时自动运行它。
#include<windows.h>
int main(){
HKEY hroot;//子键句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
RegSetValueEx(hroot,"AutoRun",0,REG_SZ,(BYTE*)"e:\\AutoRun.exe",sizeof("e:\\AutoRun.exe"));
return 0;
}
56.RegDeleteValue 根据子键句柄删除其下的一个键值
这里直接举一个例子,删除RegSetValueEx函数创建的键值
#include<windows.h>
int main(){
HKEY hroot;//子键句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
RegDeleteValue(hroot,"AutoRun");//删除子键下名为AutoRun的键值
return 0;
}
57.RegQueryValueEx 根据子键句柄获取一个键值数据,类型。
函数定义:
LONG RegQueryValueEx (
HKEY hKey,//根键句柄
LPCWSTR lpValueName,//键值名称
LPDWORD lpReserved,//预留,填0
LPDWORD lpType,//接收键值类型
LPBYTE lpData,//接收键值数据
LPDWORD lpcbData//接收数据的大小
);
例子,获取RegSetValueEx函数创建的键值的类型,数据
#include<windows.h>
#include<stdio.h>
int main(){
char Data[52];
DWORD Size,Type;
HKEY hroot;//子键句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);//获取根键句柄
RegQueryValueEx(hroot,"AutoRun",0,&Type,(BYTE*)Data,&Size);//获取AutoRun的信息
printf("键值名称:AutoRun ");
switch(Type){
case REG_SZ : printf("键值类型:REG_SZ");break;
case REG_BINARY : printf("键值类型:REG_BINARY");break;
case REG_DWORD : printf("键值类型:REG_DWORD");break;
case REG_MULTI_SZ : printf("键值类型:REG_MULTI_SZ");break;
case REG_EXPAND_SZ : printf("键值类型:REG_EXPAND");break;
}
printf(" 键值数据:%s %d\n",Data,Size);
return 0;
}
58.RegEnumValue 根据子键句柄返回对应索引的键值信息
键值信息包括名称,数据,类型,子键下第一个键值索引为0,以此类推,函数成功执行返回ERROR_SUCCESS
函数定义:
LONG RegEnumValue (
HKEY hKey,//子键句柄
DWORD dwIndex,//键值索引
LPWSTR lpValueName,//接收键值名称,字符数组
LPDWORD lpcbValueName,//指明数组大小
LPDWORD lpReserved,//预留,0
LPDWORD lpType,//键值类型,填NULL,不获取
LPBYTE lpData,//键值数据,填NULL,不获取
LPDWORD lpcbData//接收数据的大小,如果键值数据那项参数为NULL,则该项也为NULL
);
例子:输出Run下的所有键值名
#include<windows.h>
#include<stdio.h>
int main(){
char Name[52];
int Index = 0;
DWORD dwSize = 52;
DWORD Size,Type;
HKEY hroot;//子键句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);//获取根键句柄
while(RegEnumValue(hroot,Index,Name,&dwSize,NULL,NULL,NULL,NULL) == ERROR_SUCCESS){
printf("%s\n",Name);
Index++;//索引从0开始每次自增一,函数如果执行失败,则索引已到头
}
return 0;
}
其实也还可以扩充一下,可以像msconfig程序那样列出当前计算机的所有开机自启动程序,当然,注册表也不只就前面的那一个子键下可以添加自启动程序,在HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
下也可以添加,所以这些子键都需要去查看,更多添加自启动程序的子键可以到百度里去搜一下,大家如果掌握前面那几个注册表操作函数,可以结合起来试着做一个可以添加,查看,删除开机自启动程序的小程序。
59.ExitWindowsEx 关机,重启,注销计算机函数
这个函数只有两个参数,后一个参数为系统预留,填0就可以了,而第一个参数则,指明关机,还是重启,或注销,可选值如下:
EWX_LOGOFF//注销
EWX_REBOOT//重启 NT系统中需SE_SHUTDOWN_NAME 特权
EWX_SHUTDOWN//关机,需权限。
例子:关闭计算机,由于需要SE_SHUTDOWN_NAME权限,所以我们得先提升权限,代码如下:
#include<windows.h>
int main(){
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken);
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
::ExitWindowsEx(EWX_SHUTDOWN,0);
return 0;
}
60.VirtualAllocEx 在其它的进程中分配内存空间
函数定义:
LPVOID VirtualAllocEx(
HANDLE hProcess,//进程句柄,将会在该进程句柄相关的进程分配空间
LPVOID lpAddress,//默认为系统指定,填NUL
DWORD dwSize,//分配多大的内存
DWORD flAllocationType,//填MEM_COMMIT
DWORD flProtect//指定分配的内存属性,为PAGE_READWRITE,内存可读写
);
函数返回分配的内存首地址,
60.CreateRemoteThread 创建一个远程线程(在其它进程中创建线程)
函数定义:
HANDLE WINAPI CreateRemoteThread(HANDLE hProcess,//进程句柄,函数将在这个进程句柄关联的进程创建线程
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
这个函数比CreateThread函数多了一个参数,就是这个函数的第一个hProcess(函数在该进程里创建线程),后面的六个参数跟第39个函数CreateThread的六个参数一样,这里就不再解释了。
例子:远程线程注入
创建一个远程线程,就必须得有一个线程函数供线程执行,而线程函数又不能在其它程序里。那要怎么办呢?大家看一下线程函数的定义,和LoadLibrary函数的定义,它们的定义相似,都是只有一个参数,而且每个程序都能调用LoadLibrary函数,这样我们便能把LoadLibrary函数作为线程函数。这样创建的线程就会去执行LoadLibrary函数。因而我们就有了一次让其它程序调用LoadLibrar函数的机会,并还可以指定LoadLibrary函数的参数(通过创建远程线程函数传递)。前面在动态链接库提到,一个程序如果调用LoadLibrary函数,它都会自动去执行相应动态链接库里的DllMain函数,所以我们自己可以编写一个动态链接库,在DllMain函数里写入想要其它程序执行的代码。再通过CreateRemoteThread函数在其它程序创建一个线程去执行LoadLibary加载我们已经编写好的动态链接库,这样就可以让其它程序执行我们的代码了。这里还有一个问题,CreateRemoteThread函数传递过去的参数,因为要供注入的那个程序访问,所以参数数据所存储的空间不能在调用CreateRemoteThread函数的程序里。必须调用VirtualAllocEx函数,在注入程序里分配一个空间,把数据(动态链接库的名称)存在里面,而新分配空间的首地址则作为CreateRemoteThread函数的参数传过去。这样注入程序访问的是自己的地址空间。
远程线程注入:
假设动态链接库为“ReCode.dll”它的代码如下:
#include<windows.h>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved){//DllMain函数,只要加载这个动态链接库的程序,都会跑来执行这个函数
//在这里填让其它程序执行的代码
while(1){
MessageBox(NULL,"aaaa","aaaa",MB_OK);//简单的让其它程序每隔3秒弹出一个提示框
Sleep(3000);
}
return TRUE;
}
编译运行,然后把生成的“ReCode.dll
”文件复制到c:\\windows\\system23
下去。
注入线程的代码:
选择ctfmon.exe
(输入法管理)作为我们要注入进线程的程序
#include<windows.h>
#include<tlhelp32.h>
#include<stdio.h>
int main(){
HANDLE hProcessSnap,hProcess,/*用于存储ctfmon.exe的进程句柄*/,hToken;
char DllName[25] = "ReCode.dll";
PROCESSENTRY32 pe32;//进程相关信息存储这个结构里
TOKEN_PRIVILEGES tp;
LPVOID lpBuf;
DWORD WrSize;
BOOL bMore;
//先提升进程权限,使其能获取任何进程句柄,并对其进行操作
OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ),NULL, NULL );
//Process32First和Process32Next函数结合(寻找)获取ctfmon.exe进程ID号
//再调用OpenProcess函数根据进程ID获得进程句柄
pe32.dwSize = sizeof(pe32);
//给系统内的所有进程拍一个快照
hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
bMore = ::Process32First(hProcessSnap,&pe32);
while(bMore){
if(strcmp("ctfmon.exe",pe32.szExeFile) == 0)//如果找到进程名为ctfmon.exe
hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//获取句柄
bMore = ::Process32Next(hProcessSnap,&pe32);//寻找下一个
}
//在ctfmon进程中分配空间
lpBuf = VirtualAllocEx(hProcess,NULL,strlen(DllName),MEM_COMMIT,PAGE_READWRITE );
//把DllName里的数据写入到分配的空间里
WriteProcessMemory(hProcess, lpBuf, (LPVOID)DllName, strlen(DllName),&WrSize);
//创建远程线程
CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)LoadLibraryA,lpBuf,0,NULL);
return 0;//程序使命完成,结束
}
当然,给一个程序安装钩子,也可以让指定的应用程序加载特定的动态链接库,但要了解,加载动态链接库的是是应用程序的主程序,你总不能让应用程序不干它自己的事,而来一直执行DllMain函数里的代码吧!而且即使这样,当安装钩子的程序退出或卸载钩子的时候,那么被系统强迫加载动态链接库的程序,也会自动释放动态链库,退出DllMain函数。如此,那就没有办法了吗?,办法肯定是有的,用CreateThread函数。当其它程序主线程执行DllMain函数的时候,使其调用CreateThread再创建一个线程,就行了。
http://hi.baidu.com/3582077/item/8d80115206f504968c12ed19
61.GetWindowThreadProcessId 根据窗口句柄获得对应进程ID号,和线程ID号
函数只有两个参数,第一个参数是窗口句柄,第二个参数是一个DOWRD类型的指针(进程ID),函数返回线程ID,如:
DWORD ThreadId,ProcessId;
ThreadId = GetWindowThreadProcessId(wnd,&ProcessId);
62.EnumWindows 枚举当前正运行的所有主窗口,不包括子窗口
调用这个函数,还必须定义一个回调函数,它的格式是这样的:
BOOL CALLBACK lpEnumFunc(HWNDwnd, LPARAM lParam);
EnumWindows函数有两个参数,第一个就是回调函数的地址,另一个是自定义参数,对应着回调函数的第二个参数。
每枚举一次窗口,这个回调函数就会被执行一次,而获得的窗口句柄,就会传递给回调函数,对应着回调函数的第一个参数。直到枚举完所有窗口。而在回调用函数里,返回真表示继续枚举,返回假则停止枚举。
例子:枚举窗口
#include<windows.h>
#include<stdio.h>
BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam);
int main(){
::EnumWindows(lpEnumFunc,0);
return 0;
}
BOOL CALLBACK lpEnumFunc(HWND wnd, LPARAM lParam){
char WindowName[52] = {0};
GetWindowText(wnd,WindowName,sizeof(WindowName));//根据窗口句柄获得窗口名
printf("窗口句柄:%d 窗口名称:%s\n,",wnd,WindowName);
//可以在这里加个判断当前是否有一个窗口正在运行
//如if(strcmp(WindowName,"Windows任务管理器") == 0) return 0;结束枚举,
return 1;
}
63.MessageBox 弹出一个消息提示框
int MessageBox(
HWND hWnd ,//指明消息框是属于哪个窗口的,可以为NULL
LPCSTR lpText,//消息提示具体内容
LPCSTR lpCaption,//提示框窗口标题
UINT uType);//指明提示框类型,按钮,图标
这里只说下uType的常用可选值:
按钮:
MB_ABORTRETRYIGNORE//终止 重试 忽略
MB_OK//确定
MB_OKCANCEL//确定 取消
MB_RETRYCANCEL//重试 取消
MB_YESNO//是 否
MB_YESNOCANCEL//是 否 取消
图标:
MB_ICONWARNING//感叹号
MB_ICONINFORMATION//提示i
MB_ICONQUESTION//问号
MB_ICONSTOP//红X号
按钮和图标可以结合用,如:
MessageBox(NULL,"该内存不能访问","出错",MB_OK|MB_ICONSTOP);
系统模式:MB_SYSTEMMODAL
函数的返回值确定用户选择了哪个按钮,正面给各按钮的定义:
IDABORT//“放弃”按钮
IDCANCEL//“取消”按钮
IDIGNORE//“忽略”按钮
IDNO//“否”按钮
IDOK//确定
IDRETRY//重试
IDYES//是
判断返回值是否与其相等即可。
64.GetForegroundWindow 获得当前激活的窗口句柄
函数没参数,调用即返回最前窗口句柄
这里举一个例子:每当切换窗口的时候弹出一个消息提示框
#include<windows.h>
int main(){
char WindowName[52];
HWND wnd,Orgwnd;
wnd = Orgwnd = NULL;
while(1){
wnd = GetForegroundWindow();
if(wnd != Orgwnd){
GetWindowText(wnd,WindowName,sizeof(WindowName));
MessageBox(NULL,WindowName,"切换窗口到",MB_OK);
Orgwnd = wnd;
}
Sleep(200);
}
return 0;
}
65.GetTopWindow 根据窗口句柄获得其下第一子窗口句柄(如果有)
用过MFC的人都知道,在对话编辑区,如果要为控件排序的话,就按CTRL+D显示出每个控件的顺序,如下图:
而GetTopWindow函数获取的就是控件顺序为1的窗口句柄。
例子:改变一个主窗口下的第一子窗口的显示内容(前提得它有),这里就以上面那个abc对话框为例:
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"abc");
HWND FirstWnd = GetTopWindow(wnd);
SetWindowText(FirstWnd,"first");
return 0;
}
执行效果:
66.GetNextWindow 根据子窗口句柄获得下一个或上一个同级的窗口句柄
若返回NULL,函数执行失败。函数有两个参数,第一个是子窗口句柄,第二个参数指明寻找上一个,还是一下个窗口句柄,值:GW_HWNONEXT(下一个),GW_HWNDPREV(上一个)。比如子窗口句柄在主窗口的顺序为3,那么获取的是顺序为2或顺序为3的窗口句柄(具体取决于第二个参数),函数返回获得的窗口句柄.这样GetNextWindow结合GetTopWindow函数就可以遍历一个主窗口里的所有子窗口了。
例子:遍历一个窗口里的所有子窗口,以上面的abc窗口为例
#include<stdio.h>
#include<windows.h>
int main(){
char Name[52];
HWND wnd = FindWindow(NULL,"abc");
wnd = GetTopWindow(wnd);
while(wnd != NULL){
GetWindowText(wnd,Name,sizeof(Name));
printf("窗口句柄:%d,名称:%s\n",wnd,Name);
wnd = GetNextWindow(wnd,GW_HWNDNEXT);//GW_HWNDNEXT获取下一个
}
return 0;
}
67.InvalidateRect 发送一个WM_PAINT消息给窗口(刷新窗口)
函数定义:
BOOL InvalidateRect(
HWND hWnd ,//要刷新窗口的句柄
CONST RECT *lpRect,//刷新的范围
BOOL bErase//重画为TRUE
);
例子:在SetTimer函数里会举例
68.SetTimer 设置一个定时器(每隔一段时间执行一次定时器函数)
函数定义:
UINT SetTimer(
HWND hWnd ,//窗口句柄
UINT nIDEvent,//定时器ID号,为了能设置多个定时器
UINT uElapse,//时间,指明间隔多久执行一次定时器函数,单位:毫秒
TIMERPROC lpTimerFunc);//定时器回调函数的地址
定时器函数的固定格式:
VOID CALLBACK TimerProc(HWND hwnd,UINTuMsg,UINT_PTR idEvent, DWORD dwTime);
例子:在用w,a,s,d键控制一个矩形移动的同时,一个相同的矩形自动移动。
#include<windows.h>
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);//函数声明
VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime);
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){
MSG msg;//消息结构类型
HWND hwnd; //用以存储CreateWindow函数所创建的窗口句柄
WNDCLASS wndcls; //定义一个存储窗口信息WNDCLASS变量
wndcls.cbClsExtra = 0; //默认为0
wndcls.cbWndExtra = 0; //默认为0
wndcls.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH); //背景画刷
wndcls.hCursor = LoadCursor(NULL,IDC_ARROW); //光标
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR); //窗口图标
wndcls.hInstance = hInstance; //应用程序实例句柄由WinMain函数传进来
wndcls.lpfnWndProc = WinSunProc; //窗口消息处理函数
wndcls.lpszClassName = "windowclass"; //窗口类名
wndcls.lpszMenuName = NULL; //窗口菜单名,没有菜单,为NULL
wndcls.style = CS_HREDRAW | CS_VREDRAW;//窗口类型,CS_HREDRAW和CS_VERDRAW 表明
//当窗口水平方向垂直方向的宽度变化时重绘整个窗口
RegisterClass(&wndcls); //把窗口信息提交给系统,注册窗口类
hwnd = CreateWindow("windowclass","first windows",
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//创建窗口
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口创建完了,显示它
UpdateWindow(hwnd); //更新窗口,让窗口毫无延迟的显示
SetTimer(hwnd,1,200,(TIMERPROC)TimerProc);//设置定时器
while(GetMessage(&msg,NULL,0,0)){//获取消息
TranslateMessage(&msg); //此函数用于把键盘消息(WM_KEYDOWN,WM_KEYUP)转换成字符消息WM_CHAR
DispatchMessage(&msg); //这个函数调用窗口过程处理函数,并把MSG里的信息处理后传给过程函数的四个参数
}
return 0;
}
VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime){//定时器函数
HDC dc = GetDC(hwnd);
static int x = 0,y = 0;
x += 15;
if(x > 500){
y += 15;
x = 0;
}
Rectangle(dc,x,y,x+30,y+30);
}
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
HDC dc;
RECT rect;
PAINTSTRUCT ps;
static int x = 0,y = 0;
switch(uMsg){
case WM_CHAR:
if(wParam == 'a') x -= 10;
else if(wParam == 'd') x += 10;
else if(wParam == 'w') y -= 10;
else if(wParam == 's') y += 10;
GetClientRect(hwnd,&rect);
::InvalidateRect(hwnd,&rect,TRUE);//发送WM_PAINT消息,刷新窗口
break;
case WM_PAINT:
dc = BeginPaint(hwnd,&ps); //BeginPaint只能在响应WM_PAINT,不能用GetDC获取设备上下文
Rectangle(dc,x,y,x+30,y+30);
break;
case WM_CLOSE://用户关闭了窗口
DestroyWindow(hwnd);//销毁窗口,并发送WM_DESTROY消息
break;
case WM_DESTROY://如果窗口被销毁
PostQuitMessage(0);//让进程退出
break;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam); //未处理的消息通过DefWindowProc函数交给系统处理
}
69.RegisterHotKey 注册一个热键
函数定义:
BOOL RegisterHotKey(HWND hWnd,int id,UINT fsModifiers,UINT vk);
第一个参数hWnd表明热键消息(HOT_KEY)发送给哪个窗口,为NULL表明直接把消息投递给调用这个函数进程的消息队列。
第二个参数可以自定取值,取值范围0xC000-0xFFFF
,这个参数是为了程序能同时拥有多个热键而存在。
第三个参数fsModifiers的可选取值如下:MOD_ALT(Alt键),MOD_CONTROL(Ctrl键),MOD_SHIFT(Shift键),MOD_WIN(‘田’图标键)
最一个参数是一个ASCII值,指明具体和哪个非系统键组合。
如QQ的热键Ctrl+Alt+Z
,注册这个热键的语句是
RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_ALT,‘Z’);
如QQ的截图热键
RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_ALT,'A');
例子:按下Ctrl+Alt+X
热键,弹出消息提示框,询问是否要退出。
//#include "stdafx.h" 新建空工程,不需要该头文件
#include<windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow){
MSG msg;
RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_ALT,'X');//注册热键
while(GetMessage(&msg,NULL,0,0)){//从消息队伍获取消息
if(msg.message == WM_HOTKEY){//热键消息
if(MessageBox(NULL,"你确定要退出程序吗?","热键提示",MB_YESNO|MB_SYSTEMMODAL) == IDYES){
UnregisterHotKey(NULL,0x0001);//卸载热键
break;
}
}
}
return 0;
}
记得要退出程序
70.StretchBlt 在窗口输出一个位图
这个函数比BitBlt多了两个参数,那就是源目标DC的宽高,像BitBlt函数,只有目标DC的宽高。
有了这两个参数的加入,StretchBlt函数功能就比BitBlt函数强大了许多,它可以缩小或放大图片,可以把一张图片上的任意矩形区域覆盖到另一张图片上的任意区域。
函数语句:
StretchBlt(hdc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,0,0,50,50,SRCCOPY);
具体用法参考BitBlt函数。
例子:在窗口输出不规则形状的位图。
说明:
位图其实都是矩形,这里要说的是可以输出位图里的任意形状部分,但位图不输出的部分,必须是固定的颜色
为什么呢?白色的RGB值是255,255,255,把255转换成二进制,可以看到每一位都是1,如:11111111.这样任何一种颜色与白色进行按位与运算,其结果都不会变,这样两个DC合并进行按位与(SRCAND)运算,白色的部分都会被屏蔽,而任何一种颜色与黑色进行按位与运算,其结果都为黑色,与黑色进行按位或(SRCAND)运算,其结果不会改变。黑色的RGB值是0,0,0。
输出这样的图形,要准备两张图片,必须是这种模式,如下:
flower1 flower2
这里要输出在窗口里的是flower2图片里的花,黑色部分不输出
假设这两张图片在E盘,看例子:
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
HBITMAP hBmp1 = (HBITMAP)LoadImage(NULL,"e:\\flower1.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
HBITMAP hBmp2 = (HBITMAP)LoadImage(NULL,"e:\\flower2.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
HDC memDC,dc = GetDC(wnd);
BITMAP bmInfo;
GetObject(hBmp1,sizeof(BITMAP),&bmInfo);
memDC = CreateCompatibleDC(dc);
while(1){
SelectObject(memDC,hBmp1);
StretchBlt(dc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,
0,0,bmInfo.bmWidth,bmInfo.bmHeight,SRCAND);//按位与运算合并DC
SelectObject(memDC,hBmp2);
StretchBlt(dc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,
0,0,bmInfo.bmWidth,bmInfo.bmHeight,SRCPAINT);//按位或运算合并DC
Sleep(200);
}
}
运行效果如下图:
http://hi.baidu.com/3582077/item/5668f7ef963711d3ea34c919
71.TextOut 根据设备DC在窗口输出文字
函数定义:
BOOL TextOut(
HDC hdc,// 设备DC
int nXStart,// 窗口x坐标
int nYStart,// 窗口y坐标,字符串左上角是将是x,y
LPCTSTR lpString,// 字符串
int cbString// 字符串中字符的个数
);
例子:在窗口输出文字,为了方便,这里依旧在"无标题.txt - 记事本",窗口里输出文字
#include<windows.h>
#include<string.h>
int main(){
char Text[52] = "从坐标点50,50开始输出文字";
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
HDC dc = GetDC(wnd);
SetTextColor(dc,RGB(255,0,0));//设置文字颜色
while(1){
TextOut(dc,50,50,Text,strlen(Text));
Sleep(200);
}
return 0;
}
72.DrawText 根据设备DC在窗口的一个矩形区输出文字。
int DrawTextW(
HDC hDC,//设备DC
LPCWSTR lpString,//字符串
int nCount,//字符串的个数
LPRECT lpRect,//指明一个矩形区
UINT uFormat);//输出格式
uFormat的常用取值
值 | 说明 |
---|---|
DT_BOTTOM | 将正文调整到矩形底部。此值必须和DT_SINGLELINE组合。 |
DT_CENTER | 使正文在矩形中水平居中。 |
DT_VCENTER | 使正文在矩形中垂直居中。 |
DT_END_ELLIPSIS | 对于显示的文本,如果结束的字符串的范围不在矩形内,它会被截断并以省略号标识。 |
如果一个字母不是在字符串的末尾处超出了矩形范围,它不会被截断并以省略号标识。
字符串不会被修改,除非指定了DT_MODIFYSTRING标志。
T_WORD_ELLIPSIS, DT_PATH_ELLIPSIS和DT_END_ELLIPSIS不能和此参数一起使用
值 | 说明 |
---|---|
DT_LEFT | 正文左对齐。 |
T_RIGHT | 正文右对齐。 |
DT_RTLREADING | 当选择进设备环境的字体是希伯来文或阿拉伯文字体时,为双向正文安排从右到左的阅读顺序都是从左到右的。 |
DT_TOP | 正文顶端对齐(仅对单行)。 |
DT_WORDBREAK | 断开字。当一行中的字符将会延伸到由lpRect指定的矩形的边框时,此行自动地在字之间断开。一个回车一换行也能使行折断。 |
DT_WORD_ELLIPSIS | 截短不符合矩形的正文,并增加省略号。 |
73.GetLogicalDriveStrings 获取系统分区信息
函数定义:
DWORD GetLogicalDriveStrings(
DWORD nBufferLength,//指明lpBuffer参数大小
LPSTR lpBuffer//分区信息将会存储在这个参数,格式是“分区NULL分区NULL分区NULLNULL”两个NULL结尾
//假设当前电脑有C,D,E分区,那字符串的存储格式是"C:\\\0D:\\\0E:\\\0\0"; ('\\'转义字符\)
);
例子:枚举当前磁盘所有分区
#include<windows.h>
#include<stdio.h>
int main(){
char *szBuffer = new char[52];
GetLogicalDriveStrings(52,szBuffer);
while(*szBuffer != '\0'){
printf("%s\n",szBuffer);
szBuffer += strlen(szBuffer)+1;
}
return 0;
}
74.GetDiskFreeSpaceEx 获取一个分区(盘符)的信息(已用空间,总大小,可用空间)
这个函数必须用ULARGE_INTEGER联合类型来存储磁盘使用信息。因为要获取磁盘的已用空间,总大小,可用空间,所以我们必须定义三个ULARGE_INTEGER类型变量来存储这三个信息。而具体信息就存储在ULARGE_INTEGER类型的QuadPart成员变量(该成员占八位字节)
如获取C盘的信息:
ULARGE_INTEGERdwAvailable,dwFree,dwTotal;
GetDiskFreeSpaceEx("c:\\",&dwAvailable,&dwTotal,&dwFree);//获取分区信息
下面结合GetLogicalDriveStrings举个例子:获取当前磁盘所有分区信息并输出
#include<windows.h>
#include<stdio.h>
int GetSpace(char *P);
int main(){
char *szBuffer = new char[52];
GetLogicalDriveStrings(52,szBuffer);
while(*szBuffer != '\0'){
printf("%s ",szBuffer);
GetSpace(szBuffer);
szBuffer += strlen(szBuffer)+1;
}
return 0;
}
int GetSpace(char *Par){
ULARGE_INTEGER dwAvailable,dwFree,dwTotal;
double fDwa,fDwf,fDwt;
char szSize[128] = {0};
int Size;
GetDiskFreeSpaceEx(Par,&dwAvailable,&dwTotal,&dwFree);//获取分区信息
Size = dwTotal.QuadPart/1024/1024;//获取总大小
fDwt = Size/1024.0;
Size = dwAvailable.QuadPart/1024/1024;//获取已用空间
fDwa = Size/1024.0;
Size = dwFree.QuadPart/1024/1024;//获取可用空间
fDwf = Size/1024.0;
sprintf(szSize,"总大小:%0.2fGB 已用空间:%0.2fGB 可用空间:%0.2fGB",fDwt,fDwa,fDwf);
printf("%s\n",szSize);
return 0;
}
75.WritePrivateProfileString 修改或创建一个INI
文件里的数据
INI文件的内容一般由节名,键名,键值组成,先来看一下INI文件的结构,打开一个INI文件,我们可能会看到以下内容
[gmy_p]
exist_p=0
linux_p=
[boot]
a20=0
a21=0
上面的内容中[gmy_p]
和[boot]
便是INI文件的节名,节名包含键名,和键值。一个INI文件可以有多个节名.
那么哪些是键名,那些是键值呢,在“=
”左边的是键名,而在右边的就是键值,键值可以为NULL
。
好了,看一下WritePrivateProfileString的函数的定义:
BOOL WritePrivateProfileString(
LPCWSTR lpAppName,//节名
LPCWSTR lpKeyName,//键名
LPCWSTR lpString,//修改的数据
LPCWSTR lpFileName//INI文件名
);
如果要修改键值,那么要提供哪些信息呢,首先,必须要知道INI文件的路径(lpFileName),要修改的键值是在哪个节名下(lpAppName),以及具体是哪个键名(lpKeyName),还有修改的键值数据(lpString).
比如我要把之前INI文件里节名为gmy_p下的键名exist_p的键值改为100(假设这个文件的路径为d:\gho.ini).
那么就是语句:
WritePrivateProfileString("gmy_p","exist_p","100","d:\\gho.ini");
WritePrivateProfileString函数功能不止于此,当函数提供的INI文件名,节名,键名不存在时,那么函数就会创建他们。这样,我们就可以用这个函数创建一个INI文件,或在一个INI文件里创建一个节名,或在一个节名下创建一个键名。如:
WritePrivateProfileString("ZhengYong","QQ","***980073","d:\\Info.ini");
76.GetPrivateProfileString 获取一个INI
文件里的数据
函数定义:
DWORD GetPrivateProfileStringW(
LPCWSTR lpAppName,//节名
LPCWSTR lpKeyName,//键名
LPCWSTR lpDefault,//默认值,填0既可
LPWSTR lpReturnedString,//接收数据的缓存区(字符串)
DWORD nSize,//指明缓存区的大小
LPCWSTR lpFileName//INI文件名
);
例子获取一个键值:假设D盘下有一个名为Info.ini
文件,它的内容如下:
[ZhengYong]
QQ=***980073
Age=100
Weight=65kg
[LiYang]
QQ=***990129
Age=22
Weight=55kg
如果我想要获取节名为"ZhengYong
"下的键名QQ的键值,那么就是:
#include<windows.h>
#include<stdio.h>
int main(){
char KeyValue[252];
::GetPrivateProfileString("ZhengYong","QQ",0,KeyValue,252,"d:\\Info.ini");
printf("%s\n",KeyValue);
return 0;
}
同WritePrivateProfileString类似,如果提供的节名,或键名为NULL
,则获取当前所有的节名或键名。跟分区信息存储格式一样,字符串里,多个节名,或键名以’\0
‘间隔,字符串最终以两个’\0
'结束。
例子:枚举ZhengYong节名下的所有键名:
#include<windows.h>
#include<stdio.h>
int main(){
char Data[252];
::GetPrivateProfileString("ZhengYong",NULL,0,Data,252,"d:\\Info.ini");
char *pKey = Data;
while(*pKey != '\0'){
printf("%s\n",pKey);
pKey += strlen(pKey)+1;
}
return 0;
}
那么枚举节名只要在上面的例子中,把函数的节名参数设为NULL
就行了,如:
GetPrivateProfileString(NULL,NULL,0,Data,252,"d:\\Info.ini");
大家可以用这个函数编一个读取INI文件内容的程序,以便更好的掌握这个函数。记得把接收数据的缓存区设置大一点。
77.GetSystemMetrics 获得特定窗口的高宽度
该函数只有一个参数,常用取值如下:
值 | 说明 |
---|---|
SM_CXSCREEN | 屏幕宽度 |
SM_CYSCREEN | 屏幕高度 |
SM_CXFULLSCREEN | 窗口客户区宽度 |
SM_CYFULLSCREEN | 窗口客户区高度 |
SM_CYMENU | 菜单栏高度 |
SM_CYCAPTION | 标题栏高度 |
SM_CXBORDER | 窗口边框宽度 |
SM_CYBORDER | 窗口边框高度 |
SM_CXSIZE | 标题栏上的按钮宽度 |
SM_CYSIZE | 标题栏上的按钮高度 |
例子:获取屏幕分辨率(桌面宽高度)
#include<windows.h>
#include<stdio.h>
int main(){
int ScreenX = ::GetSystemMetrics(SM_CXSCREEN);
int ScreenY = ::GetSystemMetrics(SM_CYSCREEN);
printf("屏幕分辨率:%dX%d\n",ScreenX,ScreenY);
return 0;
}
78.SetWindowPos 设置一个窗口的大小和它的Z序
窗口的Z序是什么意思呢?用过MFC的人应该都清楚,在对话框编辑区按Ctrl+D
就会显示出每个控件的顺序。如下图:
设置控件的顺序有什么用呢,大家看到上面两个控件有什么特别的吗?对了,两个控件正好有一部分重叠,这时候问题就来了,重叠的部分显示的是那个窗口呢,或者说是以什么来确定显示哪个窗口,我想大家也应该猜到了,是以控件的顺序来确定的。顺序较大的会被显示。这个程序运行如下图如示:
明白窗口的Z序了,我们就来看一下这个函数的参数及其意思。
函数定义:
BOOL SetWindowPos(
HWND hWnd,//要设置的窗口句柄
HWND hWndInsertAfter,
int X,
int Y,//X,Y指明窗口左上角的位置
int cx,//窗口宽度
int cy,//窗口高度
UINT uFlags);
第二个参数hWndInsertAfter的常用取值:
值 | 说明 |
---|---|
HWND_BOTTOM | 将窗口置于Z序的底部. |
HWND_NOTOPMOST | 如果窗口在Z序顶部,则取消顶部位置,如果不是,则该参数无效 |
HWND_TOP | 将窗口置于Z序的顶部。 |
HWND_TOPMOST | 将窗口置于Z序的顶部。窗口当前未被激活,也依然是顶部位置 |
最后一个参数uFlags可以是Z序中hWnd的前一个窗口句柄的或以下常用取值:
值 | 说明 |
---|---|
SWP_HIDEWINDOW | 隐藏窗口 |
SWP_SHOWWINDOW | 显示窗口 |
SWP_NOMOVE | 指明X,Y参数无效 |
SWP_NOSIZE | 指明CX ,CY参数无效 |
SWP_NOZORDER | 指明hWndInsertAfter参数无效 |
例子:设置一个窗口像PPS和任务栏那样,总在最前显示。
以"无标题.txt - 记事本"窗口为例
#include<windows.h>
int main(){
HWND wnd = ::FindWindow(NULL,"无标题.txt - 记事本");
::SetWindowPos(wnd,HWND_TOPMOST,0,0,0,0,SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
return 0;
}
例子2:全屏一个窗口
依旧以"无标题.txt - 记事本"窗口为例:
#include<windows.h>
int main(){
int ScreenX = ::GetSystemMetrics(SM_CXSCREEN);
int ScreenY = ::GetSystemMetrics(SM_CYSCREEN);
HWND wnd = ::FindWindow(NULL,"无标题.txt - 记事本");
::SetWindowPos(wnd,HWND_TOPMOST,0,0,ScreenX,ScreenY,SWP_SHOWWINDOW);
return 0;
}
79.CreateFile 创建一个文件,或打开一个文件用于读写,函数返回文件句柄
函数定义:
HANDLE CreateFile(
LPCSTR lpFileName,//文件名
DWORD dwDesiredAccess,//指明对文件进行何种操作,是要读它(GENERIC_READ)还是要写入(GENERIC_WRITE)
DWORD dwShareMode,//指明文件可以同时被多个程序读写吗?FILE_SHARE_READ可以同时读,FILE_SHARE_WRITED可以同时写
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//指向一个SECURITY_ATTRIBUTES结构的指针,一般为NULL
DWORD dwCreationDisposition,//安全属性,指明以何种方式打开或创建文件
DWORD dwFlagsAndAttributes,//指明文件的属性,隐藏?只读?系统文件?为NULL表示默认属性
HANDLE hTemplateFile// 如果不为零,则指定一个文件句柄。新文件将从这个文件中复制扩展属性
);
第五个参数dwCreationDisposition的常用取值及意思
值 | 说明 |
---|---|
TRUNCATE_EXISTING | 将现有文件缩短为零长度,清空文件的内容,文件必须已经存在 |
CREATE_ALWAYS | 创建一个文件,如果文件已经存在,则覆盖它 |
CREATE_NEW | 创建文件,如果文件已经存在,则函数执行失败 |
OPEN_ALWAYS | 打开文件,如果文件不存在,则创建它 |
OPEN_EXISTING | 打开文件,文件必须存在。 |
第六个参数dwFlagsAndAttributes的常用取值及其意思
值 | 说明 |
---|---|
FILE_ATTRIBUTE_NORMAL | 默认属性 |
FILE_ATTRIBUTE_HIDDEN | 隐藏 |
FILE_ATTRIBUTE_READONLY | 只读 |
FILE_ATTRIBUTE_SYSTEM | 系统文件 |
80.ReadFile 根据文件句柄,从文件中读取一段数据
函数定义:
BOOL WINAPI ReadFile(
HANDLE hFile,//文件句柄
LPVOID lpBuffer,//接收文件数据的缓存区
DWORD nNumberOfBytesToRead,//指明读取多少数据(字节)
LPDWORD lpNumberOfBytesRead,//实际读取数据
LPOVERLAPPED lpOverlapped// 一般为NULL,如文件打开时指定了FILE_FLAG_OVERLAPPED,该参才有具体取值。
);
例子:读取txt文件的内容,假设E盘下有一个名a.txt的文件,文件内容为123456789
#include<windows.h>
#include<stdio.h>
int main(){
char Text[25] = {0};
DWORD dwSize;
HANDLE FileHandle = CreateFile("e:\\a.txt",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL ,NULL);//获取文件句柄
ReadFile(FileHandle,Text,15,&dwSize,NULL);//从文件中读取15个字节
printf("内容:%s 实际读入字节:%d\n",Text,dwSize);
return 0;
}
http://hi.baidu.com/3582077/item/5f4f58e718028fabc10d751b
81.WriteFile 根据文件句柄,写入一段数据到文件中
函数定义:
BOOL WriteFile(
HANDLE hFile,//文件句柄
LPCVOID lpBuffer,//该缓存区的数据将要写入到文件里
DWORD nNumberOfBytesToWrite,//指明写入多少数据
LPDWORD lpNumberOfBytesWritten,//实际写入数据
LPOVERLAPPED lpOverlapped//一般为NULL
);
例子:在E盘创建一个名为aa.txt的文件,并向其写入数据
#include<windows.h>
#include<stdio.h>
int main(){
char Text[25] = "123456789";
DWORD dwSize;
HANDLE FileHandle = CreateFile("e:\\aa.txt",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
::WriteFile(FileHandle,Text,9,&dwSize,0);
return 0;
}
82.SetFilePointer 移动一个文件指针的位置
移动一个文件指针的位置有什么用呢,作用是读取一个文件里指定位置的数据,比如我只要读取文件中第四个字节到第七个字节这一段的数据,用SetFilePointer函数就可以完成。
函数定义:
DWORD SetFilePointer(
HANDLE hFile,//文件句柄
LONG lDistanceToMove,//移动字节,负数表示反向移动
PLONG lpDistanceToMoveHigh,//为了支持超大文件而存在,一般为NULL
DWORD dwMoveMethod//从哪里开始移动,FILE_BEGIN 从文件开始处开始移动,FILE_CURRENT当前位置,FILE_END文件末尾
);
例子:假设E盘下有一个名为a.txt的文件,内容为"123456789",读取该文件第四个字节到第七个字节的数据
#include<windows.h>
#include<stdio.h>
int main(){
char Text[25] = {0};
DWORD dwSize;
HANDLE FileHandle = CreateFile("e:\\a.txt",GENERIC_READ,0,NULL,OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
SetFilePointer(FileHandle,3,NULL,FILE_BEGIN);
ReadFile(FileHandle,Text,4,&dwSize,NULL);
printf("%s\n",Text);
return 0;
}
例子2:从文件中第四个字节开始写入数据,被新数据所占位置的数据会被覆盖掉,依旧以上面a.txt文件为例子
#include<windows.h>
#include<stdio.h>
int main(){
char Text[25] = {"abcd"};
DWORD dwSize;
HANDLE FileHandle = CreateFile("e:\\a.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
SetFilePointer(FileHandle,3,NULL,FILE_BEGIN);
WriteFile(FileHandle,Text,4,&dwSize,NULL);
return 0;
}
则写入后a.txt文件里的数据为123abcd89
如果要在文件的末尾添加数据,就用这个语句:
SetFilePointer(FileHandle,0,NULL,FILE_END);
83.GetFileSize 获取一个文件的大小
函数定义:
DWORD GetFileSize(
HANDLE hFile,//文件句柄
LPDWORD lpFileSizeHigh//一般为NULL
);
如获取a.txt文件的大小:
#include<windows.h>
#include<stdio.h>
int main(){
DWORD FileSize;
HANDLE FileHandle = CreateFile("e:\\a.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
FileSize = GetFileSize(FileHandle,NULL);
printf("%d字节\n",FileSize);
return 0;
}
84.SetTextColor 设置窗口输出文本颜色
第一个参数是设备DC
,第二个参数是一个COLORREF
类型的颜色值,可用RGB
进行转换。
85.SetBkColor 设置背景颜色
参数跟SetTextColor函数一样,第一个DC
,第二个COLORREF
86.GetWindowDC 获取整个窗口设备上下文DC
像GetDC
获取的只是客户区DC
,不能对窗口标题栏,状态栏等进行操作。该函数用法跟GetDC
一样,仅区域不一样。
例子:在一个窗口的标题栏输出文字
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt- 记事本");
HDC dc = GetWindowDC(wnd);
SetTextColor(dc,RGB(255,0,0));//文本颜色设置为红色
::SetBkColor(dc,RGB(0,0,255));//文本背景颜色设置为蓝色
while(1){
TextOut(dc,0,0,"123456",6);
Sleep(200);
}
return 0;
}
87.GetDesktopWindow 获取桌面窗口句柄
该函数没有参数,调用返回桌面窗口句柄
例子:
#include<windows.h>
int main(){
HWND DeskWnd = GetDesktopWindow();//获取桌面窗口句柄
HDC DeskDC = GetWindowDC(DeskWnd);//获取DC
HBRUSH brush = ::CreateSolidBrush(RGB(255,0,0));//红色画刷
SelectObject(DeskDC,brush);//选入画刷
while(1){
::Rectangle(DeskDC,50,50,200,200);
Sleep(200);
}
return 0;
}
88.CreateCompatibleBitmap 根据DC
创造一个兼容的位图
该函数需与CreateCompatibleDC函数配合使用
函数第一个参数是窗口DC
,第二,三参数指明窗口宽高,函数返回位图句柄(HBITMAP
)
创建一个兼容的位图是什么意思呢?就好比给HBITMAP
分配内存以及指定这位图相关的一些信息(跟DC
相关的信息),如位图的宽高,数据大小,但此时数据没有具体取值。就好比一个字符串,我已经知道字符串大小了,但却不知道字符串具体是什么:
如:
char *p = new char[15];//知道字符串大小为15
但此时p
所指向的缓存区,没有具体取值。
而用CreateCompatibleBitmap函数,创建的位图,只是一个空壳子。数据没有赋值,那要怎样给数据赋值呢?
首先得把这个位图句柄选入一个DC
(该DC
必须为CreateCompatibleDC
函数创建的)里,然后再用BitBlt
函数具体给数据赋值就行了。
例子:实时获取屏幕图像
为了方便,在记事本窗口输出图像,自己就不创建窗口了(打开"无标题.txt - 记事本")
#include<windows.h>
int main(){
HWND TextWnd = FindWindow(NULL,"无标题.txt - 记事本");
HDC DeskDC,memDC,TextDC = GetDC(TextWnd);
HWND DeskWnd = ::GetDesktopWindow();
RECT DeskRC,TextRC;
HBITMAP DeskBmp;
::GetClientRect(DeskWnd,&DeskRC);
DeskDC = GetDC(DeskWnd);
DeskBmp = ::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom);
memDC = ::CreateCompatibleDC(DeskDC);
SelectObject(memDC,DeskBmp);
while(1){
StretchBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,DeskRC.right,DeskRC.bottom,SRCCOPY);
GetClientRect(TextWnd,&TextRC);
::StretchBlt(TextDC,0,0,TextRC.right,TextRC.bottom,memDC,0,0,DeskRC.right,
DeskRC.bottom,SRCCOPY);
Sleep(300);
}
return 0;
}
89.GetDIBits 从一个兼容位图里获取位图数据
先来分析一下位图文件信息结构,一个位图由以下四部分组成:
位图文件头(BITMAPFILEHEADER
)//占14字节
位图信息头(BITMAPINFOHEADER
)//占40字节
调色板(LOGPALLETE
)//如果是真彩位图,那该部分没有,直接是位图数据
实际位图数据
而GetDIBits
函数获取的就是实际位图数据这一部分了。
接来看一下BITMAPFILEHEADER
这个结构以及它成员的意思和取值
typedef struct tagBITMAPFILEHEADER {
WORD bfType;//表示文件类型,值必须为0x4d42
DWORD bfSize;//文件的大小
WORD bfReserved1;//保留,必须为0
WORD bfReserved2;//保留,必须为0
DWORD bfOffBits;//位图前三部分所占的字节,真彩色位图为54
} BITMAPFILEHEADER;
上面的成员,只有bfSize
的取值不确定,其它都一样,也就是说,每个真彩位图,这几个成员取值都是一样的.下面的例子可以说明。
读取一个真彩位图的文件信息头。
#include<windows.h>
#include<stdio.h>
int main(){
BITMAPFILEHEADER bfh;
HANDLE hFile =CreateFile("e:\\aa.bmp",GENERIC_READ,0,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
DWORD dwSize;
ReadFile(hFile,(void *)&bfh,sizeof(BITMAPFILEHEADER),&dwSize,0);
printf("bfType:%x\n",bfh.bfType);
printf("bfSize:%d\n",bfh.bfSize);
printf("bfReserved1:%d\n",bfh.bfReserved1);
printf("bfReserved2:%d\n",bfh.bfReserved2);
printf("bfOffbits:%d\n",bfh.bfOffBits);
return 0;
}
再来看一下BITMAPINFOHEADER
这个结构以及它成员的意思和取值
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//本结构大小,为40
LONG biWidth;//位图的宽度,以像素为单位
LONG biHeight;//位图的高度,以像素为单位
WORD biPlanes;//目标设备的级别,必须是1
WORD biBitCount;//每个像素所占的位数,24表示真彩位图
DWORD biCompression;//位图压缩类型,一般为BI_RGB(未经压缩)
DWORD biSizeImage;//实际位图数据所占用的字节数
LONG biXPelsPerMeter;//指定目标设备水平分辨率,单位像素/米,为0
LONG biYPelsPerMeter;//指定目标垂直分辨率真,单位像素/米,为0
DWORD biClrUsed;//指定目标设备实际用到的颜色数,如果该值为0,则用到的颜色数为2的biBitCount方
DWORD biClrImportant;//表示图像中重要的颜色数,如果为0,则所有颜色都是重要的。
} BITMAPINFOHEADER;
调色板(LOGPALLETE
)由于大部分都是针对真彩位图操作,此部分略过
GetDIBits
函数定义:
int GetDIBits(
HDC hdc, //位图兼容的DC
HBITMAP hbmp, //位图句柄
UINT uStartScan, //从哪行开始扫描
UINT cScanLines, //扫描多少行数据
LPVOID lpvBits, //接收数据的缓存区
LPBITMAPINFO lpbi, //真彩位图,此处填位图信息头就行了
UINT uUsage//真彩位图填DIB_RGB_COLORS,表示由R,G,B三色直接构成
);
例子:截屏,并把屏幕图片保存成位图
#include<windows.h>
void ScreenSnap(HBITMAP hBitmap,char *bmpPath,HDC dc);
int main(){
HWND DeskWnd=::GetDesktopWindow();//获取桌面窗口句柄
HDC memDC,DeskDC;
HBITMAP DeskBmp;
RECT DeskRC;
::GetClientRect(DeskWnd,&DeskRC);//获取窗口大小
DeskDC=GetDC(DeskWnd);//获取窗口DC
DeskBmp = ::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom);//兼容位图
memDC = ::CreateCompatibleDC(DeskDC);//兼容DC
SelectObject(memDC,DeskBmp);//把兼容位图选入兼容DC中
BitBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,SRCCOPY);//拷贝DC
ScreenSnap(DeskBmp,"d:\\Screen.bmp",DeskDC);
return 0;
}
void ScreenSnap(HBITMAP hBitmap,char *bmpPath,HDC dc){
DWORD dwSize;
BITMAP bmInfo;
DWORD bmDataSize;
char *bmData;//位图数据
BITMAPFILEHEADER bfh;//位图文件头
GetObject(hBitmap,sizeof(BITMAP),&bmInfo);//根据位图句柄,获取位图信息
bmDataSize = bmInfo.bmWidthBytes*bmInfo.bmHeight;//计算位图数据大小
bmData = new char[bmDataSize];//分配数据
bfh.bfType=0x4d42;
bfh.bfSize=bmDataSize+54;
bfh.bfReserved1=0;
bfh.bfReserved2=0;
bfh.bfOffBits=54;
BITMAPINFOHEADER bih;//位图信息头
bih.biSize=40;
bih.biWidth=bmInfo.bmWidth;
bih.biHeight=bmInfo.bmHeight;
bih.biPlanes=1;
bih.biBitCount=24;
bih.biCompression=BI_RGB;
bih.biSizeImage=bmDataSize;
bih.biXPelsPerMeter=0;
bih.biYPelsPerMeter=0;
bih.biClrUsed=0;
bih.biClrImportant=0;
::GetDIBits(dc,hBitmap,0,bmInfo.bmHeight,bmData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);//获取位图数据部分
HANDLE hFile=CreateFile(bmpPath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,0);//创建文件
WriteFile(hFile,(void *)&bfh,sizeof(BITMAPFILEHEADER),&dwSize,0);//写入位图文件头
WriteFile(hFile,(void *)&bih,sizeof(BITMAPINFOHEADER),&dwSize,0);//写入位图信息头
WriteFile(hFile,(void *)bmData,bmDataSize,&dwSize,0);//写入位图数据
::CloseHandle(hFile);//关闭文件句柄
}
// 第九十个FindWindowEx查找一个父窗口里的子窗口(根据子窗口类名,窗口名),函数返回子窗口句柄
// 函数定义:
HWND FindWindowEx(
HWND hwndParent,//父窗口句柄
HWND hwndChildAfter,//子窗口句柄,指明Z序
LPCTSTR lpszClass,//窗口类名
LPCTSTR lpszWindow//窗口名称
);
第一个参数hwndParent
如果为NULL
,则以桌面窗口为父窗口,此时,这个函数与FindWindow
函数一样,
第二个参数,子窗口句柄,如果为NULL
,则从主窗口的第一Z序开始查找,否则从该子窗口的Z序开始查找。
后面两个个参数跟FindWindow
的两个参数意思一样。
例子:模仿按钮单击
查找一个父窗口下特定的按钮,然后用SendMessage
函数给父窗口发送按钮被单击的消息
假设这个父窗口名为"abc
",其下有一个名为"确定
"的按钮
提示:
控件消息是:WM_COMMAND
,wParam
参数低十六位存储存控件ID号,高十六位存储有具体控件消息(如BN_CLICKED
,单击),lParam
存储控件窗口句柄,控件ID号
可以用GetDlgCtrlID
函数获取,该函数根据控件窗口句柄,获取控件ID号
。
#include<windows.h>
int main(){
HWND wnd = ::FindWindow(NULL,"abc");
HWND ButtonWnd = ::FindWindowEx(wnd,NULL,NULL,"确定");//查找名为"确定"的子窗口句柄
int CtrlID = ::GetDlgCtrlID(ButtonWnd);//获得控件ID
SendMessage(wnd,WM_COMMAND,MAKEWPARAM(CtrlID,BN_CLICKED),LPARAM(ButtonWnd));//发送控件消息
return 0;
}
http://hi.baidu.com/3582077/item/90827a51687894abacc8571b
90.CreateFont 创建一种字体,函数返回字体句柄HFONT
HFONT CreateFont(
int nHeight,//字符高度
int nWidth,//字符宽度,nHeight 和nWidth参数指明字体大小
int nEscapement,//文本显示时的倾斜角度,以字符框左上角为原点,负数表示顺时针旋转,正数逆时针(如2700,旋转270度)
int nOrientation,//字符显示时的倾斜角度。nEscaperment和nOnentation这两个参数一般取0就可以了。
int fnWeight,//字体重量,即字体粗细程序,范围在0到1000之间。填FW_NORMAL就行了
DWORD fdwItalic,//指明字体是否倾斜,true表明是斜体字,false则不是
DWORD fdwUnderline,//指明字体是否具有下划线,true有,false没有。
DWORD fdwStrikeOut,//指明字体是否显示删除线,取值同上。
DWORD fdwCharSet,//指明字符集,填DEFAULT_CHARSET(默认)
DWORD fdwOutputPrecision, //指定输出精度,填OUT_DEFAULT_PRECIS(默认)
DWORD fdwClipPrecision,//指定裁剪精度,填CLIP_DEFAULT_PRECIS(默认)
DWORD fdwQuality,//字符输出质量,填DEFAULT_QUALITY(默认)
DWORD fdwPitchAndFamily,//字符间距,填DEFAULT_PITCH(默认)
LPCTSTR lpszFace//字体名称,如"宋体","黑体".
);
标准创建字体的语句:
HFONTFont = CreateFont(20,20,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,"宋体");
后面的函数将会有例子
91.BeginPath 开启一个路径
需设备DC
作参数,开启一个路径后,再调用画图函数在开启路径的DC
上的进行操作,则没有实质图形,仅仅是路径,这些路径是看不见,但它确实存在。但可以调用其它函数对路径进行操作,如StroekPath
函数,用当前画笔描绘路径。这里还要说明一点,并不是所有的画图函数支持路径的。下面给出支持路径的画图函数:
AngleArc
Arc画弧函数
ArcTo
Chord
CloseFigure
Ellipse画椭圆
ExtTextOut输出文本
LineTo 画直线,与MoveToEx函数结合使用
MoveToEx 画直线
Pie画饼图函数
PolyBezier
PolyBezierTo
PolyDraw
Polygon
Polyline
PolylineTo
PolyPolygon
PolyPolyline
Rectangle画矩形
RoundRect圆色矩形
TextOut输出文本
当不需要再用上面这些函数绘制路径后,就用EndPath
函数结束路径
92.StrokePath 用当前笔描绘路径
函数只有一个参数,DC
句柄。
例子:在一个窗口显示镂空字
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
HDC dc = GetDC(wnd);
HFONT Font = CreateFont(50,50,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,"宋体");//创建字体
HPEN pen = CreatePen(PS_SOLID,1,RGB(255,0,0));//创建红色画笔
SelectObject(dc,pen);//选入画笔
SelectObject(dc,Font);//选入字体
SetBkMode(dc,TRANSPARENT);//设置文本背景颜色为透明
while(1){
BeginPath(dc);//开启路径
TextOut(dc,0,0,"ABC",3);
EndPath(dc);//结束路径
StrokePath(dc);//用当前画笔描绘路径
Sleep(300);
}
return 0;
}
93.SetWindowRgn 设置窗口显示区域
函数定义:
int SetWindowRgn(
HWND hWnd,//要设置的窗口句柄
HRGN hRgn,//区域句柄,设置的窗口显示区域将由区域句柄决定。
BOOL bRedraw//指明窗口是否重画,true重画
);
SetWindowRgn
函数用于设置一个窗口的显示区域,所以必须提供给这个函数一个区域,而该函数的第二个参数hRgn
就指明了这个区域。
区域句柄可以通过CreateRectRgn
函数(创建一个矩形区域)获取。类似的函数还有CreateEllipticRgn
(创建一个圆形区域)。
例子:让一个窗口只显示其四分之一,(什么窗口为例就不需要说了吧)
#include<windows.h>
int main(){
HWND wnd = ::FindWindow(NULL,"无标题.txt - 记事本");
int Width,Height;
RECT rect;
HRGN hRgn;
GetWindowRect(wnd,&rect);//整个窗口,不是客户区域
Width = rect.right-rect.left;
Height = rect.bottom-rect.top;
hRgn = ::CreateRectRgn(0,0,Width/4,Height/4);//有兴趣的朋友可以把这句的CreateRectRgn函数换成CreateEllipticRgn试试
SetWindowRgn(wnd,hRgn,true);
return 0;
}
怎么样,窗口是不是只显示了四分之一,但此时用GetClientRect
函数获得窗口大小是多少呢,窗口的大小依旧没变,跟原来的一样
SetWindowRgn
函数只是设置窗口显示区域,并不是设置窗口大小.
那要如何获得窗口显示区域部分呢,用GetWindowRgn
函数,该函数获取窗口显示区域句柄。
94.CombineRgn 合并两个区域
函数定义:
int CombineRgn(
HRGN hDestRgn, //接收新区域的句柄
HRGN hSrcRgn1,//要合并的区域句柄1
HRGN hSrcRgn2//要合并的区域句柄2
int nCombineMode//指明具体如何合并
);
这里要注意参数hDesRgn
,必须是一个已创建的区域句柄
下面是nCombineMode
参数的取值以及各取值所获取的部分
值 | 说明 |
---|---|
RGN_AND | 两个区域的重叠部分 |
RGN_OR | 组合两个区域 |
RGN_DIFF | hSrcRgn1未重叠的部分 |
RGN_XOR | hSrcRgn1和hSrcRgn2未重叠的部分 |
RGN_COPY | 拷贝hSrcRgn1(hSrcRgn1部分) |
例子:用FillRgn
函数填充两个区域的共同部分
#include<windows.h>
int main(){
HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
HDC dc=GetDC(wnd);
HRGN hRect=CreateRectRgn(0,0,200,200);
HRGN hElliptic=CreateEllipticRgn(100,100,250,250);
HRGN ComRgn=CreateRectRgn(0,0,0,0);
CombineRgn(ComRgn,hRect,hElliptic,RGN_AND );
while(1){
FillRgn(dc,ComRgn,CreateSolidBrush(RGB(255,0,0)));//调用FillRgn函数填充区域
Sleep(300);
}
return 0;
}
95.GetPixel 根据DC
获取一个坐标点的颜色值
该函数第一个参数,是DC
,后两个参数指明坐标点,函数返回该坐标点的颜色值,在这里要说明的是,最好不要直接获取DC
里颜色值,用CreateCompatibleDC
函数创造一个兼容的DC
,然后再用BitBlt
函数把DC
里的数据复制到兼容的DC
里,再用GetPixel
函数获取兼容DC
里的颜色值。这样的话,就不会出什么问题。 如:
HWND wnd = FindWindow(NULL,"无标题.txt- 记事本");
HDC dc = GetDC(wnd);
HDC memDC = CreateCompatibleDC(dc);
HBITMAP bmp;
RECT rect;
GetClientRect(wnd,&rect);
bmp = CreateCompatibleBitmap(dc,rect.right,rect.top);
SelectObject(memDC,bmp);
BitBlt(memDC,0,0,rect.right,rect.top,dc,0,0,SRCCOPY);
COLORREF clr = GetPixel(memDC,10,10);//获取客户区坐标点10.10的颜色值
例子:不规则位图窗口
说明:
要创建一个不规则的位图窗口,首先必须得获得这个不规则位图的区域,是用什么方法呢,大家看一下StretchBlt
函数里的flower1
位图,
花部分是黑色,其它部分是白色,这样我们可以用GetPixel
函数判断哪些像素是黑色,知道了哪些像素是黑色,我就是可以用CreateRectRgn
函数创建一个像素区域的区域句柄,再用CombineRgn
函数把这个区域添加到一个主区域里。这样就可以获取花部分的区域了
代码如下:(以StretchBlt
函数里的两副位图为例)
#include<windows.h>
int main(){
HRGN bmRGN;
HDC dc,memDC;
HWND wnd=FindWindow(NULL,"无标题.txt- 记事本");
HBITMAPhBmp1=(HBITMAP)LoadImage(NULL,"e:\\flower1.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
HBITMAPhBmp2=(HBITMAP)LoadImage(NULL,"e:\\flower2.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
BITMAP bmInfo;
GetObject(hBmp1,sizeof(BITMAP),&bmInfo);
::MoveWindow(wnd,0,0,bmInfo.bmWidth,bmInfo.bmHeight,TRUE);
bmRGN = CreateRectRgn(0,0,0,0);
dc = GetWindowDC(wnd);
memDC = CreateCompatibleDC(dc);
SelectObject(memDC,hBmp1);
for(int y=0;y<bmInfo.bmHeight;y++)
for(int x=0;x<bmInfo.bmWidth;x++){
if(GetPixel(memDC,x,y)==0){//如果是黑色
HRGN pRgn=CreateRectRgn(x,y,x+1,y+1);//创建区域
CombineRgn(bmRGN,bmRGN,pRgn,RGN_OR);//把pRgn添加到bmRGN区域里
}
}
SetWindowRgn(wnd,bmRGN,TRUE);
while(1){
SelectObject(memDC,hBmp1);
StretchBlt(dc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,
0,0,bmInfo.bmWidth,bmInfo.bmHeight,SRCAND);//按位与运算合并DC
SelectObject(memDC,hBmp2);
StretchBlt(dc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,
0,0,bmInfo.bmWidth,bmInfo.bmHeight,SRCPAINT);//按位或运算合并DC
Sleep(300);
}
return 0;
}
运行效果:
对应的函数还有SetPixel
这个是设置一个像素点的颜色值
96.PathToRegion 将一个DC里的路径转换成选区(HRGN
)
函数只有一个参数,是转换路径的DC
,函数返回区域句柄。
例子:设置一个文字窗口
#include<windows.h>
int main(){
HRGN rgn;
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
HFONT Font = CreateFont(60,60,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,"宋体");//创建一种字体
HDC dc = GetDC(wnd);
BeginPath(dc);//请求路径
SelectObject(dc,Font);
SetBkMode(dc,TRANSPARENT);
TextOut(dc,0,0,"文字窗口",8);//形成路径
EndPath(dc);//结束路径
rgn = ::PathToRegion(dc);//将路径转换成选区,rgn接收该区域
SetWindowRgn(wnd,rgn,TRUE);//设置窗口显示区域
return 0;
}
97.GetWindowLong 获取一个窗口的信息
函数定义:
LONG GetWindowLong (HWND hWnd,intnlndex);
第二个参数nIndex指明获取窗口什么信息,它可以有以下取值:(来自百度百科)
值 | 说明 |
---|---|
GWL_EXSTYLE | 获得扩展窗口风格 对应着CreateWindowEx函数的DWORD wExStyle参数 |
GWL_STYLE | 获得窗口风格 对应着CreateWindowEx函数的DWORD dwStyle参数 |
GWL_WNDPROC | 获得窗口过程的地址,对应着WNDCLASS结构的 pfnWndProc成员 |
GWL_HINSTANCE | WNDCLASS结构的hInstance成员 |
GWL_HWNDPAAENT | 如果父窗口存在,获得父窗口句柄。 |
GWL_ID | 获得窗口标识(窗口ID) |
函数的返回值指明窗口信息,依据nIndex取值而定
98.SetWindowLong 设置一个窗口信息
函数定义:
LONG SetWindowLong(HWND hWnd,int nlndex,LONG dwNewLong);
nIndex
参数的解释跟GetWindowLong
函数的nIndex参数一样,dwNewLong
参数指明要设置的信息,跟GetWindowLong
函数的返回值相对应。
例子:去掉一个窗口的标题栏
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
LONG dw = GetWindowLong(wnd,GWL_STYLE);//获取窗口风格
dw ^= WS_CAPTION;//去掉dw的WS_CAPTION(标题)属性
SetWindowLong(wnd, GWL_STYLE,dw); //设置窗口风格
ShowWindow(wnd,SW_MINIMIZE);//最小化窗口
return 0;
}
99.CreateFileMapping 创建一个内存文件映射或共享内存
函数返回文件映射对象句柄
将一个文件映射到内存,对映射的这块内存的读写,就跟磁盘中的文件读写是一样的,由于文件内存映射处理磁盘的文件时,省去了文件I/O操作,因此特别适合处理较大的文件。
函数定义:
HANDLE CreateFileMapping(
HANDLE hFile,//要映射的文件句柄,通过CreateFile函数获得,如果为INVALID_HANDLE_VALUE,则表明创建的是共享内存
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//它指明返回的句柄是否可以被子进程所继承,一般为NULL
DWORD flProtect,//指明映射打开方式,PAGE_READONLY(只读),PAGE_READWRITE(读写)
DWORD dwMaximumSizeHigh,//文件映射的最大长度的高32位
DWORD dwMaximumSizeLow,//文件映射的最大长度的低32位,
LPCWSTR lpName//文件内存映射对象的名称,可以为NULL
);
由于文件的在最大长度可能会超过32位大小,所以这个函数用两个32位变量(dwMaximumSizeHigh和dwMaximumSizeLow
)来表示一个文件的最大长度,当这两个参数的取值都为0时,也就是文件最大长度为0时,就用磁盘文件的实际长度。
100.MapViewOfFile 将一个文件映射对象映射到当前应用程序的地址空间
函数定义:
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,//文件映射对象的句柄,CreateFileMapping函数获得
DWORD dwDesiredAccess,//映射方式,FILE_MAP_WRITE (读写)FILE_MAP_READ(只读)
DWORD dwFileOffsetHigh,//文件中映射起点的高32位地址
DWORD dwFileOffsetLow,//文件中映射起点的低32位地址 ,高低可为0
DWORD dwNumberOfBytesToMap//文件中要映射的字节数。为0映射整个文件映射对象
);
函数返回文件映射在内存中的起始地址。
例子:读写一个文件(假设E盘下有一个名为a.txt的文件,内容为0123456789)
#include<windows.h>
#include<stdio.h>
int main(){
HANDLE hFile = CreateFile("e:\\a.txt", GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//以读写方式打开文件
HANDLE hMap = ::CreateFileMapping(hFile, NULL, PAGE_READWRITE , NULL, NULL,NULL);
LPVOID lpBase = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
for(int i=0;i<10;i++)//读映射内存中的数据
printf("%c ",((char *)lpBase)[i]);
printf("\n");
for(i=0;i<10;i++)//写入数据到映射内存中
((char *)lpBase)[i]=L'A'+i;
return 0;
}
此时再去打开E盘下a.txt文件,是不是里面的内容全变为"A"了。
这里要说明的是,文件打开方式,映射打开方式,映射方式必须是一致的,不能以只读打开文件,又以只写映射打开文件
http://hi.baidu.com/3582077/item/33998ace7814e326a0b50a19
101.OpenFileMapping 获取CreateFileMapping
函数创建的文件映射对象句柄
函数定义:
HANDLE OpenFileMappingW(
DWORD dwDesiredAccess,//取值参考MapViewOfFile函数的dwDesiredAccess参数
BOOL bInheritHandle,//依据CreateFileMapping函数的lpFileMappingAttributes参数而定,不能被新进程继承,为FALSE
LPCWSTR lpName//指明获取哪个文件映射对象句柄,对应着CreateFileMapping函数的lpName参数
);
函数返回文件映射对象句柄。
例子:两进程间传递数据
进程1程序代码:
#include<windows.h>
#include<stdio.h>
int main(){
char Data[11]="0123456789";
HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,11,"ShareMemory");
LPVOID lpBase = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
strcpy((char *)lpBase,Data);//写入数据
while(1)
Sleep(2000);
return 0;
}
进程2程序代码:
#include<stdio.h>
#include<windows.h>
int main(){
HANDLE hMap=::OpenFileMapping(FILE_MAP_WRITE,FALSE,"ShareMemory");
LPVOID lpBase = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0);
printf("ShareMemory Data:%s\n",lpBase);//输出数据
return 0;
}
102.CopyMemory 复制一段内存
函数定义:
VOID CopyMemory(
PVOID Destination, //目的地址
CONST VOID *Source, //源地址
DWORD Length //复制多少字节
);
这个函数跟C语言的memcpy
函数一样,这里仅举个例子:
#include<stdio.h>
#include<windows.h>
int main(){
DWORD dwDestin=0;
DWORD dwSource=120;
CopyMemory((void *)&dwDestin,(void *)&dwSource,sizeof(DWORD));
printf("%d\n",dwDestin);
return 0;
}
103.SetParent 设置(更改)一个子窗口的父窗口
该函数有两个参数,第一个是子窗口的句柄,第二个是父窗口的句柄。
如果父窗口句柄为空,则以桌面窗口作为父窗口
例子:把记事本里状态栏的父窗口改为编辑框窗口
用spy++查看记事本编辑框窗口的类名为"Edit",状态栏为"msctls_statusbar32"(可能会有差异,具体可以用Spy++查看)
#include<windows.h>
int main(){
RECT rect;
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
HWND EditWnd = FindWindowEx(wnd,NULL,"Edit",NULL);//查找获取编辑框窗口句柄
HWND StatusWnd = FindWindowEx(wnd,NULL,"msctls_statusbar32",NULL);//查找获取状态栏窗口句柄
::SetParent(StatusWnd,EditWnd);//设置状态栏父窗口
GetClientRect(StatusWnd,&rect);//获取状态栏客户区大小
//移动状态栏窗口,如果编辑框窗口不是状态栏的父窗口,则状态栏是移不进编辑框窗口的
MoveWindow(StatusWnd,0,0,rect.right,rect.bottom,TRUE);
return 0;
}
104.OpenClipboard 打开一个剪切板
剪切板是什么意思呢,可以把它看做一块内存,这块内存可以被任何进程调用相关函数进行读写。事实上,像我们平常的复制粘贴就是在读写这块内存,比如用选中一段文字,然后右击,选择复制,这时对应程序就会调用OpenClipboard
函数打开剪切板,往里面写入数据,
当不再需要操作剪切板的时候就调用CloseClipboard
函数关闭剪切板,剪切板不能同时被多个程序打开,也就是说如果其它程序已经调用OpenClipboard
打开剪切板了,并且没有调用CloseClipboard
关闭剪切板的话,那么其它程序调用OpenClipboard
就会失败,这一点从下面的例子可以说明。
这个函数只有一个参数,是一个窗口句柄,也就是具体在哪个窗口里进行复制粘贴操作(剪切也算),如果为NULL
,则跟调用这个函数的进程相关联。
例子:让粘贴复制失效
#include<windows.h>
int main(){
OpenClipboard(NULL);
while(1) Sleep(1000);
}
由于这个程序打开了剪切板却并不关闭它,导致了其它程序无法调用OpenClipboard
函数打开剪切板,无法打开剪切板,自然也就无法复制粘贴数据了
105.SetClipboardData 写入一段数据到剪切板(设置剪切板的数据)
HANDLE SetClipboardData( UINT uFormat,HANDLE hMem);
像我们使用windows的复制粘贴的时候,不仅能复制文字,也能复制文件,图片等。所以uFormat
指明要写入的是什么数据。
uFormat的常用取值如下:
值 | 说明 |
---|---|
CF_UNICODETEXT | 含有Unicode文字的内存块 |
CF_BITMAP | 与设备相关的位图格式。位图是通过位图句柄传送给剪切板的 |
CF_TEXT | 以NULL结尾的ANSI字符集字符串 |
第二个指向存储要写入的数据内存,注意,存储数据的内存必须是在堆中分配的。
例子1:写入ASCII字符串到剪切版
#include<windows.h>
#include<string.h>
int main(){
char *pData = new char[15];//在堆中申请内存
strcpy(pData,"ABCDEFG");
OpenClipboard(NULL);//打开剪切板
EmptyClipboard();//清空剪切板的数据
SetClipboardData(CF_TEXT,pData);//往剪切板里写入数据
CloseClipboard();//关闭剪切板
delete pData;
return 0;
}
随便到可以输入文字的地方,右击->粘贴看写入是否成功
例子2:写入宽字符串(Unicode
)到剪切板
#include<string.h>
#include<windows.h>
int main(){
WCHAR *pData=new WCHAR[15];
WCHAR str[15]=L"ABC我们D";//用L把ASCII转换成Unicode
CopyMemory((void *)pData,(void *)str,sizeof(str));//相当于ASCII字符串处理函数strcpy
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT,pData);
CloseClipboard();
return 0;
}
106.GlobalAlloc 从堆中分配一定数目的字节数
HGLOBAL WINAPI GlobalAlloc(
UINT uFlags,//分配属性(方式)
DWORD dwBytes//分配多少字节
);
uFlags常用取值:
值 | 说明 |
---|---|
GMEM_FIXED | 分配固定的内存,返回值是一个指针 |
GMEM_MOVEABLE | 返回值是一个内存句柄,可用GlobalLock函数根据一个内存句柄,返回内存句柄对应的首地址,并锁定内存中指定的内存块,对应的函数是:GlobalUnlock |
对应的释放内存函数是GlobalFree
,这里还是说明一个函数GlobalSize
,这个根据内存句柄获得对应内存块大小
例子:GlobalAlloc
函数应用
#include<stdio.h>
#include<windows.h>
int main(){
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE,8);//按GEME_MOVEABLE方式分配内存
DWORD dwSize = GlobalSize(hMem);//获取内存块大小
int *pNumber = (int *)GlobalLock(hMem);//把内存句柄转换成内存首地址
pNumber[0] = 5;pNumber[1] = 6;
printf("内存块大小%d\n%d,%d\n",dwSize,pNumber[0],pNumber[1]);
GlobalFree(hMem);//释放内存
return 0;
}
107.GetClipboardData 该函数获得剪切板里的数据,函数返回剪切板内存句柄
该函数只有一个参数,指明获取的是什么类型的数据,跟SetClipboardData
函数的uFormat
参数相对应,这里有一个问题,如何知道剪切板存储的是什么类型数据呢,用IsClipboardFormatAvailable
函数,这个函数只有一个参数,如:
IsClipboardFormatAvailable(CF_TEXT);
这个语句用于判断当前剪切板有没有含有CF_TEXT
数据,如果有的话,返回真(TRUE
),否返回假。这个函数不用事先打开剪切板,也可以正常调用。
注意如果如果剪切板的数据是CF_UNICODETEXT
形式的,用CF_TEXT
类型打开也是可以的,因为windows系统会自动进行转换。
例子:获取剪切板里的CF_TEXT
数据(先随便复制一段文字)
#include<stdio.h>
#include<windows.h>
int main(){
char *pGlobal;
HGLOBAL hGlobal;
if(IsClipboardFormatAvailable(CF_TEXT)){//判断剪切板里是否有CF_TEXT数据
::OpenClipboard(NULL);
hGlobal = GetClipboardData(CF_TEXT);//以CF_TEXT方式打开剪切板
pGlobal = (char *)GlobalLock(hGlobal);//获得剪切板内存的首地址,直接转换也可以,如(char *)hGlobal
printf("%s\n",pGlobal);//输出剪切板里的文字
GlobalUnlock(hGlobal);
CloseClipboard();
}
return 0;
}
108.Shell_NotifyIcon 在任务栏里注册图标(任务栏托盘菜单)
(PS:今天看了一下以前写的这个函数,我描述这个函数为托盘菜单,今天来看,似乎压根就跟菜单没关系,我想,应该是那时候是在MFC里看到这个函数,那个例子是做一个托盘菜单,我就用了托盘菜单了,却没有实现托盘菜单的功能,不过,并不影响理解这个函数用法,以及它的意思,我也较烦)
函数定义:
Shell_NotifyIcon(DWORD dwMessage, PNOTIFYICONDATAWlpData);
要想注册一个托盘菜单,首先你得提供一些信息,如在任务栏要显示的图标,系托盘菜单属于哪个窗口(严格来说,是托盘菜单产生的消息,发送给哪个窗口)
而这些信息就由Shell_NotifyIcon
函数的第二个参数lpData
指明。它是一个NOTIFYICONDATAW
结构指针。
NOTIFYICONDATAW
结构定义及各成员意思如下:
typedef struct _NOTIFYICONDATAW {
DWORD cbSize;//指明NOTIFYICONDATA结构大小,用sizeof(NOTIFYICONDATA)给它赋值。
HWND hWnd;//窗口句柄,由鼠标在托盘图标产生的消息将发送给这个窗口
UINT uID;//标识托盘菜单的唯一ID号。
UINT uFlags;//指明结构中哪些成员有效,可以是NIF_ICON(对应hIcon), NIF_MESSAGE(对应uCallbackMessage),
//NIF_TIP(对应 szTip),可以用|按位或运算符结合在一起。
UINT uCallbackMessage;//自定义消息,鼠标在托菜图标上产生的行为将发送这个消息给hWnd成员对应的窗口
HICON hIcon;//图标句柄,
WCHAR szTip[64];//鼠标停留在托盘图标上产生的提示消息内容
} NOTIFYICONDATA, *PNOTIFYICONDATA;
dwMessage
参数指明要进行的操作,常用取值有
NIM_ADD
(添加托盘菜单)
NIM_DELETE
(删除一个托盘菜单)
NIM_MODIFY
(修改一个托盘菜单)
了解了上面些我们来简单的在任务栏上显示一个托盘菜单的图标,其它什么也不做。
假设e盘下有一个名为"i.ico
"的图标
代码如下:
#include<windows.h>
int main(){
NOTIFYICONDATA notify;//定义一个托盘菜单信息结构
notify.cbSize=sizeof(NOTIFYICONDATA);//设置结构大小
notify.hIcon=(HICON)LoadImage(NULL,"e:\\i.ico",IMAGE_ICON,48,48,LR_LOADFROMFILE);//设置图标句柄
notify.uFlags=NIF_ICON;//指明哪些成员有效
::Shell_NotifyIcon(NIM_ADD,¬ify);//添加托盘菜单
while(1){
Sleep(1000);//避免程序退出,自动卸载托盘菜单
}
return 0;
}
怎么样任务栏是不是有一个图标显示了,但此时,用鼠单击,或双击任务上的图标都没反应。
如果想响应鼠标操作消息的话,得使 NOTIFYICONDATA
结构的uCallbackMessage
成员有效,这样当鼠标在托盘菜单图标上单击或双击后,就会产生uCallbackMessage
成员对应的消息。而且还必须指定窗口句柄,不然产生了这个消息,发送给哪个窗口呢。
例子2:完整的托盘菜单应用
完整应用,必须得有自己的窗口,新建一个 “Win32 Application"工程。如果对用API函数创建窗口不了解的话,可以参考常用函数CreateWindow
.
#include<windows.h>
#include<string.h>
//在WM_USER的基础上定义一个消息类型,用于托盘与窗口之间的消息传递
#define WM_TRAYMESSAGE WM_USER+25
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);//函数声明
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow){
MSG msg;//消息结构类型
HWND hwnd; //用以存储CreateWindow函数所创建的窗口句柄
WNDCLASS wndcls; //定义一个存储窗口信息WNDCLASS变量
wndcls.cbClsExtra=0; //默认为0
wndcls.cbWndExtra=0; //默认为0
wndcls.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //背景画刷
wndcls.hCursor=LoadCursor(NULL,IDC_ARROW); //光标
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); //窗口图标
wndcls.hInstance=hInstance; //应用程序实例句柄由WinMain函数传进来
wndcls.lpfnWndProc=WinSunProc; //窗口消息处理函数
wndcls.lpszClassName="windowclass"; //窗口类名
wndcls.lpszMenuName=NULL; //窗口菜单名,没有菜单,为NULL
wndcls.style=CS_HREDRAW | CS_VREDRAW;//窗口类型,CS_HREDRAW和CS_VERDRAW 表明
//当窗口水平方向垂直方向的宽度变化时重绘整个窗口
RegisterClass(&wndcls); //把窗口信息提交给系统,注册窗口类
hwnd=CreateWindow("windowclass","托盘菜单应用",
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//创建窗口
NOTIFYICONDATAnotify;//定义一个托盘菜单信息结构
notify.cbSize=sizeof(NOTIFYICONDATA);//设置结构大小
notify.hIcon=(HICON)LoadImage(NULL,"e:\\i.ico",IMAGE_ICON,48,48,LR_LOADFROMFILE);//设置图标句柄
notify.hWnd=hwnd;//接收消息的窗口句柄
strncpy(notify.szTip,"托盘菜单",sizeof("托盘菜单"));
notify.uCallbackMessage=WM_TRAYMESSAGE;//托盘菜单将发送这个消息给窗口
notify.uFlags=NIF_ICON|NIF_MESSAGE|NIF_TIP;//指明哪些成员有效
::Shell_NotifyIcon(NIM_ADD,¬ify);//添加托盘菜单
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口创建完了,显示它
UpdateWindow(hwnd); //更新窗口,让窗口毫无延迟的显示
while(GetMessage(&msg,NULL,0,0)){//获取消息
TranslateMessage(&msg); //此函数用于把键盘消息(WM_KEYDOWN,WM_KEYUP)转换成字符消息WM_CHAR
DispatchMessage(&msg); //这个函数调用窗口过程处理函数,并把MSG里的信息处理后传给过程函数的四个参数
}
::Shell_NotifyIcon(NIM_DELETE,¬ify);//卸载托盘菜单
return 0;
}
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
switch(uMsg){
case WM_SYSCOMMAND:
if(wParam==SC_MINIMIZE)//最小化了窗口{
ShowWindow(hwnd,SW_HIDE);//隐藏窗口
else
return DefWindowProc(hwnd,uMsg,wParam,lParam);
break;
case WM_TRAYMESSAGE:
if(lParam==WM_LBUTTONDBLCLK)//双击托盘菜单图标,鼠标消息在lParam参数里
ShowWindow(hwnd,SW_RESTORE);//显示窗口
break;
case WM_CLOSE://用户关闭了窗口
DestroyWindow(hwnd);//销毁窗口,并发送WM_DESTROY消息
break;
case WM_DESTROY://如果窗口被销毁
PostQuitMessage(0);//让进程退出
break;
default://未处理的消息通过DefWindowProc函数交给系统处理
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
109.FindResource 从应用程序模块句柄找出一个资源
资源就是MFC里的资源,像位图,图标,对话框等这些资源都包含在exe文件里,如果看过PE文件结构分析及应用的话,应该会容易理解。
这个函数就是定位一个资源在模块里的位置。
函数定义:
HRSRC FindResource(HMODULE hModule,//应用程序模块句柄,NULL表示本模块句柄
LPCTSTR lpName,//资源名
LPCTSTR lpType//资源类型
);
其中第一个参数hModule应用程序模块句柄,自身模块填NULL就行,如果想获取其它正在运行的应用程序模块,就用GetModuleHandle
函数。
资源名就像资源的ID号一样,比如一个MFC的一个资源ID号是147,那么此参数应该是这样的"#147
",或用MAKEINTRESOURCE
宏转换
如:MAKEINTRESOURCE(147)
lpType
用于表明资源类型,它是对话框?菜单?位图?还是自定义资源。。。
常用取值如下:
#define RT_CURSOR MAKEINTRESOURCE(1) //光标
#define RT_BITMAP MAKEINTRESOURCE(2) //位图
#define RT_ICON MAKEINTRESOURCE(3)//图标
#define RT_MENU MAKEINTRESOURCE(4)//菜单
#define RT_DIALOG MAKEINTRESOURCE(5)//对话框
#define RT_STRING MAKEINTRESOURCE(6)//字符串
#define RT_FONTDIR MAKEINTRESOURCE(7)
#define RT_FONT MAKEINTRESOURCE(8)//字体
#define RT_ACCELERATOR MAKEINTRESOURCE(9)
#define RT_RCDATA MAKEINTRESOURCE(10)
#define RT_MESSAGETABLE MAKEINTRESOURCE(11)
#define DIFFERENCE 11
#define RT_GROUP_CURSOR MAKEINTRESOURCE((DWORD)RT_CURSOR + DIFFERENCE)//光标组
#define RT_GROUP_ICON MAKEINTRESOURCE((DWORD)RT_ICON + DIFFERENCE)//图标组
#define RT_VERSION MAKEINTRESOURCE(16)
#define RT_DLGINCLUDE MAKEINTRESOURCE(17)
110.LoadResource 根据资源指向句柄(HRSRC
)把资源装载到内存。
函数返回资源所在内存句柄(HGLOBAL
),可以用LockResource
根据内存句柄,获取内存首地址
函数定义:
HGLOBAL LoadResource(HMODULE hModule,HRSRC hResiInfo);
hModule
参数同FindResource
对应的参一样,模块句柄,hResiInfo
参数对应FindResource
函数返回的值
LockResource
函数定义:
LPVOID LockResource(HGLOBAL hResDate);
hResDate
参数对应LoadResource
函数返回的值
http://hi.baidu.com/3582077/item/eed4cc5fa6fff43795eb0508
111.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<windows.h>
int main(){
HANDLE hFile;
DWORD dwSize;
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';
hFile = CreateFile(pathName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);//创建文件
WriteFile(hFile,pBuffer,dllSize,&dwSize,NULL);//写入数据
return 0;
}
运行,然后把生成的EXE程序随便复制到一个目录下,双击运行一下。看不是在同一目录下释放出了一个DLL文件。
这可以应用到任何文件上,像可以让一个EXE文件里包含两个EXE文件,当执行这个EXE文件后,会释放出两个EXE文件,然后删除自身。
112.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
里面,关于调用动态链接库里的函数请参考http://hi.baidu.com/3582077/blog/item/8471dcdd82e15e205882dd68.html,本例采用第二种方法用LoadLibrary
,这个函数的用法不懂的可以参考 API常用函数第四十九个。
这里的窗口依旧以无标题记事本为例
#include<windows.h>
int main(){
int value = 0;
HINSTANCE hInst;
typedef BOOL (WINAPI *FSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
FSetLayeredWindowAttributes SetLayeredWindowAttributes;
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
SetWindowLong(wnd,GWL_EXSTYLE,GetWindowLong(wnd,GWL_EXSTYLE)|0x80000);
hInst = LoadLibrary("User32.DLL");
SetLayeredWindowAttributes =(FSetLayeredWindowAttributes)GetProcAddress(hInst,"SetLayeredWindowAttributes");
while(1){
if(value == 256) value = 0;
SetLayeredWindowAttributes(wnd,RGB(0,0,0),value,0x2);//0x2 让第二个参数无效。
value++;
Sleep(50);
}
return 0;
}
113.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 Windowswith MFC)
R2_NOPdest = destR2_NOTdest = NOT destR2_BLACK dest =BLACKR2_WHITEdest = WHITER2_COPYPEN dest = srcR2_NOTCOPYPEN dest = NOTsrcR2_MERGEPENNOT dest = (NOT dest) OR srcR2_MASKPENNOT dest = (NOT dest) ANDsrcR2_MERGENOTPENdest = (NOT src) OR destR2_MASKNOTPEN dest = (NOT src) ANDdestR2_MERGEPEN dest = dest OR srcR2_NOTMERGEPEN dest = NOT (dest ORsrc)R2_MASKPEN dest = dest AND srcR2_NOTMASKPENdest = NOT (dest ANDsrc)R2_XORPENdest = src XOR destR2_NOTXORPENdest = NOT (src XOR dest)
例子:以R2_MASKPEN
画图模式在窗口里画一个矩形
#include<windows.h>
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;
}
运行效果:
用另外几种模式试一下,看看结果又会有什么不同。
114.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)
所画的矩形应该是1000.1mm宽,1000.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<windows.h>
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建一个
五厘米的宽的图像,对比一下。
115.DPtoLP 将设备坐标转换成逻辑坐标
其实就是将MM_TEXT
模式(以像素为单位)坐标点转换成当前模式下的坐标,比如在MM_TEXT
模式下150,150这个坐标点位置在MM_LOMETRIC
模式里是在哪里。也就是说50*0.1mm是多少像素。
先来看下面这个例子,将150,150这个在MM_TEXT
模式下的坐标点转换成MM_LOMETRIC
模式下对应的坐标点.
#include<windows.h>
#include<iostream.h>
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指针,第三个指明数组数量。用于多个转换
//如:POINTpt[2] = {{100,100},{200,200}};DPtoLP(dc,pt,2);
cout<<pt.x<<","<<pt.y<<endl;
return 0;
}
我这儿的输出结果是 469,-469
具体依据电脑设置而定,也就是说在MM_LOMETRIC
模式下469,-469这个坐标点和MM_TEXT
模式下100,100这个坐标点的位置是相同的。
例子:在一个窗口里的两种模式下画同一长度的线
#include<windows.h>
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);//记得要设置模式。
http://hi.baidu.com/3582077/item/eebe0ae316019bafcf2d4f08
116.SetViewportOrgEx 将窗口原点(0,0)映射到设备坐标点X,Y
函数定义:
BOOL SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINTlpPoint);
hdc
是要映射的设备上下文,X,Y是要映射的坐标,lpPoint
是窗口原点原来所映射的坐标,不需获取可为NULL
。
比如这样一个语句:(假设hdc
的设备模式为MM_TEXT
)那么调用下面语句后
SetViewportOrgEx(hdc,200,200,NULL);
窗口的坐标就变成下面这样了:
看例子:
#include<windows.h>
int main(){
HWND wnd = FindWindow(NULL,"无标题.txt - 记事本");
HDC hdc = GetDC(wnd);
::SetViewportOrgEx(hdc,200,200,NULL);//设置坐标点200,200为窗口原坐标点0,0
while(1){
Rectangle(hdc,0,0,100,100);
Rectangle(hdc,0,0,-100,-100);
Sleep(300);
}
return 0;
}
117.etWindowOrgEx 将设备坐标点X,Y映射到窗口原点0,0
函数定义:
BOOL SetWindowOrgEx(HDC hdc, int X, int Y, LPPOINTlpPoint);
跟SetViewportOrgEx
的映射正好相反,这个函数的参数解释参照SetViewportOrgEx
。
这里仅给出调用
SetWidnowOrgEx(hdc,200,200,NULL);
语句后的窗口坐标图。当然参数也可以为负数,可自行推算窗口坐标点。
118.SetScrollPos 设置滚动条位置
函数定义:
int SetScrollPos(
HWND hWnd,//滚动条所在窗口句柄
int nBar,//滚动条类型,参照SetScrollInfo函数
int nPos,//滚动条位置
BOOL bRedraw);//是否重画滚动条
例子:参考SetScrollInfo
函数的例子。
119.SetScrollInfo 设置滚动条信息
函数定义:
int SetScrollInfo(HWND hWnd;//滚动条所在的窗口句柄
int fnBar,//滚动条类型,SB_VERT垂直滚动条,SB_HORZ水平滚动条
LPSCROLLINFO lpsi,//滚动条各项信息结构体,后面会有介绍
BOOL fRedraw);//指明是否重画滚动条
SetScrollInfo
函数的第三个参数是一个SCROLLINFO
结构体指针,关于该结构体的解释如下:
typedef struct tagSCROLLINFO{
UINT cbSize;//指明SCROLLINFO结构体大小,赋值sizeof(SCROLLINFO)
UINT fMask;//指明nMin-nMax、nPage、nPos哪些成员有效,分别对应SIF_RANGE、SIF_PAGE、SIF_POS。
//为SIF_DISABLENOSCROLL表明禁用滚动条。
int nMin;//滚动条范围最小值
int nMax;//滚动条范围最大值
UINT nPage;//页大小(跟滚动条大小相关联)
int nPos;//滚动条位置
int nTrackPos;//这个成员在GetScrollInfo函数中有用。用于接收滚动条位置
} SCROLLINFO, FAR *LPSCROLLINFO;
这里要说明的是,为了避免不必要的麻烦,滚动条大小,滚动条范围必须和可见窗口以及实际窗口(视图?)大小一致。
也就是说当你可见窗口大小是300的时候,那么滚动条页大小也必须是300。如果实际窗口大小是2000的时候,那么滚动条范围就是01999,分别是最小值和最大值。而滚动条可移动范围也就是位置是在01700,因为当滚动条位置是0的时候,显示窗口是0-300,而位置是1700的时候刚好显示完窗口也就是1700~2000。滚动条位置是以滚动条上边为准。
要想让一个窗口具有滚动条风格,在调用CreateWindow
函数时在第三个参数里指明就行了,在CreateWindow
函数里也解释过,WS_VSCROLL
指明窗口具有垂直滚动条风格,而WS_HSCROLL
是水平滚动条。
关于滚动条消息的解释(WM_VSCROLL
垂直,WM_HSCROLL
水平)
下面的消息码存存于wParam
参数里:
值 | 说明 |
---|---|
SB_LINEUP | 滚动条上面的箭头按钮被单击了一下,水平滚动条对应的是左边的按钮 |
SB_LINEDOWN | 滚动条下面的箭头按钮被单击了下,水平滚动条对应的是右边的按钮 |
SB_PAGEUP | 滚动条滑柄与上面箭头按钮之间的区域被单击了一下,水平滚动条是左边之间 |
SB_PAGEDOWN | 滚动条滑柄与下面箭头按钮之间的区域被单击了一下,水平滚动条是右边之间。 |
SB_ENDSCROLL | 鼠标离开了滚动条。 |
SB_THUMBTRACK | 滚动条滑柄被拉动 |
SB_THUMBPOSITION | 滚动条滑柄被拉完后,鼠标左键抬起时。 |
例子:垂直滚动条窗口应用
水平滚动条的使用方法,跟这个大同小异,所以就不加放进来了,徒增负担,这里面使用了一个最关键的函数SetWindowOrgEx
,映射窗口原点,时时根据滚动条位置设置对应的窗口原点。以保证当前显示窗口正确的部分。每次有滚动信息产生的时候,都重画了窗口,这个方法很原始。以后讲到ScrollWIndow
函数时会有改进的,还有本例部分参考了
Programing Windows with MFC里面的内容,只不过那里面是用类来实现的,而我这个是完全是用API来实现的,包括创建窗口,消息处理。
好吧,新建一个WIN32 工程,下面是代码:
#include<windows.h>
#include<stdio.h>
#define LINESIZE 8
int nVScrollPos = 0,nVPageSize,nViewHeight = 2000;
LRESULT CALLBACK WinSunProc(HWND wnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow){
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon = LoadIcon(NULL,IDI_ERROR);
wndcls.hInstance = hInstance;
wndcls.lpfnWndProc = WinSunProc;
wndcls.lpszClassName = "windowclass";
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls);
HWND wnd;
wnd = CreateWindow("windowclass","first windows",
WS_OVERLAPPEDWINDOW^WS_SIZEBOX^WS_MAXIMIZEBOX|WS_VSCROLL,300,200,600,400,
NULL,NULL,hInstance,NULL);
RECT rect;
GetClientRect(wnd,&rect);
nVPageSize = rect.bottom;//可见窗口高度
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.nMin = 0;
si.nMax = 1999;//窗口实际大小是2000
si.nPage = nVPageSize;//可见窗口高度和页大小设置一致
si.nPos = 0;
si.fMask = SIF_ALL;
::SetScrollInfo(wnd,SB_VERT,&si,FALSE);//设置滚动条信息
ShowWindow(wnd,SW_SHOWNORMAL);
UpdateWindow(wnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
};
LRESULT CALLBACK WinSunProc(HWND wnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
char str[20];
PAINTSTRUCT ps;
HDC hdc;
int y,nDelta;
int nScrollPos,nMaxPos;
switch(uMsg){
case WM_VSCROLL:
switch(LOWORD(wParam)){//wParam的低二字节存储有滚动条消息码。
case SB_LINEUP:
nDelta =- LINESIZE;//负数是向上滚动 LINESIZE 是滚动大小8
break;
case SB_PAGEUP:
nDelta =- nVPageSize;//滚动一页,也就是可见窗口高度
break;
case SB_THUMBTRACK:
//wParam的高二字节储存有滚动条的位置
nDelta = (short int)HIWORD(wParam)-nVScrollPos;
break;
case SB_PAGEDOWN:
nDelta = nVPageSize;
break;
case SB_LINEDOWN:
nDelta = LINESIZE;
break;
default:
return 0;
}
//计算新位置
//有这样一种情况,当滚动位置是5时,再向上滚动8个点,那是不可能的,所以要计算。
nScrollPos = nVScrollPos+nDelta;
nMaxPos = nViewHeight-nVPageSize;
if(nScrollPos<0)
nDelta =- nVScrollPos;
else if(nScrollPos>nMaxPos)
nDelta = nMaxPos-nVScrollPos;
if(nDelta != 0){
nVScrollPos += nDelta;
SetScrollPos(wnd,SB_VERT,nVScrollPos,TRUE);//设置新位置
::InvalidateRect(wnd,NULL,TRUE);//刷新窗口
}
return 0;
case WM_PAINT:
hdc = BeginPaint(wnd,&ps);
::SetWindowOrgEx(hdc,0,nVScrollPos,NULL);
for(y = 0;y<2000;y += 20){
MoveToEx(hdc,0,y,NULL);
LineTo(hdc,600,y);
sprintf(str,"%d:",y);
TextOut(hdc,0,y+2,str,strlen(str));
}
return 0;
case WM_CLOSE:
::DestroyWindow(wnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(wnd,uMsg,wParam,lParam);
}
运行效果:
http://hi.baidu.com/3582077/item/1546021772fbdadebf90423e
120.SQLConnect 连接数据源(ODBC API)
创建数据源有两种方法,一种用数据源ODBC
管理器创建,一种用函数SQLConfigDataSource
创建,先来说一下ODBC
管理器创建,了解一些概念,首先用Access
创建一个数据库(mdb
文件),然后打开ODBC
管理器,选择开始->
设置->
控制面板->
管理工具->
数据源(ODBC
)。
程序窗口如下:
点“添加”按钮,添加一个数据源,到时候就可以用SQLConnect
连接你所创建的数据源了。点“添加”按钮,弹出的窗口如下:
因为要连接的数据库文件是Access
创建的,所以驱动程序选择MisrosoftAccess Driver (*.mdb)
,点完成
(如果连接的是Sql server
数据库,驱动程序选择“SQL Server
")。
。给数据源取一个名称,描述信息,如下:
记住数据源的名称,SQLConnect
函数的第二个参数填的就是数据源名称。填完上面信息后,点击“选择”按钮,选择数据库文件。如下图:
一路点确定,完成,就可以了,创建了数据源我们就用SQLConnect
函数来连接它吧。
SQLConnect
函数定义:
SQLRETURN SQL_API SQLConnect(SQLHDBC ConnectionHandle,//数据源连接需要的连接变量,可用SQLAllocEnv创建
SQLCHAR *ServerName, //数据源名称
SQLSMALLINT NameLength1,//ServerName参数的长度,取SQL_NTS就行了。
SQLCHAR *UserName, //用户名,没有为NULL
SQLSMALLINT NameLength2,//用户名字符串长度,可取SQL_NTS。没有为0
SQLCHAR *Authentication, //用户密码
SQLSMALLINT NameLength3);//用户密码字符串长度
连接数据源的例子:
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
int main(){
HENV henv;//数据据源连接需要的环境变量
HDBC hdbc;//数据源连接需要的连接变量
RETCODE rcode;
//创建环境句柄
SQLAllocEnv(&henv);
//创建连接句柄
SQLAllocConnect(henv,&hdbc);
//上面的语句是固定的,记住就行了。
//连接数据源
rcode = ::SQLConnect(hdbc,(PUCHAR)"MyODBC",SQL_NTS,NULL,0,NULL,0);
if(rcode == SQL_SUCCESS)
cout<<"连接数据源成功!"<<endl;
else
cout<<"连接失败!"<<endl;
return 0;
}
连接数据库,也并非得通过连接数据源的方法实现,像SQLDriverConnect
函数不使用 ODBC
数据源进行连接的,以后会介绍这个函数。
121.SQLExecDirect 执行SQL语句 (ODBC API)
函数定义:
SQLRETURN SQL_API SQLExecDirect(SQLHSTMT StatementHandle,//语句句柄,SQLAllocStmt函数可分配
SQLCHAR *StatementText,/*SQL语句*/SQLINTEGER TextLength);//SQL语句字符串长度,可为SQL_NTS
一个执行SQL语句的例子:
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
int main(){
HENV henv;//数据据源连接需要的环境变量
HDBC hdbc;//数据源连接需要的连接变量
HSTMT hstmt;//语句变量
RETCODE rcode;
//创建环境句柄
SQLAllocEnv(&henv);
//创建连接句柄
SQLAllocConnect(henv,&hdbc);
//上面的语句是固定的,记住就行了。
//连接数据源
rcode = ::SQLConnect(hdbc,(PUCHAR)"MyODBC",SQL_NTS,NULL,0,NULL,0);
if(rcode == SQL_SUCCESS)
cout<<"连接数据源成功!"<<endl;
else
cout<<"连接失败!"<<endl;
//分配句柄
::SQLAllocStmt(hdbc,&hstmt);
LPSTR pszSQL = new char[52];
//假设数据库里表名为BookInfo,里面有BookPrice和BookName列。
strcpy(pszSQL,"Update BookInfo set BookPrice = 1000 whereBookName = 'VC++'");
//执行SQL语句
rcode = ::SQLExecDirect(hstmt,(UCHAR *)pszSQL,SQL_NTS);
if(rcode == SQL_SUCCESS){
cout<<"执行SQL语句成功!"<<endl;
}
else{
cout<<"执行失败!"<<endl;
}
::SQLDisconnect(hdbc);//解除连接
return 0;
}
122.SQLConfigDataSource 配置数据源
函数定义:
BOOL SQLConfigDataSource(
HWND hwndParent,//创建数据源的父窗口句柄,为NULL不显示创建数据源对话框。
WORD fRequest,//指明对数据源进行什么操作,创建、修改、删除等。
LPCSTR lpszDriver,//驱动程序名,对应上面创建新数据源对话框里的驱动程序名称。
LPCSTR lpszAttributes);//此字符串以双'\0'结尾,包含数据各项属性,如用户ID,数据库文件路径,数据源名称等,项属性以'\0'间隔
fRequest参数的可选值信息如下:
值 | 说明 |
---|---|
ODBC_ADD_DSN | 创建数据源 |
ODBC_CONFIG_DSN | 配置或者修改已经存在的数据源 |
ODBC_REMOVE_DSN | 删除已经存在的数据源 |
删除数据源的代码:
SQLConfigDataSource(NULL,ODBC_REMOVE_DSN,"MicrosoftAccess Driver (*.mdb)","DSN = MyODBC\0\0");
值 | 说明 |
---|---|
ODBC_ADD_SYS_DSN | 创建系统数据源 |
ODBC_CONFIG_SYS_DSN | 配置或者修改已经存在的系统数据源 |
ODBC_REMOVE_SYS_DSN | 删除已经存在的系统数据源 |
ODBC_REMOVE_DEFAULT_DSN | 删除缺省的数据源 |
lpszAttributes参数,各属性名如下:
值 | 说明 |
---|---|
DSN | 数据源的名称 |
DBQ | 数据库路径 |
DEFAULTDIR | 数据库缺省路径,也就是DBQ只填一个文件名,那么系统要到哪个目录去找这个文件 |
DESCRIPTION | 说明,可随便赋值,就是上面的那个"Thisis my ODBC" |
SERVER | 服务器名称 (SQL Server) |
DATABASE | 数据库名称,如master,model |
一个创建数据源的例子:
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
#include <odbcinst.h>//SQLConfigDataSource函数头文件
int main(){
BOOL Result;
Result = SQLConfigDataSource(NULL,ODBC_ADD_DSN,"Microsoft Access Driver(*.mdb)",
"DSN = DsAccess\0DBQ = d:\\book.mdb\0\0");
if(Result == TRUE)
cout<<"数据源创建成功!"<<endl;
else
cout<<"创建失败!"<<endl;
return 0;
}
可以到ODBC
数据源管理器去查看,看看有没有DsAccess
这个数据源当然上面也有提示了。。。。
再举一个例子,SQL Server
的:
先在SQL Server
里创建一个数据库,名为MyDatabase
,在其下新建一个表BookInfo
,字段BookName,BookPrice
,跟上面的Access
表一样,创建好后,添加一行数据吧,BookName:VC++,BookPrice:87
,关闭,保存。
然后添加Sql Server
数据源,这里给出关键几步的截图。
数据源名称,服务器名如下:
服务器名是20110304-1717
,这个根据自己的SqlServer
服务器名来填。看下面,Sql Server
截图。
知道是什么了吧。。那么用SQLConfigDataSource
创建数据源的时候,最后一个参数的SERVER
项填的就是这个名称。
上面有两个选项,第一项不需要用户填用户ID和密码,就可以登陆,使用默认。这里我选择第二个,那么我在调用SQLConnect
连接数据源时候,就要填入ID和密码,而选择第一项的话,则不需要。
更改默认的数据库为MyDatabase
,这个对应SQLConfigDataSource
函数最后一个参数的DATABASE
之后,设置保持默认不变,一直点下一步,然后测试一下数据,点完成。。就可以了。。创建好了数据源,我们就用代码来测试一下。
如下:
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
#include <odbcinst.h>//SQLConfigDataSource函数头文件
int main(){
/*//这个是使用SQLConfigDataSource函数创建SQLServer数据源的代码
BOOL Result;
Result = SQLConfigDataSource(NULL,ODBC_ADD_DSN,"SQL Server",
"DSN = MySqlServer\0SERVER = 20110304-1717\0DATABASE = MyDatabase\0\0");
if(Result == TRUE)
cout<<"数据源创建成功!"<<endl;
else
cout<<"创建失败!"<<endl;*/
HENV henv;//数据据源连接需要的环境变量
HDBC hdbc;//数据源连接需要的连接变量
HSTMT hstmt;//语句变量
RETCODE rcode;
//创建环境句柄
SQLAllocEnv(&henv);
//创建连接句柄
SQLAllocConnect(henv,&hdbc);
//如果配置数据源的时候,指定了使用用户输入ID和密码方式验证,那么连接的时候,
//也必需提供正确的密码,反之则不需要
rcode = ::SQLConnect(hdbc,(PUCHAR)"MySqlServer",SQL_NTS,(PUCHAR)"sa",SQL_NTS,
(PUCHAR)"123456",SQL_NTS);
if(rcode == SQL_SUCCESS||rcode == SQL_SUCCESS_WITH_INFO)
cout<<"连接数据源成功!"<<endl;
else
cout<<"连接失败!"<<endl;
::SQLAllocStmt(hdbc,&hstmt);
LPSTR pszSQL = new char[52];
//假设数据库里表名为BookInfo,里面有BookPrice和BookName列。
strcpy(pszSQL,"Update BookInfo set BookPrice = 1000 whereBookName = 'VC++'");
//执行SQL语句
rcode = ::SQLExecDirect(hstmt,(UCHAR *)pszSQL,SQL_NTS);
if(rcode == SQL_SUCCESS)
cout<<"执行SQL语句成功!"<<endl;
else
cout<<"执行失败!"<<endl;
::SQLDisconnect(hdbc);//解除连接
return 0;
}
123.SQLDescribeCol 获取结果集一列的信息,如列名,列的数据类型等
在接触SQLDescribeCol
之前先提一个函数:SQLNumResultCols
,这个函数获取结果集列的个数(就是一张表有几列)
这个函数只有两个参数,最后一个参数存储列数。
SQLDescribeCol
函数定义:
SQLRETURN SQLDescribeCol(
SQLHSTMT StatementHandle,//htstmt句柄
SQLUSMALLINT ColumnNumber,//列索引,获取哪一列的信息,从1开始
SQLCHAR * ColumnName,//存储列名
SQLSMALLINT BufferLength,//参数ColumnName的大小(指向的内存)以字节为单位
SQLSMALLINT * NameLengthPtr,//存储列名实际获得的长度
SQLSMALLINT * DataTypePtr,//列的数据类型,如SQL_CHAR,SQL_FLOAT
SQLULEN * ColumnSizePtr,//数据的长度,数据所占的字节,如int型默认4,char(10),就是10
SQLSMALLINT * DecimalDigitsPtr,//小数点位数
SQLSMALLINT * NullablePtr);//列属性,数据是否可以为空,
SQL_NO_NULLS
列不允许空值,SQL_NULLABLE
可以为空
例子:获取一张表里的所有列名。假设有一张Student
表
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
#include <odbcinst.h>//SQLConfigDataSource函数头文件
int main(){
HENV henv;//数据据源连接需要的环境变量
HDBC hdbc;//数据源连接需要的连接变量
HSTMT hstmt;//语句变量
RETCODE rcode;
//创建环境句柄
SQLAllocEnv(&henv);
//创建连接句柄
SQLAllocConnect(henv,&hdbc);
rcode = ::SQLConnect(hdbc,(PUCHAR)"MySqlServer",SQL_NTS,NULL,0,
NULL,0);
if(rcode == SQL_SUCCESS||rcode == SQL_SUCCESS_WITH_INFO)
cout<<"连接数据源成功!"<<endl;
else
cout<<"连接失败!"<<endl;
::SQLAllocStmt(hdbc,&hstmt);
LPSTR pszSQL = new char[52];
//假设数据库里表名为Student
strcpy(pszSQL,"Select * from Student");
//执行SQL语句
rcode = ::SQLExecDirect(hstmt,(UCHAR *)pszSQL,SQL_NTS);
if(rcode == SQL_SUCCESS){
cout<<"执行SQL语句成功!"<<endl;
short ColumnCount;//列数
SQLNumResultCols(hstmt,&ColumnCount);
cout<<"列数:"<<ColumnCount<<endl;
LPSTR pszName = new char[256];//列名
UWORD ColumnIndex;//列的索引
short BufferLength;//列名的长度
SWORD ColumnType;//列的数据类型
UDWORD ColumnSize;//数据的长度
SWORD DecimalDigits;//小数点位数
SWORD Nullable;//值是否可以为NULL
cout<<"列名:"<<endl;
//读取每一列的信息,从索引1开始
for(ColumnIndex = 1;ColumnIndex <= ColumnCount;ColumnIndex++){
SQLDescribeCol(hstmt,ColumnIndex,(UCHAR *)pszName,256,&BufferLength,
&ColumnType,&ColumnSize,&DecimalDigits,&Nullable);
//输出列名
cout<<pszName<<" ";
}
cout<<endl;
}else
cout<<"执行失败!"<<endl;
::SQLDisconnect(hdbc);//解除连接
return 0;
}
运行效果如下图:
124.SQLBindCol 绑定变量到一个列
绑定变量到一个列,就可以获得对应列任何一行的内容,比如StuName
列,我定义一个字符串指针变量pName
,并分配内存,把pName
跟StuName
列绑定后,调用SQLFetch
函数,就可以获得StuName
列一行内容,默认是第一行,也就是第一行StuName
列的数据,再次调用SQLFetch
的话,pName
存储的就是第二行StuName
列的内容,依次递增。
SQLBindCol
函数定义:
SQLRETURN SQLBindCol(
SQLHSTMT StatementHandle,//hstmt句柄
SQLUSMALLINT ColumnNumber,//获取哪一列的内容,列索引
SQLSMALLINT TargetType,//数据类型
SQLPOINTER TargetValuePtr,//绑定的变量
SQLLEN BufferLength,//存储数据缓存区大小,也就是TargetValuePtr的大小
SQLLEN * StrLen_or_Ind);//实际获取数据大小(相对于BufferLength)。每次绑定变量的数据改变,这个参数值也跟着改变,指明数据大小
这里得说一下第三个参数TargetType
数据类型,这个参数对应SQLDescribeCol
函数第六个参数DataTypePtr
,它们的解释是一样的。
另外,即使函数填的数据类型和列实际类型不匹配,也可以,可以自动转换,当然这里有一个前提,它们得可以转换,比如字符串"12345"转换成整型12345是没有问题的,但字符串"1abc"显然不可以转换成整型。不过,读取的时候,不想去了解这其中关系的,数据类型填SQL_C_CHAR
就行了,也就是SQL_CHAR
,这个表示字符串。
试想,不管是整型,浮点型,还是日期类型都可以以字符串的形式来显示。而且你也可以调用相关的函数来转化类型,用这个就可以了。
如果愿望花时间去了解数据类型的区别,在MSDN查,地址:(看那张表就行了)
http://msdn.microsoft.com/en-us/library/ms714556(v=VS.85).aspx
例子:读取第一列,和第二列所有行数据。
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
#include <odbcinst.h>//SQLConfigDataSource函数头文件
int main(){
HENV henv;//数据据源连接需要的环境变量
HDBC hdbc;//数据源连接需要的连接变量
HSTMT hstmt;//语句变量
RETCODE rcode;
//创建环境句柄
SQLAllocEnv(&henv);
//创建连接句柄
SQLAllocConnect(henv,&hdbc);
rcode = ::SQLConnect(hdbc,(PUCHAR)"MySqlServer",SQL_NTS,NULL,0,
NULL,0);
if(rcode == SQL_SUCCESS||rcode == SQL_SUCCESS_WITH_INFO)
cout<<"连接数据源成功!"<<endl;
else
cout<<"连接失败!"<<endl;
::SQLAllocStmt(hdbc,&hstmt);
LPSTR pszSQL = new char[52];
//假设数据库里表名为Student
strcpy(pszSQL,"Select * from Student");
//执行SQL语句
rcode = ::SQLExecDirect(hstmt,(UCHAR *)pszSQL,SQL_NTS);
if(rcode == SQL_SUCCESS){
cout<<"执行SQL语句成功!"<<endl;
short ColumnCount;//列数
SQLNumResultCols(hstmt,&ColumnCount);
cout<<"列数:"<<ColumnCount<<endl;
LPSTR pszName = new char[256];//列名
UWORD ColumnIndex;//列的索引
short BufferLength;//列名的长度
SWORD ColumnType;//列的数据类型
UDWORD ColumnSize;//数据的长度
SWORD DecimalDigits;//小数点位数
SWORD Nullable;//值是否可以为NULL
cout<<"列名:"<<endl;
//读取每一列的信息,从索引1开始
for(ColumnIndex = 1;ColumnIndex <= ColumnCount;ColumnIndex++){
SQLDescribeCol(hstmt,ColumnIndex,(UCHAR *)pszName,256,&BufferLength,
&ColumnType,&ColumnSize,&DecimalDigits,&Nullable);
//输出列名
cout<<pszName<<" ";
}
cout<<endl;
char *pStuNo = new char[256];//绑定的变量,学号
char *pStuName = new char[256];//绑定的变量,学生姓名
long StuNolen;//实际获取数据的大小
long StuNamelen;
//绑定变量
SQLBindCol(hstmt,1,SQL_C_CHAR,pStuNo,256,&StuNolen);//第一列
SQLBindCol(hstmt,2,SQL_C_CHAR,pStuName,256,&StuNamelen);//第二列
rcode = SQLFetch(hstmt);//获取内容
while(rcode == SQL_ROW_SUCCESS||rcode == SQL_ROW_SUCCESS_WITH_INFO){
cout<<pStuNo<<" "<<pStuName<<endl;
rcode = SQLFetch(hstmt);//获取下一行内容
}
SQLCancel(hstmt);
}else
cout<<"执行失败!"<<endl;
::SQLDisconnect(hdbc);//解除连接
return 0;
}
运行效果截图:
http://hi.baidu.com/3582077/item/08b15d260bc4bf3194f62b21
125.SQLDriverConnect 连接数据库
函数定义:
SQLRETURN SQLDriverConnect(
SQLHDBC ConnectionHandle,//hdbc句柄
SQLHWND WindowHandle,//窗口句柄,不显示窗口为NULL
SQLCHAR * InConnectionString,//连接字符串,参照SQLConfigDataSource函数的最后一个参数
SQLSMALLINT StringLength1,//InConnectionString缓存区大小
SQLCHAR * OutConnectionString,//该参数接收连接详细信息。
SQLSMALLINT BufferLength,//OutConnectionString缓存区大小
SQLSMALLINT * StringLength2Ptr,//OutConnectionString实际大小(接收后)
SQLUSMALLINT DriverCompletion);//是否显示连接窗口,
SQL_DRIVER_NOPROMPT
不显示。
SQLDriverConnect
相比SQLConnect
函数,功能强大许多,它可以直接连接数据库驱动,不需要通过数据源,当然它也可以连接数据源,SQLDriverConnect
完全可以替代SQLConnect
函数。
那么连接数据源的时候,如果需要ID和密码,这些信息填在哪里呢,填在InConnectionString
连接字符串里。对应的项属性是
UID
和PWD
。这个参数的解释可以参考SQLConfigDataSource
函数的最后一个参数。
SQLDriverConnect
函数的InConnectionString
多了几个项属性(较之SQLConfigDataSource
函数的最后一个参数),前面UID
和PWD
算两个。还有一个是DRIVER
,这个指明驱动名称。
SQLDriverConnect
应用例子:
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
#include <odbcinst.h>//SQLConfigDataSource函数头文件
int main(){
HENV henv;//数据据源连接需要的环境变量
HDBC hdbc;//数据源连接需要的连接变量
RETCODE rcode;
//连接数据源,项属性用分号隔开,不需要帐号密码验证的话,仅填数据源就行了
SQLCHAR ConnStr[256] = {"DSN = MySqlServer;UID = sa;PWD = 123456"};
//直接连接Access数据库
//SQLCHAR ConnStr[256] = {"DBQ = d:\\book.mdb;DRIVER = Microsoft Access Driver(*.mdb)"};
//直接连接SQL Server
//SQLCHARConnStr[256] = {"SERVER = 20110304-1717;DATABASE = MyDatabase;DRIVER = SQLServer"};
SQLCHAR OutConnStr[256];
short OutConnStrLen;
//创建环境句柄
SQLAllocEnv(&henv);
HWND desktopHandle = GetDesktopWindow();
//创建连接句柄
SQLAllocConnect(henv,&hdbc);
rcode = ::SQLDriverConnect(hdbc,NULL,ConnStr,
SQL_NTS,
OutConnStr,256,&OutConnStrLen,SQL_DRIVER_NOPROMPT);
if(rcode == SQL_SUCCESS||rcode == SQL_SUCCESS_WITH_INFO){
cout<<"连接成功!"<<endl;
cout<<OutConnStr<<'\t'<<OutConnStrLen<<endl;
}else
cout<<"连接失败!"<<endl;
::SQLDisconnect(hdbc);//解除连接
return 0;
}
126.SQLFetchScroll 滚动结果集行
前面提到的SQLFetch函数,只能向一个方向移动,就是当游标在第一行的时候,调用SQLFetch函数获得的是第二行的数据(关联的变量)。
而SQLFetchScroll不仅能向下一行移动,还能向上一行移动。也能获取特定行的数据。。
函数定义:
SQLRETURN SQLFetchScroll(
SQLHSTMT StatementHandle,//hstmt句柄
SQLSMALLINT FetchOrientation,//标明,向前移动,还是向后移动。或者特定行。
SQLLEN FetchOffset);//用于特定行,指明移动到哪行,也就获取哪行数据。向后或向前无意义,为0
第二个参数FetchOrientation有以下取值:
SQL_FETCH_NEXT//获取下一行
SQL_FETCH_PRIOR//获取上一行
SQL_FETCH_FIRST //获取首行
SQL_FETCH_LAST //获取最后一行
SQL_FETCH_ABSOLUTE //用于特指获取哪一行,FetchOffset指明行数
SQL_FETCH_RELATIVE //用于特指获取哪一行,位置为相对。如当前在第二行,FetchOffset为2,那么获取的是第4行。
如果直接调用SQLFetchScroll
函数,会发现除了SQL_FETCH_PRIOR
和SQL_FETCH_NEXT
参数有效外,其它参数都无效,是什么原因呢。。是由于hstmt的属性造成的,默认属性规定游标只能向前移动。也就是SQL_CURSOR_FORWARD_ONLY
。
(这里前一行,上行都给我搞乱了,FORWARD
是向前的意思,NEXT
是下一行的意思。。。没个统一的叫法,真搞不明白)
我们只要将HSTMT
的SQL_ATTR_CURSOR_TYPE
游标属性设置成静态的就行了。也就是SQL_CURSOR_STATIC
。
用SQLSetStmtAttr
函数可以设置。这个函数可以到MSDN里查。这里我只用它设置一下属性就行了,解决上面的问题。设置的语句哪下:
SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (void*)SQL_CURSOR_STATIC, SQL_IS_INTEGER);
接着来一个简单例子,获取特定行的数据。
#include<windows.h>
#include<sqlext.h>//SQLConnect函数头文件
#include<iostream.h>
#include <odbcinst.h>//SQLConfigDataSource函数头文件
int main(){
HENV henv;//数据据源连接需要的环境变量
HDBC hdbc;//数据源连接需要的连接变量
HSTMT hstmt;//语句变量
RETCODE rcode;
//创建环境句柄
SQLAllocEnv(&henv);
//创建连接句柄
SQLAllocConnect(henv,&hdbc);
rcode = ::SQLConnect(hdbc,(PUCHAR)"MySqlServer",SQL_NTS,NULL,0,
NULL,0);
if(rcode == SQL_SUCCESS||rcode == SQL_SUCCESS_WITH_INFO)
cout<<"连接数据源成功!"<<endl;
else
cout<<"连接失败!"<<endl;
::SQLAllocStmt(hdbc,&hstmt);
LPSTR pszSQL = new char[52];
//假设数据库里表名为Student
strcpy(pszSQL,"Select * from Student");
//执行SQL语句
//设置静态游标
SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (void *)SQL_CURSOR_STATIC,SQL_IS_INTEGER);
rcode = ::SQLExecDirect(hstmt,(UCHAR *)pszSQL,SQL_NTS);
if(rcode == SQL_SUCCESS){
cout<<"执行SQL语句成功!"<<endl;
char *pStuNo = new char[256];//绑定的变量,学号
long StuNolen;//实际获取数据的大小
//绑定变量
SQLBindCol(hstmt,1,SQL_C_CHAR,pStuNo,256,&StuNolen);//第一列
//获取第二行的学号,学号最好按顺序排,如第一行001,第二行002,这样好查看结果的时候好区分的些。
rcode = SQLFetchScroll(hstmt,SQL_FETCH_ABSOLUTE,2);
cout<<pStuNo<<endl;
SQLCancel(hstmt);
}else
cout<<"执行失败!"<<endl;
::SQLDisconnect(hdbc);//解除连接
return 0;
}