Win32汇编教程九复杂形状的窗口

    技术2022-05-11  104

    --------------------------------------------------------------------------------概述在前面八篇的 Win32asm 教程中,已经初步讲述了消息框、对话框、菜单、资源、GDI 等内容,基本上已经设计到了 Windows 界面的大部分内容,在继续新的 Windows 其他部分的内容如多线程、文件操作、内存操作之前,我先综合前面的内容并加上一些新内容,写上一篇综合篇。本篇的例子程序是一个复杂形状的窗口,窗口的形状是根据位图自动计算得到的,这也就是在我编写的小闹钟中使用的技术(大家可以到我的软件发布中下载一个看看),由于以前在网上看到的有关特殊形状窗口的例子最多就是画一个圆形,或者几个方块和椭圆结合的形状,没有一篇文章指出如何画出如“唐老鸭”这样一个造型的窗口。本文使用的算法可以自动根据位图的形状计算窗口形状。在源程序中,很多代码都是前面教程提到的,主要有以下部分:首先建立一个标准的窗口。(参考窗口一节) 设置窗口为特殊形状。(见下面的程序分析) 在窗口的 WM_PAINT 消息中更新窗口的图片。(参考图形界面一节) 由于窗口没有标题栏,所以在右击窗口时弹出一个菜单。(参考菜单一节) 菜单中有个“关于本程序”项,里面有超联结文本。(参考窗口子类化一节) Windows 里有专门的 API 来实现特殊形状的窗口,步骤是首先建立区域(Region),Region 可以合并,这样一来就可以用几个简单的区域合并出一个复杂的区域,建立、合并区域和设置窗口的 API 主要有以下几条:CreateRectRgn(Left,Top,Right,Bottom) - 建立矩型区域 CreateEllipticRgn(Left,Top,Right,Bottom) - 建立椭圆区域 CreatePolygonRgn(lpPoints,NumberOfPoints,Mode) - 建立多边形区域,这些API返回区域句柄 CombineRgn(hDest,hSource1,hSource2,CombineMode) - 合并区域 SetWindowRgn(hWnd,hRgn,bRedraw) - 根据区域设置窗口形状 本程序的方法是扫描位图的点,按行设置区域,然后合并到总的区域中。源程序 - 汇编源文件;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 是否包括调试代码;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>DEBUG = 0;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Programmed by 罗云彬, bigluo@telekbird.com.cn; Website: http://asm.yeah.net; LuoYunBin's Win32 ASM page (罗云彬的编程乐园);>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 版本信息; 特殊形状窗口的演示程序 Ver 1.0; 可以根据位图自动设置窗口的形状。;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.386.model flat, stdcalloption casemap :none ; case sensitive;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Include 数据;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>include windows.incinclude user32.incinclude kernel32.incinclude comctl32.incinclude comdlg32.incinclude shell32.incinclude gdi32.incincludelib user32.libincludelib kernel32.libincludelib comctl32.libincludelib comdlg32.libincludelib shell32.libincludelib gdi32.lib;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; Equ 数据;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>;************** Equ 数据 **********************************IDI_MAIN equ 1 ;iconIDC_HANDLE equ 2 ;Cursor;************** Equ 数据 **********************************DLG_ABOUT equ 1200 ;dialog - aboutID_ABOUT_OK equ 1201ID_EMAIL equ 1202ID_HOMEPAGE equ 1203;************** Equ 数据 **********************************IDM_MAIN equ 2000IDM_ABOUT equ 2001IDM_EXIT equ 2002;************** Equ 数据 **********************************IDB_0 equ 3000 ;bitmap;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 数据段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.data?hInstance dd ?hWinMain dd ?hIcon dd ?hCursor dd ?hMenu dd ?hBmpBack dd ? ;background bitmaphDcBack dd ?;************** 数据段 ************************************.dataszClassName db 'ShapeWindow',0;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 代码段;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.codeif DEBUGinclude Debug.asmendif;********************************************************************; 设置窗口形状为BMP图形形状; 参数:窗口句柄,BMP图形句柄; 输入BMP图形要求:0,0处颜色为背景色;********************************************************************_SetWindowShape proc hWnd:DWORD,hBitMap:DWORDlocal @hDC:DWORD,@hBmpDC:DWORDlocal @stPs:PAINTSTRUCTlocal @stRect:RECTlocal @stBmp:BITMAPlocal @dwX:DWORD,@dwY:DWORD,@dwStartX:DWORDlocal @hRgn:DWORD,@hRgnTemp:DWORDlocal @rgbBack:DWORDinvoke GetObject,hBitMap,sizeof BITMAP,addr @stBmpinvoke GetWindowRect,hWnd,addr @stRectinvoke ShowWindow,hWnd,SW_HIDEinvoke MoveWindow,hWnd,@stRect.left,@stRect.top,/@stBmp.bmWidth,@stBmp.bmHeight,FALSEinvoke GetDC,hWndmov @hDC,eaxinvoke CreateCompatibleDC,@hDCmov @hBmpDC,eaxinvoke SelectObject,@hBmpDC,hBitMap;*************** 计算窗口形状 ***************************************invoke GetPixel,@hBmpDC,0,0mov @rgbBack,eaxinvoke CreateRectRgn,0,0,0,0mov @hRgn,eaxmov @dwY,0.while TRUEmov @dwX,0mov @dwStartX,-1.while TRUEinvoke GetPixel,@hBmpDC,@dwX,@dwY.if @dwStartX == -1.if eax != @rgbBackmov eax,@dwXmov @dwStartX,eax.endif.else.if eax == @rgbBackmov ecx,@dwYinc ecxinvoke CreateRectRgn,@dwStartX,@dwY,@dwX,ecxinvoke CombineRgn,@hRgn,@hRgn,eax,RGN_ORmov @dwStartX,-1.elsemov eax,@dwX.if eax == @stBmp.bmWidthinc eaxmov ecx,@dwYinc ecxinvoke CreateRectRgn,@dwStartX,@dwY,eax,ecxinvoke CombineRgn,@hRgn,@hRgn,eax,RGN_ORmov @dwStartX,-1.endif.endif.endifinc @dwXmov eax,@dwX.break .if eax > @stBmp.bmWidth.endwinc @dwYmov eax,@dwY.break .if eax > @stBmp.bmHeight.endwinvoke SetWindowRgn,hWnd,@hRgn,TRUE;********************************************************************invoke BitBlt,@hDC,0,0,@stBmp.bmWidth,@stBmp.bmHeight,/@hBmpDC,0,0,SRCCOPYinvoke DeleteDC,@hBmpDCinvoke ReleaseDC,hWnd,@hDCinvoke InvalidateRect,hWnd,NULL,-1ret_SetWindowShape endp;********************************************************************; 将窗口移动到屏幕中间; 参数:窗口句柄;********************************************************************_CenterWindow proc hWnd:DWORDlocal @stRectDeskTop:RECT,@stRectWin:RECTlocal @dwWidth:DWORD,@dwHeight:DWORDinvoke GetWindowRect,hWnd,addr @stRectWininvoke GetDesktopWindowmov ebx,eaxinvoke GetWindowRect,ebx,addr @stRectDeskTopmov eax,@stRectWin.bottomsub eax,@stRectWin.topmov @dwHeight,eaxmov eax,@stRectWin.rightsub eax,@stRectWin.leftmov @dwWidth,eaxmov ebx,@stRectDeskTop.bottomsub ebx,@dwHeightshr ebx,1mov ecx,@stRectDeskTop.rightsub ecx,@dwWidthshr ecx,1invoke MoveWindow,hWnd,ecx,ebx,@dwWidth,@dwHeight,FALSEret_CenterWindow endp;********************************************************************include About.asm;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 程序开始;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>start:call _WinMaininvoke ExitProcess,NULL;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>; 主窗口程序;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>_WinMain proclocal @stWcMain:WNDCLASSEXlocal @stMsg:MSGinvoke InitCommonControlsinvoke GetModuleHandle,NULLmov hInstance,eaxinvoke LoadIcon,hInstance,IDI_MAINmov hIcon,eaxinvoke LoadMenu,hInstance,IDM_MAINinvoke GetSubMenu,eax,0 ;PopUp 菜单要用到子菜单mov hMenu,eax;*************** 注册窗口类 *****************************************invoke LoadCursor,0,IDC_ARROWmov @stWcMain.hCursor,eaxmov @stWcMain.cbSize,sizeof WNDCLASSEXmov @stWcMain.hIconSm,0mov @stWcMain.style,CS_HREDRAW or CS_VREDRAWmov @stWcMain.lpfnWndProc,offset WndMainProcmov @stWcMain.cbClsExtra,0mov @stWcMain.cbWndExtra,0mov eax,hInstancemov @stWcMain.hInstance,eaxmov @stWcMain.hIcon,0mov @stWcMain.hbrBackground,COLOR_WINDOW + 1mov @stWcMain.lpszClassName,offset szClassNamemov @stWcMain.lpszMenuName,0invoke RegisterClassEx,addr @stWcMain;***************** 建立输出窗口 *****************************************; 属性:没有标题栏,不显示在任务栏;********************************************************************invoke CreateWindowEx,WS_EX_TOOLWINDOW,/offset szClassName,NULL,/WS_POPUP or WS_SYSMENU,/0,0,1,1,/NULL,NULL,hInstance,NULLinvoke ShowWindow,hWinMain,SW_SHOWNORMALinvoke UpdateWindow,hWinMain;*************** 消息循环 *******************************************.while TRUEinvoke GetMessage,addr @stMsg,NULL,0,0.break .if eax == 0invoke TranslateMessage,addr @stMsginvoke DispatchMessage,addr @stMsg.endwret_WinMain endp;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>WndMainProc proc uses ebx edi esi, /hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORDlocal @stPos:POINTlocal @stPs:PAINTSTRUCT,@hDC:DWORDmov eax,uMsg.if eax == WM_CREATEmov eax,hWndmov hWinMain,eaxcall _Init;********************************************************************.elseif eax == WM_PAINTinvoke BeginPaint,hWnd,addr @stPsmov @hDC,eaxmov eax,@stPs.rcPaint.rightsub eax,@stPs.rcPaint.leftmov ecx,@stPs.rcPaint.bottomsub ecx,@stPs.rcPaint.topinvoke BitBlt,@hDC,@stPs.rcPaint.left,@stPs.rcPaint.top,eax,ecx,/hDcBack,@stPs.rcPaint.left,@stPs.rcPaint.top,SRCCOPYinvoke EndPaint,hWnd,addr @stPs;********************************************************************; 由于没有菜单,下面代码用于按下右键时弹出POPUP菜单;********************************************************************.elseif eax == WM_RBUTTONDOWN.if wParam == MK_RBUTTONinvoke GetCursorPos,addr @stPosinvoke TrackPopupMenu,hMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL.endif;********************************************************************; 由于没有标题栏,下面代码用于按下左键时移动窗口;********************************************************************.elseif eax == WM_LBUTTONDOWNinvoke UpdateWindow,hWnd ;即时刷新invoke ReleaseCaptureinvoke SendMessage,hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0;********************************************************************.elseif eax == WM_COMMAND.if lParam == 0mov eax,wParam.if ax == IDM_EXITcall _Quit.elseif ax == IDM_ABOUTinvoke DialogBoxParam,hInstance,DLG_ABOUT,hWnd,offset AboutDialogProc,DLG_ABOUT.endif.endif;********************************************************************.elseif eax == WM_CLOSEcall _Quit;********************************************************************.elseinvoke DefWindowProc,hWnd,uMsg,wParam,lParamret.endif;********************************************************************; 注意:WndProc 处理 Windows 消息后,必须在 Eax 中返回 0; 但是由 DefWindowProc 处理后的返回值不能改变,否则窗口; 将无法显示!;********************************************************************xor eax,eaxretWndMainProc endp;********************************************************************_Init proclocal @hDCinvoke SendMessage,hWinMain,WM_SETTEXT,0,offset szClassNameinvoke SendMessage,hWinMain,WM_SETICON,ICON_SMALL,hIconinvoke LoadBitmap,hInstance,IDB_0 ;装入背景图片mov hBmpBack,eaxinvoke _SetWindowShape,hWinMain,hBmpBack ;设置窗口形状为背景图片invoke GetDC,hWinMainmov @hDC,eaxinvoke CreateCompatibleDC,@hDC ;建立背景及数字 DCmov hDcBack,eaxinvoke ReleaseDC,hWinMain,@hDCinvoke SelectObject,hDcBack,hBmpBackinvoke _CenterWindow,hWinMainret_Init endp;********************************************************************_Quit proclocal @stWindow:RECTinvoke DestroyMenu,hMenuinvoke DeleteDC,hDcBackinvoke DeleteObject,hBmpBackinvoke DestroyWindow,hWinMaininvoke PostQuitMessage,NULLret_Quit endp;********************************************************************end start程序的分析和要点创建窗口的时候,窗口风格为 WS_POPUP,所以创建的窗口没有标题栏,这样的窗口适合于设置成特殊形状的窗口invoke CreateWindowEx,WS_EX_TOOLWINDOW,/offset szClassName,NULL,/WS_POPUP or WS_SYSMENU,/0,0,1,1,/NULL,NULL,hInstance,NULL但是当窗口没有标题栏后,我们就无法用拖动标题栏的办法来移动窗口,如果让窗口一动不动呆在屏幕中间显然是不行的,这里有一个替代办法,我们可以响应按下鼠标左键的消息,在 WM_LBUTTONDOWN 消息中想窗口发送 WM_NCLBUTTONDOWN (非客户区鼠标按下消息) 位置在 HTCAPTION 来模拟鼠标按在标题栏中来实现移动的功能。.elseif eax == WM_LBUTTONDOWNinvoke UpdateWindow,hWnd ;即时刷新invoke ReleaseCaptureinvoke SendMessage,hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0

     


    最新回复(0)