VC API 举例2

来源:互联网 发布:codeblocks linux 编辑:程序博客网 时间:2024/06/06 15:51

第四十四个GetWindowText根据窗口句柄获得窗口标题名

函数定义:int GetWindowText(HWND hWnd,LPTSTR lpString,int nMaxCount);

第一个参数hWnd是要获取窗口标题名的窗口句柄,第二个lpString是个字符串,窗口标题名,将会存储在这里面,第三个参数nMaxCount指明了第二个参数字符数组的大小。

下面结合GetCursorPos和WindowFromPoint举个例子,鼠标指向哪个窗口,就在界面显示那窗口的标题名


 1 #include<windows.h>
2 #include<stdio.h>
3 int main()
4 {
5 char Text[256]={0};
6 HWND wnd;
7 POINT curpos;
8 while(1)
9 {
10 GetCursorPos(&curpos);
11 wnd = WindowFromPoint(curpos);
12 GetWindowText(wnd,Text,256);
13 printf("%s\n",Text);
14 Sleep(300);
15 }
16
17 return 0;
18 }


第四十五个SetWindowText根据窗口句柄设置窗口标题名

这个函数有两个参数,一个是窗口句柄,一个是标题名,这里就不需要解释了吧,直接看例子,设置一个窗口标题名,依旧以

"无标题.txt - 记事本"为例。


1 #include<windows.h>
2 #include<stdio.h>
3 int main(int argc, char* argv[])
4 {
5 HWND wnd;
6 wnd=FindWindow(NULL,"无标题.txt - 记事本"); //获取窗口句柄
7 SetWindowText(wnd,"新的窗口标题"); //设置窗口标题名
8 return 0;
9 }


第四十六个GetCurrentProcess获得当前线程句柄

没有参数,直接调用即可,该函数返回线程句柄


第四十七个OpenProcessToken获得一个进程的访问令牌句柄

获得一个进程的访问令牌有什么用呢?主要是为了修改它的权限,前面在介绍结束一个进程的时候说过了,无法结束系统进程,是什么原因呢,原因是调用OpenProcess函数失败,无法获取系统进程句柄而引起的,那为什么会失败呢,权限不够,普通程序的进程没有SeDeDebug权限,而一个进程的权限是与访问令牌相关的,这样我们只要获取一个进程的访问令牌句柄,再以这个句柄为参数调用相应的函数提升进程的权限为SeDeDebug就可以获取系统进程句柄,进而结束它。

函数定义:BOOL OpenProcessToken(HANDLE ProcessHandle,DWORD DesiredAccess,PHANDLE TokenHandle)

第一个参数ProcessHandle待获取的进程句柄,第二个参数DesiredAccess操作类型,填TOKEN_ADJUST_PRIVILEGES就行了,

第三个TokenHandle是访问令牌句柄的指针,该参数接收句柄。

如获得本进程的访问令牌句柄:HANDLE hToken;

OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);


第四十七个LookupPrivilegeValue函数查看对应系统权限的特权值,返回信息到一个LUID结构体里
上面讲过了,进程有权限一说,那么大家也能猜到,进程权限的信息也一定存储在一个结构体里,这个结构体描述了进程权限相关的一些信息。这个结构体在这里就不具体描述了,我们所要做的,只是把一个进程权限设置成SeDeDebug就行了,所以我们只要知道TOKEN_PRIVILEGES便是描述进程权限的结构体就可以了。而LookupPrivilegeValue函数是根据访问令牌句获取相应的权限信息吗?
不是的。TOKEN_PRIVILEGES结构里的Privileges[0].Luid跟这个函数所查询的东西相对应,也就是说,如果进程是SeDeDeBug权限,那Privileges[0].Luid的取值是怎样的呢?用LookupPrivilegeValue函数便可以获取其取值。
这个函数是这样定义的:BOOL LookupPrivilegeValue(LPCTSTR lpSystemName,LPCTSTR lpName,PLUID lpLuid);
第一个参数lpSystemName通常都填NULL,本地系统调用,第二个参数lpName填要查询的权限名,如要查询的是SeDeDebug权限则取值是SE_DEBUG_NAME,第三个参数lpLuid接收其取值。
如LUID luid;LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid);


第四十八个AdjustTokenPrivileges调整一个进程的访问令牌信息(权限)

函数定义:BOOL AdjustTokenPrivileges(HANDLE TokenHandle,BOOL DisableAllPrivileges,PTOKEN_PRIVILEGES NewState,DWORD BufferLength,PTOKEN_PRIVILEGES PreviousState,PDWORD ReturnLength)

第一个参数TokenHandle是令牌句柄,第二个是禁用所有权限标志,后面填FALSE就行了。第三个NewState是待刷进令牌句柄的PTOKEN_PRIVILEGES结构信息指针,第四个BufferLength指明TOKEN_PRIVILEGES结构大小,第五,六个参数填NULL就行了。

那么结束上面两个函数,提升一个进程权限制,让它能够结束系统进程的代码就是:


1 HANDLE hToken;
2 OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
3 TOKEN_PRIVILEGES tp;
4 LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
5 tp.PrivilegeCount = 1; //tp里其它一些属性设置
6 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
7 AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );

只上把上面的代码,加入结束普通进程例子的前面,那么就能结束系统进程了。


第四十九个LoadLibrary加载动态链接库,返回动态链接库模块句柄

该函数只有一个参数,那就是动态链接库的名称,如user32.dll,函数返回HMOUDLE类型的模块句柄,获得了一个动态链接库的模块句柄,就可以调用GetProcAddress函数获得模块里面的函数地址,从而调用动态链接库里的函数。


第五十个GetProcAddress根据模块句柄获取相应的函数地址

提到GetProcAddress函数,不得不讲一下怎么设计一个动态链接库,这里我们就以自己设计动态链接库作为GetProcAddress函数的例子。

动态链接库里的函数相对于头文件的函数有什么优势呢?更节省内存,相对于比较常用的函数而已。如果在一个程序里,调用一个头文件里的函数的话,那不管如何,函数的代码就会被复制一份到当前程序里,所以,当有十几个程序调用同一个函数的时候,这个函数在内存中所占用的空间,就会有十几份,分别在各自调用的进程内存空间里,而动态链接库的函数,只在内存中有一份空间(公用空间)如果哪个程序要用到这个函数的话,只要给这个函数的地址,进程就可以跑到这个空间执行函数,那么如何获取函数地址呢,用GetProcAddress函数就行了。

下面我们就自己设计一个动态链接库,点“文件->新建->工程",然后选中“Win32 Dynamic-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)就行了,声明导出函数,防止函数重命名。那么接下来就举个例子。

动态链接里的代码:


 1 #include "stdafx.h"
2
3 BOOL APIENTRY DllMain( HANDLE hModule,
4 DWORD ul_reason_for_call,
5 LPVOID lpReserved
6 )
7 {
8 return TRUE;
9 }
10 extern "C" __declspec(dllexport) int Add(int a,int b)
11 {
12 return a+b;
13 }

点编译执行,然后就会弹出一个调试对话框,直接点取消,接着便生成了动态链接库DLL,然后到你的工程里把后缀名为dll的文件找到,

位置在MyProject\"你的工程名"\Debug下。接着把这个文件复制到要调用的工程下,或者直接复制C:\windows\system32目录下。

假设这个文件名为"sss.dll",那么要调用里面的Add函数便是如下代码:


1 HMODULE hmod=::LoadLibrary("sss.dll"); //获取sss.dll的模块,加载sss.dll动态链接库
2 typedef int (*pAdd)(int a,int b); //定义一个对应的函数型,以便识别
3 pAdd add=(pAdd)GetProcAddress(hmod,"Add"); //获取hmod模块里的Add函数地址
4 int a=add(3,5); //调用模块里的Add函数


第五十一个SetWindowsHookEx安装一个钩子

WINDOWS是基于消息的系统,鼠标移动,单击,键盘按键,窗口关闭等都会产生相应的消息,那么钩子是什么意思呢,它可以监控一个消息,比如在一个窗口里单击了一下,首先获得这个消息的,不是应用程序,而是系统,系统获取这个消息后,就去查看这个消息是在哪个窗口产生的,找到窗口后,再把消息投递到相应程序里的消息队列里,这之间有一个传递过程,那么钩子的作用就是在消息到达应用程序之前截获它,钩子可以关联一个函数(钩子处理函数),也就是说,如果对一个进程安装了一个钩子,进程再接收到相应在消息之前,会先去执行钩子所关联的函数,

先来看一下这个函数定义:

HHOOK WINAPI SetWindowsHookEx(int idHook,HOOKPROC lpfn,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)


 1 #include "stdafx.h"
2 #include<windows.h>
3 BOOL APIENTRY DllMain( HANDLE hModule,
4 DWORD ul_reason_for_call,
5 LPVOID lpReserved
6 )
7 {
8 return TRUE;
9 }
10 HMODULE WINAPI ModuleFromAddress(PVOID pv) //该函数根据内存地址,获得其所在的模块句柄
11 {
12 MEMORY_BASIC_INFORMATION mbi;
13 VirtualQuery(pv,&mbi,sizeof(mbi));
14 return (HMODULE)mbi.AllocationBase;
15 }
16 LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
17 {
18 return TRUE; //返回真,扔掉该消息
19 }
20 extern "C" __declspec(dllexport) void SetHook(void)
21 {
22 SetWindowsHookEx(WH_KEYBOARD,HookKey,ModuleFromAddress(HookKey),0);
23 }

生成dll文件后,把它复制到相应的目录下去。

再新建一个工程,调用用动态链接库里的函数,代码如下:


 1 #include<windows.h>
2 int main()
3 {
4 HMODULE hMod=LoadLibrary("KeyDll.dll");
5 typedef void(*pSetHook)(void);
6 pSetHook SetHook=(pSetHook)GetProcAddress(hMod,"SetHook");
7 SetHook();
8 while(1)
9 {
10 Sleep(1000); //避免程序结束,自动释放动态链接库
11 }
12 return 0;
13 }

这样当按下了一个键后,接收该按键消息的进程,会先去执行钩子处理函数,然后再处理消息,而钩子处理函数的几个参数说明了按键的详细信息,如按了哪个键,是按下(KEYDOWN)还是松开(KEYUP)。如果有兴趣的话,把上面那钩子处理函数的代码换成下面这个


1 LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
2 {
3 char sz[25];
4 sprintf(sz,"%c",wParam); //这个函数头文件#include<stdio.h>
5 MessageBox(NULL,sz,sz,MB_OK);
6 return FALSE;
7 }

每按下一个键,就会弹出一个提示框,并输出所按下的键,只对字符键有用。


第五十二个SHGetFileInfo获取一个文件的各项信息(文件关联图标,属性等)
函数定义: DWORD SHGetFileInfo(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA FAR *psfi, UINT cbFileInfo, UINT uFlags);
pszPath是文件的路径,dwFileAttributes一般取0,如果想要获取文件夹信息的话,则取值为FILE_ATTRIBUTE_DIRECTORY,psfi是一个SHFILEINFO结构的指针,该结构存储文件信息,定义如下:


1 typedef struct _SHFILEINFOA
2 {
3 HICON hIcon; // 文件关联图标句柄
4 int iIcon; // 系统图标列表索引
5 DWORD dwAttributes; // 文件的属性
6 CHAR szDisplayName[MAX_PATH]; // 文件的路径名
7 CHAR szTypeName[80]; // 文件的类型名,如是bmp文件,还是执行文件exe,或者其它
8 } SHFILEINFO;

第四个参数cbFileInfo指明SHFILEINFO结构的大小,填sizoef(SHFILEINFO);
最后一个参数uFlags指定获取文件的什么信息,可选取值如下:(对应着SHFILEINFO里的成员)


1 SHGFI_ICON; //获得图标 
2 SHGFI_DISPLAYNAME; //获得显示名
3 SHGFI_TYPENAME; //获得类型名
4 SHGFI_USEFILEATTRIBUTES; //获得属性
5 SHGFI_LARGEICON; //获得大图标
6 SHGFI_SMALLICON; //获得小图标
7 SHGFI_PIDL; // pszPath是一个标识符

比如,我只要获取文件图标,那么参数填SHGFI_LARGEICON就行了。如果又想获取文件关联的图标,又想获取文件类型名,那么就是
SHGFI_LARGEICON|SHGFI_TYPENAME;
函数例子:


1 SHFILEINFO sfi;
2 SHGetFileInfo("e:\\aa.bmp",0,&sfi,sizeof(sfi),
3 SHGFI_ICON|SHGFI_LARGEICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);

接着可以用DrawIcon函数画出文件关联图标:该函数定义:BOOL DrawIcon(HDC hDC,int X,int Y, HICON hlcon );


第五十三个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函数的各个参数吧,先来看一下函数定义:


 1 LONG RegCreateKeyEx (
2
3 HKEY hKey, //根键句柄,指明要在哪个根键下创建子键,填根键名既可
4 LPCSTR lpSubKey, //子键名,包含完整路径名
5 DWORD Reserved,. //一般取0
6 LPSTR lpClass, //一般取NULL
7 DWORD dwOptions, //创建子键时的选项,可选值REG_OPTION_NON_VOLATILE,REG_OPTION_VOLATILE,这里取0既可
8 REGSAM samDesired, //打开方式,填KEY_ALL_ACCESS,在任何情况都行。
9 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指定继承性,还是取0
10 PHKEY phkResult, //子键对应句柄,待创建或打开的子键句柄将存储在该句柄里
11 LPDWORD lpdwDisposition //打开还是创建子键,对应REG_CREATED_NEW_KEY和REG_OPENED_EXISTING_KEY
12 );

在这里举一个例子,以便我们能更好的理解该函数。

在HKEY_CURRENT_CONFIG根键下的Software主键里创建一个名为MySelf的子键。


1 #include<windows.h>
2 int main()
3 {
4 HKEY hroot; //子键句柄
5 DWORD dwDisposition; //对应着最后一个参数
6 RegCreateKeyEx(HKEY_CURRENT_CONFIG,"Software\\MySelf",0,NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
7 return 0;
8 }


第五十四个RegSetValueEx根据子键句柄在其下创建或修改一个键值

函数定义:


1 LONG RegSetValueEx(
2 HKEY hKey, // 子键句柄
3 LPCTSTR lpValueName, // 键值名称,如果提供的子键下没有该名称,则创建
4 DWORD Reserved, // 保留,填0
5 DWORD dwType, // 键值类型,
6 CONST BYTE *lpData, // 键值的数据
7 DWORD cbData // 键值的数据的大小
8 );

接着我们以增加开机自启动为例,来看一下函数是如何创建一个键值的,我们知道,像程序添加开机自启动一般都在

HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run下添加一个键值,键值类型为二进制(REG_SZ),而键值的数据就为要自启动程序的路径。

假设e盘下有一个AutoRun.exe的应用程序,让电脑开机时自动运行它。


 1 #include<windows.h>
2 int main()
3 {
4 HKEY hroot; //子键句柄
5 DWORD dwDisposition;
6 RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
7 NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
8 RegSetValueEx(hroot,"AutoRun",0,REG_SZ,(BYTE *)"e:\\AutoRun.exe",sizeof("e:\\AutoRun.exe"));
9 return 0;
10 }


第五十五个RegDeleteValue根据子键句柄删除其下的一个键值

这里直接举一个例子,删除RegSetValueEx函数创建的键值


 1 #include<windows.h>
2 int main()
3 {
4 HKEY hroot; //子键句柄
5 DWORD dwDisposition;
6 RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
7 NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
8 RegDeleteValue(hroot,"AutoRun"); //删除子键下名为AutoRun的键值
9 return 0;
10 }


第五十六个RegQueryValueEx根据子键句柄获取一个键值数据,类型。

函数定义:


1 LONG RegQueryValueEx (
2 HKEY hKey, //根键句柄
3 LPCWSTR lpValueName, //键值名称
4 LPDWORD lpReserved, //预留,填0
5 LPDWORD lpType, //接收键值类型
6 LPBYTE lpData, //接收键值数据
7 LPDWORD lpcbData //接收数据的大小
8 );

例子,获取RegSetValueEx函数创建的键值的类型,数据


 1 int main()
2 {
3 char Data[52];
4 DWORD Size,Type;
5 HKEY hroot; //子键句柄
6 DWORD dwDisposition;
7 RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
8 NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition); //获取根键句柄
9 RegQueryValueEx(hroot,"AutoRun",0,&Type,(BYTE *)Data,&Size); //获取AutoRun的信息
10 printf("键值名称:AutoRun ");
11 switch(Type)
12 {
13 case REG_SZ:printf("键值类型:REG_SZ");break;
14 case REG_BINARY:printf("键值类型:REG_BINARY");break;
15 case REG_DWORD:printf("键值类型:REG_DWORD");break;
16 case REG_MULTI_SZ:printf("键值类型:REG_MULTI_SZ");break;
17 case REG_EXPAND_SZ:printf("键值类型:REG_EXPAND");break;
18 }
19 printf(" 键值数据:%s %d\n",Data,Size);
20 return 0;
21 }


第五十七个RegEnumValue根据子键句柄返回对应索引的键值信息(名称,数据,类型,子键下第一个键值索引为0,以此类推,函数成功执行返回ERROR_SUCCESS)

函数定义:


 1 LONG RegEnumValue (
2 HKEY hKey, //子键句柄
3 DWORD dwIndex, //键值索引
4 LPWSTR lpValueName, //接收键值名称,字符数组
5 LPDWORD lpcbValueName, //指明数组大小
6 LPDWORD lpReserved, //预留,0
7 LPDWORD lpType, //键值类型,填NULL,不获取
8 LPBYTE lpData, //键值数据,填NULL,不获取
9 LPDWORD lpcbData //接收数据的大小,如果键值数据那项参数为NULL,则该项也为NULL
10 );

例子:输出Run下的所有键值名


 1 #include<windows.h>
2 #include<stdio.h>
3 int main()
4 {
5 char Name[52];
6 int Index=0;
7 DWORD dwSize=52;
8 DWORD Size,Type;
9 HKEY hroot; //子键句柄
10 DWORD dwDisposition;
11 RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
12 NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition); //获取根键句柄
13 while(RegEnumValue(hroot,Index,Name,&dwSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS)
14 {
15 printf("%s\n",Name);
16 Index++; //索引从0开始每次自增一,函数如果执行失败,则索引已到头
17 }
18 return 0;
19 }

其实也还可以扩充一下,可以像msconfig程序那样列出当前计算机的所有开机自启动程序,当然,注册表也不只就前面的那一个子键下可以添加自启动程序,在HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run下也可以添加,所以这些子键都需要去查看,更多添加自启动程序的子键可以到百度里去搜一下,大家如果掌握前面那几个注册表操作函数,可以结合起来试着做一个可以添加,查看,删除开机自启动程序的小程序。


第五十八个ExitWindowsEx关机,重启,注销计算机函数

这个函数只有两个参数,后一个参数为系统预留,填0就可以了,而第一个参数则,指明关机,还是重启,或注销,可选值如下:

EWX_LOGOFF//注销 EWX_REBOOT//重启 NT系统中需SE_SHUTDOWN_NAME 特权 EWX_SHUTDOWN//关机,需权限。

例子:关闭计算机,由于需要SE_SHUTDOWN_NAME权限,所以我们得先提升权限,代码如下:


 1 #include<windows.h>
2 int main()
3 {
4 HANDLE hToken;
5 TOKEN_PRIVILEGES tkp;
6 OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken);
7 LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
8 tkp.PrivilegeCount=1;
9 tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
10 AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
11 ::ExitWindowsEx(EWX_SHUTDOWN,0);
12 return 0;
13 }


第五十九个VirtualAllocEx在其它的进程中分配内存空间

函数定义:


1 LPVOID VirtualAllocEx(
2 HANDLE hProcess, //进程句柄,将会在该进程句柄相关的进程分配空间
3 LPVOID lpAddress, //默认为系统指定,填NUL
4 DWORD dwSize, //分配多大的内存
5 DWORD flAllocationType, //填MEM_COMMIT
6 DWORD flProtect //指定分配的内存属性,为PAGE_READWRITE,内存可读写
7 );

函数返回分配的内存首地址,


第六十个CreateRemoteThread创建一个远程线程(在其它进程中创建线程)

函数定义:


 1 HANDLE WINAPI
2 CreateRemoteThread(HANDLE hProcess, //进程句柄,函数将在这个进程句柄关联的进程创建线程
3
4 LPSECURITY_ATTRIBUTES lpThreadAttributes,
5
6 DWORD dwStackSize,
7 LPTHREAD_START_ROUTINE lpStartAddress,
8 LPVOID lpParameter,
9 DWORD dwCreationFlags,
10 LPDWORD lpThreadId
11 );

这个函数比CreateThread函数多了一个参数,就是这个函数的第一个hProcess(函数在该进程里创建线程),后面的六个参数跟第三十九个函数CreateThread的六个参数一样,这里就不再解释了。

例子:远程线程注入

创建一个远程线程,就必须得有一个线程函数供线程执行,而线程函数又不能在其它程序里。那要怎么办呢?大家看一下线程函数的定义,和LoadLibrary函数的定义,它们的定义相似,都是只有一个参数,而且每个程序都能调用LoadLibrary函数,这样我们便能把LoadLibrary函数作为线程函数。这样创建的线程就会去执行LoadLibrary函数。因而我们就有了一次让其它程序调用LoadLibrar函数的机会,并还可以指定LoadLibrary函数的参数(通过创建远程线程函数传递)。前面在动态链接库提到,一个程序如果调用LoadLibrary函数,它都会自动去执行相应动态链接库里的DllMain函数,所以我们自己可以编写一个动态链接库,在DllMain函数里写入想要其它程序执行的代码。再通过CreateRemoteThread函数在其它程序创建一个线程去执行LoadLibary加载我们已经编写好的动态链接库,这样就可以让其它程序执行我们的代码了。这里还有一个问题,CreateRemoteThread函数传递过去的参数,因为要供注入的那个程序访问,所以参数数据所存储的空间不能在调用CreateRemoteThread函数的程序里。必须调用VirtualAllocEx函数,在注入程序里分配一个空间,把数据(动态链接库的名称)存在里面,而新分配空间的首地址则作为CreateRemoteThread函数的参数传过去。这样注入程序访问的是自己的地址空间。

远程线程注入:

假设动态链接库为“ReCode.dll”它的代码如下:


 1 #include<windows.h>
2 BOOL APIENTRY DllMain( HANDLE hModule,
3 DWORD ul_reason_for_call,
4 LPVOID lpReserved
5 ) //DllMain函数,只要加载这个动态链接库的程序,都会跑来执行这个函数
6 { //在这里填让其它程序执行的代码
7 while(1)
8 {
9 MessageBox(NULL,"aaaa","aaaa",MB_OK); //简单的让其它程序每隔3秒弹出一个提示框
10 Sleep(3000);
11 }
12 return TRUE;
13 }

编译运行,然后把生成的“ReCode.dll”文件复制到c:\\windows\\system23下去。

注入线程的代码:


 1 //选择ctfmon.exe(输入法管理)作为我们要注入进线程的程序
2 #include<windows.h>
3 #include<tlhelp32.h>
4 #include<stdio.h>
5 int main()
6 {
7 char DllName[25]="ReCode.dll";
8 HANDLE hProcess; //用于存储ctfmon.exe的进程句柄
9 //先提升进程权限,使其能获取任何进程句柄,并对其进行操作
10 HANDLE hToken;
11 OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
12 TOKEN_PRIVILEGES tp;
13 LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
14 tp.PrivilegeCount = 1;
15 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
16 AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );
17 ////////////////////////////////////////////////////////////////////////////
18 //Process32First和Process32Next函数结合(寻找)获取ctfmon.exe进程ID号
19 //再调用OpenProcess函数根据进程ID获得进程句柄
20 PROCESSENTRY32 pe32; //进程相关信息存储这个结构里
21 pe32.dwSize=sizeof(pe32);
22 //给系统内的所有进程拍一个快照
23 HANDLE hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
24 BOOL bMore=::Process32First(hProcessSnap,&pe32);
25 while(bMore)
26 {
27 if(strcmp("ctfmon.exe",pe32.szExeFile)==0) //如果找到进程名为ctfmon.exe
28 hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID); //获取句柄
29 bMore=::Process32Next(hProcessSnap,&pe32); //寻找下一个
30 }
31
32//在ctfmon进程中分配空间
33 LPVOID lpBuf=VirtualAllocEx(hProcess,NULL,strlen(DllName),MEM_COMMIT, PAGE_READWRITE );
34 DWORD WrSize;
35 //把DllName里的数据写入到分配的空间里
36 WriteProcessMemory(hProcess, lpBuf, (LPVOID)DllName, strlen(DllName), &WrSize);
37//创建远程线程
38 CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)LoadLibraryA,lpBuf,0,NULL);
39 return 0; //程序使命完成,结束
40 }

当然,给一个程序安装钩子,也可以让指定的应用程序加载特定的动态链接库,但要了解,加载动态链接库的是是应用程序的主程序,你总不能让应用程序不干它自己的事,而来一直执行DllMain函数里的代码吧!而且即使这样,当安装钩子的程序退出或卸载钩子的时候,那么被系统强迫加载动态链接库的程序,也会自动释放动态链库,退出DllMain函数。如此,那就没有办法了吗?,办法肯定是有的,用CreateThread函数。当其它程序主线程执行DllMain函数的时候,使其调用CreateThread再创建一个线程,就行了


第六十一个GetWindowThreadProcessId根据窗口句柄获得对应进程ID号,和线程ID号

函数只有两个参数,第一个参数是窗口句柄,第二个参数是一个DOWRD类型的指针,函数返回线程ID

如:


1 DWORD ThreadId,ProcessId;
2 ThreadId=GetWindowThreadProcessId(wnd,&ProcessId);


第六十二个EnumWindows枚举当前正运行的所有主窗口,不包括子窗口

调用这个函数,还必须定义一个回调函数,它的格式是这样的:BOOL CALLBACK lpEnumFunc(HWND wnd, LPARAM lParam);

EnumWindows函数有两个参数,第一个就是回调函数的地址,另一个是自定义参数,对应着回调函数的第二个参数。

每枚举一次窗口,这个回调函数就会被执行一次,而获得的窗口句柄,就会传递给回调函数,对应着回调函数的第一个参数。直到枚举完所有窗口。而在回调用函数里,返回真表示继续枚举,返回假则停止枚举。

例子:枚举窗口


 1 #include<windows.h>
2 #include<stdio.h>
3 BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam);
4 int main()
5 {
6 ::EnumWindows(lpEnumFunc,0);
7 return 0;
8 }
9 BOOL CALLBACK lpEnumFunc(HWND wnd, LPARAM lParam)
10 {
11 char WindowName[52]={0};
12 GetWindowText(wnd,WindowName,sizeof(WindowName)); //根据窗口句柄获得窗口名
13 printf("窗口句柄:%d 窗口名称:%s\n,",wnd,WindowName);
14
15 //可以在这里加个判断当前是否有一个窗口正在运行
16
17 //如if(strcmp(WindowName,"Windows 任务管理器")==0) return 0;结束枚举,
18 return 1;
19 }


第六十三个MessageBox弹出一个消息提示框


1 int MessageBox(
2 HWND hWnd , //指明消息框是属于哪个窗口的,可以为NULL
3 LPCSTR lpText, //消息提示具体内容
4 LPCSTR lpCaption, //提示框窗口标题
5 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号
按钮和图标可以结合用,如:


1 MessageBox(NULL,"该内存不能访问","出错",MB_OK|MB_ICONSTOP);

系统模式:MB_SYSTEMMODAL

函数的返回值确定用户选择了哪个按钮,正面给各按钮的定义:
IDABORT “放弃”按钮
IDCANCEL “取消”按钮
IDIGNORE “忽略”按钮
IDNO “否”按钮
IDOK 确定
IDRETRY 重试
IDYES 是
判断返回值是否与其相等即可。


第六十四个GetForegroundWindow获得当前激活的窗口句柄

函数没参数,调用即返回最前窗口句柄

这里举一个例子:每当切换窗口的时候弹出一个消息提示框


 1 #include<windows.h>
2 int main()
3 {
4 char WindowName[52];
5 HWND wnd,Orgwnd;
6 wnd=Orgwnd=NULL;
7 while(1)
8 {
9 wnd=GetForegroundWindow();
10 if(wnd!=Orgwnd)
11 { GetWindowText(wnd,WindowName,sizeof(WindowName));
12 MessageBox(NULL,WindowName,"切换窗口到",MB_OK);
13 Orgwnd=wnd;
14 }
15 Sleep(200);
16 }
17 }


第六十五个GetTopWindow根据窗口句柄获得其下第一子窗口句柄(如果有)

用过MFC的人都知道,在对话编辑区,如果要为控件排序的话,就按CTRL+D显示出每个控件的顺序,如下图:

而GetTopWindow函数获取的就是控件顺序为1的窗口句柄。

例子:改变一个主窗口下的第一子窗口的显示内容(前提得它有),这里就以上面那个abc对话框为例:


 1 #include<windows.h>
2 int main()
3 {
4 HWND wnd;
5 wnd=FindWindow(NULL,"abc");
6 HWND FirstWnd=GetTopWindow(wnd);
7 SetWindowText(FirstWnd,"first");
8 return 0;
9
10 }

执行效果:


第六十六个GetNextWindow根据子窗口句柄获得下一个或上一个同级的窗口句柄(返回NULL,函数执行失败)

函数有两个参数,第一个是子窗口句柄,第二个参数指明寻找上一个,还是一下个窗口句柄,值:GW_HWNONEXT(下一个),GW_HWNDPREV(上一个)。比如子窗口句柄在主窗口的顺序为3,那么获取的是顺序为2或顺序为3的窗口句柄(具体取决于第二个参数),函数返回获得的窗口句柄.这样GetNextWindow结合GetTopWindow函数就可以遍历一个主窗口里的所有子窗口了。

例子:遍历一个窗口里的所有子窗口,以上面的abc窗口为例


 1 #include<stdio.h>
2 #include<windows.h>
3 int main()
4 {
5 char Name[52];
6 HWND wnd;
7 wnd=FindWindow(NULL,"abc");
8 wnd=GetTopWindow(wnd);
9 while(wnd!=NULL)
10 {
11 GetWindowText(wnd,Name,sizeof(Name));
12 printf("窗口句柄:%d,名称:%s\n",wnd,Name);
13 wnd=GetNextWindow(wnd,GW_HWNDNEXT); //GW_HWNDNEXT获取下一个
14 }
15 return 0;
16 }



第六十七个InvalidateRect发送一个WM_PAINT消息给窗口(刷新窗口)

函数定义:


1 BOOL InvalidateRect(
2 HWND hWnd , //要刷新窗口的句柄
3 CONST RECT *lpRect, //刷新的范围
4 BOOL bErase //重画为TRUE
5
6 );

例子:在SetTimer函数里会举例


第六十八个SetTimer设置一个定时器(每隔一段时间执行一次定时器函数)

函数定义:


1 UINT SetTimer(
2 HWND hWnd , //窗口句柄
3 UINT nIDEvent, //定时器ID号,为了能设置多个定时器
4 UINT uElapse, //时间,指明间隔多久执行一次定时器函数,单位:毫秒
5 TIMERPROC lpTimerFunc); //定时器回调函数的地址

定时器函数的固定格式:VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime);

例子:在用w,a,s,d键控制一个矩形移动的同时,一个相同的矩形自动移动。


 1 #include<windows.h>
2 LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);//函数声明
3 VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime);
4 int APIENTRY WinMain(HINSTANCE hInstance,
5 HINSTANCE hPrevInstance,
6 LPSTR lpCmdLine,
7 int nCmdShow)
8 {
9
10 WNDCLASS wndcls; //定义一个存储窗口信息WNDCLASS变量
11 wndcls.cbClsExtra=0; //默认为0
12 wndcls.cbWndExtra=0; //默认为0
13 wndcls.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //背景画刷
14 wndcls.hCursor=LoadCursor(NULL,IDC_ARROW); //光标
15 wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); //窗口图标
16 wndcls.hInstance=hInstance; //应用程序实例句柄由WinMain函数传进来
17 wndcls.lpfnWndProc=WinSunProc; //窗口消息处理函数
18 wndcls.lpszClassName="windowclass"; //窗口类名
19 wndcls.lpszMenuName=NULL; //窗口菜单名,没有菜单,为NULL
20 wndcls.style=CS_HREDRAW | CS_VREDRAW; //窗口类型,CS_HREDRAW和CS_VERDRAW 表明
21 //当窗口水平方向垂直方向的宽度变化时重绘整个窗口
22 RegisterClass(&wndcls); //把窗口信息提交给系统,注册窗口类
23 HWND hwnd; //用以存储CreateWindow函数所创建的窗口句柄
24 hwnd=CreateWindow("windowclass","first windows",
25 WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL); //创建窗口
26 ShowWindow(hwnd,SW_SHOWNORMAL); //窗口创建完了,显示它
27 UpdateWindow(hwnd); //更新窗口,让窗口毫无延迟的显示
28 SetTimer(hwnd,1,200,(TIMERPROC)TimerProc); //设置定时器
29 MSG msg; //消息结构类型
30 while(GetMessage(&msg,NULL,0,0)) //获取消息
31 {
32 TranslateMessage(&msg); //此函数用于把键盘消息(WM_KEYDOWN,WM_KEYUP)转换成字符消息WM_CHAR
33 DispatchMessage(&msg); //这个函数调用窗口过程处理函数,并把MSG里的信息处理后传给过程函数的四个参数
34 }
35 return 0;
36 }
37 VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime) //定时器函数
38 {
39 static int x=0,y=0;
40 x+=15;
41 if(x>500)
42 {y+=15;x=0;}
43 HDC dc=GetDC(hwnd);
44 Rectangle(dc,x,y,x+30,y+30);
45 }
46 LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
47 { static int x=0,y=0;
48 switch(uMsg)
49 {
50
51 case WM_CHAR:
52 {
53 if(wParam=='a') x-=10;
54 else if(wParam=='d') x+=10;
55 else if(wParam=='w') y-=10;
56 else if(wParam=='s') y+=10;
57 RECT rect;
58 GetClientRect(hwnd,&rect);
59 ::InvalidateRect(hwnd,&rect,TRUE); //发送WM_PAINT消息,刷新窗口
60 }
61 break;
62 case WM_PAINT:
63 HDC dc;
64 PAINTSTRUCT ps;
65 dc=BeginPaint(hwnd,&ps); //BeginPaint只能在响应WM_PAINT,不能用GetDC获取设备上下文
66 Rectangle(dc,x,y,x+30,y+30);
67 break;
68 case WM_CLOSE: //用户关闭了窗口
69 DestroyWindow(hwnd); //销毁窗口,并发送WM_DESTROY消息
70 break;
71 case WM_DESTROY: //如果窗口被销毁
72 PostQuitMessage(0); //让进程退出
73 break;
74 }
75 return DefWindowProc(hwnd,uMsg,wParam,lParam); //未处理的消息通过DefWindowProc函数交给系统处理
76 }


第六十九个RegisterHotKey注册一个热键

函数定义:


1 BOOL RegisterHotKey(
2 HWND hWnd ,
3 int id,
4 UINT fsModifiers,
5 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热键,弹出消息提示框,询问是否要退出。


 1 //#include "stdafx.h" 新建空工程,不需要该头文件
2 #include<windows.h>
3 int APIENTRY WinMain(HINSTANCE hInstance,
4 HINSTANCE hPrevInstance,
5 LPSTR lpCmdLine,
6 int nCmdShow)
7 {
8 RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_ALT,'X'); //注册热键
9 MSG msg;
10 while(GetMessage(&msg,NULL,0,0)) //从消息队伍获取消息
11 {
12 if(msg.message==WM_HOTKEY) //热键消息
13 {
14 if(MessageBox(NULL,"你确定要退出程序吗?","热键提示",MB_YESNO|MB_SYSTEMMODAL)==IDYES)
15 {
16 UnregisterHotKey(NULL,0x0001); //卸载热键
17 break;
18 }
19 }
20 }
21 return 0;
22 }

记得要退出程序


第七十个StretchBlt在窗口输出一个位图

这个函数比BitBlt多了两个参数,那就是源目标DC的宽高,像BitBlt函数,只有目标DC的宽高。

有了这两个参数的加入,StretchBlt函数功能就比BitBlt函数强大了许多,它可以缩小或放大图片,可以把一张图片上的任意矩形区域覆盖到另一张图片上的任意区域。

函数语句:StretchBlt(hdc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,0,0,50,50,SRCCOPY);

具体用法参考BitBlt函数。


第七十一个TextOut根据设备DC在窗口输出文字

函数定义:


1 BOOL TextOut(HDC hdc, // 设备DC
2 int nXStart, // 窗口x坐标
3 int nYStart, // 窗口y坐标,字符串左上角是将是x,y
4 LPCTSTR lpString, // 字符串
5 int cbString // 字符串中字符的个数
6 );

例子:在窗口输出文字,为了方便,这里依旧在"无标题.txt - 记事本",窗口里输出文字


 1 #include<windows.h>
2 #include<string.h>
3 int main()
4 {
5 char Text[52]="从坐标点50,50开始输出文字";
6 HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
7 HDC dc=GetDC(wnd);
8 SetTextColor(dc,RGB(255,0,0)); //设置文字颜色
9 while(1)
10 {
11 TextOut(dc,50,50,Text,strlen(Text));
12 Sleep(200);
13 }
14 return 0;
15 }


第七十二个DrawText根据设备DC在窗口的一个矩形区输出文字。


1 int DrawTextW(
2 HDC hDC, //设备DC
3 LPCWSTR lpString, //字符串
4 int nCount, //字符串的个数
5 LPRECT lpRect, //指明一个矩形区
6 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 截短不符合矩形的正文,并增加省略号。


???第七十三个GetLogicalDriveStrings获取系统分区信息

函数定义:


1 DWORD GetLogicalDriveStrings(
2 DWORD nBufferLength, //指明lpBuffer参数大小
3 LPSTR lpBuffer //分区信息将会存储在这个参数,格式是“分区NULL分区NULL分区NULL NULL”两个NULL结尾
4 //假设当前电脑有C,D,E分区,那字符串的存储格式是 "C:\\\0D:\\\0E:\\\0\0"; ('\\'转义字符\)
5 );

例子:枚举当前磁盘所有分区


 1 #include<windows.h>
2 #include<stdio.h>
3 int main()
4 {
5 char *szBuffer=new char[52];
6 GetLogicalDriveStrings(52,szBuffer);
7 while(*szBuffer!='\0')
8 {
9 printf("%s\n",szBuffer);
10 szBuffer+=strlen(szBuffer)+1;
11 }
12 return 0;
13 }


第七十四个GetDiskFreeSpaceEx获取一个分区(盘符)的信息(已用空间,总大小,可用空间)

这个函数必须用ULARGE_INTEGER联合类型来存储磁盘使用信息。因为要获取磁盘的已用空间,总大小,可用空间,所以我们必须定义三个ULARGE_INTEGER类型变量来存储这三个信息。而具体信息就存储在ULARGE_INTEGER类型的QuadPart成员变量(该成员占八位字节)

如获取C盘的信息:ULARGE_INTEGER dwAvailable,dwFree,dwTotal;

GetDiskFreeSpaceEx("c:\\",&dwAvailable,&dwTotal,&dwFree);//获取分区信息

下面结合GetLogicalDriveStrings举个例子:获取当前磁盘所有分区信息并输出


 1 #include<windows.h>
2 #include<stdio.h>
3 int GetSpace(char *P);
4 int main()
5 {
6 char *szBuffer=new char[52];
7 GetLogicalDriveStrings(52,szBuffer);
8 while(*szBuffer!='\0')
9 {
10 printf("%s ",szBuffer);
11 GetSpace(szBuffer);
12 szBuffer+=strlen(szBuffer)+1;
13 }
14 return 0;
15 }
16 int GetSpace(char *Par)
17 {
18 ULARGE_INTEGER dwAvailable,dwFree,dwTotal;
19 double fDwa,fDwf,fDwt;
20 char szSize[128]={0};
21 int Size;
22 GetDiskFreeSpaceEx(Par,&dwAvailable,&dwTotal,&dwFree); //获取分区信息
23 Size=dwTotal.QuadPart/1024/1024; //获取总大小
24 fDwt=Size/1024.0;
25 Size=dwAvailable.QuadPart/1024/1024; //获取已用空间
26 fDwa=Size/1024.0;
27 Size=dwFree.QuadPart/1024/1024; //获取可用空间
28 fDwf=Size/1024.0;
29 sprintf(szSize,"总大小:%0.2fGB 已用空间:%0.2fGB 可用空间:%0.2fGB",fDwt,fDwa,fDwf);
30 printf("%s\n",szSize);
31 return 0;
32 }


第七十五个WritePrivateProfileString修改或创建一个INI文件里的数据

INI文件的内容一般由节名,键名,键值组成,先来看一下INI文件的结构,打开一个INI文件,我们可能会看到以下内容

////////////////////////////////////////////////////////////////////////////////////

[gmy_p]
exist_p=0
linux_p=

[boot]
a20=0
a21=0

///////////////////////////////////////////////////////////////////////////////////

上面的内容中[gmy_p]和[boot]便是INI文件的节名,节名包含键名,和键值。一个INI文件可以有多个节名.

那么哪些是键名,那些是键值呢,在“=”左边的是键名,而在右边的就是键值,键值可以为NULL。

好了,看一下WritePrivateProfileString的函数的定义:


1 BOOL WritePrivateProfileString(
2 LPCWSTR lpAppName, //节名
3 LPCWSTR lpKeyName, //键名
4 LPCWSTR lpString, //修改的数据
5 LPCWSTR lpFileName //INI文件名
6 );

如果要修改键值,那么要提供哪些信息呢,首先,必须要知道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");


第七十六个GetPrivateProfileString获取一个INI文件里的数据

函数定义:


1 DWORD GetPrivateProfileStringW(
2 LPCWSTR lpAppName, //节名
3 LPCWSTR lpKeyName, //键名
4 LPCWSTR lpDefault, //默认值,填0既可
5 LPWSTR lpReturnedString, //接收数据的缓存区(字符串)
6 DWORD nSize, //指明缓存区的大小
7 LPCWSTR lpFileName //INI文件名
8 );

例子获取一个键值:假设D盘下有一个名为Info.ini文件,它的内容如下:

////////////////////////////////////////////////////////////////////////////

[ZhengYong]
QQ=***980073
Age=100
Weight=65kg
[LiYang]
QQ=***990129
Age=22
Weight=55kg

///////////////////////////////////////////////////////////////////////////

如果我想要获取节名为"ZhengYong"下的键名QQ的键值,那么就是:


1 #include<windows.h>
2 #include<stdio.h>
3 int main()
4 {
5 char KeyValue[252];
6 ::GetPrivateProfileString("ZhengYong","QQ",0,KeyValue,252,"d:\\Info.ini");
7 printf("%s\n",KeyValue);
8 return 0;
9 }

同WritePrivateProfileString类似,如果提供的节名,或键名为NULL,则获取当前所有的节名或键名。跟分区信息存储格式一样,字符串里,多个节名,或键名以'\0'间隔,字符串最终以两个'\0'结束。

例子:枚举ZhengYong节名下的所有键名:


 1 #include<windows.h>
2 #include<stdio.h>
3 int main()
4 {
5 char Data[252];
6 ::GetPrivateProfileString("ZhengYong",NULL,0,Data,252,"d:\\Info.ini");
7 char *pKey=Data;
8 while(*pKey!='\0')
9 {
10 printf("%s\n",pKey);
11 pKey+=strlen(pKey)+1;
12 }
13 return 0;
14 }

那么枚举节名只要在上面的例子中,把函数的节名参数设为NULL就行了,如:

GetPrivateProfileString(NULL,NULL,0,Data,252,"d:\\Info.ini");

大家可以用这个函数编一个读取INI文件内容的程序,以便更好的掌握这个函数。记得把接收数据的缓存区设置大一点。


第七十七个GetSystemMetrics获得特定窗口的高宽度

该函数只有一个参数,常用取值如下:

SM_CXSCREEN 屏幕宽度

SM_CYSCREEN屏幕高度

SM_CXFULLSCREEN窗口客户区宽度

SM_CYFULLSCREEN窗口客户区高度

SM_CYMENU菜单栏高度

SM_CYCAPTION//标题栏高度

SM_CXBORDER窗口边框宽度

SM_CYBORDER窗口边框高度

例子:获取屏幕分辨率(桌面宽高度)


1 #include<windows.h>
2 #include<stdio.h>
3 int main()
4 {
5 int ScreenX=::GetSystemMetrics(SM_CXSCREEN);
6 int ScreenY=::GetSystemMetrics(SM_CYSCREEN);
7 printf("屏幕分辨率:%dX%d\n",ScreenX,ScreenY);
8 return 0;
9 }


第七十八个SetWindowPos设置一个窗口的大小和它的Z序

窗口的Z序是什么意思呢?用过MFC的人应该都清楚,在对话框编辑区按CTRL+D就会显示出每个控件的顺序。如下图:

设置控件的顺序有什么用呢,大家看到上面两个控件有什么特别的吗?对了,两个控件正好有一部分重叠,这时候问题就来了,重叠的部分显示的是那个窗口呢,或者说是以什么来确定显示哪个窗口,我想大家也应该猜到了,是以控件的顺序来确定的。顺序较大的会被显示。这个程序运行如下图如示:

明白窗口的Z序了,我们就来看一下这个函数的参数及其意思。

函数定义:


1 BOOL SetWindowPos(
2 HWND hWnd, //要设置的窗口句柄
3 HWND hWndInsertAfter,
4 int X,
5 int Y, //X,Y指明窗口左上角的位置
6 int cx, //窗口宽度
7 int cy, //窗口高度
8 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 - 记事本"窗口为例


1 #include<windows.h>
2 int main()
3 {
4 HWND wnd=::FindWindow(NULL,"无标题.txt - 记事本");
5 ::SetWindowPos(wnd,HWND_TOPMOST,0,0,0,0,SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
6 return 0;
7 }

例子2:全屏一个窗口

依旧以"无标题.txt - 记事本"窗口为例:


1 #include<windows.h>
2 int main()
3 {
4 int ScreenX=::GetSystemMetrics(SM_CXSCREEN);
5 int ScreenY=::GetSystemMetrics(SM_CYSCREEN);
6 HWND wnd=::FindWindow(NULL,"无标题.txt - 记事本");
7 ::SetWindowPos(wnd,HWND_TOPMOST,0,0,ScreenX,ScreenY,SWP_SHOWWINDOW);
8 return 0;
9 }


第七十九个CreateFile创建一个文件,或打开一个文件用于读写,函数返回文件句柄

函数定义:


 1 HANDLE CreateFile(
2 LPCSTR lpFileName, //文件名
3 DWORD dwDesiredAccess, //指明对文件进行何种操作,是要读它(GENERIC_READ)还是要写入(GENERIC_WRITE)
4 DWORD dwShareMode, //指明文件可以同时被多个程序读写吗?FILE_SHARE_READ可以同时读,FILE_SHARE_WRITED可以同时写
5 LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向一个SECURITY_ATTRIBUTES结构的指针,一般为NULL
6
7 DWORD dwCreationDisposition, //安全属性,指明以何种方式打开或创建文件
8 DWORD dwFlagsAndAttributes, //指明文件的属性,隐藏?只读?系统文件?为NULL表示默认属性
9 HANDLE hTemplateFile //如果不为零,则指定一个文件句柄。新文件将从这个文件中复制扩展属性
10 );

第五个参数dwCreationDisposition的常用取值及意思

TRUNCATE_EXISTING 将现有文件缩短为零长度,清空文件的内容,文件必须已经存在

CREATE_ALWAYS 创建一个文件,如果文件已经存在,则覆盖它

CREATE_NEW 创建文件,如果文件已经存在,则函数执行失败

OPEN_ALWAYS打开文件,如果文件不存在,则创建它

OPEN_EXISTING 打开文件,文件必须存在。

第六个参数dwFlagsAndAttributes的常用取值及其意思

FILE_ATTRIBUTE_NORMAL 默认属性

FILE_ATTRIBUTE_HIDDEN 隐藏

FILE_ATTRIBUTE_READONLY 只读

FILE_ATTRIBUTE_SYSTEM 系统文件


第八十个ReadFile根据文件句柄,从文件中读取一段数据

函数定义:


1 BOOL WINAPI ReadFile(
2 HANDLE hFile, //文件句柄
3 LPVOID lpBuffer, //接收文件数据的缓存区
4 DWORD nNumberOfBytesToRead, //指明读取多少数据(字节)
5 LPDWORD lpNumberOfBytesRead, //实际读取数据
6 LPOVERLAPPED lpOverlapped //一般为NULL,如文件打开时指定了FILE_FLAG_OVERLAPPED,该参才有具体取值。
7 );

例子:读取txt文件的内容,假设E盘下有一个名a.txt的文件,文件内容为123456789


 1 #include<windows.h>
2
3 #include<stdio.h>
4
5 int main()
6 {
7 char Text[25]={0};
8 DWORD dwSize;
9 HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_READ,0,NULL,OPEN_EXISTING
10 ,FILE_ATTRIBUTE_NORMAL ,NULL); //获取文件句柄
11 ReadFile(FileHandle,Text,15,&dwSize,NULL); //从文件中读取15个字节
12 printf("内容:%s 实际读入字节:%d\n",Text,dwSize);
13 return 0;
14 }


第八十一个WriteFile根据文件句柄,写入一段数据到文件中

函数定义:


1 BOOL WriteFile(
2 HANDLE hFile, //文件句柄
3 LPCVOID lpBuffer, //该缓存区的数据将要写入到文件里
4 DWORD nNumberOfBytesToWrite, //指明写入多少数据
5 LPDWORD lpNumberOfBytesWritten, //实际写入数据
6 LPOVERLAPPED lpOverlapped //一般为NULL
7 );

例子:在E盘创建一个名为aa.txt的文件,并向其写入数据


 1 #include<windows.h>
2
3 #include<stdio.h>
4
5 int main()
6 {
7 char Text[25]="123456789";
8 DWORD dwSize;
9 HANDLE FileHandle=CreateFile("e:\\aa.txt",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
10 FILE_ATTRIBUTE_NORMAL,NULL);
11 ::WriteFile(FileHandle,Text,9,&dwSize,0);
12 return 0;
13 }


第八十二个SetFilePointer移动一个文件指针的位置

移动一个文件指针的位置有什么用呢,作用是读取一个文件里指定位置的数据,比如我只要读取文件中第四个字节到第七个字节这一段的数据,用SetFilePointer函数就可以完成。

函数定义:


1 DWORD SetFilePointer(
2 HANDLE hFile, //文件句柄
3 LONG lDistanceToMove, //移动字节,负数表示反向移动
4 PLONG lpDistanceToMoveHigh, //为了支持超大文件而存在,一般为NULL
5 DWORD dwMoveMethod //从哪里开始移动,FILE_BEGIN 从文件开始处开始移动,FILE_CURRENT当前位置,FILE_END文件末尾
6 );

例子:假设E盘下有一个名为a.txt的文件,内容为"123456789",读取该文件第四个字节到第七个字节的数据


 1 #include<stdio.h>
2
3 int main()
4 {
5 char Text[25]={0};
6 DWORD dwSize;
7 HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_READ,0,NULL,OPEN_ALWAYS,
8 FILE_ATTRIBUTE_NORMAL,NULL);
9 SetFilePointer(FileHandle,3,NULL,FILE_BEGIN);
10 ReadFile(FileHandle,Text,4,&dwSize,NULL);
11 printf("%s\n",Text);
12 return 0;

例子2:从文件中第四个字节开始写入数据,被新数据所占位置的数据会被覆盖掉,依旧以上面a.txt文件为例子


 1 #include<windows.h>
2
3 #include<stdio.h>
4
5 int main()
6 {
7 char Text[25]={"abcd"};
8 DWORD dwSize;
9 HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
10 FILE_ATTRIBUTE_NORMAL,NULL);
11 SetFilePointer(FileHandle,3,NULL,FILE_BEGIN);
12 WriteFile(FileHandle,Text,4,&dwSize,NULL);
13
14 return 0;
15 }

则写入后a.txt文件里的数据为123abcd89

如果要在文件的末尾添加数据,就用这个语句:SetFilePointer(FileHandle,0,NULL,FILE_END);


第八十三个GetFileSize获取一个文件的大小

函数定义:


1 DWORD GetFileSize(
2 HANDLE hFile, //文件句柄
3 LPDWORD lpFileSizeHigh //一般为NULL
4 );

如获取a.txt文件的大小:


 1 #include<windows.h>
2
3 #include<stdio.h>
4
5 int main()
6 {
7 DWORD FileSize;
8 HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
9 FILE_ATTRIBUTE_NORMAL,NULL);
10 FileSize=GetFileSize(FileHandle,NULL);
11 printf("%d字节\n",FileSize);
12 return 0;
13 }


第八十四个SetTextColor根据窗口输出文本颜色

第一个参数是设备DC,第二个参数是一个COLORREF类型的颜色值,可用RGB进行转换。


第八十五个SetBkColor设置背景颜色

参数跟SetTextColor函数一样,第一个DC,第二个COLORREF


第八十六个GetWindowDC获取整个窗口设备上下文DC

像GetDC获取的只是客户区DC,不能对窗口标题栏,状态栏等进行操作。该函数用法跟GetDC一样,仅区域不一样。

例子:在一个窗口的标题栏输出文字


 1 #include<windows.h>
2
3 int main()
4
5 {
6
7 HWND wnd=FindWindow(NULL,"无标题.txt - 记事本");
8 HDC dc=GetWindowDC(wnd);
9 SetTextColor(dc,RGB(255,0,0)); //文本颜色设置为红色
10 ::SetBkColor(dc,RGB(0,0,255)); //文本背景颜色设置为蓝色
11 while(1)
12 {
13 TextOut(dc,0,0,"123456",6);
14 Sleep(200);
15 }
16 return 0;
17 }


第八十七个GetDesktopWindow获取桌面窗口句柄

该函数没有参数,调用返回桌面窗口句柄

例子:


 1 #include<windows.h>
2 int main()
3 {
4 HWND DeskWnd=GetDesktopWindow(); //获取桌面窗口句柄
5 HDC DeskDC=GetWindowDC(DeskWnd); //获取DC
6 HBRUSH brush=::CreateSolidBrush(RGB(255,0,0)); //红色画刷
7 SelectObject(DeskDC,brush); //选入画刷
8 while(1)
9 {
10 ::Rectangle(DeskDC,50,50,200,200);
11 Sleep(200);
12 }
13 return 0;
14 }


第八十八个CreateCompatibleBitmap根据DC创造一个兼容的位图

该函数需与CreateCompatibleDC函数配合使用

函数第一个参数是窗口DC,第二,三参数指明窗口宽高,函数返回位图句柄(HBITMAP)

创建一个兼容的位图是什么意思呢?就好比给HBITMAP分配内存以及指定这位图相关的一些信息(跟DC相关的信息),如位图的宽高,数据大小,但此时数据没有具体取值。就好比一个字符串,我已经知道字符串大小了,但却不知道字符串具体是什么:

如:


1 char *p;
2
3 p=new char[15]; //知道字符串大小为15

但此时p所指向的缓存区,没有具体取值。

而用CreateCompatibleBitmap函数,创建的位图,只是一个空壳子。数据没有赋值,那要怎样给数据赋值呢?

首先得把这个位图句柄选入一个DC(该DC必须为CreateCompatibleDC函数创建的)里,然后再用BitBlt函数具体给数据赋值就行了。

例子:实时获取屏幕图像

为了方便,在记事本窗口输出图像,自己就不创建窗口了(打开"无标题.txt - 记事本")


 1 #include<windows.h>
2 int main()
3 {
4 HWND TextWnd=FindWindow(NULL,"无标题.txt - 记事本");
5 HDC TextDC=GetDC(TextWnd);
6 HWND DeskWnd=::GetDesktopWindow();
7 RECT DeskRC;
8 ::GetClientRect(DeskWnd,&DeskRC);
9 HDC DeskDC=GetDC(DeskWnd);
10 HBITMAP DeskBmp=::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom);
11 HDC memDC;
12 memDC=::CreateCompatibleDC(DeskDC);
13 SelectObject(memDC,DeskBmp);
14 while(1)
15 {
16 StretchBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,DeskRC.right,DeskRC.bottom,SRCCOPY);
17 RECT TextRC;
18 GetClientRect(TextWnd,&TextRC);
19 ::StretchBlt(TextDC,0,0,TextRC.right,TextRC.bottom,memDC,0,0,DeskRC.right,
20 DeskRC.bottom,SRCCOPY);
21 Sleep(300);
22 }
23 return 0;
24 }


第八十九个GetDIBits从一个兼容位图里获取位图数据
先来分析一下位图文件信息结构,一个位图由以下四部分组成:

位图文件头(BITMAPFILEHEADER)//占14字节

位图信息头(BITMAPINFOHEADER)//占40字节

调色板(LOGPALLETE)//如果真彩位图,那该部分没有,直接是位图数据

实际位图数据

而GetDIBits函数获取的就是实际位图数据这一部分了。

接来看一下BITMAPFILEHEADER这个结构以及它成员的意思和取值


1 typedef struct tagBITMAPFILEHEADER {
2 WORD bfType; //表示文件类型,值必须为0x4d42
3 DWORD bfSize; //文件的大小
4 WORD bfReserved1; //保留,必须为0
5 WORD bfReserved2; //保留,必须为0
6 DWORD bfOffBits; //位图前三部分所占的字节,真彩色位图为54
7 } BITMAPFILEHEADER;

上面的成员,只有bfSize的取值不确定,其它都一样,也就是说,每个真彩位图,这几个成员取值都是一样的.下面的例子可以说明。

读取一个真彩位图的文件信息头。


 1 #include<windows.h>
2
3 #include<stdio.h>
4 int main()
5 {
6 BITMAPFILEHEADER bfh;
7 HANDLE hFile=CreateFile("e:\\aa.bmp",GENERIC_READ,0,NULL,OPEN_EXISTING,
8 FILE_ATTRIBUTE_NORMAL,0);
9 DWORD dwSize;
10 ReadFile(hFile,(void *)&bfh,sizeof(BITMAPFILEHEADER),&dwSize,0);
11 printf("bfType:%x\n",bfh.bfType);
12 printf("bfSize:%d\n",bfh.bfSize);
13 printf("bfReserved1:%d\n",bfh.bfReserved1);
14 printf("bfReserved2:%d\n",bfh.bfReserved2);
15 printf("bfOffbits:%d\n",bfh.bfOffBits);
16 return 0;
17 }

再来看一下BITMAPINFOHEADER这个结构以及它成员的意思和取值


 1 typedef struct tagBITMAPINFOHEADER{
2 DWORD biSize; //本结构大小,为40
3 LONG biWidth; //位图的宽度,以像素为单位
4 LONG biHeight; //位图的高度,以像素为单位
5 WORD biPlanes; //目标设备的级别,必须是1
6 WORD biBitCount; //每个像素所占的位数,24表示真彩位图
7 DWORD biCompression; //位图压缩类型,一般为BI_RGB(未经压缩)
8 DWORD biSizeImage; //实际位图数据这部分的所占用的字节数
9 LONG biXPelsPerMeter; //指定目标设备水平分辨率,单位像素/米,为0
10 LONG biYPelsPerMeter; //指定目标垂直分辨率真,单位像素/米,为0
11 DWORD biClrUsed; //指定目标设备实际用到的颜色数,如果该值为0,则用到的颜色数为2的biBitCount方
12 DWORD biClrImportant; //表示图像中重要的颜色数,如果为0,则所有颜色都是重要的。
13 } BITMAPINFOHEADER;

调色板(LOGPALLETE)由于大部分都是针对真彩位图操作,此部分略过

GetDIBits函数定义:


 1 int GetDIBits(
2
3 HDC hdc, //位图兼容的DC
4
5 HBITMAP hbmp, //位图句柄
6
7 UINT uStartScan, //从哪行开始扫描
8
9 UINT cScanLines, //扫描多少行数据
10
11 LPVOID lpvBits, //接收数据的缓存区
12
13 LPBITMAPINFO lpbi, //真彩位图,此处填文件信息头就行了
14
15 UINT uUsage //真彩位图填DIB_RGB_COLORS,表示由R,G,B三色直接构成
16
17 );

例子:截屏,并把屏幕图片保存成位图


 1 #include<windows.h>
2 void ScreenSnap(HBITMAP hBitmap,char *bmpPath,HDC dc);
3 int main()
4 {
5 HWND DeskWnd=::GetDesktopWindow(); //获取桌面窗口句柄
6 RECT DeskRC;
7 ::GetClientRect(DeskWnd,&DeskRC); //获取窗口大小
8 HDC DeskDC=GetDC(DeskWnd); //获取窗口DC
9 HBITMAP DeskBmp=::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom); //兼容位图
10 HDC memDC=::CreateCompatibleDC(DeskDC); //兼容DC
11 SelectObject(memDC,DeskBmp); //把兼容位图选入兼容DC中
12 BitBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,SRCCOPY); //拷贝DC
13 ScreenSnap(DeskBmp,"d:\\Screen.bmp",DeskDC);
14 return 0;
15 }
16 void ScreenSnap(HBITMAP hBitmap,char *bmpPath,HDC dc)
17 {
18 BITMAP bmInfo;
19 DWORD bmDataSize;
20 char *bmData; //位图数据
21 GetObject(hBitmap,sizeof(BITMAP),&bmInfo); //根据位图句柄,获取位图信息
22 bmDataSize=bmInfo.bmWidthBytes*bmInfo.bmHeight; //计算位图数据大小
23 bmData=new char[bmDataSize]; //分配数据
24 BITMAPFILEHEADER bfh; //位图文件头
25 bfh.bfType=0x4d42;
26 bfh.bfSize=bmDataSize+54;
27 bfh.bfReserved1=0;
28 bfh.bfReserved2=0;
29 bfh.bfOffBits=54;
30 BITMAPINFOHEADER bih; //位图信息头
31 bih.biSize=40;
32 bih.biWidth=bmInfo.bmWidth;
33 bih.biHeight=bmInfo.bmHeight;
34 bih.biPlanes=1;
35 bih.biBitCount=24;
36 bih.biCompression=BI_RGB;
37 bih.biSizeImage=bmDataSize;
38 bih.biXPelsPerMeter=0;
39 bih.biYPelsPerMeter=0;
40 bih.biClrUsed=0;
41 bih.biClrImportant=0;
42 ::GetDIBits(dc,hBitmap,0,bmInfo.bmHeight,bmData,(BITMAPINFO *)&bih,DIB_RGB_COLORS);//获取位图数据部分
43 HANDLE hFile=CreateFile(bmpPath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
44 FILE_ATTRIBUTE_NORMAL,0); //创建文件
45 DWORD dwSize;
46 WriteFile(hFile,(void *)&bfh,sizeof(BITMAPFILEHEADER),&dwSize,0); //写入位图文件头
47 WriteFile(hFile,(void *)&bih,sizeof(BITMAPINFOHEADER),&dwSize,0); //写入位图信息头
48 WriteFile(hFile,(void *)bmData,bmDataSize,&dwSize,0); //写入位图数据
49 ::CloseHandle(hFile); //关闭文件句柄
50 }

0 0
原创粉丝点击