« WINDOWS游戏编程大师技巧笔记之——WIN32编程遇上OpenGL,一年 »

Win32编程实现的一点小功能

稍微记录一下这几天小小弄WIN32编程发现的一点点东西。发现是颇有乐趣的,可是烦恼往往出现在GOOGLE不出需要的资料以及试验不成功上。呵呵。上篇是WINDOWS游戏编程大师技巧笔记之——WIN32编程——ZwqXin.com

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/MFC/know-a-little-about-win32.html

1. 透明的窗口

突如其来的想法,透明的窗口,怎么实现呢?GOGLE一下,出来了:SetLayeredWindowAttributes

Win32 小技巧

  1. long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  2.          SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);   
  3.       SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);

首先,我要用GetWindowLong获得当前窗口(句柄hwnd,我的主窗口)的某些设置,GWL_EXSTYLE是拓展风格。SetWindowLong直接看来就是把该设置改变了,这里的按位或有什么作用呢?很显然地是在原“基础”上加上一项WS_EX_LAYERED(分层特性)。只是为什么要是按位或呢?

好了,SetLayeredWindowAttributes第三个参数明显就是透明度了(满不透是255)。另两个是什么和为什么,不知道了。要想回去不用透明的世界,只要:( 非 了之后再 与 么)

  1. long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  2.       SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong & ~WS_EX_LAYERED);

可是这样设置恐怕不成功吧。因为SetLayeredWindowAttributes是个拓展函数(至少在我这里,它是),我想这跟OPENGL的拓展机制差不多吧。而且在我看来它的解决之道也差不多。问题是一个关键词Module(组件),是说com么?不知道呢。在这里把它当拓展函数库就得了 - -。

  1. typedef BOOL (WINAPI * TRANSFUNC)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
  2. TRANSFUNC SetLayeredWindowAttributes;
  3.  
  4. #define WS_EX_LAYERED 0x00080000

这是获取函数入口吧,果然差不多。看这函数的签名式,第二个参数就是colorkey了,有什么用么,请知情者告解。最后一个是标志位。于是SetLayeredWindowAttributes就这么出来了,另外WS_EX_LAYERED也要给它“真正”的名字。

  1. void LoadTransparenceModule()
  2. {
  3.     HMODULE trModule = GetModuleHandle("User32.dll");
  4.     SetLayeredWindowAttributes = (TRANSFUNC)GetProcAddress(trModule, "SetLayeredWindowAttributes");
  5.  
  6. }

所谓组件就是这个User32.dll嘛,取地址后,之前代码里的SetLayeredWindowAttributes有了意义。注意上面这函数是初始化里要有的(我放在WM_CREATE了)。

2. 判断鼠标是否位于客户区内,在客户区内外采取不同行为

最初我是这么做的:

  1. case WM_MOUSEMOVE:
  2.     {
  3.         RECT MyRect;
  4.         GetClientRect(hwnd, &MyRect);
  5.  
  6.         POINT mousept;
  7.         mousept.x = LOWORD(lparam);
  8.         mousept.y = HIWORD(lparam);
  9.  
  10.         if(!PtInRect(&MyRect,mousept))
  11.         {
  12.             long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  13.             SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);    
  14.             SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);
  15.         }
  16.         else{
  17.             long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  18.                SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong & ~WS_EX_LAYERED);
  19.         
  20.         }
  21.         
  22.     }
  23. break;

我是想鼠标移到窗口外的时候,窗口变透明,移动回里面的时候变回不透明。当然想到自己熟悉的WM_MOUSEMOVE消息了,但是这是不行的:WM_MOUSEMOVE貌似只能处理客户区内部的消息,得到的鼠标坐标也是根植于客户区。于是我:

  1. case WM_NCHITTEST:
  2.     {
  3.         RECT MyRect;
  4.         GetClientRect(hwnd, &MyRect);
  5.  
  6.         POINT mousept;
  7.         mousept.x = LOWORD(lparam);
  8.         mousept.y = HIWORD(lparam);
  9.  
  10.         ScreenToClient(hwnd, &mousept);
  11.  
  12.         if(!PtInRect(&MyRect,mousept))
  13.         {
  14.             long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  15.             SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);    
  16.             SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);
  17.         }
  18.         else{
  19.             SetCapture(hwnd);
  20.             long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  21.                SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong & ~WS_EX_LAYERED);
  22.         
  23.         }
  24.     }
  25. break;

注意我是把窗口坐标变换回客户区坐标坐标跟客户区矩形做比较。SetCapture是个比较厉害的函数,它能为当前窗口跟踪鼠标,即使鼠标到了屏幕任何角落。当然一个时刻只能给一个窗口这种权利。传说有个比它厉害的钩子函数SetWindowsHookEx来着。但是这里我就用SetCapture了,在分支飞出else的时候看来是会直接ReleaseCapture的,加不加差不多——重要的是,即使用了能针对整个屏幕判断鼠标的WM_NCHITTEST,也没有好结果——我鼠标移动到按钮等等子控件上或者菜单栏后,都会ReleaseCapture——也就是说它们“不在窗口内”。因为控件 == 窗口嘛。有没有其他简便方法呢?最后GOOGLE出可以是这么来着:

  1. case WM_MOUSEMOVE:
  2.     {
  3.  
  4.         if(!mouseTrack)
  5.         {
  6.             TRACKMOUSEEVENT mouseevent;
  7.             mouseevent.cbSize = sizeof(TRACKMOUSEEVENT);
  8.             mouseevent.dwFlags = TME_LEAVE;
  9.             mouseevent.hwndTrack = hwnd;
  10.  
  11.             if( TrackMouseEvent(&mouseevent) )
  12.             {
  13.                 mouseTrack = true;
  14.                 long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  15.                    SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong & ~WS_EX_LAYERED);
  16.  
  17.                 long currentWinStyle = GetWindowLong(hwnd, GWL_STYLE);
  18.                 SetWindowLong(hwnd, GWL_STYLE, currentWinStyle | WS_CAPTION);
  19.  
  20.             }
  21.         }         
  22.     }
  23. break;
  24.  
  25. case WM_MOUSELEAVE:
  26.     {
  27.         RECT MyRect;
  28.         GetClientRect(hwnd, &MyRect);
  29.  
  30.         POINT mousept;
  31.         GetCursorPos(&mousept);
  32.  
  33.         ScreenToClient(hwnd, &mousept);
  34.  
  35.         if(!PtInRect(&MyRect,mousept))
  36.         {
  37.         long currentWindowLong = GetWindowLong(hwnd, GWL_EXSTYLE);
  38.         SetWindowLong(hwnd, GWL_EXSTYLE, currentWindowLong | WS_EX_LAYERED);    
  39.         SetLayeredWindowAttributes(hwnd, 0, 255 * 70/100, 0x2);
  40.  
  41.         long currentWinStyle = GetWindowLong(hwnd, GWL_STYLE);
  42.         SetWindowLong(hwnd, GWL_STYLE, currentWinStyle &~ WS_CAPTION);
  43.  
  44.         }
  45.         mouseTrack = false;
  46.     }
  47. break;

TRACKMOUSEEVENT!在鼠标离开某一窗口或在某一窗口上停留超过某一特定时间长度时发送消息(mouseevent.dwFlags 有TME_LEAVE和TME_HOOK两种标志)。这专用的鼠标行为记录函数很是厉害,因为它能配合WM_MOUSELEAVE这种消息一起作用。窗口是子窗口还是控件已经没所谓了(但是菜单仍然不行,遗憾)。

这在我的VC6.0里,有些功能不被直接支持,其中就有WM_MOUSELEAVE和TRACKMOUSEEVENT。但实际上它还是支持的,在最程序开头(我这里是stdafx的#define WIN32_LEAN_AND_MEAN之上)加#define   _WIN32_WINNT   0x0500就没问题了(XP明明是NT之上嘛...嘛嘛)。

另外必要吐槽一下的是标题栏的消隐~!我按透明那样类似的手续做,但是它只在最开始那次离开窗口有效(标题栏消失),但是再回去窗口内却看不见标题栏回来TAT。

3.调用控制台

别以为WIN32了就不能开控制台了。AllocConsole()能让你与控制台再次想见!

 Win32 小技巧

  1. AllocConsole();
  2.  freopen("CONOUT$","w+t",stdout); 
  3.   freopen("CONIN$","r+t",stdin); 
  4.   printf("hello\n");

设置程这样就可以了。我在一个按钮上执行以上代码,调用成功。不过还是会有意外的(具体是什么不说了,不具有一般性)。然后关闭控制台只要FreeConsole()就可以了。注意直接关闭控制台是会连主窗口一起关闭的(两者一心同体~)。可以这样使用户不能手动关闭控制台,在上面代码两个fopen之后写上:

  1. GetConsoleTitle(buffer,  80);   
  2. HWND  hwnd  =  FindWindow(NULL,  buffer);         
  3. HMENU hmenu =  GetSystemMenu(hwnd,  FALSE);         
  4. RemoveMenu(hmenu, SC_CLOSE,MF_BYCOMMAND);

这样就通过FindWindow以控制台标题为线索能找到该控制台,diable它的关闭按钮了。当然,你不能总让用户对着这个控制台无法关闭,人家会怒的。可以设定一个按钮或按键等等调用FreeConsole()。

4. XP菜单风格

VC6不能直接嵌入manifest脚本文件来改变样式,但是其实更简单:在RESOURCEVIEW里插入类型,选择自定义,资源类型填24。内容复制粘贴成以下的(它自己会转*进制的了),然后在资源属性里改其名字(ID)为1就可:

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2. <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  3. <assemblyIdentity
  4.   name="XP style manifest"
  5.   processorArchitecture="x86"
  6.   version="1.0.0.0"
  7.   type="win32"/>
  8. <dependency>
  9.   <dependentAssembly>
  10.     <assemblyIdentity
  11.       type="win32"
  12.       name="Microsoft.Windows.Common-Controls"
  13.       version="6.0.0.0"
  14.       processorArchitecture="x86"
  15.       publicKeyToken="6595b64144ccf1df"
  16.       language="*"
  17.     />
  18.   </dependentAssembly>
  19. </dependency>
  20. </assembly>

最后是不成气候的尝试物: WindowZwq.rar

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/MFC/know-a-little-about-win32.html

  • quote 1.bluebird
  • 老大,你的dk1.ico找不到呢,你放哪去了?WindowZwq\WindowZwq.cpp(21) : error C2372: 'SetLayeredWindowAttributes' : redefinition; different types of indirection
    这里还有一句错的呢,编译不过呢,5555
    zwqxin 于 2011-7-22 21:59:15 回复
    重新上传了一份里面有图标了。另外你不是用VC6编译的话,直接用SetLayeredWindowAttributes这个函数就OK了,不用像我这里这样typedef。

    由 zwqxin 于 2011-7-22 22:09:07 最后编辑
  • 2011-7-22 15:59:03 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.