在關機或Logff前訊息的攔截
來源:cww

如果我們關機或Logoff時,我們的程式有時會因而無法按正常程序結束,一般我們會在
Form的Unload中一段程式結束時要做什麼事,但是,如果使用者直接用開始功能表的關
機,會使UnLoad的部份沒有做到,我們現在就想辦法來攔截關機(或Logoff)時的訊息。

一般來說,關機或Logff後,Windows會傳依序送出WM_QUERYENDSESSION的訊息給每個
Process,如果中間有一個Process不能順利結束(例如:Word修改後未存檔,而出現是
否存檔,但我們按取消),這時該訊息執行的結果會傳回False(0),這時Windows也就
不再繼續送WM_QUERYENDSESSION給下一個Proccess。反之,如果所有的Process都可以
順利結束(也就是每個送出的WM_QUERYENDSESSION都傳回True),那才代表可以順利結束。

不管WM_QUERYENDSESSION最後的結果是可以順利結束或不能順利結束,Windows會再送
一個WM_ENDSESSION的訊息給所有的Process,而wParam的內容便是指出是否可以順利
結束(True表可以,False表不行,在vb中則Check wParam = 0 表False ,<> 0表True)
,說到這裡大概就知道該如何做啦,程式如下:
'以下在Form 
Private Sub Form_Load()
  Dim ret As Long
  '記錄原本的Window Procedure的位址
  preWinProc = GetWindowLong(Me.hwnd, GWL_WNDPROC)
  '設定form的window Procedure到wndproc
  ret = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf wndproc)
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Dim ret As Long
  '取消Message的截取,而使之又只送往原來的Window Procedure
  ret = SetWindowLong(Me.hwnd, GWL_WNDPROC, preWinProc)
  '這裡只是要看看用關機的方式結束程式時,會不會執行到這裡
  Dim fno As Long
  fno = FreeFile
  Open "c:\tt2" For Append As fno
  Print #fno, "ccc" + vbCrLf
  Close #fno
End Sub
'以下在.Bas
Option Explicit

 Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
   (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
 Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
   (ByVal hwnd As Long, ByVal nIndex As Long) As Long
 Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" _
   (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long

  Public Const GWL_WNDPROC = (-4)
  Public Const WM_ENDSESSION = &H16
  Public Const WM_QUERYENDSESSION = &H11

  Public preWinProc As Long

  Public Function wndproc(ByVal hwnd As Long, ByVal Msg As Long, _
                          ByVal wParam As Long, ByVal lParam As Long) As Long
  If Msg = WM_QUERYENDSESSION Then
     Debug.Print "QryEnd", wParam, lParam
  Else
      If Msg = WM_ENDSESSION Then
         If wParam <> 0 Then '代表將順利關機或LogOff,這時便得做正常結束程式的動作
            Dim fno As Long
            Open "c:\ttt" For Output As #1
            Print #1, "hahcccc5"
            Close #1
         End If
      End If
  End If
  '將之送往原來的Window Procedure
  wndproc = CallWindowProc(preWinProc, hwnd, Msg, wParam, lParam)
  End Function