邪恶八进制信息安全团队技术讨论组's Archiver

金州 2006-1-5 03:00

[转载]Win32汇编教程9-复杂形状的窗口[综合篇一]

作者:罗云彬

概述

   在前面八篇的 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 罗云彬, [email]bigluo@telekbird.com.cn[/email]
;  Website: [url]http://asm.yeah.net[/url]
;  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

页: [1]
© 1999-2008 EvilOctal Security Team