Visual Basic Subclassing Routines
WM_TIMECHANGE: Detect System Changes to the Date/Time
Posted:   Tuesday June 8, 1999
Updated:   Monday December 26, 2011
Applies to:   VB5, VB6
Developed with:   VB6, Windows 98
OS restrictions:   None
Author:   VBnet - Randy Birch


SetLocaleInfo: Change System Long and Short Date Formats
WM_TIMECHANGE: Detect System Changes to the Date/Time
RegQueryValueEx: Identify Time Zones by Time Zone Bias
EnumDateFormats: Regional Locale Date Settings
EnumTimeFormats: Regional Locale Time Settings
GetLocaleInfo: Regional Locale Date Settings
VB5 or VB6.

datetime.gif (11787 bytes)When the Control Panel's Date/Time property dialog "Apply" or "OK" button is pressed, Windows broadcasts a message to all applications notifying them of the change to the system.  By subclassing a form and trapping the WM_TIMECHANGE message, a VB application can receive this notification to make whatever adjustments are needed. An application should return zero if it processes this message.  (Note that changes made through the DOS or CMD window are not detected.)

In addition, MSDN states that applications (subject to operating system version) can use the WM_TIMECHANGE message to notify other applications that a Date/Time change has been made. To send the WM_TIMECHANGE message to all top-level windows, an application can use the SendMessage function with the hwnd parameter set to HWND_TOPMOST. The application should post the WM_TIMECHANGE message with both the wParam and lParam members passed as zero.

The course to take when posting a change notification differs with the operating system version:

  • Windows NT 5.0 and later: An application should not broadcast this message, because the system will broadcast this message when the application changes the system time.
  • Windows NT 4.0 and earlier: An application should send the WM_TIMECHANGE message to all top-level windows after changing the system time.
  • Windows 95/98: An application should send the WM_TIMECHANGE message to all top-level windows after changing the system time.
 BAS Module Code
Place the following code into the general declarations area of a bas module:

Option Explicit
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
Public defWindowProc As Long

Public Const GWL_WNDPROC As Long = (-4)
Public Const WM_TIMECHANGE = &H1E

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

Public Sub SubClass(hwnd As Long)

  'assign window message procedure (WindowProc)
   On Error Resume Next
   defWindowProc = SetWindowLong(hwnd, _
                                 GWL_WNDPROC, _
                                 AddressOf WindowProc)
End Sub

Public Sub UnSubClass(hwnd As Long)

  'restore the default message handling before exiting
   If defWindowProc Then
      SetWindowLong hwnd, GWL_WNDPROC, defWindowProc
      defWindowProc = 0
   End If
End Sub

Public Function WindowProc(ByVal hwnd As Long, _
                           ByVal uMsg As Long, _
                           ByVal wParam As Long, _
                           ByVal lParam As Long) As Long

'window message procedure

   On Error Resume Next
   Select Case hwnd
     'If the handle returned is to our form,
     'perform form-specific message handling
     'to deal with the notifications.
      Case Form1.hwnd
        'form-specific handler
         Select Case uMsg
            Case WM_TIMECHANGE
              'take some action if desired
               Form1.Caption = "Subclassed: DATE/TIME changed"
               Form1.Label3.Caption = "New Time : " & Time()
               Form1.Label4.Caption ="New Date : " & Format$(Date, "Long Date")
              'message processed so return 0
               WindowProc = 0          
              Case Else
               'if subclassing has been activated, pass
               'messages to the default message handler
               'If it hasn't, then the default handler
               'will take care of them by default.
               WindowProc = CallWindowProc(defWindowProc, _
                                           hwnd, _
                                           uMsg, _
                                           wParam, _
               Exit Function
          End Select
      Case Else
         'this takes care of messages when the
         'handle specified is not that of the form
          WindowProc = CallWindowProc(defWindowProc, _
                                      hwnd, _
                                      uMsg, _
                                      wParam, _
   End Select
End Function
 Form Code
Create a form containing four Labels (Label1-Label4), and three command buttons (Command1, Command2 and Command3). Keep the form name as "Form1", or be sure to change Form1 references throughout the app before running. And add the following code to the form:

Option Explicit
Private Sub Command1_Click()

  'reset the titlebar caption during testing
   Me.Caption = "Subclassed: Change the Date/Time"
End Sub

Private Sub Command2_Click()

  'show the control panel date/time properties
   Call Shell("rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,0",vbNormalFocus)
End Sub

Private Sub Command3_Click()

   Unload Me
End Sub

Private Sub Form_Load()

   Label1.Caption = "Start Time : " & Time()
   Label2.Caption = "Start Date : " & Format$(Date, "Long Date")
   Call SubClass(Form1.hwnd)
   Command1.Value = True
End Sub

Private Sub Form_Unload(Cancel As Integer)

   If defWindowProc Then
      Call UnSubClass(Me.hwnd)
   End If
End Sub
Save the program before trying to run, and use Start with Full Compile to catch any coding errors. Open the date/time panel and change a setting, pressing OK or Apply.

Responding to the WM_TIMECHANGE event is not limited to changes made through the control panel. If an application happens to broadcast this message after making a system date/time change, this routine will detect that as well.

To test the SendMessage method with this demo, add the following additional declarations to the BAS module:

Public Declare Function SendMessage Lib "user32" _
    Alias "SendMessageA" _
   (ByVal hwnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    lParam As Any) As Long

Public Const HWND_TOPMOST = -1

..and add new form to the project (Form1) and the following code to a command button on that form:
Private Sub Command1_Click()

   Date = #2/6/1998#
  'specify HWND_TOPMOST as the hWnd parameter 
  'to broadcast to all top-level windows 
   Call SendMessage(HWND_TOPMOST, _
                    WM_TIMECHANGE, 0, ByVal 0)
End Sub
Add a Form1.Show command to the Form1 Load event, then run the demo again and press the Form1 command button. The system clock and the Form1 captions and labels will reflect the new change. And remember to set the date back before posting in a newsgroup or sending email !!


PayPal Link
Make payments with PayPal - it's fast, free and secure!


Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy


Hit Counter