-
2020-12-18 21:54:48
新建一个文本文档,打开,Windows就会给这个文本文档的窗口临时分配唯一的一串数字来标识这个窗体,以区别于其他窗口,这串数字就叫句柄。
因为句柄是临时随机分配的,所以每次虽然是打开同一个文件,但是分配到的一串数字却是不一样的。
所以我们要通过窗体的标题栏来确定每次不同的句柄。
例如,我打开一个“新建 文本文档 (5).txt”,打开,文本文档的标题栏是“新建 文本文档 (5).txt - 记事本”,那么,我就通过“新建 文本文档 (5).txt - 记事本”(注意:不是文件名)来查找这个文本文档的句柄。
Hwnd = Plugin.Window.Find(0, "新建 文本文档 (5).txt - 记事本")
Delay 2000
然后通过句柄对这个文档进行最大化操作
Plugin.Window.Min Hwnd
Delay 2000
要对这个文本文档进行写入,就要另外单击文本区域,文本区域是这个窗体的一部分,有自己的句柄,我们称之为“子句柄”。我们可以通过刚才找到的窗口句柄,类(抓抓里面获取),来查找窗体里面的某一独立部分的子句柄。然后对子句柄所标识的窗体部分写入文本。
HwndEx = Plugin.Window.FindEx(Hwnd, 0, "Edit", 0)
Delay 2000
Call Plugin.Window.SendString(HwndEx, "hello,word,I am the king")
Delay 2000
“全部命令”--“插件命令”--“windows 窗口插件” 可找到以上函数
后台:什么意思?
比如一个游戏,窗口最大化后,我们可以玩;但是我们把它最小化后,虽然我们看不见了,它还是没有停止,它还在幕后(前台后面)继续运行,这个就叫后台。
又例如,我们挪动鼠标双击我的电脑,这个过程可视;如果通过后台执行这个操作,我们虽然看不到鼠标挪动的过程,但是也可以达到双击我的电脑的效果。
Hwnd = Plugin.Window.MousePoint() 获得当前鼠标所在窗体的句柄的命令
Call Plugin.Bkgnd.LeftClick(Hwnd, 36, 98) 在后台单击选中“我的电脑”,前提是执行时候鼠标要挪到桌面上。貌似按键精灵对鼠标后台双击单击做得不好,有时不灵。
Hwnd = Plugin.Window.MousePoint()
XY = Plugin.Bkgnd.FindColor(Hwnd, 0, 0, 1024, 768, "0201E1") '区域找色
ZB = InStr(XY, "|")
X = Clng(Left(XY, ZB - 1))
Y = Clng(Right(XY, Len(XY) - ZB))
MsgBox "x"&X &"y"& Y '弹出窗口,指定颜色如果找到,显示他的点的坐标
注意:插入语句默认是X = Clng(Left(XY, ZB - 1)): Y = Clng(Right(XY, Len(XY) - ZB))这样的
要把冒号改掉,分2行。
获得句柄的几个函数。注意,如果不打开窗口,是无法获得句柄的。标题名也不能写错,写错也无法获取。但是会有返回值。
Hwnd = Plugin.Window.GetKeyFocusWnd() 获得当前激活的窗口句柄,激活的窗口鼠标不一定在上面
Hwnd = Plugin.Window.MousePoint() 获得鼠标当前停留的窗口的句柄,当前窗口状态未必激活(被点选)
Hwnd = Plugin.Window.Find(0, "无标题 - 记事本") 获取窗口标题栏为“无标题 - 记事本”的窗口的句柄
一般优先使用Plugin.Window.Find,如果窗口标题不固定,再考虑使用其他两个函数
Hwnd = Plugin.Window.Find(0, "新建 文本文档 (7).txt - 记事本")
sRect = Plugin.Window.GetClientRect(Hwnd) '获取窗口的4个坐标
myArray=Split(srect,"|") '分离出四个坐标 因为Split就是数组函数,所myArray虽然没有定义,也被默认定义成了动态数组变量
MessageBox myArray(0) & "," & myArray(1) '显示出变量起始点的坐标
a = CLng(myArray(0)) '将变量里的值从字符变为数字类型,然后才能用moveTo函数
b = CLng(myArray(1))
MoveTo a, b
获取窗口坐标的目的:窗口的位置不固定,但是窗口内的内容相对于窗口的位置是固定的,“绝对坐标”+“相对坐标”,结合ifColor函数,进行判断
KeyDown 就要记得 keyUp
截取一部分,才有共性。比如找图,找游戏人物,不建议截取增个人,因为人的穿着装备是会变化的;可以截取脸部或者其他一部分。
抓抓工具,可以设定起始坐标,来获得相对坐标。
用户自定义变量http://zy.anjian.com/index.php?action-viewnews-itemid-220
用户自定义变量,也就是说用户可以输入参数(在脚本属性--其他),然后传给程序执行,提高程序灵活性。
UserVar TheKeyYouPress "你要按下的键" '注意最后的中文是固定格式,一定要有,不是注释
UserVar TheTimeYouWant "你要按几次"
For TheTimeYouWant
KeyPress TheKeyYouPress, 1
Next
OCX界面(很鸡肋的一个功能,效果也不好,建议直接用“用户自定义变量”)
利用VB对C:\Program Files\按键精灵9\source\自定义界面例子(VB 6.0)\VB-BIG-001\QMacroUI.vbp修改制作界面,然后做成OCX文件。
然后再按键精灵右侧 脚本属性--其他--设置界面 里面,把OCX文件包含进来。
主要原理:例如,VB设计界面的时候,文本框的名称设置成 TheKeyYouPress 要和按键精灵脚本的自定义变量UserVar TheKeyYouPress "你要按下的键",两个要一样。
http://www.aipai.com/c9/ODY9JSknImgnaiYp.html 例子里面大致那样,实际用按键精灵9用起来还有一定问题。
例子里面改了很多地方,其实主要功能就是保存上次你输入的参数,一个将保存好的参数导出。
【笨嘴拙舌WINDOWS】实践检验之按键精灵【Delphi】
通过记录键盘和鼠标位置和输入信息,然后模拟发送,就能够创建一个按键精灵! 主要代码如下: library KeyBoardHook; { Important note about DLL memory ...
Windows窗口消息大全(转)
Windows窗口消息大全,全不全自己看 // #inc ...
Windows窗口消息大全
// #include "AFXPRIV.H& ...
【转】Windows 窗口层次关系
原文链接:undefined! 相信在Windows 下面编程的很多兄弟们都不是很清楚Windows 中窗口的层次关系是怎么样的,这个东西很久已经研究过一下,后来又忘记了,今天又一次遇到了这个问题,所 ...
教程-隐藏/显示任务栏-程序不在任务显示-全面控制Windows
1.隐藏任务条 var h:THandle; //变量h:=FindWindow('Shell_TrayWnd',nil);ShowWindow(h,SW_hide); 2.显示任务条h:=Find ...
win32 api Windows窗口的创建
windows窗口的创建有以下几个步骤: 1.创建注册窗口类 2.创建窗口句柄 3.显示更新窗口 4.消息循环 1.创建注册窗口类 所谓创建窗口类就是定义一个WNDCLASS类对象,并将该对象进行初始 ...
[MFC]_在vs2019中使用MFC快速构建简单windows窗口程序
微软基础类库(英语: Classes,简称MFC)是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发 ...
[教程] 以本论坛为例,手把手教你使用按键精灵POST登陆网页
本帖最后由 isaacc 于 2012-2-26 11:08 编辑 整个操作,很无脑.只要你够勤快,你学不会,你来咬我.懒人和伸手党就直接复制代码去玩吧,但我不是叫你拿去干坏事. 准备工具:WPE和I ...
Windows窗口的创建
Windows窗口创建的基本代码: #include LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); i ...
随机推荐
Jquery
使用时jquery先引进jquery文件包 一个页面有多个文件jq ...
Date类型时间转换
/* 时间转换start */ public static void main(String args[]) { Date nowTime = new Date(); System.out.print ...
wince6.0应用程序自启动
主要思想:将应用程序添加到image里,然后用应用程序代替桌面应用程序,从而使应用程序自启动. 主要步骤: 1.将应用程序MyApp.exe拷贝到wince600\OS ...
cocos2dx跨平台使用自定义字体
首先需要一个ttf文件的字体. 在ios中的方法: 把ttf文件放入资源文件下,然后在你的工程的Info.plist文件中新建一行(Add Row),添加key为:Fonts provided by ...
Win64位操作系统无法运行暗黑2战网D2GS的解决办法
前几天想在我的Win7 x64系统里做个战网自己玩,搭建完毕后进入战网创建房间出现经典的问题,“排队1”. 原因很清楚,就是D2GS无法启动:但是使用之前的各种办法尝试后无果,后来查看D2GS同目录下 ...
【POJ】【1067】取石子游戏
博弈论 这个是博弈游戏中的Wythoff博弈: 以下为我的代码: //POJ 1067 #include #include #include< ...
group by的SQL语句
有一张项目表 CREATE TABLE [ProjectTable] ( [ProjectID] NVARCHAR(16) NOT NULL, [ProjectName] NVARCHAR(20) N ...
Excel Skill (1) -- 判断时如何去掉框里的空格
使用命令 TRIM 说明: Purpose. Remove extra spaces from text. Text with extra spaces removed. =TRIM (text) t ...
[洛谷U990]传递游戏(90分)
[题目描述 Description] n个人在做传递物品的游戏,编号为1-n. 游戏规则是这样的:开始时物品可以在任意一人手上,他可把物品传递给其他人中的任意一位:下一个人可以传递给未接过物品的任意一 ...
C/C++的静态库与动态库
C/C++编程中相关文件后缀(以Linux系统下为例): .a: 静态库(archive) .c/.cpp: C/C++源程序 .h/.hpp: C/C++源程序的头文件 .i: ...
更多相关内容 -
C#通过Windows API捕获窗,获取窗口文本(FindWindow、GetWindowText),附录:Windows窗口消息大全、...
2021-01-21 15:03:56文章目录一、前言二、使用Spy++工具分析窗口三、C#通过Windows API捕获窗口,获取窗口文本四、附录:Windows窗口消息 一、前言 项目是Unity开发的,上架了QQ游戏大厅,需要兼容XP系统。 QQ游戏大厅启动游戏的流程是...文章目录
一、前言
项目是
Unity
开发的,上架了QQ
游戏大厅,需要兼容XP
系统。
QQ
游戏大厅启动游戏的流程是这样:
1 QQ游戏大厅.exe -------> 2 下载器.exe -------> 3 Unity游戏.exe
在XP
中测试的时候,Unity游戏.exe
运行时报了如下的Error
。
那么,我们不确定有多少用户遇到了这个问题,所以需要进行数据上报,在下载器中加上逻辑检测,当下载器去启动Unity
游戏的时候,等待2秒,判断是否有Error
窗口,如果有,则捕获这个窗口并取出里面的文本,上报给服务器,方便数据分析统计。下载器我是用
C#
的.Net Framework
桌面应用做的,所以这里就涉及到如何在C#
中捕获Windows
窗口的问题,下面就介绍一下我的实现方法。二、使用Spy++工具分析窗口
我们可以使用
Spy++
工具分析一下窗口。这里我用的是Spy++ Lite
,只有几百K大小,非常轻便。
Spy++ Lite
下载:
https://pan.baidu.com/s/1Iah1TyWo6dUv8cHSMcvBLg
提取码:mtsqSpy++ Lite是一款功能丰富的编程辅助工具,适用于获取窗口句柄并分析窗体结构。可以探测32位和64位应用程序。可以以十六进制和十进制显示窗口句柄、窗口样式和类样式等数值。可以获取父窗口和兄弟窗口、子窗口结构并形成句柄树,还可以调节窗口的状态和行为,获取程序路径、给窗口截图等。软件还支持获取列表控件数据,如任务管理器、股票行情数据等,还支持获取树视图、下拉框、列表框和菜单数据和字体信息等。
注意,
.OCX
文件需要注册,注册.OCX
文件步骤:
(1) 将COMCTL32.OCX
文件拷贝到C盘
下面这个目录中:32位系统:c:\WINDOWS\system32
;64位系统:c:\Windows\SysWOW64
如下,因为我是32位 XP
,所以放在c:\WINDOWS\system32
中。
(2)打开运行
,输入注册命令,
32
位系统:regsvr32 c:\WINDOWS\system32\COMCTL32.OCX
64
位系统:regsvr32 c:\Windows\SysWOW64\COMCTL32.OCX
注册成功。
接下来就可以运行Spy++ Lite
了。
使用Spy++
分析Error
窗口的信息。
根据上面的操作,我们可以知道:
窗口标题是Error
,这个窗口下有一个子窗口,子窗口标题为Edit
,子窗口中的文本为:Failed to initialize Direct3D. Make sure you have at least DirectX 9.0c installed, have drivers for your graphics card and have not disabled 3D acceleration in display settings. InitializeEngineGraphics failed
我们要做的,就是找到这个子窗口,取出文本,下面就是
C#
代码来实现这个逻辑了。三、C#通过Windows API捕获窗口,获取窗口文本
Windows
封装了很多API
,我们可以通过这些API
来访问窗口和操作窗口。
其中用到了几个API
// 查找窗口 [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); // 遍历窗口的所有子窗口,通过CallBack回调 [DllImport("user32.dll")] public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam); public delegate bool CallBack(IntPtr hwnd, int lParam); // 获取窗口的类名 [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); // 判断窗口是否可见 [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); // 获取窗口文本长度 [DllImport("user32.dll")] public static extern int GetWindowTextLength(IntPtr hWnd); // 获取窗口文本,文本会塞入StringBuilder中,需要指明字符串最大长度nMaxCount [DllImport("User32.dll", EntryPoint = "GetWindowText")] private static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount); // 给窗口发送消息 [DllImport("user32.dll", EntryPoint = "SendMessageA")] public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); // 给窗口发送消息,事件返回的数据通过Byte[]数组获得 [DllImport("user32.dll", EntryPoint = "SendMessageA")] public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, Byte[] lParam);
完整代码如下:
using System; using System.Runtime.InteropServices; using System.Text; public partial class WindowApiHelper { /// <summary> /// 尝试查找Error窗口并取出窗口文本 /// </summary> /// <returns></returns> public static string TryFindErrorWindowText() { string errorText = ""; // 查找标题为Error的窗口 IntPtr mainHandle = FindWindow(null, "Error"); if (mainHandle != IntPtr.Zero) { // 枚举子窗体,查找控件句柄 int i = EnumChildWindows(mainHandle, (h, l) => { StringBuilder sbr = new StringBuilder(); GetClassName(h, sbr, 255); string classname = sbr.ToString(); // 获取Edit子窗口 if ("Edit" == classname) { // 是否可见 if (IsWindowVisible(h)) { // 取出窗口文本 int textLen; textLen = SendMessage(h, WM_GETTEXTLENGTH, 0, 0); Byte[] byt = new Byte[textLen]; SendMessage(h, WM_GETTEXT, textLen + 1, byt); errorText = Encoding.Default.GetString(byt); // 关闭Error窗口 // SendMessage(h, WM_CLOSE , 0, 0); } } return true; }, 0); } return errorText; } /*--Windows API------------------------------------------------------------------------------------*/ [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam); public delegate bool CallBack(IntPtr hwnd, int lParam); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] public static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("User32.dll", EntryPoint = "GetWindowText")] private static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", EntryPoint = "SendMessageA")] public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); [DllImport("user32.dll", EntryPoint = "SendMessageA")] public static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, Byte[] lParam); const int WM_GETTEXT = 0x000D; const int WM_GETTEXTLENGTH = 0x000E; const int WM_CLOSE = 0x10; /*--Windows API------------------------------------------------------------------------------------*/ }
四、附录:Windows窗口消息
消息 描述 WM_CREATE = 0x0001; 应用程序创建一个窗口 WM_DESTROY = 0x0002; 一个窗口被销毁 WM_MOVE = 0x0003; 移动一个窗口 WM_SIZE = 0x0005; 改变一个窗口的大小 WM_ACTIVATE = 0x0006; 一个窗口被激活或失去激活状态; WM_SETFOCUS = 0x0007; 获得焦点后 WM_KILLFOCUS = 0x0008; 失去焦点 WM_ENABLE = 0x000A; 改变enable状态 WM_SETREDRAW = 0x000B; 设置窗口是否能重画 WM_SETTEXT = 0x000C; 应用程序发送此消息来设置一个窗口的文本 WM_GETTEXT = 0x000D; 应用程序发送此消息来复制对应窗口的文本到缓冲区 WM_GETTEXTLENGTH = 0x000E; 得到与一个窗口有关的文本的长度(不包含空字符) WM_PAINT = 0x000F; 要求一个窗口重画自己 WM_CLOSE = 0x0010; 当一个窗口或应用程序要关闭时发送一个信号 WM_QUERYENDSESSION = 0x0011; 当用户选择结束对话框或程序自己调用ExitWindows函数 WM_QUIT = 0x0012; 用来结束程序运行或当程序调用postquitmessage函数 WM_QUERYOPEN = 0x0013; 当用户窗口恢复以前的大小位置时,把此消息发送给某个图标 WM_ERASEBKGND = 0x0014; 当窗口背景必须被擦除时(例在窗口改变大小时) WM_SYSCOLORCHANGE = 0x0015; 当系统颜色改变时,发送此消息给所有顶级窗口 WM_ENDSESSION = 0x0016; 当系统进程发出WM_QUERYENDSESSION消息后,此消息发送给应用程序,通知它对话是否结束 WM_SHOWWINDOW = 0x0018; 当隐藏或显示窗口是发送此消息给这个窗口 WM_ACTIVATEAPP = 0x001C; 发此消息给应用程序哪个窗口是激活的,哪个是非激活的 WM_FONTCHANGE = 0x001D; 当系统的字体资源库变化时发送此消息给所有顶级窗口 WM_TIMECHANGE = 0x001E; 当系统的时间变化时发送此消息给所有顶级窗口 WM_CANCELMODE = 0x001F; 发送此消息来取消某种正在进行的摸态(操作) WM_SETCURSOR = 0x0020; 如果鼠标引起光标在某个窗口中移动且鼠标输入没有被捕获时,就发消息给某个窗口 WM_MOUSEACTIVATE = 0x0021; 当光标在某个非激活的窗口中而用户正按着鼠标的某个键发送此消息给当前窗口 WM_CHILDACTIVATE = 0x0022; 发送此消息给MDI子窗口当用户点击此窗口的标题栏,或当窗口被激活,移动,改变大小 WM_QUEUESYNC = 0x0023; 此消息由基于计算机的训练程序发送,通过WH_JOURNALPALYBACK的hook程序分离出用户输入消息 WM_GETMINMAXINFO = 0x0024; 此消息发送给窗口当它将要改变大小或位置; WM_PAINTICON = 0x0026; 发送给最小化窗口当它图标将要被重画 WM_ICONERASEBKGND = 0x0027; 此消息发送给某个最小化窗口,仅当它在画图标前它的背景必须被重画 WM_NEXTDLGCTL = 0x0028; 发送此消息给一个对话框程序去更改焦点位置 WM_SPOOLERSTATUS = 0x002A; 每当打印管理列队增加或减少一条作业时发出此消息 WM_DRAWITEM = 0x002B; 当button,combobox,listbox,menu的可视外观改变时发送此消息给这些空件的所有者 WM_MEASUREITEM = 0x002C; 当button, combo box, list box, list view control, or menu item 被创建时发送此消息给控件的所有者 WM_DELETEITEM = 0x002D; 当the list box 或 combo box 被销毁 或 当 某些项被删除通过LB_DELETESTRING, LB_RESETCONTENT, CB_DELETESTRING, or CB_RESETCONTENT 消息 WM_VKEYTOITEM = 0x002E; 此消息有一个LBS_WANTKEYBOARDINPUT风格的发出给它的所有者来响应WM_KEYDOWN消息 WM_CHARTOITEM = 0x002F; 此消息由一个LBS_WANTKEYBOARDINPUT风格的列表框发送给他的所有者来响应WM_CHAR消息 WM_SETFONT = 0x0030; 当绘制文本时程序发送此消息得到控件要用的颜色 WM_GETFONT = 0x0031; 应用程序发送此消息得到当前控件绘制文本的字体 WM_SETHOTKEY = 0x0032; 应用程序发送此消息让一个窗口与一个热键相关连 WM_GETHOTKEY = 0x0033; 应用程序发送此消息来判断热键与某个窗口是否有关联 WM_QUERYDRAGICON = 0x0037; 此消息发送给最小化窗口,当此窗口将要被拖放而它的类中没有定义图标,应用程序能返回一个图标或光标的句柄,当用户拖放图标时系统显示这个图标或光标 WM_COMPAREITEM = 0x0039; 发送此消息来判定combobox或listbox新增加的项的相对位置 WM_COMPACTING = 0x0041; 显示内存已经很少了 WM_WINDOWPOSCHANGING = 0x0046; 发送此消息给那个窗口的大小和位置将要被改变时,来调用setwindowpos函数或其它窗口管理函数 WM_WINDOWPOSCHANGED = 0x0047; 发送此消息给那个窗口的大小和位置已经被改变时,来调用setwindowpos函数或其它窗口管理函数 WM_POWER = 0x0048;(适用于16位的windows) 当系统将要进入暂停状态时发送此消息 WM_COPYDATA = 0x004A; 当一个应用程序传递数据给另一个应用程序时发送此消息 WM_CANCELJOURNAL = 0x004B; 当某个用户取消程序日志激活状态,提交此消息给程序 WM_NOTIFY = 0x004E; 当某个控件的某个事件已经发生或这个控件需要得到一些信息时,发送此消息给它的父窗口 WM_INPUTLANGCHANGEREQUEST = 0x0050; 当用户选择某种输入语言,或输入语言的热键改变 WM_INPUTLANGCHANGE = 0x0051; 当平台现场已经被改变后发送此消息给受影响的最顶级窗口 WM_TCARD = 0x0052; 当程序已经初始化windows帮助例程时发送此消息给应用程序 WM_HELP = 0x0053; 此消息显示用户按下了F1,如果某个菜单是激活的,就发送此消息个此窗口关联的菜单,否则就发送给有焦点的窗口,如果当前都没有焦点,就把此消息发送给当前激活的窗口 WM_USERCHANGED = 0x0054; 当用户已经登入或退出后发送此消息给所有的窗口,当用户登入或退出时系统更新用户的具体设置信息,在用户更新设置时系统马上发送此消息; WM_NOTIFYFORMAT = 0x0055; 公用控件,自定义控件和他们的父窗口通过此消息来判断控件是使用ANSI还是UNICODE结构在WM_NOTIFY消息,使用此控件能使某个控件与它的父控件之间进行相互通信 WM_CONTEXTMENU = 0x007B; 当用户某个窗口中点击了一下右键就发送此消息给这个窗口 WM_STYLECHANGING = 0x007C; 当调用SETWINDOWLONG函数将要改变一个或多个 窗口的风格时发送此消息给那个窗口 WM_STYLECHANGED = 0x007D; 当调用SETWINDOWLONG函数一个或多个 窗口的风格后发送此消息给那个窗口 WM_DISPLAYCHANGE = 0x007E; 当显示器的分辨率改变后发送此消息给所有的窗口 WM_GETICON = 0x007F; 此消息发送给某个窗口来返回与某个窗口有关连的大图标或小图标的句柄; WM_SETICON = 0x0080; 程序发送此消息让一个新的大图标或小图标与某个窗口关联; WM_NCCREATE = 0x0081; 当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送; WM_NCDESTROY = 0x0082; 此消息通知某个窗口,非客户区正在销毁 WM_NCCALCSIZE = 0x0083; 当某个窗口的客户区域必须被核算时发送此消息 WM_NCHITTEST = 0x0084; 移动鼠标,按住或释放鼠标时发生 WM_NCPAINT = 0x0085; 程序发送此消息给某个窗口当它(窗口)的框架必须被绘制时; WM_NCACTIVATE = 0x0086; 此消息发送给某个窗口 仅当它的非客户区需要被改变来显示是激活还是非激活状态; WM_GETDLGCODE = 0x0087; 发送此消息给某个与对话框程序关联的控件,widdows控制方位键和TAB键使输入进入此控件,通过响应WM_GETDLGCODE消息,应用程序可以把他当成一个特殊的输入控件并能处理它 WM_NCMOUSEMOVE = 0x00A0; 当光标在一个窗口的非客户区内移动时发送此消息给这个窗口 非客户区为:窗体的标题栏及窗的边框体 WM_NCLBUTTONDOWN = 0x00A1; 当光标在一个窗口的非客户区同时按下鼠标左键时提交此消息 WM_NCLBUTTONUP = 0x00A2; 当用户释放鼠标左键同时光标某个窗口在非客户区十发送此消息; WM_NCLBUTTONDBLCLK = 0x00A3; 当用户双击鼠标左键同时光标某个窗口在非客户区十发送此消息 WM_NCRBUTTONDOWN = 0x00A4; 当用户按下鼠标右键同时光标又在窗口的非客户区时发送此消息 WM_NCRBUTTONUP = 0x00A5; 当用户释放鼠标右键同时光标又在窗口的非客户区时发送此消息 WM_NCRBUTTONDBLCLK = 0x00A6; 当用户双击鼠标右键同时光标某个窗口在非客户区十发送此消息 WM_NCMBUTTONDOWN = 0x00A7; 当用户按下鼠标中键同时光标又在窗口的非客户区时发送此消息 WM_NCMBUTTONUP = 0x00A8; 当用户释放鼠标中键同时光标又在窗口的非客户区时发送此消息 WM_NCMBUTTONDBLCLK = 0x00A9; 当用户双击鼠标中键同时光标又在窗口的非客户区时发送此消息 WM_KEYDOWN = 0x0100; 按下一个键 WM_KEYUP = 0x0101; 释放一个键 WM_CHAR = 0x0102; 按下某键,并已发出WM_KEYDOWN, WM_KEYUP消息 WM_DEADCHAR = 0x0103; 当用translatemessage函数翻译WM_KEYUP消息时发送此消息给拥有焦点的窗口 WM_SYSKEYDOWN = 0x0104; 当用户按住ALT键同时按下其它键时提交此消息给拥有焦点的窗口; WM_SYSKEYUP = 0x0105; 当用户释放一个键同时ALT 键还按着时提交此消息给拥有焦点的窗口 WM_SYSCHAR = 0x0106; 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后提交此消息给拥有焦点的窗口 WM_SYSDEADCHAR = 0x0107; 当WM_SYSKEYDOWN消息被TRANSLATEMESSAGE函数翻译后发送此消息给拥有焦点的窗口 WM_INITDIALOG = 0x0110; 在一个对话框程序被显示前发送此消息给它,通常用此消息初始化控件和执行其它任务 WM_COMMAND = 0x0111; 当用户选择一条菜单命令项或当某个控件发送一条消息给它的父窗口,一个快捷键被翻译 WM_SYSCOMMAND = 0x0112; 当用户选择窗口菜单的一条命令或当用户选择最大化或最小化时那个窗口会收到此消息 WM_TIMER = 0x0113; 发生了定时器事件 WM_HSCROLL = 0x0114; 当一个窗口标准水平滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件 WM_VSCROLL = 0x0115; 当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口也,发送给拥有它的控件 WM_INITMENU = 0x0116; 当一个菜单将要被激活时发送此消息,它发生在用户菜单条中的某项或按下某个菜单键,它允许程序在显示前更改菜单 WM_INITMENUPOPUP = 0x0117; 当一个下拉菜单或子菜单将要被激活时发送此消息,它允许程序在它显示前更改菜单,而不要改变全部 WM_MENUSELECT = 0x011F; 当用户选择一条菜单项时发送此消息给菜单的所有者(一般是窗口) WM_MENUCHAR = 0x0120; 当菜单已被激活用户按下了某个键(不同于加速键),发送此消息给菜单的所有者; WM_ENTERIDLE = 0x0121; 当一个模态对话框或菜单进入空载状态时发送此消息给它的所有者,一个模态对话框或菜单进入空载状态就是在处理完一条或几条先前的消息后没有消息它的列队中等待 WM_CTLCOLORMSGBOX = 0x0132; 在windows绘制消息框前发送此消息给消息框的所有者窗口,通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置消息框的文本和背景颜色 WM_CTLCOLOREDIT = 0x0133; 当一个编辑型控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置编辑框的文本和背景颜色 WM_CTLCOLORLISTBOX = 0x0134; 当一个列表框控件将要被绘制前发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置列表框的文本和背景颜色 WM_CTLCOLORBTN = 0x0135; 当一个按钮控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置按纽的文本和背景颜色 WM_CTLCOLORDLG = 0x0136; 当一个对话框控件将要被绘制前发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置对话框的文本背景颜色 WM_CTLCOLORSCROLLBAR= 0x0137; 当一个滚动条控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置滚动条的背景颜色 WM_CTLCOLORSTATIC = 0x0138; 当一个静态控件将要被绘制时发送此消息给它的父窗口;通过响应这条消息,所有者窗口可以通过使用给定的相关显示设备的句柄来设置静态控件的文本和背景颜色 WM_MOUSEMOVE = 0x0200; 移动鼠标 WM_LBUTTONDOWN = 0x0201; 按下鼠标左键 WM_LBUTTONUP = 0x0202; 释放鼠标左键 WM_LBUTTONDBLCLK = 0x0203; 双击鼠标左键 WM_RBUTTONDOWN = 0x0204; 按下鼠标右键 WM_RBUTTONUP = 0x0205; 释放鼠标右键 WM_RBUTTONDBLCLK = 0x0206; 双击鼠标右键 WM_MBUTTONDOWN = 0x0207; 按下鼠标中键 WM_MBUTTONUP = 0x0208; 释放鼠标中键 WM_MBUTTONDBLCLK = 0x0209; 双击鼠标中键 WM_MOUSEWHEEL = 0x020A; 当鼠标轮子转动时发送此消息个当前有焦点的控件 WM_PARENTNOTIFY = 0x0210; 当MDI子窗口被创建或被销毁,或用户按了一下鼠标键而光标在子窗口上时发送此消息给它的父窗口 WM_ENTERMENULOOP = 0x0211; 发送此消息通知应用程序的主窗口that已经进入了菜单循环模式 WM_EXITMENULOOP = 0x0212; 发送此消息通知应用程序的主窗口that已退出了菜单循环模式 WM_SIZING = 532; 当用户正在调整窗口大小时发送此消息给窗口;通过此消息应用程序可以监视窗口大小和位置也可以修改他们 WM_CAPTURECHANGED = 533; 发送此消息 给窗口当它失去捕获的鼠标时; WM_MOVING = 534; 当用户在移动窗口时发送此消息,通过此消息应用程序可以监视窗口大小和位置也可以修改他们; WM_POWERBROADCAST = 536; 此消息发送给应用程序来通知它有关电源管理事件; WM_DEVICECHANGE = 537; 当设备的硬件配置改变时发送此消息给应用程序或设备驱动程序 WM_MDICREATE = 0x0220; 应用程序发送此消息给多文档的客户窗口来创建一个MDI 子窗口 WM_MDIDESTROY = 0x0221; 应用程序发送此消息给多文档的客户窗口来关闭一个MDI 子窗口 五、Windows API大全
1、API之网络函数
windows api 描述 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一个网络资源的连接 WNetAddConnection3 创建同一个网络资源的连接 WNetCancelConnection 结束一个网络连接 WNetCancelConnection2 结束一个网络连接 WNetCloseEnum 结束一次枚举操作 WNetConnectionDialog 启动一个标准对话框,以便建立同网络资源的连接 WNetDisconnectDialog 启动一个标准对话框,以便断开同网络资源的连接 WNetEnumResource 枚举网络资源 WNetGetConnection 获取本地或已连接的一个资源的网络名称 WNetGetLastError 获取网络错误的扩展错误信息 WNetGetUniversalName 获取网络中一个文件的远程名称以及/或者UNC(统一命名规范)名称 WNetGetUser 获取一个网络资源用以连接的名字 WNetOpenEnum 启动对网络资源进行枚举的过程 2、API之消息函数
windows api 描述 BroadcastSystemMessage 将一条系统消息广播给系统中所有的顶级窗口 GetMessagePos 取得消息队列中上一条消息处理完毕时的鼠标指针屏幕位置 GetMessageTime 取得消息队列中上一条消息处理完毕时的时间 PostMessage 将一条消息投递到指定窗口的消息队列 PostThreadMessage 将一条消息投递给应用程序 RegisterWindowMessage 获取分配给一个字串标识符的消息编号 ReplyMessage 答复一个消息 SendMessage 调用一个窗口的窗口函数,将一条消息发给那个窗口 SendMessageCallback 将一条消息发给窗口 SendMessageTimeout 向窗口发送一条消息 SendNotifyMessage 向窗口发送一条消息 3、API之文件处理函数
windows api 描述 CloseHandle 关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等 CompareFileTime 对比两个文件的时间 CopyFile 复制文件 CreateDirectory 创建一个新目录 CreateFile 打开和创建文件、管道、邮槽、通信服务、设备以及控制台 CreateFileMapping 创建一个新的文件映射对象 DeleteFile 删除指定文件 DeviceIoControl 对设备执行指定的操作 DosDateTimeToFileTime 将DOS日期和时间值转换成一个 win32 FILETIME 值 FileTimeToDosDateTime 将一个 win32 FILETIME 值转换成DOS日期和时间值 FileTimeToLocalFileTime 将一个FILETIME结构转换成本地时间 FileTimeToSystemTime 根据一个FILETIME结构的内容,装载一个SYSTEMTIME结构 FindClose 关闭由FindFirstFile函数创建的一个搜索句柄 FindFirstFile 根据文件名查找文件 FindNextFile 根据调用FindFirstFile函数时指定的一个文件名查找下一个文件 FlushFileBuffers 针对指定的文件句柄,刷新内部文件缓冲区 FlushViewOfFile 将写入文件映射缓冲区的所有数据都刷新到磁盘 GetBinaryType 判断文件是否可以执行 GetCompressedFileSize 判断一个压缩文件在磁盘上实际占据的字节数 GetCurrentDirectory 在一个缓冲区中装载当前目录 GetDiskFreeSpace 获取与一个磁盘的组织有关的信息,以及了解剩余空间的容量 GetDiskFreeSpaceEx 获取与一个磁盘的组织以及剩余空间容量有关的信息 GetDriveType 判断一个磁盘驱动器的类型 GetExpandedName 取得一个压缩文件的全名 GetFileAttributes 判断指定文件的属性 GetFileInformationByHandle 这个函数提供了获取文件信息的一种机制 GetFileSize 判断文件长度 GetFileTime 取得指定文件的时间信息 GetFileType 在给出文件句柄的前提下,判断文件类型 GetFileVersionInfo 从支持版本标记的一个模块里获取文件版本信息 GetFileVersionInfoSize 针对包含了版本资源的一个文件,判断容纳文件版本信息需要一个多大的缓冲区 GetFullPathName 获取指定文件的完整路径名 GetLogicalDrives 判断系统中存在哪些逻辑驱动器字母 GetLogicalDriveStrings 获取一个字串,其中包含了当前所有逻辑驱动器的根驱动器路径 GetOverlappedResult 判断一个重叠操作当前的状态 GetPrivateProfileInt 为初始化文件(.ini文件)中指定的条目获取一个整数值 GetPrivateProfileSection获取指定小节(在 .ini文件中)所有项名和值的一个列表 GetPrivateProfileString 为初始化文件中指定的条目取得字串 GetProfileInt 取得win.ini初始化文件中指定条目的一个整数值 GetProfileSection 获取指定小节(在win.ini文件中)所有项名和值的一个列表 GetProfileString 为win.ini初始化文件中指定的条目取得字串 GetShortPathName 获取指定文件的短路径名 GetSystemDirectory 取得Windows系统目录(即System目录)的完整路径名 GetTempFileName 这个函数包含了一个临时文件的名字,它可由应用程序使用 GetTempPath 获取为临时文件指定的路径 GetVolumeInformation 获取与一个磁盘卷有关的信息 GetWindowsDirectory 获取Windows目录的完整路径名 hread 参考lread hwrite 参考lwrite函数 lclose 关闭指定的文件 lcreat 创建一个文件 llseek 设置文件中进行读写的当前位置 LockFile 锁定文件的某一部分,使其不与其他应用程序共享 LockFileEx 与LockFile相似,只是它提供了更多的功能 lopen 以二进制模式打开指定的文件 lread 将文件中的数据读入内存缓冲区 lwrite 将数据从内存缓冲区写入一个文件 LZClose 关闭由LZOpenFile 或 LZInit函数打开的一个文件 LZCopy 复制一个文件 LZInit 这个函数用于初始化内部缓冲区 LZOpenFile 该函数能执行大量不同的文件处理,而且兼容于压缩文件 LZRead 将数据从文件读入内存缓冲区 LZSeek 设置一个文件中进行读写的当前位置 MapViewOfFile 将一个文件映射对象映射到当前应用程序的地址空间 MoveFile 移动文件 OpenFile 这个函数能执行大量不同的文件操作 OpenFileMapping 打开一个现成的文件映射对象 QueryDosDevice 在Windows NT中,DOS设备名会映射成NT系统设备名。该函数可判断当前的设备映射情况 ReadFile 从文件中读出数据 ReadFileEx 与ReadFile相似,只是它只能用于异步读操作,并包含了一个完整的回调 RegCloseKey 关闭系统注册表中的一个项(或键) RegConnectRegistry 访问远程系统的部分注册表 RegCreateKey 在指定的项下创建或打开一个项 RegCreateKeyEx 在指定项下创建新项的更复杂的方式。在Win32环境中建议使用这个函数 RegDeleteKey 删除现有项下方一个指定的子项 RegDeleteValue 删除指定项下方的一个值 RegEnumKey 枚举指定项的子项。在Win32环境中应使用RegEnumKeyEx RegEnumKeyEx 枚举指定项下方的子项 RegEnumValue 枚举指定项的值 RegFlushKey 将对项和它的子项作出的改动实际写入磁盘 RegGetKeySecurity 获取与一个注册表项有关的安全信息 RegLoadKey 从以前用RegSaveKey函数创建的一个文件里装载注册表信息 RegNotifyChangeKeyValue 注册表项或它的任何一个子项发生变化时,用这个函数提供一种通知机制 RegOpenKey 打开一个现有的注册表项 RegOpenKeyEx 打开一个现有的项。在win32下推荐使用这个函数 RegQueryInfoKey 获取与一个项有关的信息 RegQueryValue 取得指定项或子项的默认(未命名)值 RegQueryValueEx 获取一个项的设置值 RegReplaceKey 用一个磁盘文件保存的信息替换注册表信息;并创建一个备份,在其中包含当前注册表信息 RegRestoreKey 从一个磁盘文件恢复注册表信息 RegSaveKey 将一个项以及它的所有子项都保存到一个磁盘文件 RegSetKeySecurity 设置指定项的安全特性 RegSetValue 设置指定项或子项的默认值 RegSetValueEx 设置指定项的值 RegUnLoadKey 卸载指定的项以及它的所有子项 RemoveDirectory 删除指定目录 SearchPath 查找指定文件 SetCurrentDirectory 设置当前目录 SetEndOfFile 针对一个打开的文件,将当前文件位置设为文件末尾 SetFileAttributes 设置文件属性 SetFilePointer 在一个文件中设置当前的读写位置 SetFileTime 设置文件的创建、访问及上次修改时间 SetHandleCount 这个函数不必在win32下使用;即使使用,也不会有任何效果 SetVolumeLabel 设置一个磁盘的卷标(Label) SystemTimeToFileTime 根据一个FILETIME结构的内容,载入一个SYSTEMTIME结构 UnlockFile 解除对一个文件的锁定 UnlockFileEx 解除对一个文件的锁定 UnmapViewOfFile 在当前应用程序的内存地址空间解除对一个文件映射对象的映射 VerFindFile 用这个函数决定一个文件应安装到哪里 VerInstallFile 用这个函数安装一个文件 VerLanguageName 这个函数能根据16位语言代码获取一种语言的名称 VerQueryValue 这个函数用于从版本资源中获取信息 WriteFile 将数据写入一个文件 WriteFileEx 与WriteFile类似,只是它只能用于异步写操作,并包括了一个完整的回调 WritePrivateProfileSection 为一个初始化文件(.ini)中指定的小节设置所有项名和值 WritePrivateProfileString 在初始化文件指定小节内设置一个字串 WriteProfileSection 为Win.ini初始化文件中一个指定的小节设置所有项名和值 WriteProfileString 在Win.ini初始化文件指定小节内设置一个字串 4、API之打印函数
windows api 描述 AbortDoc 取消一份文档的打印 AbortPrinter 删除与一台打印机关联在一起的缓冲文件 AddForm 为打印机的表单列表添加一个新表单 AddJob 用于获取一个有效的路径名,以便用它为作业创建一个后台打印文件。它也会为作业分配一个作业编号 AddMonitor 为系统添加一个打印机监视器 AddPort 启动“添加端口”对话框,允许用户在系统可用端口列表中加入一个新端口 AddPrinter 在系统中添加一台新打印机 AddPrinterConnection 连接指定的打印机 AddPrinterDriver 为指定的系统添加一个打印驱动程序 AddPrintProcessor 为指定的系统添加一个打印处理器 AddPrintProvidor 为系统添加一个打印供应商 AdvancedDocumentProperties 启动打印机文档设置对话框 ClosePrinter 关闭一个打开的打印机对象 ConfigurePort 针对指定的端口,启动一个端口配置对话框 ConnectToPrinterDlg 启动连接打印机对话框,用它同访问网络的打印机连接 DeleteForm 从打印机可用表单列表中删除一个表单 DeleteMonitor 删除指定的打印监视器 DeletePort 启动“删除端口”对话框,允许用户从当前系统删除一个端口 DeletePrinter 将指定的打印机标志为从系统中删除 DeletePrinterConnection 删除与指定打印机的连接 DeletePrinterDriver 从系统删除一个打印机驱动程序 DeletePrintProcessor 从指定系统删除一个打印处理器 DeletePrintProvidor 从系统中删除一个打印供应商 DeviceCapabilities 利用这个函数可获得与一个设备的能力有关的信息 DocumentProperties 打印机配置控制函数 EndDocAPI 结束一个成功的打印作业 EndDocPrinter 在后台打印程序的级别指定一个文档的结束 EndPage 用这个函数完成一个页面的打印,并准备设备场景,以便打印下一个页 EndPagePrinter 指定一个页在打印作业中的结尾 EnumForms 枚举一台打印机可用的表单 EnumJobs 枚举打印队列中的作业 EnumMonitors 枚举可用的打印监视器 EnumPorts 枚举一个系统可用的端口 EnumPrinterDrivers 枚举指定系统中已安装的打印机驱动程序 EnumPrinters 枚举系统中安装的打印机 EnumPrintProcessorDatatypes 枚举由一个打印处理器支持的数据类型 EnumPrintProcessors 枚举系统中可用的打印处理器 Escape 设备控制函数 FindClosePrinterChangeNotification 关闭用FindFirstPrinterChangeNotification函数获取的一个打印机通告对象 FindFirstPrinterChangeNotification 创建一个新的改变通告对象,以便我们注意打印机状态的各种变化 FindNextPrinterChangeNotification 用这个函数判断触发一次打印机改变通告信号的原因 FreePrinterNotifyInfo 释放由FindNextPrinterChangeNotification函数分配的一个缓冲区 GetForm 取得与指定表单有关的信息 GetJob 获取与指定作业有关的信息 GetPrinter 取得与指定打印机有关的信息 GetPrinterData 为打印机设置注册表配置信息 GetPrinterDriver 针对指定的打印机,获取与打印机驱动程序有关的信息 GetPrinterDriverDirectory 判断指定系统中包含了打印机驱动程序的目录是什么 GetPrintProcessorDirectory 判断指定系统中包含了打印机处理器驱动程序及文件的目录 OpenPrinter 打开指定的打印机,并获取打印机的句柄 PrinterMessageBox 在拥有指定打印作业的系统上显示一个打印机出错消息框 PrinterProperties 启动打印机属性对话框,以便对打印机进行配置 ReadPrinter 从打印机读入数据 ResetDC 重设一个设备场景 ResetPrinter 改变指定打印机的默认数据类型及文档设置 ScheduleJob 提交一个要打印的作业 SetAbortProc 为Windows指定取消函数的地址 SetForm 为指定的表单设置信息 SetJob 对一个打印作业的状态进行控制 SetPrinter 对一台打印机的状态进行控制 SetPrinterData 设置打印机的注册表配置信息 StartDoc 开始一个打印作业 StartDocPrinter 在后台打印的级别启动一个新文档 StartPage 打印一个新页前要先调用这个函数 StartPagePrinter 在打印作业中指定一个新页的开始 WritePrinter 将发送目录中的数据写入打印机 5、API之文本和字体函数
windows api 描述 AddFontResource 在Windows系统中添加一种字体资源 CreateFont 用指定的属性创建一种逻辑字体 CreateFontIndirect 用指定的属性创建一种逻辑字体 CreateScalableFontResource 为一种TureType字体创建一个资源文件,以便能用API函数AddFontResource将其加入Windows系统 DrawText 将文本描绘到指定的矩形中 DrawTextEx 与DrawText相似,只是加入了更多的功能 EnumFontFamilies 列举指定设备可用的字体 EnumFontFamiliesEx 列举指定设备可用的字体 EnumFonts 列举指定设备可用的字体 ExtTextOut 经过扩展的文本描绘函数。也请参考SetTextAlign函数 GectRatioFilterEx 用SetMapperFlags要求Windows只选择与设备当前纵横比相符的光栅字体时,本函数可判断纵横比大小 GetCharABCWidths 判断TureType字体中一个或多个字符的A-B-C大小 GetCharABCWidthsFloat 查询一种字体中一个或多个字符的A-B-C尺寸 GetCharacterPlacement 该函数用于了解如何用一个给定的字符显示一个字串 GetCharWidth 调查字体中一个或多个字符的宽度 GetFontData 接收一种可缩放字体文件的数据 GetFontLanguageInfo 返回目前选入指定设备场景中的字体的信息 GetGlyphOutline 取得TureType字体中构成一个字符的曲线信息 GetKerningPairs 取得指定字体的字距信息 GetOutlineTextMetrics 接收与TureType字体内部特征有关的详细信息 GetRasterizerCaps 了解系统是否有能力支持可缩放的字体 GetTabbedTextExtent 判断一个字串占据的范围,同时考虑制表站扩充的因素 GetTextAlign 接收一个设备场景当前的文本对齐标志 GetTextCharacterExtra 判断额外字符间距的当前值 GetTextCharset 接收当前选入指定设备场景的字体的字符集标识符 GetTextCharsetInfo 获取与当前选定字体的字符集有关的详细信息 GetTextColor 判断当前字体颜色。通常也称为“前景色” GetTextExtentExPoint 判断要填入指定区域的字符数量。也用一个数组装载每个字符的范围信息 GetTextExtentPoint 判断一个字串的大小(范围) GetTextFace 获取一种字体的字样名 GetTextMetrics 获取与选入一种设备场景的物理字体有关的信息 GrayString 描绘一个以灰色显示的字串。通常由Windows用于标识禁止状态 PolyTextOut 描绘一系列字串 RemoveFontResource 从Windows系统中删除一种字体资源 SetMapperFlags Windows对字体进行映射时,可用该函数选择与目标设备的纵横比相符的光栅字体 SetTextAlign 设置文本对齐方式,并指定在文本输出过程中使用设备场景的当前位置 SetTextCharacterExtra 描绘文本的时候,指定要在字符间插入的额外间距 SetTextColor 设置当前文本颜色。这种颜色也称为“前景色” SetTextJustification 通过指定一个文本行应占据的额外空间,可用这个函数对文本进行两端对齐处理 TabbedTextOut 支持制表站的一个文本描绘函数 TextOut 文本绘图函数 6、API之菜单函数
windows api 描述 AppendMenu 在指定的菜单里添加一个菜单项 CheckMenuItem 复选或撤消复选指定的菜单条目 CheckMenuRadioItem 指定一个菜单条目被复选成“单选”项目 CreateMenu 创建新菜单 CreatePopupMenu 创建一个空的弹出式菜单 DeleteMenu 删除指定的菜单条目 DestroyMenu 删除指定的菜单 DrawMenuBar 为指定的窗口重画菜单 EnableMenuItem 允许或禁止指定的菜单条目 GetMenu 取得窗口中一个菜单的句柄 GetMenuCheckMarkDimensions 返回一个菜单复选符的大小 GetMenuContextHelpId 取得一个菜单的帮助场景ID GetMenuDefaultItem 判断菜单中的哪个条目是默认条目 GetMenuItemCount 返回菜单中条目(菜单项)的数量 GetMenuItemID 返回位于菜单中指定位置处的条目的菜单ID GetMenuItemInfo 取得(接收)与一个菜单条目有关的特定信息 GetMenuItemRect 在一个矩形中装载指定菜单条目的屏幕坐标信息 GetMenuState 取得与指定菜单条目状态有关的信息 GetMenuString 取得指定菜单条目的字串 GetSubMenu 取得一个弹出式菜单的句柄,它位于菜单中指定的位置 GetSystemMenu 取得指定窗口的系统菜单的句柄 HiliteMenuItem 控制顶级菜单条目的加亮显示状态 InsertMenu 在菜单的指定位置处插入一个菜单条目,并根据需要将其他条目向下移动 InsertMenuItem 插入一个新菜单条目 IsMenu 判断指定的句柄是否为一个菜单的句柄 LoadMenu 从指定的模块或应用程序实例中载入一个菜单 LoadMenuIndirect 载入一个菜单 MenuItemFromPoint 判断哪个菜单条目包含了屏幕上一个指定的点 ModifyMenu 改变菜单条目 RemoveMenu 删除指定的菜单条目 SetMenu 设置窗口菜单 SetMenuContextHelpId 设置一个菜单的帮助场景ID SetMenuDefaultItem 将一个菜单条目设为默认条目 SetMenuItemBitmaps 设置一幅特定位图,令其在指定的菜单条目中使用,代替标准的复选符号(√) SetMenuItemInfo 为一个菜单条目设置指定的信息 TrackPopupMenu 在屏幕的任意地方显示一个弹出式菜单 TrackPopupMenuEx 与TrackPopupMenu相似,只是它提供了额外的功能 7、API之位图、图标和光栅运算函数
windows api 描述 BitBlt 将一幅位图从一个设备场景复制到另一个 CopyIcon 制作指定图标或鼠标指针的一个副本。这个副本从属于发出调用的应用程序 CopyImage 复制位图、图标或指针,同时在复制过程中进行一些转换工作 CreateBitmap 按照规定的格式创建一幅与设备有关位图 CreateBitmapIndirect 创建一幅与设备有关位图 CreateCompatibleBitmap 创建一幅与设备有关位图,它与指定的设备场景兼容 CreateCursor 创建一个鼠标指针 CreateDIBitmap 根据一幅与设备无关的位图创建一幅与设备有关的位图 CreateDIBSection 创建一个DIBSection CreateIcon 创建一个图标 CreateIconIndirect 创建一个图标 DestroyCursor 清除指定的鼠标指针,并释放它占用的所有系统资源 DestroyIcon 清除图标 DrawIcon 在指定的位置画一个图标 DrawIconEx 描绘一个图标或鼠标指针。与DrawIcon相比,这个函数提供了更多的功能 ExtractAssociatedIcon 判断一个可执行程序或DLL中是否存在图标,或是否有图标与系统注册表中指定的文件存在关联并提取之 ExtractIcon 判断一个可执行文件或DLL中是否有图标存在,并将其提取出来 GetBitmapBits 将来自位图的二进制位复制到一个缓冲区 GetBitmapDimensionEx 取得一幅位图的宽度和高度 GetDIBColorTable 从选入设备场景的DIBSection中取得颜色表信息 GetDIBits 将来自一幅位图的二进制位复制到一幅与设备无关的位图里 GetIconInfo 取得与图标有关的信息 GetStretchBltMode 判断StretchBlt 和 StretchDIBits函数采用的伸缩模式 LoadBitmap 从指定的模块或应用程序实例中载入一幅位图 LoadCursor 从指定的模块或应用程序实例中载入一个鼠标指针 LoadCursorFromFile 在一个指针文件或一个动画指针文件的基础上创建一个指针 LoadIcon 从指定的模块或应用程序实例中载入一个图标 LoadImage 载入一个位图、图标或指针 MaskBlt 执行复杂的图象传输,同时进行掩模(MASK)处理 PatBlt 在当前选定的刷子的基础上,用一个图案填充指定的设备场景 PlgBlt 复制一幅位图,同时将其转换成一个平行四边形。利用它可对位图进行旋转处理 SetBitmapBits 将来自缓冲区的二进制位复制到一幅位图 SetBitmapDimensionEx 设置一幅位图的宽度。以一毫米的十分之一为单位 SetDIBColorTable 设置选入设备场景的一个DIBSection的颜色表信息 SetDIBits 将来自与设备无关位图的二进制位复制到一幅与设备有关的位图里 SetDIBitsToDevice 将一幅与设备无关位图的全部或部分数据直接复制到一个设备 SetStretchBltMode 指定StretchBlt 和 StretchDIBits函数的伸缩模式 StretchBlt 将一幅位图从一个设备场景复制到另一个 StretchDIBits 将一幅与设备无关位图的全部或部分数据直接复制到指定的设备场景 8、API之绘图函数
windows api 描述 AbortPath 抛弃选入指定设备场景中的所有路径。也取消目前正在进行的任何路径的创建工作 AngleArc 用一个连接弧画一条线 Arc 画一个圆弧 BeginPath 启动一个路径分支 CancelDC 取消另一个线程里的长时间绘图操作 Chord 画一个弦 CloseEnhMetaFile 关闭指定的增强型图元文件设备场景,并将新建的图元文件返回一个句柄 CloseFigure 描绘到一个路径时,关闭当前打开的图形 CloseMetaFile 关闭指定的图元文件设备场景,并向新建的图元文件返回一个句柄 CopyEnhMetaFile 制作指定增强型图元文件的一个副本(拷贝) CopyMetaFile 制作指定(标准)图元文件的一个副本 CreateBrushIndirect 在一个LOGBRUSH数据结构的基础上创建一个刷子 CreateDIBPatternBrush 用一幅与设备无关的位图创建一个刷子,以便指定刷子样式(图案) CreateEnhMetaFile 创建一个增强型的图元文件设备场景 CreateHatchBrush 创建带有阴影图案的一个刷子 CreateMetaFile 创建一个图元文件设备场景 CreatePatternBrush 用指定了刷子图案的一幅位图创建一个刷子 CreatePen 用指定的样式、宽度和颜色创建一个画笔 CreatePenIndirect 根据指定的LOGPEN结构创建一个画笔 CreateSolidBrush 用纯色创建一个刷子 DeleteEnhMetaFile 删除指定的增强型图元文件 DeleteMetaFile 删除指定的图元文件 DeleteObject 删除GDI对象,对象使用的所有系统资源都会被释放 DrawEdge 用指定的样式描绘一个矩形的边框 DrawEscape 换码(Escape)函数将数据直接发至显示设备驱动程序 DrawFocusRect 画一个焦点矩形 DrawFrameControl 描绘一个标准控件 DrawState 为一幅图象或绘图操作应用各式各样的效果 Ellipse 描绘一个椭圆,由指定的矩形围绕 EndPath 停止定义一个路径 EnumEnhMetaFile 针对一个增强型图元文件,列举其中单独的图元文件记录 EnumMetaFile 为一个标准的windows图元文件枚举单独的图元文件记录 EnumObjects 枚举可随同指定设备场景使用的画笔和刷子 ExtCreatePen 创建一个扩展画笔(装饰或几何) ExtFloodFill 在指定的设备场景里,用当前选择的刷子填充一个区域 FillPath 关闭路径中任何打开的图形,并用当前刷子填充 FillRect 用指定的刷子填充一个矩形 FlattenPath 将一个路径中的所有曲线都转换成线段 FloodFill 用当前选定的刷子在指定的设备场景中填充一个区域 FrameRect 用指定的刷子围绕一个矩形画一个边框 GdiComment 为指定的增强型图元文件设备场景添加一条注释信息 GdiFlush 执行任何未决的绘图操作 GdiGetBatchLimit 判断有多少个GDI绘图命令位于队列中 GdiSetBatchLimit 指定有多少个GDI绘图命令能够进入队列 GetArcDirection 画圆弧的时候,判断当前采用的绘图方向 GetBkColor 取得指定设备场景当前的背景颜色 GetBkMode 针对指定的设备场景,取得当前的背景填充模式 GetBrushOrgEx 判断指定设备场景中当前选定刷子起点 GetCurrentObject 获得指定类型的当前选定对象 GetCurrentPositionEx 在指定的设备场景中取得当前的画笔位置 GetEnhMetaFile 取得磁盘文件中包含的一个增强型图元文件的图元文件句柄 GetEnhMetaFileBits 将指定的增强型图元文件复制到一个内存缓冲区里 GetEnhMetaFileDescription 返回对一个增强型图元文件的说明 GetEnhMetaFileHeader 取得增强型图元文件的图元文件头 GetEnhMetaFilePaletteEntries 取得增强型图元文件的全部或部分调色板 GetMetaFile 取得包含在一个磁盘文件中的图元文件的图元文件句柄 GetMetaFileBitsEx 将指定的图元文件复制到一个内存缓冲区 GetMiterLimit 取得设备场景的斜率限制(Miter)设置 GetNearestColor 根据设备的显示能力,取得与指定颜色最接近的一种纯色 GetObjectAPI 取得对指定对象进行说明的一个结构 GetObjectType 判断由指定句柄引用的GDI对象的类型 GetPath 取得对当前路径进行定义的一系列数据 GetPixel 在指定的设备场景中取得一个像素的RGB值 GetPolyFillMode 针对指定的设备场景,获得多边形填充模式 GetROP2 针对指定的设备场景,取得当前的绘图模式 GetStockObject 取得一个固有对象(Stock) GetSysColorBrush 为任何一种标准系统颜色取得一个刷子 GetWinMetaFileBits 通过在一个缓冲区中填充用于标准图元文件的数据,将一个增强型图元文件转换成标准windows图元文件 InvertRect 通过反转每个像素的值,从而反转一个设备场景中指定的矩形 LineDDA 枚举指定线段中的所有点 LineTo 用当前画笔画一条线,从当前位置连到一个指定的点 MoveToEx 为指定的设备场景指定一个新的当前画笔位置 PaintDesk 在指定的设备场景中描绘桌面墙纸图案 PathToRegion 将当前选定的路径转换到一个区域里 Pie 画一个饼图 PlayEnhMetaFile 在指定的设备场景中画一个增强型图元文件 PlayEnhMetaFileRecord 回放单独一条增强型图元文件记录 PlayMetaFile 在指定的设备场景中回放一个图元文件 PlayMetaFileRecord 回放来自图元文件的单条记录 PolyBezier 描绘一条或多条贝塞尔(Bezier)曲线 PolyDraw 描绘一条复杂的曲线,由线段及贝塞尔曲线组成 Polygon 描绘一个多边形 Polyline 用当前画笔描绘一系列线段 PolyPolygon 用当前选定画笔描绘两个或多个多边形 PolyPolyline 用当前选定画笔描绘两个或多个多边形 Rectangle 用当前选定的画笔描绘矩形,并用当前选定的刷子填充 RoundRect 用当前选定的画笔画一个圆角矩形,并用当前选定的刷子在其中填充 SelectClipPath 将设备场景当前的路径合并到剪切区域里 SelectObject 为当前设备场景选择图形对象 SetArcDirection 设置圆弧的描绘方向 SetBkColor 为指定的设备场景设置背景颜色 SetBkMode 指定阴影刷子、虚线画笔以及字符中的空隙的填充方式 SetBrushOrgEx 为指定的设备场景设置当前选定刷子的起点 SetEnhMetaFileBits 用指定内存缓冲区内包含的数据创建一个增强型图元文件 SetMetaFileBitsEx 用包含在指定内存缓冲区内的数据结构创建一个图元文件 SetMiterLimit 设置设备场景当前的斜率限制 SetPixel 在指定的设备场景中设置一个像素的RGB值 SetPixelV 在指定的设备场景中设置一个像素的RGB值 SetPolyFillMode 设置多边形的填充模式 SetROP2 设置指定设备场景的绘图模式。与vb的DrawMode属性完全一致 SetWinMetaFileBits 将一个标准Windows图元文件转换成增强型图元文件 StrokeAndFillPath 针对指定的设备场景,关闭路径上打开的所有区域 StrokePath 用当前画笔描绘一个路径的轮廓。打开的图形不会被这个函数关闭 UnrealizeObject 将一个刷子对象选入设备场景之前,如刷子的起点准备用SetBrushOrgEx修改,则必须先调用本函数 WidenPath 根据选定画笔的宽度,重新定义当前选定的路径 9、API之设备场景函数
windows api 描述 CombineRgn 将两个区域组合为一个新区域 CombineTransform 驱动世界转换。它相当于依顺序进行两次转换 CreateCompatibleDC 创建一个与特定设备场景一致的内存设备场景 CreateDC 为专门设备创建设备场景 CreateEllipticRgn 创建一个椭圆 CreateEllipticRgnIndirect 创建一个内切于特定矩形的椭圆区域 CreateIC 为专用设备创建一个信息场景 CreatePolygonRgn 创建一个由一系列点围成的区域 CreatePolyPolygonRgn 创建由多个多边形构成的区域。每个多边形都应是封闭的 CreateRectRgn 创建一个矩形区域 CreateRectRgnIndirect 创建一个矩形区域 CreateRoundRectRgn 创建一个圆角矩形 DeleteDC 删除专用设备场景或信息场景,释放所有相关窗口资源 DPtoLP 将点阵从设备坐标转换到专用设备场景逻辑坐标 EqualRgn 确定两个区域是否相等 ExcludeClipRect 从专用设备场景的剪裁区中去掉一个矩形区。矩形内不能进行绘图 ExcludeUpdateRgn 从专用设备场景剪裁区去掉指定窗口的刷新区域 ExtCreateRegion 根据世界转换修改区域 ExtSelectClipRgn 将指定区域组合到设备场景的当前剪裁区 FillRgn 用指定刷子填充指定区域 FrameRgn 用指定刷子围绕指定区域画一个外框 GetBoundsRect 获取指定设备场景的边界矩形 GetClipBox 获取完全包含指定设备场景剪裁区的最小矩形 GetClipRgn 获取设备场景当前剪裁区 GetDC 获取指定窗口的设备场景 GetDCEx 为指定窗口获取设备场景。相比GetDC,本函数提供了更多的选项 GetDCOrgEx 获取指定设备场景起点位置(以屏幕坐标表示) GetDeviceCaps 根据指定设备场景代表的设备的功能返回信息 GetGraphicsMode 确定是否允许增强图形模式(世界转换) GetMapMode 为特定设备场景调入映象模式 GetRegionData 装入描述一个区域信息的RgnData结构或缓冲区 GetRgnBox 获取完全包含指定区域的最小矩形 GetUpdateRgn 确定指定窗口的刷新区域。该区域当前无效,需要刷新 GetViewportExtEx 获取设备场景视口(viewport)范围 GetViewportOrgEx 获取设备场景视口起点 GetWindowDC 获取整个窗口(包括边框、滚动条、标题栏、菜单等)的设备场景 GetWindowExtEx 获取指定设备场景的窗口范围 GetWindowOrgEx 获取指定设备场景的逻辑窗口的起点 GetWindowRgn 获取窗口区域 GetWorldTransform 如果有世界转换,为设备场景获取当前世界转换 IntersectClipRect 为指定设备定义一个新的剪裁区 InvalidateRgn 使窗口指定区域不活动,并将它加入窗口刷新区,使之可随后被重画 InvertRgn 通过颠倒每个像素值反转设备场景指定区域 LPtoDP 将点阵从指定设备场景逻辑坐标转换为设备坐标 ModifyWorldTransform 根据指定的模式修改世界转换 OffsetClipRgn 按指定量平移设备场景剪裁区 OffsetRgn 按指定偏移量平移指定区域 OffsetViewportOrgEx 平移设备场景视口区域 OffsetWindowOrgEx 平移指定设备场景窗口起点 PaintRgn 用当前刷子背景色填充指定区域 PtInRegion 确定点是否在指定区域内 PtVisible 确定指定点是否可见(即,点是否在设备场景剪裁区内) RectInRegion 确定矩形是否有部分在指定区域内 RectVisible 确定指定矩形是否有部分可见(是否在设备场景剪裁区内) ReleaseDC 释放由调用GetDC或GetWindowDC函数获取的指定设备场景 RestoreDC 从设备场景堆栈恢复一个原先保存的设备场景 SaveDC 将指定设备场景状态保存到Windows设备场景堆栈 ScaleViewportExtEx 缩放设备场景视口的范围 ScaleWindowExtEx 缩放指定设备场景窗口范围 ScrollDC 在窗口(由设备场景代表)中水平和(或)垂直滚动矩形 SelectClipRgn 为指定设备场景选择新的剪裁区 SetBoundsRect 设置指定设备场景的边界矩形 SetGraphicsMode 允许或禁止增强图形模式,以提供某些支持(包括世界转换) SetMapMode 设置指定设备场景的映射模式 SetRectRgn 设置区域为指定的矩形 SetViewportExtEx 设置设备场景视口范围 SetViewportOrgEx 设置设备场景视口起点 SetWindowExtEx 设置指定设备场景窗口范围 SetWindowOrgEx 设置指定设备场景窗口起点 SetWindowRgn 设置窗口区域 SetWorldTransform 设置世界转换 ValidateRgn 激活窗口中指定区域,把它从刷新区移走 WindowFromDC 取回与某一设备场景相关的窗口的句柄 10、API之硬件与系统函数
windows api 描述 ActivateKeyboardLayout 激活一个新的键盘布局。键盘布局定义了按键在一种物理性键盘上的位置与含义 Beep 用于生成简单的声音 CharToOem 将一个字串从ANSI字符集转换到OEM字符集 ClipCursor 将指针限制到指定区域 ConvertDefaultLocale 将一个特殊的地方标识符转换成真实的地方ID CreateCaret 根据指定的信息创建一个插入符(光标),并将它选定为指定窗口的默认插入符 DestroyCaret 清除(破坏)一个插入符 EnumCalendarInfo 枚举在指定“地方”环境中可用的日历信息 EnumDateFormats 列举指定的“当地”设置中可用的长、短日期格式 EnumSystemCodePages 枚举系统中已安装或支持的代码页 EnumSystemLocales 枚举系统已经安装或提供支持的“地方”设置 EnumTimeFormats 枚举一个指定的地方适用的时间格式 ExitWindowsEx 退出windows,并用特定的选项重新启动 ExpandEnvironmentStrings 扩充环境字串 FreeEnvironmentStrings 翻译指定的环境字串块 GetACP 判断目前正在生效的ANSI代码页 GetAsyncKeyState 判断函数调用时指定虚拟键的状态 GetCaretBlinkTime 判断插入符光标的闪烁频率 GetCaretPos 判断插入符的当前位置 GetClipCursor 取得一个矩形,用于描述目前为鼠标指针规定的剪切区域 GetCommandLine 获得指向当前命令行缓冲区的一个指针 GetComputerName 取得这台计算机的名称 GetCPInfo 取得与指定代码页有关的信息 GetCurrencyFormat 针对指定的“地方”设置,根据货币格式格式化一个数字 GetCursor 获取目前选择的鼠标指针的句柄 GetCursorPos 获取鼠标指针的当前位置 GetDateFormat 针对指定的“当地”格式,对一个系统日期进行格式化 GetDoubleClickTime 判断连续两次鼠标单击之间会被处理成双击事件的间隔时间 GetEnvironmentStrings 为包含了当前环境字串设置的一个内存块分配和返回一个句柄 GetEnvironmentVariable 取得一个环境变量的值 GetInputState 判断是否存在任何待决(等待处理)的鼠标或键盘事件 GetKBCodePage 由GetOEMCP取代,两者功能完全相同 GetKeyboardLayout 取得一个句柄,描述指定应用程序的键盘布局 GetKeyboardLayoutList 获得系统适用的所有键盘布局的一个列表 GetKeyboardLayoutName 取得当前活动键盘布局的名称 GetKeyboardState 取得键盘上每个虚拟键当前的状态 GetKeyboardType 了解与正在使用的键盘有关的信息 GetKeyNameText 在给出扫描码的前提下,判断键名 GetKeyState 针对已处理过的按键,在最近一次输入信息时,判断指定虚拟键的状态 GetLastError 针对之前调用的api函数,用这个函数取得扩展错误信息 GetLocaleInfo 取得与指定“地方”有关的信息 GetLocalTime 取得本地日期和时间 GetNumberFormat 针对指定的“地方”,按特定的格式格式化一个数字 GetOEMCP 判断在OEM和ANSI字符集间转换的windows代码页 GetQueueStatus 判断应用程序消息队列中待决(等待处理)的消息类型 GetSysColor 判断指定windows显示对象的颜色 GetSystemDefaultLangID 取得系统的默认语言ID GetSystemDefaultLCID 取得当前的默认系统“地方” GetSystemInfo 取得与底层硬件平台有关的信息 GetSystemMetrics 返回与windows环境有关的信息 GetSystemPowerStatus 获得与当前系统电源状态有关的信息 GetSystemTime 取得当前系统时间,这个时间采用的是“协同世界时间”(即UTC,也叫做GMT)格式 GetSystemTimeAdjustment 使内部系统时钟与一个外部的时钟信号源同步 GetThreadLocale 取得当前线程的地方ID GetTickCount 用于获取自windows启动以来经历的时间长度(毫秒) GetTimeFormat 针对当前指定的“地方”,按特定的格式格式化一个系统时间 GetTimeZoneInformation 取得与系统时区设置有关的信息 GetUserDefaultLangID 为当前用户取得默认语言ID GetUserDefaultLCID 取得当前用户的默认“地方”设置 GetUserName 取得当前用户的名字 GetVersion 判断当前运行的Windows和DOS版本 GetVersionEx 取得与平台和操作系统有关的版本信息 HideCaret 在指定的窗口隐藏插入符(光标) IsValidCodePage 判断一个代码页是否有效 IsValidLocale 判断地方标识符是否有效 keybd_event 这个函数模拟了键盘行动 LoadKeyboardLayout 载入一个键盘布局 MapVirtualKey 根据指定的映射类型,执行不同的扫描码和字符转换 MapVirtualKeyEx 根据指定的映射类型,执行不同的扫描码和字符转换 MessageBeep 播放一个系统声音。系统声音的分配方案是在控制面板里决定的 mouse_event 模拟一次鼠标事件 OemKeyScan 判断OEM字符集中的一个ASCII字符的扫描码和Shift键状态 OemToChar 将OEM字符集的一个字串转换到ANSI字符集 SetCaretBlinkTime 指定插入符(光标)的闪烁频率 SetCaretPos 指定插入符的位置 SetComputerName 设置新的计算机名 SetCursor 将指定的鼠标指针设为当前指针 SetCursorPos 设置指针的位置 SetDoubleClickTime 设置连续两次鼠标单击之间能使系统认为是双击事件的间隔时间 SetEnvironmentVariable 将一个环境变量设为指定的值 SetKeyboardState 设置每个虚拟键当前在键盘上的状态 SetLocaleInfo 改变用户“地方”设置信息 SetLocalTime 设置当前地方时间 SetSysColors 设置指定窗口显示对象的颜色 SetSystemCursor 改变任何一个标准系统指针 SetSystemTime 设置当前系统时间 SetSystemTimeAdjustment 定时添加一个校准值使内部系统时钟与一个外部的时钟信号源同步 SetThreadLocale 为当前线程设置地方 SetTimeZoneInformation 设置系统时区信息 ShowCaret 在指定的窗口里显示插入符(光标) ShowCursor 控制鼠标指针的可视性 SwapMouseButton 决定是否互换鼠标左右键的功能 SystemParametersInfo 获取和设置数量众多的windows系统参数 SystemTimeToTzSpecificLocalTime 将系统时间转换成地方时间 ToAscii 根据当前的扫描码和键盘信息,将一个虚拟键转换成ASCII字符 ToUnicode 根据当前的扫描码和键盘信息,将一个虚拟键转换成Unicode字符 UnloadKeyboardLayout 卸载指定的键盘布局 VkKeyScan 针对Windows字符集中一个ASCII字符,判断虚拟键码和Shift键的状态 11、API之进程和线程函数
windows api 描述 CancelWaitableTimer 这个函数用于取消一个可以等待下去的计时器操作 CallNamedPipe 这个函数由一个希望通过管道通信的一个客户进程调用 ConnectNamedPipe 指示一台服务器等待下去,直至客户机同一个命名管道连接 CreateEvent 创建一个事件对象 CreateMailslot 创建一个邮路。返回的句柄由邮路服务器使用(收件人) CreateMutex 创建一个互斥体(MUTEX) CreateNamedPipe 创建一个命名管道。返回的句柄由管道的服务器端使用 CreatePipe 创建一个匿名管道 CreateProcess 创建一个新进程(比如执行一个程序) CreateSemaphore 创建一个新的信号机 CreateWaitableTimer 创建一个可等待的计时器对象 DisconnectNamedPipe 断开一个客户与一个命名管道的连接 DuplicateHandle 在指出一个现有系统对象当前句柄的情况下,为那个对象创建一个新句柄 ExitProcess 中止一个进程 FindCloseChangeNotification 关闭一个改动通知对象 FindExecutable 查找与一个指定文件关联在一起的程序的文件名 FindFirstChangeNotification 创建一个文件通知对象。该对象用于监视文件系统发生的变化 FindNextChangeNotification 重设一个文件改变通知对象,令其继续监视下一次变化 FreeLibrary 释放指定的动态链接库 GetCurrentProcess 获取当前进程的一个伪句柄 GetCurrentProcessId 获取当前进程一个唯一的标识符 GetCurrentThread 获取当前线程的一个伪句柄 GetCurrentThreadId 获取当前线程一个唯一的线程标识符 GetExitCodeProces 获取一个已中断进程的退出代码 GetExitCodeThread 获取一个已中止线程的退出代码 GetHandleInformation 获取与一个系统对象句柄有关的信息 GetMailslotInfo 获取与一个邮路有关的信息 GetModuleFileName 获取一个已装载模板的完整路径名称 GetModuleHandle 获取一个应用程序或动态链接库的模块句柄 GetPriorityClass 获取特定进程的优先级别 GetProcessShutdownParameters 调查系统关闭时一个指定的进程相对于其它进程的关闭早迟情况 GetProcessTimes 获取与一个进程的经过时间有关的信息 GetProcessWorkingSetSize 了解一个应用程序在运行过程中实际向它交付了多大容量的内存 GetSartupInfo 获取一个进程的启动信息 GetThreadPriority 获取特定线程的优先级别 GetTheardTimes 获取与一个线程的经过时间有关的信息 GetWindowThreadProcessId 获取与指定窗口关联在一起的一个进程和线程标识符 LoadLibrary 载入指定的动态链接库,并将它映射到当前进程使用的地址空间 LoadLibraryEx 装载指定的动态链接库,并为当前进程把它映射到地址空间 LoadModule 载入一个Windows应用程序,并在指定的环境中运行 MsgWaitForMultipleObjects 等侯单个对象或一系列对象发出信号。如返回条件已经满足,则立即返回 SetPriorityClass 设置一个进程的优先级别 SetProcessShutdownParameters 在系统关闭期间,为指定进程设置他相对于其它程序的关闭顺序 SetProcessWorkingSetSize设置操作系统实际划分给进程使用的内存容量 SetThreadPriority 设定线程的优先级别 ShellExecute 查找与指定文件关联在一起的程序的文件名 TerminateProcess 结束一个进程 WinExec 运行指定的程序 12、API之控件与消息函数
windows api 描述 AdjustWindowRect 给定一种窗口样式,计算获得目标客户区矩形所需的窗口大小 AnyPopup 判断屏幕上是否存在任何弹出式窗口 ArrangeIconicWindows 排列一个父窗口的最小化子窗口 AttachThreadInput 连接线程输入函数 BeginDeferWindowPos 启动构建一系列新窗口位置的过程 BringWindowToTop 将指定的窗口带至窗口列表顶部 CascadeWindows 以层叠方式排列窗口 ChildWindowFromPoint 返回父窗口中包含了指定点的第一个子窗口的句柄 ClientToScreen 判断窗口内以客户区坐标表示的一个点的屏幕坐标 CloseWindow 最小化指定的窗口 CopyRect 矩形内容复制 DeferWindowPos 该函数为特定的窗口指定一个新窗口位置 DestroyWindow 清除指定的窗口以及它的所有子窗口 DrawAnimatedRects 描绘一系列动态矩形 EnableWindow 指定的窗口里允许或禁止所有鼠标及键盘输入 EndDeferWindowPos 同时更新DeferWindowPos调用时指定的所有窗口的位置及状态 EnumChildWindows 为指定的父窗口枚举子窗口 EnumThreadWindows 枚举与指定任务相关的窗口 EnumWindows 枚举窗口列表中的所有父窗口 EqualRect 判断两个矩形结构是否相同 FindWindow 寻找窗口列表中第一个符合指定条件的顶级窗口 FindWindowEx 在窗口列表中寻找与指定条件相符的第一个子窗口 FlashWindow 闪烁显示指定窗口 GetActiveWindow 获得活动窗口的句柄 GetCapture 获得一个窗口的句柄,这个窗口位于当前输入线程,且拥有鼠标捕获(鼠标活动由它接收) GetClassInfo 取得WNDCLASS结构(或WNDCLASSEX结构)的一个副本,结构中包含了与指定类有关的信息 GetClassLong 取得窗口类的一个Long变量条目 GetClassName 为指定的窗口取得类名 GetClassWord 为窗口类取得一个整数变量 GetClientRect 返回指定窗口客户区矩形的大小 GetDesktopWindow 获得代表整个屏幕的一个窗口(桌面窗口)句柄 GetFocus 获得拥有输入焦点的窗口的句柄 GetForegroundWindow 获得前台窗口的句柄 GetLastActivePopup 获得在一个给定父窗口中最近激活过的弹出式窗口的句柄 GetParent 判断指定窗口的父窗口 GetTopWindow 搜索内部窗口列表,寻找隶属于指定窗口的头一个窗口的句柄 GetUpdateRect 获得一个矩形,它描叙了指定窗口中需要更新的那一部分 GetWindow 获得一个窗口的句柄,该窗口与某源窗口有特定的关系 GetWindowContextHelpId 取得与窗口关联在一起的帮助场景ID GetWindowLong 从指定窗口的结构中取得信息 GetWindowPlacement 获得指定窗口的状态及位置信息 GetWindowRect 获得整个窗口的范围矩形,窗口的边框、标题栏、滚动条及菜单等都在这个矩形内 GetWindowText 取得一个窗体的标题(caption)文字,或者一个控件的内容 GetWindowTextLength 调查窗口标题文字或控件内容的长短 GetWindowWord 获得指定窗口结构的信息 InflateRect 增大或减小一个矩形的大小 IntersectRect 这个函数在lpDestRect里载入一个矩形,它是lpSrc1Rect与lpSrc2Rect两个矩形的交集 InvalidateRect 屏蔽一个窗口客户区的全部或部分区域 IsChild 判断一个窗口是否为另一窗口的子或隶属窗口 IsIconic 判断窗口是否已最小化 IsRectEmpty 判断一个矩形是否为空 IsWindow 判断一个窗口句柄是否有效 IsWindowEnabled 判断窗口是否处于活动状态 IsWindowUnicode 判断一个窗口是否为Unicode窗口。这意味着窗口为所有基于文本的消息都接收Unicode文字 IsWindowVisible 判断窗口是否可见 IsZoomed 判断窗口是否最大化 LockWindowUpdate 锁定指定窗口,禁止它更新 MapWindowPoints 将一个窗口客户区坐标的点转换到另一窗口的客户区坐标系统 MoveWindow 改变指定窗口的位置和大小 OffsetRect 通过应用一个指定的偏移,从而让矩形移动起来 OpenIcon 恢复一个最小化的程序,并将其激活 PtInRect 判断指定的点是否位于矩形内部 RedrawWindow 重画全部或部分窗口 ReleaseCapture 为当前的应用程序释放鼠标捕获 ScreenToClient 判断屏幕上一个指定点的客户区坐标 ScrollWindow 滚动窗口客户区的全部或一部分 ScrollWindowEx 根据附加的选项,滚动窗口客户区的全部或部分 SetActiveWindow 激活指定的窗口 SetCapture 将鼠标捕获设置到指定的窗口 SetClassLong 为窗口类设置一个Long变量条目 SetClassWord 为窗口类设置一个条目 SetFocusAPI 将输入焦点设到指定的窗口。如有必要,会激活窗口 SetForegroundWindow 将窗口设为系统的前台窗口 SetParent 指定一个窗口的新父 SetRect 设置指定矩形的内容 SetRectEmpty 将矩形设为一个空矩形 SetWindowContextHelpId 为指定的窗口设置帮助场景(上下文)ID SetWindowLong 在窗口结构中为指定的窗口设置信息 SetWindowPlacement 设置窗口状态和位置信息 SetWindowPos 为窗口指定一个新位置和状态 SetWindowText 设置窗口的标题文字或控件的内容 SetWindowWord 在窗口结构中为指定的窗口设置信息 ShowOwnedPopups 显示或隐藏由指定窗口所有的全部弹出式窗口 ShowWindow 控制窗口的可见性 ShowWindowAsync 与ShowWindow相似 SubtractRect 装载矩形lprcDst,它是在矩形lprcSrc1中减去lprcSrc2得到的结果 TileWindows 以平铺顺序排列窗口 UnionRect 装载一个lpDestRect目标矩形,它是lpSrc1Rect和lpSrc2Rect联合起来的结果 UpdateWindow 强制立即更新窗口 ValidateRect 校验窗口的全部或部分客户区 WindowFromPoint 返回包含了指定点的窗口的句柄。忽略屏蔽、隐藏以及透明窗口 -
Windows运行程序时桌面窗口卡死
2021-11-02 20:01:38我们在使用windows 系统过程中,经常会遇到执行某些程序的时候,程序窗口显示“未想一个响应”,桌面窗口整个卡死的情况,但是过一段时间会自动恢复,查看任务管理器,并未发现负载异常的进程,一段时间又会出现卡死...一、问题描述
我们在使用windows 系统过程中,经常会遇到执行某些程序的时候,程序窗口显示“未想一个响应”,桌面窗口整个卡死的情况,但是过一段时间会自动恢复,查看任务管理器,并未发现负载异常的进程,一段时间又会出现卡死的情况如此反复。
二、分析思路
1、窗口卡死,又称UI卡死;windows的UI程序都是消息驱动的,每个窗体有一个消息线程,用于响应用户事件(鼠标、键盘等),也就是一个消息队列。当一个事件里消息线程阻塞了,后面的消息一直卡在消息队列里,卡到一定程度,就界面就停止响应了。所以出现界面卡死,思考的方向是消息循环是不是能出现问题了。下面分析windows程序界面卡死的几个可能的原因:
1)主线程(UI线程)出现死循环:
如果主线程出现死循环,那么windows将不能从消息队列中取出消息,并进行处理,所以出现卡死现象。为了验证是这个原因导致界面卡死,打开任务管理器,如果该进程的cpu使用率一直保持非零,比如一直保持在3%,那么界面卡死的原因是主线程死循环了。
2)主线程和其他的线程由于资源或者锁争夺,出现了死锁。
如果主线程由于跟其他的线程由于争夺资源或者锁,出现了死锁,那么主线程会一直等待资源或者锁,导致主线程不能继续往下执行,分发和处理消息,所以出现卡死。这种情况下,任务管理器中这个进程的cpu使用率一般是0,也有意外是除这个线程外,其他的线程也在运行,耗费cpu。所以最好的办法是使用调试器,挂载在这个进程上,观察这个进程的各个线程调用栈,观察线程等待的资源等。
进程被调试挂载了,调试器在一个断点处断下。这种情况出现的非常少,只有在调试一个程序的时候才会出现。
3)程序有GDI对象泄漏,导致界面卡死
对于这种情况。通过观察任务管理器的GDI数据可以发现问题。首先任务管理器的“查看”->“选择列”出现如下的界面,选择GDI对象。这样在任务管理器中就可以看到进程的GDI对象数目了。如果GDI对象数达到千数量级,那么很有可能是这个原因。
2、查看是否中毒和恶意软件注入
现场通过杀毒未发现中毒现象。系统如果感染了病毒、木马,它们会在电脑后台不断的运行复制,导致一直占用CPU资源,电脑系统出现严重卡顿。流氓软件是介于病毒和正规软件之间的软件。如果电脑中有流氓软件,会不经用户许可,自动运行,占用CPU资源等等。此时,就需要使用杀毒、强力卸载软件来进行杀毒和软件的卸载。
3、服务异常排查:
1)Win+R输入:msconfig 打开系统配置
点击”服务”标签卡,勾选”隐藏所有的 Microsoft 服务”,然后点击全部禁用(若您启用了指纹识别功能,请不要关闭相关服务)
点击”启动”标签卡, 点击”打开任务管理器”,然后禁用全部启动项并确定
重启设备尝试。
2)“服务” 界面找到名为“Connected User Experiences and Telemetry”和superfetch的服务,禁用。
4、查看异常服务对系统调用的影响,排除问题
现场因是vm,报vmtools服务处于“未运行”状态,实际服务是启动状态的,造成了系统卡死,重新安装vmtools未果(最新版)。
5、电源选项调试
1)HKEY_Local_Machine\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\0012ee47-9041-4b5d-9b77-535fba8b1442\0b2d69d7-a2a1-449c-9680-f91c70521c60,选择Attributes属性将数值从1改为2;
2)HKEY_Local_Machine\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\0012ee47-9041-4b5d-9b77-535fba8b1442\dab60367-53fe-4fbc-825e-521d069d2456,Attributes属性将数值从1改为2;
3)打开电源设置,更改高级电源设置 —> 展开“硬盘”,把“AHCI Link Power Management- HIPM/DIPM”下的选项都改成“Active”,把“AHCI Link Power Management - Adaptive”下的选项都设置成“0 ms”;把“PCI Express”下的“链接状态电源管理”都设置成“关闭”,并点击确定;
6、powershell脚本调试应用重置
Get-AppXPackage -AllUsers | Foreach {Add-AppxPackage -DisableDevelopmentMode -Register “ ( ( (_.InstallLocation)\AppXManifest.xml”}
7、系统更新导致的可能问题
查看最近windows更新,win10强制要求数字签名的驱动,自带了大量驱动。而且会通过系统更新下载没有或者过时的驱动,所以如果你之前使用win7或者win10 1511没有问题而升级到新版本频繁各种问题的原因就极可能是驱动问题。结合事件管理器综合判断。
三、调试工具
1)ProcessExplorer
它是一款windows官方的增强型任务管理工具。Process Explorer不仅可以帮万平米监视或重启、终止任何程序,还可以帮助我们清楚了解程序的运行变化情况。并且Process Explorer还能够看到用户电脑在运行的程序和CPU、内存的使用情况,方便管理电脑的后台程序。
工具下载:Process Explorer v16.43。
2)VTune
3)LoaderLocker:调试助手
参考混合程序集的初始化。
4)ProcDump
ProcDump 是一个命令行实用程序,其主要目的是监视应用程序的 CPU 峰值并在峰值期间生成crash dumps ,管理员或开发人员可以使用它来确定峰值的原因。 ProcDump 还包括挂起的窗口监控(使用与 Windows 和任务管理器使用的窗口挂起的相同定义)、未处理的异常监控并可以根据系统性能计数器的值生成转储。它还可以用作通用进程转储实用程序,您可以将其嵌入到其他脚本中。
使用参考。
5)Windows 调试程序 (WinDbg)
用于调试内核模式和用户模式代码、分析故障转储以及在代码执行时检查 CPU 寄存器。
使用参考。
案例参考。
6)PsSuspend
PsSuspend 允许挂起本地或远程系统上的进程,挂起后可在稍后的某个时间点继续运行,而不是终止消耗资源的进程。
pssuspend.exe pid 挂起进程
pssuspend.exe -r pid 恢复进程
pskill.exe pid 杀死进程,同Linux下的kill
-
Windows窗口自动化操作类forVB6_V2.0 clsWindow源码
2015-11-24 14:34:09目前该类封装了绝大部分对windows窗口的常用操作,例如:获取窗口句柄,设置窗口为活动窗口,设置窗口内文本框内容,点击窗口内的某些按钮等。 这个类现在还在一直不断地扩充,功能已经很强大很广泛,使用它可以... -
windows窗口分析,父窗口,子窗口,所有者窗口
2016-12-14 11:50:57(本文尝试通过一些简单的实验,来分析Windows的窗口机制,并对微软的设计理由进行一定的猜测,需要读者具备C++、Windows编程及MFC经验,还得有一定动手能力。文中可能出现一些术语不统一的现象,比如“子窗口”,有...(本文尝试通过一些简单的实验,来分析Windows的窗口机制,并对微软的设计理由进行一定的猜测,需要读者具备C++、Windows编程及MFC经验,还得有一定动手能力。文中可能出现一些术语不统一的现象,比如“子窗口”,有时候我写作“child window”,有时候写作“child”,我想应该不会有太大影响,文章太长,不一一更正了)
问题开始于我的最近的一次开发经历,我打算把程序的一部分界面放在DLL中,而这部分界面又需要使用到Tooltip,但DLL中的虚函数PreTranslateMessage无法被调用到,原因大家可以在网上搜索一下,这并不是我这篇文章要讲的。PreTranslateMessage不能被调,那Tooltip也就不能起作用,因为Tooltip需要在PreTranslateMessage中加入tooltip.RelayEvent(&msg)来触发事件,方可正常显示。解决方法有好几个,我用的是比较麻烦的一个——完全自己手动编写Tooltip,然后用WM_MOUSEMOVE等事件来触发Tooltip显示,写好之后发现些小问题,那就是调试运行时候IDE给了个warning,说我在析构函数中调用了DestroyWindow,这样会导致窗口OnDestry和OnNcDestroy不被正常调用,这个问题我以前遇到过,当然解决方法也是显而易见的,只需要在窗口对象(C++概念,非Windows内核对象,下文同)销毁前,调用DestroyWindow即可。对于要销毁的这个窗口的子窗口,是不需要显式调用DestroyWindow的,因为父窗口在销毁的时候也会销毁掉它们,OK,我把这个过程用个示意图说明一下:
图1
上图表示了App Window及其子窗口的关系,现在假设我们要销毁Parent Window 1(对应的对象指针是m_pWndParent1),我们可以m_pWndParent1->DestroyWindow(),这样Child Window 1,Parent Window 2,Child Window 2都被销毁了,销毁的时候这些窗口的OnDestry和OnNcDestroy都被调用了,最后delete m_pWndParent1,此时m_pWndParent1->m_hWnd已经是NULL,不会再去调用Destroy,在析构的时候也就不会出现Warning。但如果不先执行m_pWndParent1->DestroyWindow()而直接delete m_pWndParent1,那么在CWnd::~CWnd中就会调用DestroyWindow(m_hWnd),这样会产生WM_DESTROY和WM_NCDESTROY,会尝试去调用OnDestry和OnNcDestroy,但由于是在CWnd的函数~CWnd()的内部调用这两个成员,此时的虚函数表指针并不指向派生类的虚函数表,因此调用的其实是CWnd::OnDestroy和CWnd::OnNcDestroy,派生类的OnDestry和OnNcDestroy不被调用,但我们很多时候把释放内存等操作写在派生类的OnDestroy和OnNcDestroy中,这样,就容易导致内存泄露和逻辑混乱了。
上面这些道理我当然是知道的,但Warning还是出现了,而且我用排除法确定了是跟我写的那个Tooltip有关,下面是关于我的Tooltip的截图:
图2
大家看到,Tooltip显示在我的图形窗口上,它是个弹出式(popup)窗口,其内容为当前鼠标光标的坐标值,图形窗口之外,我是不想让它显示的,那么按照我的思路,Tooltip就应该设计是图形窗口的子窗口,它的窗口对象就应该作为图形窗口对象的成员,在图形窗口OnCreate的时候创建,在图形窗口被DestroyWindow的时候自动销毁,前面提到过,父窗口被销毁的时候,其子窗口会被自动销毁,没错吧,所以不需要显式去对Tooltip调用DestroyWindow。可事实证明了这样是有问题的,因为Tooltip的父窗口根本不是,也不能是图形窗口。大家可以看到我的图形窗口是作为一个子窗口嵌入到别的窗口中去的,它的属性包含了WS_CHILD,通过实验,我发现Tooltip的父窗口只能指定为程序主窗口,如果企图指定为那个图形窗口的话,它就自动变为程序主窗口,再进一步研究发现,弹出式窗口的父窗口都不能是带WS_CHILD风格的窗口,然后打开spy++查看,弹出式窗口的上一级都是桌面,可是,通过GetParent函数,得到的弹出式窗口的父窗口却是程序主窗口而不是桌面,为什么?……问题越来越多,我糊涂了,上面说的都是在我深入理解前,所看到的现象,包括了我的一些概念认识方面的错误。
好吧,我们现在开始,一点点地通过实验去攻破这些难题!
一、神秘的WS_OVERLAPPED
我们从WinUser.h头文件中可以看出,窗口可分三种,其Window Styles定义如下:
- #define WS_OVERLAPPED 0x00000000L
- #define WS_POPUP 0x80000000L
- #define WS_CHILD 0x40000000L
那么我们很容易得到这个结论:style的最高位是1的,是一个popup窗口,style的次高位是1的,代表是一个child窗口,如果最高位次高位都是0,那这个窗口就是一个overlapped窗口,如果两位都是1,厄……MSDN告诉我们不能这么干,事实呢?我后面再讲。其实这个结论是有点过时的,甚至很能误导人,不是我们的原因,很可能是Windows的历史原因,为什么?具体也是后面讲。嘿嘿。
OK,我们现在开始来尝试,看看这些风格究竟影响窗口几何,对了,准备spy++,这是必备工具。
用VC++的向导创建一个Hello World的Windows程序,注意是Windows程序,不是MFC的Hello World,这样我们可以绕开MFC,专注于查看一些Windows的技术细节,编译,运行。
图3
然后用spy++查看这个窗口的风格,发现其风格显示为“WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_CLIPSIBLING|WS_OVERLAPPED”。此时它的创建函数为:
- hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
只制定了一个WS_OVERLAPPEDWINDOW,但我们很快就找到了WS_OVERLAPPEDWINDOW的定义:
- #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | /
- WS_CAPTION | /
- WS_SYSMENU | /
- WS_THICKFRAME | /
- WS_MINIMIZEBOX | /
- WS_MAXIMIZEBOX)
原来overlapped窗口就是有标题,系统菜单,最小最大化按钮和可调整大小边框的窗口,这个定义是正确的,但只是个我们认知上的概念的问题,因为popup和child窗口也同样可以拥有这些(后面证明)。由于WS_OVERLAPPED为0,那我们是不是可以把WS_OVERLAPPEDWINDOW定义中的WS_OVERLAPPED拿掉呢?那是肯定的,那也就是说WS_OVERLAPPED什么都不是!我们只作popup和child的区分,是不是这样?也不是,我们继续实验。
很简单,接下去我们只给这个向导生成的代码加一点点东西,就是把CreateWindow改成:
- hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW|WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
对,给窗口风格增一个popup风格,看看会怎么样?运行!这回可不得了,窗口缩到了屏幕的左上角,并且宽度高度都变为了最小,当然,你还是可以用鼠标拖动窗口边缘来调整它的大小的。如图:
图4
这是为什么呢?观察CreateWindow的,第四、第五、第六和第七参数,分别为窗口的x坐标,y坐标,宽度,和高度,CW_USEDEFAULT被define成0,所以窗口被缩到左上角去也就不奇怪了,可没有popup,光是overlapped风格的窗口,为什么不会缩呢?看MSDN的说明,对第四个参数的说明:“If this parameter is set to CW_USEDEFAULT, the system selects the default position for the window's upper-left corner and ignores the y parameter. CW_USEDEFAULT is valid only for overlapped windows; if it is specified for a pop-up or child window, the x and y parameters are set to zero. ”其余几个参数也有类似的描述,这说明了什么?说明Windows对overlapped和popup还是作区分的,而这点,算是我们发现的第一个不同。哦,还有件事情,就是用spy++观察其风格,发现其确实多了一个WS_POPUP,其余没什么变化。
继续,这回还是老地方,把WS_POPUP改为WS_CHILD,试试看,这回创建窗口失败了,返回0,用GetLastError查看具体错误信息,得到的是:“1406:无法创建最上层子窗口。”看来桌面是不让我们随便搞的。继续,还是老地方,这回改成:
- hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW|WS_POPUP|WS_CHILD, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
嗯?有没搞错,又是popup又是child,肯定不能成功吧,不试不知道,居然成功了,这个创建出来的窗口乍一看,跟popup风格的很像,但用起来有些怪异,比如:当它被别的窗口挡住的时候,不能通过点击它的客户区来让它显示在前面,即使点击它的标题栏,也是要松开鼠标左键,它才能显示在前面,还有就是用spy++的“瞄准器”没法准确捕捉到这个窗口,瞄准器对准它的时候,就显示Caption为“Program Manager”,class为“Program”,“Program Manager”是什么?其实就是我们所看到的这个桌面(注意,不是桌面,我说的是我们说“看到的桌面”,就是显示桌面图标的这个所能看到的桌面窗口,和前面提到的桌面窗口是有区别的)的父窗口的父窗口,这个窗口一般情况下是不能直接“瞄准”到的,这点可以通过spy++证实,如图:
图5
图6
spy++不能直接“瞄准”这个popup和child并存的怪窗口,但我们有别的办法捕捉到它,<Alt>+<F3>,输入窗口的标题来查找(记得运行程序后刷新一下才能找到),结果见下图:
图7
我们从上图中清楚地看到,popup和child并存!用spy++逐个查看桌面窗口的下属,这种情况还是无独有偶的,但这样的窗口代表了什么意义,我就不清楚了,总之用起来怪怪的,对Microsoft来说,这可能就是Undocumented,OK,我们了解到这里就行了,但一般情况下,我们不要去创建这种奇怪的窗口。这几轮实验给我们什么启示?设计上的启示:一个应用程序的主窗口通常是一个Overlapped类型的窗口,当然有时可以是一个popup窗口,比如基于对话框的程序,但不应该是一个child窗口,尽管上面演示了如何给应用程序主窗口加入child风格。
那还有一个问题,我为什么认为WS_OVERLAPPED神秘呢?这还算是拜spy++所赐,按照我们一般的想法,如果一个窗口的风格的最高两位都是0,它既不是popup也不是child的时候,那它就是Overlapped。事实上spy++的判定不是这样的,就以刚才的实验为例,当使用WS_OVERLAPPEDWINDOW|WS_POPUP风格创建窗口的时候,WS_OVERLAPPED和WS_POPUP属性同时出现了,我做了很多很多的尝试,企图找出其中规律,看看spy++是怎么判定WS_OVERLAPPED的,但至今没结论,我到MSDN上search,未果,有人提起这个问题,但没有令我满意的答复,下面这段文字是我找到的可能有点线索的答复:
Actually, Microsoft Spy++ is wrong.
There are two bits in the window style that control its type. If the high-order bit of the style DWORD is set, the window is a popup window. If the next bit is set, the window is a child window. If neither is set, the window is overlapped. (If both are set, the result is undocumented.)Look at these definitions from WinUser.h.
- #define WS_OVERLAPPED 0x00000000L
- #define WS_POPUP 0x80000000L
- #define WS_CHILD 0x40000000L
Your window style (0x94c00880) has the high-order bit set and the next bit clear so it is a popup window, not an overlapped window.
The correct way to identify all three types of windows (this is what Spy++ should do) is
- dwStyle = GetWindowLong(hWnd, GWL_STYLE);
- if (dwStyle&WS_POPUP)
- // it's a popup window
- else if (dwStyle&WS_CHILD)
- // it's a child window
- else
- // it's an overlapped window
这断描述跟我的想法一致。要知道,就算你只给窗口一个WS_POPUP的风格,WS_OVERLAPPED也会显示在spy++上的,我认为这十分有问题,究竟spy++如何判,估计得请教比尔盖茨了。还有一段有趣的描述,估计也有所帮助:
As long as...
WS_POPUP | WS_OVERLAPPED
...is absolutelly equivalent with...
WS_POPUP
... why do you care if Spy++ lists WS_OVERLAPPED or not?Please stop playing "Thomas Unbeliever" with us.
Becomes too expensive to use "walking on the water" device here again, and again. ;)虽然这么说,我还是认为,spy++给了我们不少误导,那么对WS_OVERLAPPED的讨论就暂时告一段落吧,作为一个技术人,很难容忍自己无法理解的逻辑,我就是这么种人……不过如果再扯下去的话这篇文章就不能结束了,所以姑且认为,这是spy++的错,而我们还是认为窗口分3种——popup,child和Overlapped。(Undocumented不在此列,也不在本文讲述之列)
二、Parent与Owner
这是内容最多的一节,做好心理准备。
微软和我们开了个玩笑,告诉我们,窗口和人一样,可以有父母,有主人……我们先来看一个最著名的Windows API:
- HWND CreateWindowEx(
- DWORD dwExStyle, // extended window style
- LPCTSTR lpClassName, // registered class name
- LPCTSTR lpWindowName, // window name
- DWORD dwStyle, // window style
- int x, // horizontal position of window
- int y, // vertical position of window
- int nWidth, // window width
- int nHeight, // window height
- HWND hWndParent, // handle to parent or owner window
- HMENU hMenu, // menu handle or child identifier
- HINSTANCE hInstance, // handle to application instance
- LPVOID lpParam // window-creation data
- );
猜对了,我就是从MSDN上copy下来的,看第九个参数的名字叫hWndParent,顾名思义哦,这就是Parent窗口了,不过我们中国人不喜欢称之“父母窗口”,我们喜欢叫它“父窗口”,简单一点。其实这个名字对我们造成了不少的误导,我只能说,可能也是由于历史原因,比如在Windows 1.0(1985年出的,当时没什么影响力)的时候,只有Parent这个概念,没有Owner的概念。
回头看看文章开始我提起的,我企图将Tooltip的父窗口设置为一个图形窗口,不能成功,Tooltip的父窗口会自动变成应用程序主窗口,这是为什么?好,现在开始讲概念了,都是我花了很多时间在互联网上搜索,筛选,确认,得出来的结论:
规则一:Owner window控制了Owned window的生存,当Owner window被销毁的时候,其所属的Owned window就会被销毁。
规则二:Parent window控制了Child window的绘制,Child window不可能显示在其Parent window的客户区之外。
规则三:Parent window同时控制了Child window的生存,当Parent window被销毁的时候,其所属的Child window就会被销毁。
规则四:Owner window不能是Child window。
规则五:Child window一定有Parent(否则怎么叫Child?),一定没有Owner。
规则六:非Child window的Parent一定是桌面,它们不一定有Owner。这是比较重要的几点,如果你认为这跟你以前学到的,或者认知的有所不同,先别急着抗议,先看看我是怎么理解的。除了这几条规则,下面我还会逐步给出一些规则。
先说比较好理解的Child window,上文提到了,包含了WS_CHILD风格的窗口就叫Child window,我们中文叫“子窗口”。那么我前面提到的我写的那个Tooltip,是不是“子窗口”呢?——当然不是了,它没有WS_CHILD风格啊,它是popup风格的,我想当然地认为在创建它的时候给它指定了那个Parent参数,那它的Parent就是那个参数,其实是错的。这个实验最简单了,随便找些应用程序,比如“附件”里的计算器,用spy++的“瞄准器”观察上面的按钮等“子窗口”,在Styles标签中,我们可以看到WS_CHILD(或者WS_CHILDWINDOW,一样的)属性,然后在Windows标签中,我们可以清楚地看到,凡是包含了WS_CHILD属性的窗口(子窗口),都没有Owner window,不信还可以继续观察其它应用程序,省去自己编程了。再看它们的Parent window,是不是一定有的?——当然一定有。
前面说了,子窗口不能显示在父窗口客户区之外,我们最常见的子窗口就是那些摆在对话框上的控件,什么button啊,listbox啊,combobox啊……都有个共同特点,不能拖动的,除非你重写它们的window procedure,然后响应WM_MOUSEMOVE等消息,实现所谓“拖动”。那么有没有能够像应用程序主窗口那样有标题栏,能够被自由拖动的子窗口呢?——当然有!要创建是吗?简单,直接用MFC向导创建一个MDI程序即可,MDI的那些View其实就是可以自由拖动的子窗口,可以用spy++查看一下它们的属性,当然,你是不能把它们拖出主窗口的客户区的。也许你跟我一样,觉得MFC封装了过多的技术细节,想完全自己手动创建一个能拖动的子窗口,而且看起来就像个MDI的界面,OK,follow me。
首先当然是用应用程序向导生成最普通的Window应用程序了。然后增加一个窗口处理函数,也就是我们准备创建的子窗口的处理函数了。
- LRESULT CALLBACK WndProcDoNothing(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
DoNothing?好名字。注册之:
- WNDCLASSEX wcex;
- wcex.cbSize = sizeof(WNDCLASSEX);
- wcex.style = CS_HREDRAW | CS_VREDRAW;
- wcex.lpfnWndProc = (WNDPROC)WndProcDoNothing;
- wcex.cbClsExtra = 0;
- wcex.cbWndExtra = 0;
- wcex.hInstance = hInstance;
- wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_ALLWINDOWTEST);
- wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
- wcex.lpszMenuName = NULL; //子窗口不能拥有菜单,指定了也没有用
- wcex.lpszClassName = TEXT("child_window");
- wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
- RegisterClassEx(&wcex);
最后当然是把它给创建出来了:
- g_hwndChild = CreateWindowEx(NULL, TEXT("child_window"), TEXT(""), WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300, hWnd, NULL, hInstance, NULL);
关于WS_CLIPSIBLINGS属性,下文将提到。好,就这样,大家看看运行效果:
图8
是不是很少遇到这种窗口组织结构?确实很少人这样用,而且哦,你会发现子窗口的标题栏没办法变为彩色,它一直是灰的,就表示它一直处于未激活状态,你怎么点它,拖它,调它,都没用的,而这个时候程序主窗口一直显示为激活状态,如何激活这个子窗口?我曾经对此苦思冥想,最后才知道,子窗口是无法被激活的,你立即反驳:“那MFC如何做到的?”哈哈,好,你反应够快,我下文会给你演示如何“激活”子窗口。(注意是加引号的)现在尝试移动主窗口,你会发现所有它的子窗口都会跟着主窗口移动的,这就好像我们看苹果落地一样,不会觉得奇怪,但你有没有想过,主窗口移动的时候,其子窗口对屏幕的位置也发生了变化,不变的是相对主窗口的客户区坐标。这就是子窗口的特性。再试试看启用/禁用主窗口,显示/隐藏主窗口看看,就不难得出结论:
规则七:子窗口会随着其父窗口移动,启用/禁用,显示/隐藏。
子窗口我们就暂时讲那么多,接着讲所有者窗口,就是Owner window,由于子窗口一定没有Owner,因此Owner window是对popup和Overlapped而言的,而popup和Overlapped前面也提到了,不一定有Owner,不像Child那样一定有Parent。现在进入我们下一个实验:
还是用向导生成最普通的Windows hello world程序,步骤和上一个实验很相似,仅仅改了一点点东西,改了哪点?就是把CreateWindowEx函数的第四个参数的WS_CHILD拿掉,其余不变,代码我就不贴了,大家编译并运行看看。大家会看到类似这个效果:
图9
弹出窗口的caption是蓝色的,说明它处于激活状态,如果你现在点击程序主窗口,那弹出窗口的标题栏就变灰,而程序主窗口的标题栏变蓝,两个窗口看起来就像并列的关系,但你很快发现它们其实不并列,因为如果它们有重叠部分的话,弹出窗口总是遮挡程序主窗口。用spy++观察之,发现程序主窗口就是弹出窗口的Owner。
规则八:非Child window总是显示在它们的Owner之前。
看到了没?这个时候CreateWindowEx的第九个参数的意义就不是Parent window,而是Owner,那把这个参数改为NULL,会有什么效果呢?马上试试看,反正这么容易。
图10
初一看没什么变化,其实变化大了,一是主窗口这回可以显示在弹出窗口之前了,二是任务栏上出现了两个button。
图11
用spy++观察到这两个窗口的Owner都是NULL。
规则九:Owner为NULL的非Child窗口能够(不是一定哦)在任务栏上出现它们的按钮。
这个时候,你应该清楚为什么给一个MessageBox正确指定一个Owner这么重要了吧?我以前有个同事,非常“厉害”,他创建了一个程序,一旦出现点什么问题,就能把MessageBox弹得满屏都是,而且把任务栏霸占得渣都不剩,他大概是没明白这个道理。MessageBox是一个非child窗口,如果不指定一个正确的Owner,那弹出MessageBox之后,Owner还是处于可操作的状态,两个窗口看起来是并列的,都在任务栏上有显示,如果再弹出MessageBox,先关闭那个MessageBox?我看先关哪个都没问题,因为界面操作上没有限制,但这样很容易导致逻辑混乱,如果不幸走入了个死循环,连续弹MessageBox,那就像这位同事写的那个程序那样,满屏皆是消息框了。
我们现在来进行一些稍微复杂点点的实验,就是创建A弹出窗口,其Owner为主窗口,创建B弹出窗口,其Owner为A窗口,创建C弹出窗口,其Owner为B窗口。步骤模仿上面的窗口创建步骤即可,好,编译,运行,效果大致如此:
图12
现在,把主窗口最小化,看看发生了什么事情。你会发现A窗口不见了,而B,C窗口尚在,A窗口究竟是跟随主窗口一起最小化了呢,或者被销毁了呢?还是被隐藏了呢?答案是被隐藏了,我们可以通过spy++找到它,发现它的属性里边没有WS_VISIBLE。那现在将主窗口还原,A这时候出现了,那现在我们最小化A,Oh?What happen?B不见了,主窗口和C都还在,我们还是老办法,用spy++看B,发现它没了WS_VISIBLE属性,现在还原A窗口,方法如下图所示:
图12_x
注意,最小化的A并不显示在任务栏上。还原A后B也出现了。规则十:Owner窗口最小化后,被它拥有的窗口会被隐藏。
前面测试的是最小化,那我们现在不妨来测试一下,让A隐藏,会怎么样?在主窗口里创建一个button,点这个button,就执行ShowWindow(g_hwndA, SW_HIDE),如图:
图13
你会发现,被隐藏的只有A,A隐藏后主窗口,B和C都是可见的,你可以继续尝试,隐藏B和C,或者主窗口,不过,你隐藏了主窗口的话恐怕就没法通过主窗口的菜单来关闭程序了,只能打开任务管理器结束掉程序。
规则十一:Owner隐藏,不会影响其拥有的窗口。
现在不是最小化,也不是隐藏,而是测试“关闭”,即销毁窗口,尝试关闭A,发现B,C被关闭;尝试关闭B,发现C被关闭。这个规则也就是规则一了,不必再列。
好,我不可能把所有的规则都列出来,但我相信前面所写的这些东西,对大家起到了抛砖引玉的作用了,其它规则,也可以通过类似的实验得出,或者用已有的规则去推导。那在转入下一节前,我提点问题:
为什么子窗口没有Owner?(就是我们来猜猜微软为什么这样设计)试想一个Child既有Parent,又有Owner,Parent控制其绘制,Owner控制其存在,在Owner销毁的时候,子窗口就要被销毁,而其Parent有可能还继续存在,那这个子窗口的消失可能有点不明不白,这是其中一个原因,另一个原因也类似,如果Parent不控制子窗口的存在,只管其绘制,那么在Parent销毁的时候,Owner可以继续存在,这个时候的子窗口是存在,而又不能显示和访问的,这可能会导致别的怪异问题,既然起了Child这个名字,就应该把它全权交给Parent,由Parent来决定它的一切,我想这就是微软的道理。
那我们如何获取一个窗口的Parent和Owner?大家都知道API函数,GetParent,这是用来获取Parent窗口句柄的API——慢!这并不完全正确!大家再仔细点看看MSDN,再仔细点:
If the window is a child window, the return value is a handle to the parent window. If the window is a top-level window, the return value is a handle to the owner window.
什么是top-level window?就是非Child window,这个后面再详细谈这个,现在注意看了,GetParent返回的有可能不是parent,对于非child窗口来说,返回的就不是parent,为什么?因为非child窗口的parent恒定是Desktop啊(规则6),这还需要获取吗?我们接下去的实验是用来测试GetParent这个函数是否工作正常的,什么?测试M$提供的API,没错,呵呵,当一把微软的测试员吧。接上面那个实验:
//在窗口创建完成后,调用下面的代码,在第一个GetParent处设置个断点,查看返回值,如果返回NULL,按照MSDN所说的,用GetLastError看看是否有出错。
- {
- DWORD rtn;
- HWND hw = GetParent(hWnd); //获取主窗口的“Parent”
- if(hw==NULL)
- rtn = GetLastError();
- hw = GetParent(g_hwndA); //获取A的“Parent”
- if(hw==NULL)
- rtn = GetLastError();
- hw = GetParent(g_hwndB); //获取B的“Parent”
- if(hw==NULL)
- rtn = GetLastError();
- hw = GetParent(g_hwndC); //获取C的“Parent”
- if(hw==NULL)
- rtn = GetLastError();
- }
我的实验结果有些令我不解,清一色返回0,包括GetLastError,也就是说没有出错,那GetParent返回0,根据MSDN上的描述,原因只可能是:这些窗口确实没有Owner。不对啊?难道前面的规则和推论都是错误的不成?我创建它们的时候,就明明白白地指定了hWndParent参数,而且上面的实验也表明了他们之间的Owner和Owned关系,那是不是GetParent错了?我想是的,你先别对着我扔砖头,想看到正确的情况么?好,我弄给你看。
我们是如何创建A,B和C这几个弹出窗口的?我再把创建它们的语句贴一下吧:
- g_hwndX = CreateWindowEx(NULL, TEXT("child_window"), TEXT("X"), WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300, hWnd, NULL, hInstance, NULL);
现在把这个语句改为:
- g_hwndX = CreateWindowEx(NULL, TEXT("child_window"), TEXT("X"), WS_POPUP|WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300, hWnd, NULL, hInstance, NULL);
对,就是加上一个WS_POPUP,看看情况变得怎么样?
很惊讶,对不?GetParent这回全部都正确地按照MSDN的描述工作了,这是我发现的popup和Overlapped的第二个差别,第一个差别?在文章开头附近,自己回去找。而spy++显示出来的那个Parent,其实就是GetParent返回的结果。记住,对于非child窗口来说,GetParent返回的并不是Parent,MSDN也是这么说的,你看看这个函数的名字是不是很有误导性?还有spy++也真是的,将错就错。好吧,就让它错去吧,但我们得记住:对非Child窗口来说,Parent一定是桌面。好,再有个问题,看刚刚这个实验,对于有WS_POPUP风格的非Child窗口来说,GetParent能够取回它的Owner,可对于没有WS_POPUP风格的非Child窗口来说,GetParent恒定返回0,那我们如何有效地取得非Child窗口真正的主人呢?方法当然是有的,看:
- {
- DWORD rtn;
- HWND hw = GetWindow(hWnd, GW_OWNER); //获取主窗口的Owner
- if(hw==NULL)
- rtn = GetLastError();
- hw = GetWindow(g_hwndA, GW_OWNER); //获取A的Owner
- if(hw==NULL)
- rtn = GetLastError();
- hw = GetWindow(g_hwndB, GW_OWNER); //获取B的Owner
- if(hw==NULL)
- rtn = GetLastError();
- hw = GetWindow(g_hwndC, GW_OWNER); //获取C的Owner
- if(hw==NULL)
- rtn = GetLastError();
- }
这么一来,无论是否带有WS_POPUP风格,都能够正常取得其所有者了,这个跟spy++的结果一致,用GetWindow取得的Owner总是正确的,那有没有一种方法,使得取得的Parent总是正确的?很遗憾,没有直接的API,包括使用GetWindowLong(hwnd, GWL_HWNDPARENT)都不能一直正确返回Parent,BTW,有位高人说,GetWindowLong(hwnd, GWL_HWNDPARENT)和GetParent(hwnd)有时候会得到不同的结果,不过这个我尝试不出来,我观察的,它们总是返回一样的结果,无论对什么窗口,真怀疑GetParent(hwnd)就是return (HWND)GetWindowLong(hwnd, GWL_HWNDPARENT),虽然我们不能直接一步获取正确的Parent,但我们可以写一个简单的函数:
- HWND GetTrueParent(HWND hwnd)
- {
- DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
- if((dwStyle & WS_CHILD) == WS_CHILD)
- return GetParent(hwnd);
- else
- return GetDesktopWindow();
- }
你终于憋不住了,对我大吼:“你有什么依据说非Child窗口的Parent一定是Desktop?”我当然是有依据的,首先是这些非child window的绘制,不能超出桌面,超出桌面就什么都看不见了,只能是桌面管理着它们的绘制,如果它们确实存在Parent的话,当然,聪明你认为这个理由并不充分,OK,我们编程来证明,先介绍一个API:
- HWND FindWindowEx(
- HWND hwndParent, // handle to parent window
- HWND hwndChildAfter, // handle to child window
- LPCTSTR lpszClass, // class name
- LPCTSTR lpszWindow // window name
- );
又被你猜对了,我是从MSDN上copy下来的(^_^),看MSDN对这个函数的说明:
hwndParent
[in] Handle to the parent window whose child windows are to be searched.
If hwndParent is NULL, the function uses the desktop window as the parent window. The function searches among windows that are child windows of the desktop.hwndChildAfter
[in] Handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window.
If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.lpszClass
窗口类名(我来翻译,简单点)lpszWindow
窗口标题关键是看第一个参数,如果hwndParent为NULL,函数就查找desktop的“子窗口”,但这个“子窗口”是加引号的,因为这里的“子窗口”和本文前面一直提到的子窗口确实不太一样,那就是这里的“子窗口”没有WS_CHILD风格,算是一个特殊吧,也难怪GetParent不愿意告诉我们desktop就是这些非Child的父窗口。好,有这个函数,我们就可以知道刚才创建的那几个弹出窗口的老爸究竟是不是桌面。代码十分简单:
- {
- DWORD rtn;
- HWND hw = FindWindowEx(NULL, NULL, TEXT("ALLWINDOWTEST"), TEXT("AllWindowTest")); //从桌面开始查找主窗口
- if(hw==NULL)
- rtn = GetLastError();
- hw = FindWindowEx(NULL, NULL, TEXT("child_window"), TEXT("A")); //从桌面开始查找A
- if(hw==NULL)
- rtn = GetLastError();
- hw = FindWindowEx(NULL, NULL, TEXT("child_window"), TEXT("B")); //从桌面开始查找B
- if(hw==NULL)
- rtn = GetLastError();
- hw = FindWindowEx(NULL, NULL, TEXT("child_window"), TEXT("C")); //从桌面开始查找C
- if(hw==NULL)
- rtn = GetLastError();
- }
结果如何?(是不是偷懒干脆不做,等着我说结果啊?)我的结果是全部找到了,和用spy++查找的结果一样,所以我有充分的理由认为,所有非child窗口其实是desktop的child,spy++的树形结构组织确实也是这么阐述的。你很厉害,你还是能够驳斥我:“根据规则三,Parent被销毁的时候,其Child将被销毁,你证明给我看?”这个……有点难:
- HWND hwndDesktop = GetDesktopWindow();
- BOOL rtn = DestroyWindow(hwndDesktop);
- if(!rtn)
- DWORD dwErr = GetLastError();
My god,Desktop没了,你说我们还能看到什么呢?当然微软不会没想到这点,DestroyWindow当然不能成功,错误代码为5,“拒绝访问”。好,我有些累了,不能再纠缠了,转入下一节!留个作业如何?尝试使用SetParent这个API,改变窗口的Parent,观察运行情况,并思考这样做有什么不好之处。
三、如何体现WS_CLIPSIBLING和WS_CLIPCHILD?
看了这个标题,应该怎么做?我想你十有八九是打开MSDN,输入这两个关键字去搜索吧?OK,不用了,我把MSDN对这两个窗口风格的说明贴出来:
WS_CLIPCHILDREN Excludes the area occupied by child windows when you draw within the parent window. Used when you create the parent window.
WS_CLIPSIBLINGS Clips child windows relative to each other; that is, when a particular child window receives a paint message, the WS_CLIPSIBLINGS style clips all other overlapped child windows out of the region of the child window to be updated. (If WS_CLIPSIBLINGS is not given and child windows overlap, when you draw within the client area of a child window, it is possible to draw within the client area of a neighboring child window.) For use with the
WS_CHILD style only.找到是不难,但如果光看这个就明白的话我也不必要写这种文章了,没有适当的代码去实践,估计很多人是不懂这两个风格什么含义的。OK,现在我来带你实践。spy++开着不?哈,别关啊,后面还要用到。用spy++观察各个top-level window(非Child窗口)的属性,是不是都有个WS_CLIPSIBLINGS?想找个没有的都不行,如果你不服气,你要自己创建一个没有WS_CLIPSIBLINGS风格的顶层窗口,好吧,我在这里等你一会儿(……一会儿过去了……),你垂头丧气地回来了:“不行,即便我不指定这个风格,Windows也强制帮我加上。”那……你可以强制剥离掉这个风格啊,这样:
- DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
- dwStyle &= ~(WS_CLIPSIBLINGS);
- SetWindowLong(hWnd, GWL_STYLE);
执行后用spy++一看,还是没有把WS_CLIPSIBLINGS风格去掉,看来Windows是吃定你的了。嗯,前面说的都是top-level window,那对于child window呢?创建一个MFC对话框,在上面加几个button,然后增加/删除这几个button的WS_CLIPSIBLINGS风格?你除了发现child window对与WS_CLIPSIBLING风格不再是强制的之外,恐怕仍然一无所获吧。还是得Follow me,我还是不用MFC,用最简单的Windows API。模仿第二节的创建几个popup窗口A、B、C的那个例子,只不过现在的CreateWindowEx改成这样:
- g_hwndA = CreateWindowEx(NULL, TEXT("child_window"), TEXT("A"),
- WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 30, 30, 400, 300, hWnd, NULL, hInst, NULL);
- g_hwndB = CreateWindowEx(NULL, TEXT("child_window"), TEXT("B"),
- WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 60, 60, 400, 300, hWnd, NULL, hInst, NULL);
- g_hwndC = CreateWindowEx(NULL, TEXT("child_window"), TEXT("C"),
- WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 90, 90, 400, 300, hWnd, NULL, hInst, NULL);
创建出来的效果如图:
图14
一眼看没什么奇怪的,但尝试拖动里边的窗口就出现些问题了,首先是显示在最前端的C窗口不能拖动(其实是被挡住了),然后你发现B也不能拖动,A可以,A一拖,就出现这种情况:
图15
如果你尝试拖动B,C,情况可能更奇怪,总之就是窗口似乎不能正常绘制。那如何才能正常呢?我不说你都知道了,就是这节的主题,给这几个child window加上WS_CLIPSIBLINGS风格,就OK了,那如何解释?现在看图14,表面上看是C叠在B上面,而B叠在A上面,事实上正好相反不是,(关于窗口Z order的问题看下一节)事实是B叠在C之上,A叠在B上面,所以企图拖C,其实点到的是A的客户区,C当然“拖不动”,那为什么看起来是C叠B,B叠A?这跟绘制顺序有关系,A先绘,然后B,最后C,也许你又要我验证了,好,我改一下代码,打个log出来给你看。把Do nothing的那个窗口过程改为:
- LRESULT CALLBACK WndProcDoNothing(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch(message)
- {
- case WM_PAINT:
- {
- TCHAR szOut[20];
- TCHAR szWindowTxt[10];
- GetWindowText(hWnd, szWindowTxt, 10);
- wsprintf(szOut, TEXT("%s Paint/n"), szWindowTxt);
- OutputDebugString(szOut);
- }
- break;
- }
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
打印结果为:
A Paint
B Paint
C Paint那B为什么绘在A的上面?那就是因为没有指定WS_CLIPSIBLINGS,WS_CLIPSIBLINGS这个风格会在窗口绘制的时候裁掉“它被它的兄弟姐妹挡住的区域”,被裁掉的区域当然不会被绘制。对子窗口来说,这个风格不是一定有的,因为微软考虑到大多数子窗口,比如dialog上的控件,基本上都是固定不会移动的,不会产生互相叠起来的现象。那对于top-level窗口,如果可以没有这个风格,那我们的界面可能很容易混乱,所以这个风格是强制的。也许你要问:“那为什么我移动A的时候,A自己不会重绘?”当然不会了,因为我移动A,A本来就是在最顶层,完全可见的,没有什么区域变得无效需要重新绘制,所以它不会被重绘,这个可以通过log看出来。
现在分析下一个风格WS_CLIPCHILDREN,前一个是裁兄弟姐妹,这个是裁孩子,微软也够狠的。不多说了,直接改代码来体会这个风格的作用,按照这个意思,有这个风格的父窗口在绘制的时候,不会把东西绘到子窗口的区域上去,这个嘛,简单,我们只要在父窗口的WM_PAINT里画点东西试试看就好了。代码还是前面的代码,把A,B,C都加上WS_CLIPSIBLINGS,主窗口不要WS_CLIPCHILDREN风格,我们看看是不是能把东西画到子窗口的区域去。
- case WM_PAINT:
- hdc = BeginPaint(hWnd, &ps);
- RECT rt;
- GetClientRect(hWnd, &rt);
- DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
- MoveToEx(hdc, 0, 0, NULL);
- LineTo(hdc, 600, 400); //To be simple, just a line.
- EndPaint(hWnd, &ps);
- break;
运行结果如图:
图16
嗯?没有穿过啊?为什么?先动脑想想半分钟。
那是因为我们的实验不够严谨,现在在主窗口WM_PAINT消息的处理中加入一个Debug内容:- OutputDebugString(TEXT("Main window paint/n"));
再看看debug出来的log:
Main window paint
A Paint
B Paint
C Paint
因为是主窗口先绘制,然后才是子窗口,所以即便这根线是穿过子窗口区域的,恐怕也看不出来了。那我们就不要在WM_PAINT里绘制,我们增加一个菜单项,叫paint a line,点这个菜单就执行下面的代码:- //在主窗口的WM_COMMAND消息处理中
- switch (wmId)
- {
- //...
- case ID_PAINT_A_LINE:
- {
- HDC hdc = GetDC(hWnd);
- MoveToEx(hdc, 0, 0, NULL);
- LineTo(hdc, 600, 400); //To be simple, just a line.
- ReleaseDC(hWnd, hdc);
- }
- }
运行程序,点菜单“paint a line”,看运行效果:
图17
算是“成功穿越”了,这时候你再给父窗口加上WS_CLIPCHILDREN看看,结果我就不说了,就算不尝试其实也能想得到。相信大家到此为止都理解了这两个风格的作用了。
再顺便说些实践经验,有时候我们会发觉程序在频繁重绘的时候闪烁比较厉害,还是拿这个例子改装一下吧,先把主窗口的WS_CLIPCHILDREN风格拿掉,然后在其窗口处理函数中加入些代码:
- case WM_CREATE:
- //...
- SetTimer(hWnd, 1, 200, NULL);
- break;
- case WM_TIMER:
- if (wParam==1)
- InvalidateRect(hWnd, NULL, TRUE);
- break;
意思是说每0.2秒重绘一次主窗口,大家看看,是不是闪烁得厉害,闪烁过程中,我们依稀看到了这根线穿过了子窗口的区域……然后把WS_CLIPCHILDREN风格赋予主窗口,其余不变,再看看,是不是闪烁现象大为减少?通过这个例子告诉大家什么叫“把现有的技术用得最好”(参考我上一篇博文),有时候就差那么一点点。
四、Foreground、Active、Focus及对Z order的理解
看前面的这个“MDI”例子,也许你发现它跟MFC向导创建出来的MDI界面的最大不同就是子窗口无法“激活”,你怎么点,怎么拖都不行,它们的caption恒定是灰色的,我曾经为此苦思冥想……spy++是个好东西,前面主要是用它来查看窗口的属性,现在我们用它来查看窗口消息,(不知道怎么做的看看spy++的帮助)在消息过滤中,我们只选择一个消息,就是WM_NCACTIVATE,MSDN对这个消息的说明是:The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed to indicate an active or inactive state. 那就是窗口激活状态改变的时候,会收到这个消息啰?而我观察下来的结果是,The WM_NCACTIVATE never came.
办法总该是有的,比如利用SetActiveWindow这个API,在主界面上做个按钮,点一下这个按钮,就SetActiveWindow(g_hwndA),这样来激活A窗口,而事实上这样做是徒劳,A既没有被激活,也没有收到WM_NCACTIVATE。但我还是有办法的,大家看下面的代码,在那个叫WndProcDoNothing的窗口里加入对WM_MOUSEACTIVATE消息的处理:
- case WM_MOUSEACTIVATE:
- {
- HWND hwndFind=NULL;
- while(TRUE)
- {
- hwndFind = FindWindowEx(g_hwndMain, hwndFind, TEXT("child_window"), NULL);
- if (hwndFind==NULL)
- break;
- if (hwndFind==hWnd)
- PostMessage(hwndFind, WM_NCACTIVATE, TRUE, NULL);
- else
- PostMessage(hwndFind, WM_NCACTIVATE, FALSE, NULL);
- }
- }
- break;
现在再尝试运行程序,点击A,B,C窗口,是不是就可以把它们的caption变为彩色(我的是默认的浅蓝色)了?什么道理?虽然这几个子窗口不能真正地被激活(Windows机制决定的,只有top-level window才能被激活),但可以通过发WM_NCACTIVATE消息来欺骗它们,让它们以为自己被激活了,于是把自己的caption绘制为浅蓝色。如图:
图18
也许你还发现,点击子窗口的客户区不能让子窗口调整到其它子窗口的前面,窗口那个前,那个后的这种次序叫“Z order”,又译作“Z轴”,order是“序”的意思,这其实是窗口管理器维护的一个链表,没错,是链表,不是数组,不是队列,不是堆栈,为什么是链表?因为窗口的次序经常发生变化,链表是最方便修改次序的了,只需要改变节点的指针,这点性能考虑,微软是肯定做过的。下面是窗口的Z order的描述(我的描述,从MSDN改编):
桌面是最底层的窗口,不能改变的;对于top-level window,如果存在owner,一定会显示在owner之上(owner一定不会挡住它),不存在拥有关系的top-level窗口,互相之间都有可能会阻挡,用户的操作,窗口显示隐藏最大最小化还原,或者显式调用API设定等都有可能影响它们的次序,但微软为了使得有些窗口总是能够显示在最顶或最底,还设立了一套特殊的规则,那就是top most window,SetWindowPos这个API就有调整次序的功能,或者把某窗口设置为top most,top most总是显示在其它非top most窗口的上面,如果两个窗口同时是top most,那么谁更上面呢?——都有可能,top most之间又是“公平竞争”的关系了,虽然他们对非top most总是保持着优势,那把一个owner设置为top most,会怎么样呢?由于被拥有的窗口必须在其owner的上面,所以那些被拥有的窗口也都全部变成了top most,尽管你没有给他们指定top most,用spy++观察top most窗口的属性,在Extended Style栏目中,能看到一个“WS_EX_TOPMOST”属性,这就是top most窗口的标志了。OK,top-level window的情况看来都没什么问题了,那child window的情况呢?大家都知道,child是绘制在其parent的客户区中的,不可能超出其parent的界限,相当于是其parent的一部分,那我们可不能以认为其child的z order跟其parent的是一致的呢?对于其它top-level窗口来说,这样看是没问题的,因为一个top-level窗口被移到了前面,它的child也会跟着它显示在前面,反之亦然,但一个在Parent窗口内部,哪个child在前,哪个在后,又是有自己的一套private z order的,所谓国有国法,家有家规嘛,这样看,我想就没什么问题了。哦,不对,还有一点没说,对于child来说,不能是top most窗口,用SetWindowPos设置也是没用的。
那我们如何来知道整个Z order的链表?可以这样:
- void ListZOrder(HWND hParent)
- {
- TCHAR szOutput[10];
- HWND hwnd = GetTopWindow(hParent);
- while(hwnd!=NULL)
- {
- wsprintf(szOutput, TEXT("%08X/n"), (UINT)hwnd);
- OutputDebugString(szOutput);
- hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
- }
- }
这个函数会把某个Parent的子窗口句柄值,按照z order次序,从最顶打印到最底。如果hParent为NULL,那么就从桌面的最顶窗口开始,列出所有桌面的窗口,这样意义不大,为什么?因为你会找出来很多很多窗口,可见的,不可见的,奇奇怪怪的,变来变去的,所以这种列窗口的方法通常是用于列子窗口的。
最后我想提提Foreground、Active和Focus这三者,非常容易让人搞混的三个概念,我给出一些提示和方法,读者自己去编程序体验。
首先是Foreground窗口,说起Foreground就不能不说Foreground线程,Windows同时管理着很多线程,但为了给用户操作起来“爽”一些,需要更快地响应用户的操作,就弄了这么个Foreground线程的概念。比如用户在玩扫雷,那扫雷这个程序的某个线程(据我所知扫雷只有一个线程)就被提升为Foreground线程,这个线程拥有比别的线程略高的优先级,能获取更多的cpu时间片,以此更快一些地响应用户,用户正在使用的这个扫雷程序的主界面,就是Foreground窗口。那Active窗口是什么呢?Active窗口就是目前用户正在使用的那个窗口……厄,这种解释也未免太敷衍人了,那它跟Foreground窗口有什么异同啊?首先说“同”,那就是它们都必须是top-level window,而不能是child window,不同嘛……还是等等再说,那现在轮到Focus窗口了,Focus窗口就是目前直接接收到用户键盘输入消息的那个窗口,可以是child window。我就给那么多提示吧。
我不想直接告诉你它们究竟还有什么不同,我现在给出三个API:GetFocus、GetActiveWindow和GetForegroundWindow,大家用这三个API去做些实验就知道了。
后记
这篇文章我想已经足够长,我必须得结束了,这恐怕也是我写的最长的一篇技术文章(除了我的本科毕业论文),如果你能够从头到尾读到这里,我倍感荣幸,如果它能够给你些帮助,我想这份辛苦也是值得的。进入冬天了,天气很冷,注意保暖。(其实现在我觉得我的脚正踩在南极大陆上……)
-
QT程序 windows窗口置顶和linux窗口置顶遇到的问题和解决方案
2019-12-09 14:58:59一、QT 窗口置顶 一般情况下,我们开发QT桌面应用程序如需用到窗口置顶操作时,会这样做: setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); 但经过尝试发现此方法会导致窗口被隐藏,再次调用 show方法... -
理论题 —— Windows 7基础知识
2021-04-12 22:18:421.关于Windows 7的启动,以下描述错误的是________。 答案:D A.启动Windows7操作系统时,可选择登录账户 B.用户账户的登录密码可在控制面板中设置 C.启动Window操作系统时,按F8可进入安全模式 D.用户账户登录时... -
Windows窗口刷新机制详解
2017-12-28 16:00:131、Windows的窗口刷新管理 窗口句柄(HWND)都是由操作系统内核管理的,系统内部有一个z-order序列,记录着当前窗口从屏幕底部(假象的从屏幕到眼睛的方向),到屏幕最高层的一个窗口句柄的排序,这个排序不关注... -
Windows 窗口层次关系
2014-05-14 11:08:32相信在Windows 下面编程的很多兄弟们都不是很清楚Windows 中窗口的层次关系是怎么样的,这个东西很久已经研究过一下,后来又忘记了,今天又一次遇到了这个问题,所以便整理一下。下面就说说Windows 中桌面(Desktop... -
windows7使用技巧详细介绍【图解】
2021-07-30 10:18:51电脑在我们的生活中已经成为了生活中常见的一个设备了,现在人们在工作或者是学习的时候都会使用到电脑。所以说电脑也成为了很重要的...下面介绍一下Windows7的使用技巧。windows7使用技巧详细介绍1.显示校准功能Win... -
Windows,C++编程创建窗口的过程详解
2015-07-10 17:31:55Windows,C++编程,创建窗口的是个步骤详解 -
windows软件窗口或者对话框太大超出屏幕解决办法
2019-08-22 16:17:13具体见下面两张图片。 解决方法:使用窗口移动精灵或AltDrag这两款窗口移动工具,摆脱只能拖动标题栏移动窗口的限制,任意移动超出屏幕的窗口,将窗口或对话框移动到顶部后,仍可继续向上拖动,使窗口顶部移动到屏幕... -
使用WIN32汇编语言实现一个基本windows窗口的过程分析
2016-03-07 20:55:47一个常规的windows窗口一般都是一些一样的构造,你如果想要更改一些个性化的设置,你可以在这个一般的模板伤添砖加瓦,构造自己比较喜欢的类型,下边就分析一下一般的windows窗口的一般模板。 一. 首先看一下一般... -
windows访问文件服务器共享时不弹出输入用户名和密码窗口
2021-08-24 10:06:38问题描述: 在客户端电脑,用非administrator本地用户登陆系统,访问文件服务器共享时没有弹出输用户名和密码的窗口,而是直接进入共享枚举,但没有任何访问权限。如果在客户端电脑用administrator本地用户登陆系统... -
Windows程序设计:使用VS2010创建窗口程序
2018-12-28 14:59:09最近在学习windows程序设计的创建窗口这方面的知识,有一些收获,现在将我所学到的内容记录下来,供大家参考。 本例将做一个类似txt文档窗口,可以对用户的键盘输入打印到该窗口上,并且有菜单栏可以单击菜单栏中的... -
JavaScript - 关闭当前窗口-Scripts may close only the windows that were opened by them.
2021-10-23 14:07:17问题描述: 在Chrome浏览器中调用window.close()关闭当前页面时浏览器控制台报出 “Scripts may close only the windows that were opened by them.” 且无法关闭当前页面。 原因: ...文章里说,close只能关闭用... -
windows窗口基本代码详细解析
2012-10-30 15:55:05/*下列注释函数均在平台SDK文档中说明,并在不同的头文件中声明,其中绝大多数在WINUSER.H中声明。*/ #include ... // 窗口过程; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstan -
关闭默认共享-关于Windows的默认共享介绍
2018-12-07 09:55:25一 : 关于Windows的默认共享介绍 网上其实到处都有谈论到,现我也只是整理一下: 在在Windows 系统中,在“我的电脑”上右击“管理”,依次选择“系统工具→共享文件夹→共享”,就会看到一些带有美元“$”标记的... -
Windows的窗口刷新机制相关
2014-06-21 20:30:27Windows的窗口刷新机制 1、Windows的窗口刷新管理 窗口句柄(HWND)都是由操作系统内核管理的,系统内部有一个z-order序列,记录着当前窗口从屏幕底部(假象的从屏幕到眼睛的方向),到屏幕最高层的一个窗口句柄的... -
通过获取窗口句柄模拟键鼠点击的一些方法
2021-01-17 23:37:20每个程序窗口都有一个窗口句柄,在程序的一次运行中句柄是不会发生变化的(每当重新开启程序后句柄会发生改变),句柄的类型为HWND 【2】 我们可以用VS自带的Spy++工具获取窗口句柄,窗口类名,与标题 【2.1】 比如... -
java最简单的知识之创建一个简单的windows窗口,利用Frame类
2016-08-04 14:49:08作者:程序员小冰,CSDN博客:...当然,就像我们刚学习写代码,写hello world! 很简单,所以,慢慢从简单开始学习吧。这次介绍Frame的一个简单的用法。java最简单的知识之创建一个简单的windows窗口,利用F -
Shift+鼠标右键没有:在此处打开命令窗口(W)
2022-05-21 15:49:22Shift+鼠标右键没有:在此处打开命令窗口1.软件环境2.问题描述3.解决方法4.结果预览附:隐藏`在此处打开命令Powershell窗口(S)` 1.软件环境 Windows10 教育版64位 2.问题描述 命令提示符是在操作系统中,提示进行... -
【解决】 CMD打开命令窗口,Windows无法访问指定设备路径或文件,您可能没有合适的权限访问这个项目
2022-01-04 23:09:513,按Windows+R,输入cmd回车,能打开窗口。 在这里插入图片描述](https://img-blog.csdnimg.cn/f52255ef555b4705af3a434f5ec631ae.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1... -
openGL学习 在windows上显式一个窗口
2016-10-23 19:14:54代码如下: // openGL1.cpp: 主项目文件。 #include // Windows的头文件 #include "glew.h" // 包含最新的gl.h,glu.h库 #include "glut.h" // 包含OpenGL实用库 HGLRC hRC=NULL; / -
Android窗口设计之Dialog、PopupWindow、系统窗口的实现
2020-12-19 16:07:43窗口设计之Dialog、PopupWindow、系统窗口的实现 Android应用程序窗口设计系列博客: Android应用程序窗口设计之Window及WindowManager的创建 Android应用程序窗口设计之setContentView布局加载的实现 普法... -
Windows提权
2021-12-09 20:38:34目录为什么提权Windows提权的常见方法提权的常用命令内核漏洞Vulmap【windows????linux????】wesngWindowsVulnScan(国)在线查询可写目录或文件目录扫描上传cmd本地提权at提权(旧系统)SC提权(旧系统)PSTools... -
[学习笔记]Windows窗口概念汇总(转载)
2010-12-20 13:23:00什么是Windows,这篇文章的总结很全面。 -
window.open打开新窗口设置显示位置及大小
2021-06-08 15:11:18下面就说一下 这四个参数都代表什么: 参数 描述 URL 一个可选的字符串,声明了要在新窗口中显示的文档的 URL。如果省略了这个参数,或者它的值是空字符串,那么新窗口就不会显示任何文档。 name 一个可选... -
Python在退出时关闭自己的CMD shell窗口
2020-12-04 01:22:58感谢eryksun进行深入调查,真正使用Python导致正确的描述,现在可以在下面阅读。os.system()导致在前台使用控制台窗口执行cmd.exe /C并停止执行Python脚本,直到Windows命令解释程序终止。如果Python脚本本身在... -
分层窗口(Layered windows)
2010-10-12 17:51:00Layered WindowsWindows 2000 introduces a new extended window style bit: WS_EX_LAYERED. When used properly, it can significantly improve performance and visual effects for a window that has a complex ...