有时候在调用函数的时候需要传入一些标志性的值 而这个值是由微软给出的 也就是一些已经预先定义好的常量 而这些常量的值是可以根据文档或者一些工具来查询到的
在前面的两篇中都用到了一个MoveWindow函数 这一篇来用一个比他功能还要高级一点的一个函数SetWindowPos函数的具体说明还是自己点击看百度百科的吧 上一个函数签名吧:
BOOL SetWindowPos(HWND hWnd,HWND hWndlnsertAfter,int X,int Y,int cx,int cy,UNIT uFlags);首先 不要被一堆参数吓到 比起MoveWindow他多了两个参数 一个是hWndInsertAfter 还有一个是Flags 而我想说的重点也是这两个参数
来看看第二个参数的说明:
hWndlnsertAfter:在z序中的位于被置位的窗口前的窗口句柄。 该参数必须为一个窗口句柄,或下列值之一: HWND_BOTTOM: 将窗口置于Z序的底部。如果参数hWnd标识了一个顶层窗口 则窗口失去顶级位置,并且被置在其他窗口的底部。 HWND_NOTOPMOST: 将窗口置于所有非顶层窗口之上(即在所有顶层窗口之后) 如果窗口已经是非顶层窗口则该标志不起作用。 HWND_TOP: 将窗口置于Z序的顶部。 HWND_TOPMOST: 将窗口置于所有非顶层窗口之上。即使窗口未被激活窗口也将保持顶级位置。首先来说说什么是z序列 如果你搞过html或许可能接触过z-index这个东东 也就是叠加顺序 比如现在桌面上有十个窗口 那么这十个窗口谁在最顶上 谁在最下面的一个顺序 至于为什么要叫z序列 - -!、、那是因为x轴y轴z轴、、就是这个意思、、、、如果这个值设置为一个窗口的句柄的话 那么你的窗口将显示在这个窗口之上 重点是 "或下列值之一" 估计看到这里 就头大了 神马意思下面这四个大写的东西都是啥、、其实 好吧 就只是一个变量的名字而这些变量都是一个数值、、虽然是一个变量名 不过这个变量名是众所周知的一个变量名 根据这些变量名 可以通过文档护着工具直接知道他的值 - -!、、当然我是用工具查询的 我一般用FoxApi(你要觉得不错 自己也可以下载一个 反正又不是它可以查到 n多工具可以查来着)来查询这些值:
FoxApi貌似是给vb用的来着 不过无所谓了 、、通过上面可以看到我查询到了HWND_BOTTOM的值了
或者说你不想用这个工具在线查询的也有就是第一篇文章里面提到的pinvoke.net
先不说这个了 还是看看函数怎么用 第一个参数不用说目标窗口 第二个参数说明如上、、指定一个句柄 那么窗口就会显示在这个句柄的窗口之上 如果你想要想QQ的主窗口那样永久置顶显示那么这个值就可以设置成HWND_TOPMOST(值为-1)后面四个参数分别是坐标 然后最后一个足以让你恶心的来了:
uFlags:窗口尺寸和定位的标志 该参数可以是下列值的组合: SWP_ASYNCWINDOWPOS: 如果调用进程不拥有窗口 系统会向拥有窗口的线程发出需求 这就防止调用线程在其他线程处理需求的时候发生死锁 SWP_DEFERERASE:防止产生WM_SYNCPAINT消息 SWP_DRAWFRAME:在窗口周围画一个边框(定义在窗口类描述中) SWP_FRAMECHANGED: 给窗口发送WM_NCCALCSIZE消息 即使窗口尺寸没有改变也会发送该消息 如果未指定这个标志 只有在改变了窗口尺寸时才发送WM_NCCALCSIZE SWP_HIDEWINDOW;隐藏窗口 SWP_NOACTIVATE:不激活窗口 如果未设置标志 则窗口被激活 并被设置到其他最高级窗口或非最高级组的顶部(根据参数hWndlnsertAfter设置) SWP_NOCOPYBITS: 清除客户区的所有内容 如果未设置该标志 客户区的有效内容被保存并且在窗口尺寸更新和重定位后拷贝回客户区 SWP_NOMOVE:维持当前位置(忽略X和Y参数) SWP_NOOWNERZORDER:不改变z序中的所有者窗口的位置 SWP_NOREDRAW: 不重画改变的内容 如果设置了这个标志 则不发生任何重画动作 适用于客户区和非客户区 (包括标题栏和滚动条)和任何由于窗回移动而露出的父窗口的所有部分 如果设置了这个标志 应用程序必须明确地使窗口无效并区重画窗口的任何部分和父窗口需要重画的部分 SWP_NOREPOSITION;与SWP_NOOWNERZORDER标志相同 SWP_NOSENDCHANGING:防止窗口接收WM_WINDOWPOSCHANGING消息 SWP_NOSIZE:维持当前尺寸(忽略cx和Cy参数) SWP_NOZORDER:维持当前Z序(忽略hWndlnsertAfter参数) SWP_SHOWWINDOW:显示窗口看着一大堆 有木有抓狂的效果?而且上面说的是"下列值的组合"意思就是 还可以多选 神马意思?好吧其实就是多选的意思、重点是怎么个选法?其实这些值都有一个特点 如果把这些值全部换成二进制表示的话 它们只有一位是1其他全是0 假设上列的变量分别是A、B、、、如果用8为表示它们的值可能是这样的:
A:0000 0001 - 1 B:0000 0010 - 2 C:0000 0100 - 4 D:0000 1000 - 8每个值有一个1 这样有啥好?等一下来说 uFlags这个参数不是说了吗 可以是下列的组合值 也就是可以选着多个值 而每个值代表这一个功能 也就是说你想用哪些功能就可以选择那些值 假设我想要A和C的功能 可是参数只有一个uFlags怎么给他两个值?这个时候上面说的值的特点就派上用场了 把你选的值直接做或运算 假设我要A和C值 那么最后一个参数直接给他【A | C】这个值(如果你不知道什么是二进制的或运算请百度)那么得到的新值如下:
A:0000 0001 - 1 | C:0000 0100 - 4 //好吧其实在这里不用或运算直接加法也可以 但是或运算绝对不是加法运算 只是这里值特殊 = X:0000 0101 = 5
也就是最终uFlags的值是5而函数里面得到这个5的时候就去判断他那些位上所以1如果是1就表示启用了这个功能 现在估计应该知道"组合值"是什么了 那么继续说 那么还是来一个实际的需求来说明这个函数的使用 假设现在要求运行程序 点击一个button让程序的窗口 置顶显示就像QQ的一样(其实C#中有TopMost属性 - -!、现在不准用、)
首先既然确定了目标那么来看看 每个参数都应该用什么样的值 首先第一个this.Handle毫无疑问的 然后是第二个参数 貌似就是控制z序列的 而且在说明中也可以看到HWND_TOPMOST可以让窗体置顶显示 那么就它了 由于只是想让窗体置顶显示而不用改变窗口的大小和位置 那么接下来的四个值你可以直接赋值窗本身的信息、、但是、、可以有其他方式来解决 来看看最后一个uFlags的值的描述SWP_NOSIZE似乎可以忽略掉窗口的尺寸的参数 SWP_NOMOVE可以忽略坐标的参数 那么完全就可以只用这两个参数的组合值那么函数的调用就可以确定大致如下了:
SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);可以看到 我中间四个全部是0 因为最后一个uFlags的值本来就会忽略掉它们 所以你想传个啥都没关系反正也不会用它们 然后第二个值是置顶显示的意思其实他的值是-1 那么申明和调用的过程如下:
[DllImport("user32.dll")] public static extern bool SetWindowPos (IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); public const int HWND_TOPMOST = -1; public const uint SWP_NOSIZE = 0x01; public const uint SWP_NOMOVE = 0X02; SetWindowPos(this.Handle, (IntPtr)HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);调用之后 窗口就置顶显示了不过这里要说明的是 千万不要像这样写:
SetWindowPos(this.Handle, (IntPtr)-1, 0, 0, 0, 0, 1 | 2);更不要这样:
SetWindowPos(this.Handle, (IntPtr)-1, 0, 0, 0, 0, 3);//最后一个直接算出来了
为啥不能这样 好吧这样写本身是没啥错的 在当时也就算了 如果是等个一段时间你再来看你的代码 你知道那个-1是啥意思吗?你知道后面的3是啥意思吗 如果你声明成变量出来 至少就算到时候你不知道是啥了 根据变量名也是可以查信息的 对于我而言我是极其反感直接在函数里面写数值的 很久以前不怎么懂的时候到处百度的时候 百度出来的一些代码就是直接用值的 虽然代码运行的效果是我想要的 可是我就是不知道那些值代表的是什么意思 当时真有一种把作者拖来宰了的冲动 如果说是想上面那种值就是那么几个固定的话 你到可以一个一个查变量 看看是用的那一个变量(如果是一个算好的 组合值你还得才分二进制) 如果是像SendMessage这样的函数 第二个参数可以是n多值的你去查吧 查不死你 所以这里一定要养成一个好的习惯 不要直接用值 声明成变量 然后用变量
可是如果你想置顶显示并且像前两篇用MoveWindow那样 又要改变大小又想要置顶那么最后一个参数要怎么办?其实最后一个 你可以选择传0进去 那就表示你没有什么特殊要求:
SetWindowPos(this.Handle, (IntPtr)HWND_TOPMOST, 100, 100, 200, 200, 0);再或者如果你只想把这个函数当作MoveWindow觉得中间那几个值就够了 第二个参数你也可以用0(IntPtr.Zero)然后最后一个用SWP_NOZORDER表示忽略第二个参数、、反正用法是很多的 就看你怎么用了 这是个功能非常强大的函数、、
下面来看一个有意思的 刚才说了C#中直接
this.TopMost = true;就能实现让窗体置顶的功能 用过的人肯定知道 那么现在用反编译工具来看看这个属性的代码是啥:
这个是用【.Net Reflector】反编译出来的Form的TopMost属性的set的片段 看看 看看、、我就说吧.Net做了很多封装 看看上面HWND_TOPMOST是不是很熟悉SetWindowPos是不是也好熟悉?只不过都是被点出来的而已 - -!、、不过那个3略欠扁(估计是编译的时候被优化了)、、其实就是SWP_NOMOVE和SWP_NOSIZE的组合 那么再来看看点出他的类的代码:
看吧看吧 里面有大量的Win32的Api被封装到了内部 而在NativeMethods中(也就是点出HWND_TOPMOST的那个)有大量的常量声明和结构声明 如果你用过C++在Windows上纯SDK写代码的话 一个封装都没有 包括Main函数全部自己写然后在Main(其实是WinMain)里面自己用CreateWindow函数创建窗体然后通过ShowWindow把创建好的窗体显示、、要设置窗口的标题SetWindowText 、、、 等等 什么都是调用Api来完成的 不过在C++中 直接引用Windows.h头文件就可以使用这些函数了 在头文件里面是声明好了的 - -!、、如果声明都要自己来 就悲剧了、、、不过也说明用这些Api可以写出任何你想要的程序来确实是一堆很强大的砖块 可以让你砌出任何你想要的房子
- -!、、所以说在.Net下写程序真的是太方便了、、很多东西都不用管了 如果你继续去跟那些什么Left属性 你会发现 基本都是调用的SetWindowPos函数来完成的、、所以要记住 只要你的程序是Windows上运行的程序 是离不开这些函数的 即使你在平时写代码的时候 压根就没写过这些 甚至不知道这些 最终你的程序都是需要调用这些函数来运行的 刚才上面不是说了吗在C++纯SDK中就全是在调用函数写程序 如果在C#中你不嫌声明那些东西 那么你也可以直接找到main函数 然后从里面开始纯调用Api写程序 所以学好这些函数 在Windows上写代码是有很大帮助的、、