第26章: 启动画面
LoadLibrary proto lpDLLName:DWORD它只有一个参数: 你想要加载的 DLL 的名称所在的地址.调用成功返回指向该DLL模块的句柄,反之返回NULL.
FreeLibrary proto hLib:DWORD它也只有一个参数: 你想要卸载的 DLL 模块的句柄(通常就是上面那个函数返回的啦).
SetTimer proto hWnd:DWORD, TimerID:DWORD, uElapse:DWORD, lpTimerFunc:DWORD你可以用2种方法创建定时器:hWnd接受这个定时器消息的窗口的句柄.如果,你的定时器不需要窗口接受它的消息,你也可以 用NULL作为参数
TimerID定时器的 ID 值. 由你自己定义.
uElapse 定时器定的时间.以ms(千分之一秒)为单位.
lpTimerFunc 处理该定时器消息的函数所在的地址.如果你用NULL作为该参数,那么定时器的消息会被送给 hWnd 参数所指定的窗口.SetTimer 如果成功则返回定时器的 ID 否则返回 NULL. 所以最好不要把定时器的ID设为0(Pheadnius:NULL代表0, 记得吗?).
KillTimer proto hWnd:DWORD, TimerID:DWORD
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
 .data  
   ClassName db "SplashDemoWinClass",0  
   AppName  db "Splash Screen Example",0  
   Libname db "splash.dll",0  
 .data?  
   hInstance HINSTANCE ?  
   CommandLine LPSTR ?  
   .code  
   start:  
    invoke LoadLibrary,addr Libname  
    .if eax!=NULL  
       invoke FreeLibrary,eax  
    .endif  
    invoke GetModuleHandle, NULL  
    mov    hInstance,eax  
    invoke GetCommandLine  
    mov    CommandLine,eax  
    invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT  
    invoke ExitProcess,eax  
 WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD  
  
    LOCAL wc:WNDCLASSEX  
    LOCAL msg:MSG  
    LOCAL hwnd:HWND  
    mov   wc.cbSize,SIZEOF WNDCLASSEX  
    mov   wc.style, CS_HREDRAW or CS_VREDRAW  
    mov   wc.lpfnWndProc, OFFSET WndProc  
    mov   wc.cbClsExtra,NULL  
    mov   wc.cbWndExtra,NULL  
    push  hInstance  
    pop   wc.hInstance  
    mov   wc.hbrBackground,COLOR_WINDOW+1  
    mov   wc.lpszMenuName,NULL  
    mov   wc.lpszClassName,OFFSET ClassName  
    invoke LoadIcon,NULL,IDI_APPLICATION  
    mov   wc.hIcon,eax  
    mov   wc.hIconSm,eax  
    invoke LoadCursor,NULL,IDC_ARROW  
    mov   wc.hCursor,eax  
    invoke RegisterClassEx, addr wc  
    INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\  
              WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\  
  
              CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\  
  
              hInst,NULL  
  
    mov   hwnd,eax  
    invoke ShowWindow, hwnd,SW_SHOWNORMAL  
    invoke UpdateWindow, hwnd  
    .while TRUE  
     invoke GetMessage, ADDR msg,NULL,0,0  
     .break .if (!eax)  
     invoke TranslateMessage, ADDR msg  
     invoke DispatchMessage, ADDR msg  
    .endw  
    mov     eax,msg.wParam  
    ret  
   WinMain endp  
 WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM  
    .IF uMsg==WM_DESTROY  
     invoke PostQuitMessage,NULL  
    .ELSE  
     invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
     ret  
    .ENDIF  
    xor eax,eax  
    ret  
   WndProc endp  
   end start  
 ;--------------------------------------------------------------------  
  
   ;                         
  位图 DLL  
   ;--------------------------------------------------------------------  
  
   .386  
   .model flat, stdcall  
   include \masm32\include\windows.inc  
   include \masm32\include\user32.inc  
   include \masm32\include\kernel32.inc  
   include \masm32\include\gdi32.inc  
   includelib \masm32\lib\user32.lib  
   includelib \masm32\lib\kernel32.lib  
   includelib \masm32\lib\gdi32.lib  
   .data  
   BitmapName db "MySplashBMP",0  
   ClassName db "SplashWndClass",0  
   hBitMap dd 0  
   TimerID dd 0  
 .data  
   hInstance dd ?  
.code
 DllEntry proc hInst:DWORD, reason:DWORD, reserved1:DWORD  
      .if reason==DLL_PROCESS_ATTACH  ; When the dll is loaded  
  
         push hInst  
         pop hInstance  
         call ShowBitMap  
      .endif
     mov eax,TRUE 
      ret  
   DllEntry Endp  
   ShowBitMap proc  
           LOCAL wc:WNDCLASSEX  
           LOCAL msg:MSG  
           LOCAL hwnd:HWND  
           mov   wc.cbSize,SIZEOF 
  WNDCLASSEX  
           mov   wc.style, CS_HREDRAW 
  or CS_VREDRAW  
           mov   wc.lpfnWndProc, 
  OFFSET WndProc  
           mov   wc.cbClsExtra,NULL  
  
           mov   wc.cbWndExtra,NULL  
  
           push  hInstance  
           pop   wc.hInstance  
  
           mov   wc.hbrBackground,COLOR_WINDOW+1  
  
           mov   wc.lpszMenuName,NULL  
  
           mov   wc.lpszClassName,OFFSET 
  ClassName  
           invoke LoadIcon,NULL,IDI_APPLICATION  
  
           mov   wc.hIcon,eax  
  
           mov   wc.hIconSm,0  
  
           invoke LoadCursor,NULL,IDC_ARROW  
  
           mov   wc.hCursor,eax  
  
           invoke RegisterClassEx, addr 
  wc  
           INVOKE CreateWindowEx,NULL,ADDR 
  ClassName,NULL,\  
              WS_POPUP,CW_USEDEFAULT,\  
  
              CW_USEDEFAULT,250,250,NULL,NULL,\  
  
              hInstance,NULL  
  
           mov   hwnd,eax  
  
           INVOKE ShowWindow, hwnd,SW_SHOWNORMAL  
  
           .WHILE TRUE  
                   
  INVOKE GetMessage, ADDR msg,NULL,0,0  
                   
  .BREAK .IF (!eax)  
                   
  INVOKE TranslateMessage, ADDR msg  
                   
  INVOKE DispatchMessage, ADDR msg  
           .ENDW  
           mov     eax,msg.wParam  
  
           ret  
   ShowBitMap endp  
   WndProc proc hWnd:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD  
           LOCAL ps:PAINTSTRUCT  
           LOCAL hdc:HDC  
           LOCAL hMemoryDC:HDC  
           LOCAL hOldBmp:DWORD  
           LOCAL bitmap:BITMAP  
           LOCAL DlgHeight:DWORD  
           LOCAL DlgWidth:DWORD  
           LOCAL DlgRect:RECT  
           LOCAL DesktopRect:RECT  
         .if uMsg==WM_DESTROY  
                   
  .if hBitMap!=0  
                           
  invoke DeleteObject,hBitMap  
                   
  .endif  
                   
  invoke PostQuitMessage,NULL  
           .elseif uMsg==WM_CREATE  
  
                   
  invoke GetWindowRect,hWnd,addr DlgRect  
                   
  invoke GetDesktopWindow  
                   
  mov ecx,eax  
                   
  invoke GetWindowRect,ecx,addr DesktopRect  
                   
  push  0  
                   
  mov  eax,DlgRect.bottom  
                   
  sub  eax,DlgRect.top  
                   
  mov  DlgHeight,eax  
                   
  push eax  
                   
  mov  eax,DlgRect.right  
                   
  sub  eax,DlgRect.left  
                   
  mov  DlgWidth,eax  
                   
  push eax  
                   
  mov  eax,DesktopRect.bottom  
                   
  sub  eax,DlgHeight  
                   
  shr  eax,1  
                   
  push eax  
                   
  mov  eax,DesktopRect.right  
                   
  sub  eax,DlgWidth  
                   
  shr  eax,1  
                   
  push eax  
                   
  push hWnd  
                   
  call MoveWindow  
                   
  invoke LoadBitmap,hInstance,addr BitmapName  
                   
  mov hBitMap,eax  
                   
  invoke SetTimer,hWnd,1,2000,NULL  
                   
  mov TimerID,eax  
           .elseif uMsg==WM_TIMER  
                   
  invoke SendMessage,hWnd,WM_LBUTTONDOWN,NULL,NULL  
                   
  invoke KillTimer,hWnd,TimerID  
           .elseif uMsg==WM_PAINT  
                   
  invoke BeginPaint,hWnd,addr ps  
                   
  mov hdc,eax  
                   
  invoke CreateCompatibleDC,hdc  
                   
  mov hMemoryDC,eax  
                   
  invoke SelectObject,eax,hBitMap  
                   
  mov hOldBmp,eax  
                   
  invoke GetObject,hBitMap,sizeof BITMAP,addr bitmap  
                   
  invoke StretchBlt,hdc,0,0,250,250,\  
                          
  hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY  
                   
  invoke SelectObject,hMemoryDC,hOldBmp  
                   
  invoke DeleteDC,hMemoryDC  
                   
  invoke EndPaint,hWnd,addr ps  
           .elseif uMsg==WM_LBUTTONDOWN  
  
                   
  invoke DestroyWindow,hWnd  
           .else  
                   
  invoke DefWindowProc,hWnd,uMsg,wParam,lParam  
                   
  ret  
           .endif  
           xor eax,eax  
           ret  
   WndProc endp  
End DllEntry
invoke LoadLibrary,addr Libname我们调用 LoadLibrary 读入名称为 "splash.dll" 的 DLL. 然后, 用 FreeLibrary 卸载. 一直到 DLL 完成初始化, LoadLibrary才会返回.
.if eax!=NULL
invoke FreeLibrary,eax
.endif
    .if reason==DLL_PROCESS_ATTACH  ; When the dll is loaded  
  
         push hInst  
         pop hInstance  
         call ShowBitMap  
DLL 被加载后, Windows 调用它的有 DLL_PROCESS_ATTACH 标记的入口函数. 我们借这个机会显示启动画面. 首先,我们 保存 DLL 事例的句柄以供将来使用. 然后, 调用一个叫 ShowBitMap 的函数进行真正的工作. ShowBitMap 注册一个窗口, 创建这个窗口和显示它.就像我们以前创建窗口一样. 有趣的是这个 CreateWindowEx 调用:
         INVOKE CreateWindowEx,NULL,ADDR 
  ClassName,NULL,\  
              WS_POPUP,CW_USEDEFAULT,\  
  
              CW_USEDEFAULT,250,250,NULL,NULL,\  
  
              hInstance,NULL  
注意, 这里的窗口风格仅仅使用了 WS_POPUP . 所以窗口即没有标题栏,也没有边界. 我们同时也限定窗口的宽高为
250x250个像素.
  现在窗口创建好了. 在 WM_CREATE 的消息处理代码里我们把这个窗口移到屏幕的中央.代码如下: 
                 
  invoke GetWindowRect,hWnd,addr DlgRect  
                   
  invoke GetDesktopWindow  
                   
  mov ecx,eax  
                   
  invoke GetWindowRect,ecx,addr DesktopRect  
                   
  push  0  
                   
  mov  eax,DlgRect.bottom  
                   
  sub  eax,DlgRect.top  
                   
  mov  DlgHeight,eax  
                   
  push eax  
                   
  mov  eax,DlgRect.right  
                   
  sub  eax,DlgRect.left  
                   
  mov  DlgWidth,eax  
                   
  push eax  
                   
  mov  eax,DesktopRect.bottom  
                   
  sub  eax,DlgHeight  
                   
  shr  eax,1  
                   
  push eax  
                   
  mov  eax,DesktopRect.right  
                   
  sub  eax,DlgWidth  
                   
  shr  eax,1  
                   
  push eax  
                   
  push hWnd  
                   
  call MoveWindow  
它先找到桌面和窗口的大小. 然后,计算出一个窗口左上角的坐标. 使这个窗口能位于屏幕中央.
                 
  invoke LoadBitmap,hInstance,addr BitmapName  
                   
  mov hBitMap,eax  
                   
  invoke SetTimer,hWnd,1,2000,NULL  
                   
  mov TimerID,eax  
下一步,它用 LoadBitmap 从资源中读入位图并且创建一个定时器.定时器的 ID 为 1 时间间隔为 2 秒. 定时器 将每 2 秒 向窗口发送 WM_TIMER 消息.
         .elseif uMsg==WM_PAINT  
  
                   
  invoke BeginPaint,hWnd,addr ps  
                   
  mov hdc,eax  
                   
  invoke CreateCompatibleDC,hdc  
                   
  mov hMemoryDC,eax  
                   
  invoke SelectObject,eax,hBitMap  
                   
  mov hOldBmp,eax  
                   
  invoke GetObject,hBitMap,sizeof BITMAP,addr bitmap  
                   
  invoke StretchBlt,hdc,0,0,250,250,\  
                          
  hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY  
                   
  invoke SelectObject,hMemoryDC,hOldBmp  
                   
  invoke DeleteDC,hMemoryDC  
                   
  invoke EndPaint,hWnd,addr ps  
当窗口收到 WM_PAINT 消息, 它创建一个内存DC(Pheadnius:还记得DC吗. 在win32编程中你会经常遇到DC这个词. 它是 Device Context 的缩写, 官方译为"设备描述表". 如果你研究过vc, 你应该对它不陌生. 不过如果你不明白它是什么 也不要紧. 你可以把它看作一个句柄. 就是某个设备或某块内存的名称.),然后把位图选进内存DC. 再用 GetObject 函数获得 位图的尺寸, 然后用 StretchBlt 把位图显示在窗口上. StretchBlt的作用和 BitBlt 一样,但它可以拉伸或压缩位图到我们 希望的大小. 在这里我们希望位图能适合窗口的大小,所以我们 StretchBlt 代替 BitBlt. 之后我们删除内存DC.
         .elseif uMsg==WM_LBUTTONDOWN  
  
                   
  invoke DestroyWindow,hWnd  
如果你的程序的使用者每次都要看到启动画面消失才能用, 他们一定会厌烦. 我们可以为用户提供多一种选择. 当他单击启动画面, 它就会消失. 这就是为什么我们要在DLL里处理 WM_LBUTTONDOWN 消息. 收到这个消息后立即就用 DestroyWindow 关掉窗口.
         .elseif uMsg==WM_TIMER  
  
                   
  invoke SendMessage,hWnd,WM_LBUTTONDOWN,NULL,NULL  
                   
  invoke KillTimer,hWnd,TimerID  
如果用户选择等待, 那么启动画面会在定时器到了指定的时间后消失. (在本例中, 是 2 秒). 我们可以通过处理
WM_TIMER 消息达到这一目的. 在收到这一消息后,我们可以对窗口传送 WM_LBUTTONDOWN 消息来关掉窗口. 这是为了避免
代码重复. 现在, 我们不再需要这个定时器了,所以我们用 KillTimer 删除它. 
窗口关闭后,DLL 把控制权还给主程序.