灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:2602回复:0

汇编复杂形状的窗口实例学习

楼主#
更多 发布于:2012-09-10 18:55

在前面八篇的 Win32asm 教程中,已经初步讲述了消息框、对话框、菜单、资源、GDI 等内容,基本上已经设计到了 Windows 界面的大部分内容,在继续新的 Windows 其他部分的内容如多线程、文件操作、内存操作之前,我先综合前面的内容并加上一些新内容,写上一篇综合篇。

本篇的例子程序是一个复杂形状的窗口,窗口的形状是根据位图自动计算得到的,这也就是在我编写的小闹钟中使用的技术大家可以到我的软件发布中下载一个看看),由于以前在网上看到的有关特殊形状窗口的例子最多就是画一个圆形,或者几个方块和椭圆结合的形状,没有一篇文章指出如何画出如“唐老鸭”这样一个造型的窗口。本文使用的算法可以自动根据位图的形状计算窗口形状。

在源程序中,很多代码都是前面教程提到的,主要有以下部分:

1.首先建立一个标准的窗口。(参考窗口一节)

2.设置窗口为特殊形状。(见下面的程序分析)

3.在窗口的 WM_PAINT 消息中更新窗口的图片。(参考图形界面一节)

4.由于窗口没有标题栏,所以在右击窗口时弹出一个菜单。(参考菜单一节)

5.菜单中有个“关于本程序”项,里面有超联结文本。(参考窗口子类化一节)

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, stdcall

option casemap :none ; case sensitive

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; Include 数据

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

include windows.inc

include user32.inc

include kernel32.inc

include comctl32.inc

include comdlg32.inc

include shell32.inc

include gdi32.inc

includelib user32.lib

includelib kernel32.lib

includelib comctl32.lib

includelib comdlg32.lib

includelib shell32.lib

includelib gdi32.lib

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; Equ 数据

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

;************** Equ 数据 **********************************

IDI_MAIN equ 1 ;icon

IDC_HANDLE equ 2 ;Cursor

;************** Equ 数据 **********************************

DLG_ABOUT equ 1200 ;dialog - about

ID_ABOUT_OK equ 1201

ID_EMAIL equ 1202

ID_HOMEPAGE equ 1203

;************** Equ 数据 **********************************

IDM_MAIN equ 2000

IDM_ABOUT equ 2001

IDM_EXIT equ 2002

;************** Equ 数据 **********************************

IDB_0 equ 3000 ;bitmap

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 数据段

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.data?

hInstance dd ?

hWinMain dd ?

hIcon dd ?

hCursor dd ?

hMenu dd ?

hBmpBack dd ? ;background bitmap

hDcBack dd ?

;************** 数据段 ************************************

.data

szClassName db ’ShapeWindow’,0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 代码段

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

.code

if DEBUG

include Debug.asm

endif

;********************************************************************

; 设置窗口形状为BMP图形形状

; 参数:窗口句柄,BMP图形句柄

; 输入BMP图形要求:0,0处颜色为背景色

;********************************************************************

_SetWindowShape proc hWnd:Dword,hBitMap:Dword

local @hDC:Dword,@hBmpDC:Dword

local @stPs:PAINTSTRUCT

local @stRect:RECT

local @stBmp:BITMAP

local @dwX:Dword,@dwY:Dword,@dwStartX:Dword

local @hRgn:Dword,@hRgnTemp:Dword

local @rgbBack:Dword

invoke GetObject,hBitMap,sizeof BITMAP,addr @stBmp

invoke GetWindowRect,hWnd,addr @stRect

invoke ShowWindow,hWnd,SW_HIDE

invoke MoveWindow,hWnd,@stRect.left,@stRect.top,\

@stBmp.bmWidth,@stBmp.bmHeight,FALSE

invoke GetDC,hWnd

mov @hDC,eax

invoke CreateCompatibleDC,@hDC

mov @hBmpDC,eax

invoke SelectObject,@hBmpDC,hBitMap

;*************** 计算窗口形状 ***************************************

invoke GetPixel,@hBmpDC,0,0

mov @rgbBack,eax

invoke CreateRectRgn,0,0,0,0

mov @hRgn,eax

mov @dwY,0

.while TRUE

mov @dwX,0

mov @dwStartX,-1

.while TRUE

invoke GetPixel,@hBmpDC,@dwX,@dwY

.if @dwStartX == -1

.if eax != @rgbBack

mov eax,@dwX

mov @dwStartX,eax

.endif

.else

.if eax == @rgbBack

mov ecx,@dwY

inc ecx

invoke CreateRectRgn,@dwStartX,@dwY,@dwX,ecx

invoke CombineRgn,@hRgn,@hRgn,eax,RGN_OR

mov @dwStartX,-1

.else

mov eax,@dwX

.if eax == @stBmp.bmWidth

inc eax

mov ecx,@dwY

inc ecx

invoke CreateRectRgn,@dwStartX,@dwY,eax,ecx

invoke CombineRgn,@hRgn,@hRgn,eax,RGN_OR

mov @dwStartX,-1

.endif

.endif

.endif

inc @dwX

mov eax,@dwX

.break .if eax > @stBmp.bmWidth

.endw

inc @dwY

mov eax,@dwY

.break .if eax > @stBmp.bmHeight

.endw

invoke SetWindowRgn,hWnd,@hRgn,TRUE

;********************************************************************

invoke BitBlt,@hDC,0,0,@stBmp.bmWidth,@stBmp.bmHeight,\

@hBmpDC,0,0,SRCCOPY

invoke DeleteDC,@hBmpDC

invoke ReleaseDC,hWnd,@hDC

invoke InvalidateRect,hWnd,NULL,-1

ret

_SetWindowShape endp

;********************************************************************

; 将窗口移动到屏幕中间

; 参数:窗口句柄

;********************************************************************

_CenterWindow proc hWnd:Dword

local @stRectDeskTop:RECT,@stRectWin:RECT

local @dwWidth:Dword,@dwHeight:Dword

invoke GetWindowRect,hWnd,addr @stRectWin

invoke GetDesktopWindow

mov ebx,eax

invoke GetWindowRect,ebx,addr @stRectDeskTop

mov eax,@stRectWin.bottom

sub eax,@stRectWin.top

mov @dwHeight,eax

mov eax,@stRectWin.right

sub eax,@stRectWin.left

mov @dwWidth,eax

mov ebx,@stRectDeskTop.bottom

sub ebx,@dwHeight

shr ebx,1

mov ecx,@stRectDeskTop.right

sub ecx,@dwWidth

shr ecx,1

invoke MoveWindow,hWnd,ecx,ebx,@dwWidth,@dwHeight,FALSE

ret

_CenterWindow endp

;********************************************************************

include About.asm

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 程序开始

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

start:

call _WinMain

invoke ExitProcess,NULL

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

; 主窗口程序

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

_WinMain proc

local @stWcMain:WNDCLASSEX

local @stMsg:MSG

invoke InitCommonControls

invoke GetModuleHandle,NULL

mov hInstance,eax

invoke LoadIcon,hInstance,IDI_MAIN

mov hIcon,eax

invoke LoadMenu,hInstance,IDM_MAIN

invoke GetSubMenu,eax,0 ;PopUp 菜单要用到子菜单

mov hMenu,eax

;*************** 注册窗口类 *****************************************

invoke LoadCursor,0,IDC_ARROW

mov @stWcMain.hCursor,eax

mov @stWcMain.cbSize,sizeof WNDCLASSEX

mov @stWcMain.hIconSm,0

mov @stWcMain.style,CS_HREDRAW or CS_VREDRAW

mov @stWcMain.lpfnWndProc,offset WndMainProc

mov @stWcMain.cbClsExtra,0

mov @stWcMain.cbWndExtra,0

mov eax,hInstance

mov @stWcMain.hInstance,eax

mov @stWcMain.hIcon,0

mov @stWcMain.hbrBackground,COLOR_WINDOW + 1

mov @stWcMain.lpszClassName,offset szClassName

mov @stWcMain.lpszMenuName,0

invoke RegisterClassEx,addr @stWcMain

;***************** 建立输出窗口 *****************************************

; 属性:没有标题栏,不显示在任务栏

;********************************************************************

invoke CreateWindowEx,WS_EX_TOOLWINDOW,\

offset szClassName,NULL,\

WS_POPUP or WS_SYSMENU,\

0,0,1,1,\

NULL,NULL,hInstance,NULL

invoke ShowWindow,hWinMain,SW_SHOWNORMAL

invoke UpdateWindow,hWinMain

;*************** 消息循环 *******************************************

.while TRUE

invoke GetMessage,addr @stMsg,NULL,0,0

.break .if eax == 0

invoke TranslateMessage,addr @stMsg

invoke DispatchMessage,addr @stMsg

.endw

ret

_WinMain endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

WndMainProc proc uses ebx edi esi, \

hWnd:Dword,uMsg:Dword,wParam:Dword,lParam:Dword

local @stPos:POINT

local @stPs:PAINTSTRUCT,@hDC:Dword

mov eax,uMsg

.if eax == WM_CREATE

mov eax,hWnd

mov hWinMain,eax

call _Init

;********************************************************************

.elseif eax == WM_PAINT

invoke BeginPaint,hWnd,addr @stPs

mov @hDC,eax

mov eax,@stPs.rcPaint.right

sub eax,@stPs.rcPaint.left

mov ecx,@stPs.rcPaint.bottom

sub ecx,@stPs.rcPaint.top

invoke BitBlt,@hDC,@stPs.rcPaint.left,@stPs.rcPaint.top,eax,ecx,\

hDcBack,@stPs.rcPaint.left,@stPs.rcPaint.top,SRCCOPY

invoke EndPaint,hWnd,addr @stPs

;********************************************************************

; 由于没有菜单,下面代码用于按下右键时弹出POPUP菜单

;********************************************************************

.elseif eax == WM_RBUTTONDOWN

.if wParam == MK_RBUTTON

invoke GetCursorPos,addr @stPos

invoke TrackPopupMenu,hMenu,TPM_LEFTALIGN,@stPos.x,@stPos.y,NULL,hWnd,NULL

.endif

;********************************************************************

; 由于没有标题栏,下面代码用于按下左键时移动窗口

;********************************************************************

.elseif eax == WM_LBUTTONDOWN

invoke UpdateWindow,hWnd ;即时刷新

invoke ReleaseCapture

invoke SendMessage,hWnd,WM_NCLBUTTONDOWN,htcAPTION,0

;********************************************************************

.elseif eax == WM_COMMAND

.if lParam == 0

mov eax,wParam

.if ax == IDM_EXIT

call _Quit

.elseif ax == IDM_ABOUT

invoke DialogBoxParam,hInstance,DLG_ABOUT,hWnd,offset AboutDialogProc,DLG_ABOUT

.endif

.endif

;********************************************************************

.elseif eax == WM_CLOSE

call _Quit

;********************************************************************

.else

invoke DefWindowProc,hWnd,uMsg,wParam,lParam

ret

.endif

;********************************************************************

; 注意:WndProc 处理 Windows 消息后,必须在 Eax 中返回 0

; 但是由 DefWindowProc 处理后的返回值不能改变,否则窗口

; 将无法显示!

;********************************************************************

xor eax,eax

ret

WndMainProc endp

;********************************************************************

_Init proc

local @hDC

invoke SendMessage,hWinMain,WM_SETTEXT,0,offset szClassName

invoke SendMessage,hWinMain,WM_SETICON,ICON_SMALL,hIcon

invoke LoadBitmap,hInstance,IDB_0 ;装入背景图片

mov hBmpBack,eax

invoke _SetWindowShape,hWinMain,hBmpBack ;设置窗口形状为背景图片

invoke GetDC,hWinMain

mov @hDC,eax

invoke CreateCompatibleDC,@hDC ;建立背景及数字 DC

mov hDcBack,eax

invoke ReleaseDC,hWinMain,@hDC

invoke SelectObject,hDcBack,hBmpBack

invoke _CenterWindow,hWinMain

ret

_Init endp

;********************************************************************

_Quit proc

local @stWindow:RECT

invoke DestroyMenu,hMenu

invoke DeleteDC,hDcBack

invoke DeleteObject,hBmpBack

invoke DestroyWindow,hWinMain

invoke PostQuitMessage,NULL

ret

_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_LBUTTONDOWN

invoke UpdateWindow,hWnd ;即时刷新

invoke ReleaseCapture

invoke SendMessage,hWnd,WM_NCLBUTTONDOWN,HTCAPTION,0

喜欢0 评分0
游客

返回顶部