Results 1 to 12 of 12

Thread: [RESOLVED] Can you use sendmessage to change datepicker value in external app?

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Jun 2013
    Posts
    24

    Resolved [RESOLVED] Can you use sendmessage to change datepicker value in external app?

    I have an external app which is not a Microsoft product and I want to automate using it. I've already used sendmesage successfully to control various UI elements, like this one for pressing a button
    Code:
    Call SendMessage(OK_button, BM_CLICK, 0, ByVal 0&)
    However I just don't seem to find anything about setting the value of a datepicker in an external app. If I could use sendmessage and WM_KEYDOWN to change the value that would be OK as well. Is there a way of doing this in VBA or am I better of using some other programming language like Autoit?

  2. #2
    PowerPoster
    Join Date
    Sep 2006
    Location
    Egypt
    Posts
    2,579

    Re: Can you use sendmessage to change datepicker value in external app?

    Try SendKeys, e.g. to set date to 1/12/2013
    Code:
    SendKeys "1{Right}12{Right}2013"
    You must ensure the DTPicker has the focuse before sending keys.



  3. #3
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,647

    Re: Can you use sendmessage to change datepicker value in external app?

    Have you tried just setting it via WM_SETTEXT?

  4. #4
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: Can you use sendmessage to change datepicker value in external app?

    Perhaps the DTM_SETSYSTEMTIME message might accomplish what you want.
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  5. #5
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: Can you use sendmessage to change datepicker value in external app?

    The DTM_SETSYSTEMTIME message indeed works, but it requires copying a SYSTEMTIME structure to the target process' memory:

    Code:
    Option Explicit
    
    Private Const DTM_SETSYSTEMTIME    As Long = &H1002
    Private Const GDT_VALID            As Long = 0
    Private Const MEM_COMMIT           As Long = &H1000
    Private Const MEM_RELEASE          As Long = &H8000&
    Private Const MEM_RESERVE          As Long = &H2000
    Private Const PAGE_READWRITE       As Long = &H4
    Private Const PROCESS_VM_OPERATION As Long = &H8
    Private Const PROCESS_VM_WRITE     As Long = &H20
    
    Private Type SYSTEMTIME
        wYear         As Integer
        wMonth        As Integer
        wDayOfWeek    As Integer
        wDay          As Integer
        wHour         As Integer
        wMinute       As Integer
        wSecond       As Integer
        wMilliseconds As Integer
    End Type
    
    Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
    Private Declare Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hWnd As Long, Optional ByRef lpdwProcessId As Long) As Long
    Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    Private Declare Function SendMessageW Lib "user32.dll" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Declare Function TzSpecificLocalTimeToSystemTime Lib "kernel32.dll" (ByRef lpTimeZoneInformation As Any, ByRef lpLocalTime As SYSTEMTIME, ByRef lpUniversalTime As SYSTEMTIME) As Long
    Private Declare Function VariantTimeToSystemTime Lib "oleaut32.dll" (ByVal vtime As Date, ByRef lpSystemTime As SYSTEMTIME) As Long
    Private Declare Function VirtualAllocEx Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
    Private Declare Function VirtualFreeEx Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
    Private Declare Function WriteProcessMemory Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpBaseAddress As Long, ByRef lpBuffer As Any, ByVal nSize As Long, Optional ByRef lpNumberOfBytesWritten As Long) As Long
    
    Private Sub SetDTPTime(ByVal hWndDTP As Long, ByVal dtmLocalTime As Date)
        Dim hProcess As Long, lpST As Long, PID As Long
        Dim RV As Long, LT As SYSTEMTIME, ST As SYSTEMTIME
    
        If VariantTimeToSystemTime(dtmLocalTime, LT) Then
            If TzSpecificLocalTimeToSystemTime(ByVal 0&, LT, ST) Then
                RV = GetWindowThreadProcessId(hWndDTP, PID)
                hProcess = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_WRITE, 0&, PID)
                If hProcess Then
                    lpST = VirtualAllocEx(hProcess, 0&, LenB(ST), MEM_COMMIT Or MEM_RESERVE, PAGE_READWRITE)
                    If lpST Then
                        RV = WriteProcessMemory(hProcess, lpST, ST, LenB(ST), PID):     Debug.Assert RV <> 0& And PID = LenB(ST)
                        RV = SendMessageW(hWndDTP, DTM_SETSYSTEMTIME, GDT_VALID, lpST): Debug.Assert RV
                        RV = VirtualFreeEx(hProcess, lpST, 0&, MEM_RELEASE):            Debug.Assert RV
                    End If
                    RV = CloseHandle(hProcess):                                         Debug.Assert RV
                End If
            End If
        End If
    End Sub
    
    Private Sub Command1_Click()
        SetDTPTime CLng(Clipboard.GetText), DateAdd("ww", 2#, Now) '2 weeks from now
    End Sub
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  6. #6

    Thread Starter
    Junior Member
    Join Date
    Jun 2013
    Posts
    24

    Re: Can you use sendmessage to change datepicker value in external app?

    Changing system time doesn't work this time as this program doesn't get it's date from windows. The handle is "ctrl1". This one didn't work:
    Code:
    Call SendMessage(ctrl1, WM_SETTEXT, 0&, "01/01/2010")
    These two both work:

    Code:
    SetForegroundWindow (ctrl1)
    Call SendKeys("01{right}01{right}2012", True) 
    SetForegroundWindow (ctrl1)
    Call SendKeys("01/01/2012", True)
    However, the question is about sendmessage, because it automatically waits till the action is actually finished and I don't have to put a bunch of application.wait in my code. In addition I had unexplained reliability issues with sendkeys in the past and I decidied not to use it any more.

    I can use sendmessage and wm_keydown to set the day, but this one doesn't change the selection to the month:
    Code:
    PostMessage ctrl1, WM_KEYDOWN, 47, 0

  7. #7
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: Can you use sendmessage to change datepicker value in external app?

    Quote Originally Posted by tahi.laci View Post
    Changing system time doesn't work this time as this program doesn't get it's date from windows.
    Did you read the description of the DTM_SETSYSTEMTIME message? Here it is if you don't want to click that link:

    Quote Originally Posted by MSDN
    Sets the time in a date and time picker (DTP) control. You can send this message explicitly or use the DateTime_SetSystemtime macro.
    The code in post #5 works with both the DTPicker control from MS Win Common Controls-2 6.0 and the Date and Time Picker Control from the Windows Controls Library.

    There isn't a more direct way of manipulating a DTP control's time value than by sending that message.
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  8. #8

    Thread Starter
    Junior Member
    Join Date
    Jun 2013
    Posts
    24

    Re: Can you use sendmessage to change datepicker value in external app?

    I'm sorry I didn't read your post properly. When I looked at DTM_SETSYSTEMTIME I assumed it only changes the system time. Since then I tried and it worked, without the reliability issues associated with sendkeys(). This is just what I was looking for. Thank you.

    There's just one interesting thing when I put this:
    Code:
    SetDTPTime chld, "28/06/13"
    The date actually changes to 27/06/2013. When I put something like
    Code:
    SetDTPTime chld, now-1
    it works as expected.
    Last edited by tahi.laci; Jun 27th, 2013 at 11:11 PM.

  9. #9
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: Can you use sendmessage to change datepicker value in external app?

    Quote Originally Posted by tahi.laci View Post
    There's just one interesting thing when I put this:
    Code:
    SetDTPTime chld, "28/06/13"
    The date actually changes to 27/06/2013. When I put something like
    Code:
    SetDTPTime chld, now-1
    it works as expected.
    That must be due to the TzSpecificLocalTimeToSystemTime API function. I included it because the DTM_SETSYSTEMTIME message says "lParam is a pointer to a SYSTEMTIME structure containing the system time used to set the DTP control." I thought that I should convert the passed local time (LT As SYSTEMTIME) to the required system time (ST As SYSTEMTIME). If you want, you can comment out that function and the LT variable declaration. Replace LT with ST in the VariantTimeToSystemTime line.

    BTW, date literals are enclosed with # (e.g., #28/06/13#).



    EDIT

    I just saw this thread in the API forum: DTM_SETSYSTEMTIME SendMessage
    Last edited by Bonnie West; Jun 28th, 2013 at 03:07 AM.
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  10. #10

    Thread Starter
    Junior Member
    Join Date
    Jun 2013
    Posts
    24

    Re: [RESOLVED] Can you use sendmessage to change datepicker value in external app?

    I got type mismatch after putting "#".

    I may have done it incorrectly, but changing LT to ST didn't work for me. In excel 0 = 00/01/1900, and I know in some systems 0 = 30/12/1899. Assuming mine is such an application I just added one to the date in the code:
    Code:
    If VariantTimeToSystemTime(dtmLocalTime + 1, LT) Then

  11. #11
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: [RESOLVED] Can you use sendmessage to change datepicker value in external app?

    Quote Originally Posted by tahi.laci View Post
    I got type mismatch after putting "#".
    Can you show the date literal and/or the whole line of code?

    Quote Originally Posted by tahi.laci View Post
    I may have done it incorrectly, but changing LT to ST didn't work for me. In excel 0 = 00/01/1900, and I know in some systems 0 = 30/12/1899. Assuming mine is such an application I just added one to the date in the code:
    Code:
    If VariantTimeToSystemTime(dtmLocalTime + 1, LT) Then
    Did you remember to comment out the If TzSpecificLocalTimeToSystemTime(... line? VariantTimeToSystemTime just converts a VB Date type to a SYSTEMTIME structure; it performs no date arithmetic. The TzSpecificLocalTimeToSystemTime function, on the other hand, "converts a local time to a time in Coordinated Universal Time (UTC)". I believe that the DTM_SETSYSTEMTIME message expects date values to be in System Time. Can you clarify exactly what date/time issues you are having with the DTP control? BTW, you should not try to adjust the date yourself, let the APIs figure it out.

    You mentioned Excel, are you using that code in VBA? I'm not familiar with VBA but I believe that code should still work correctly in VBA.
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

  12. #12

    Thread Starter
    Junior Member
    Join Date
    Jun 2013
    Posts
    24

    Re: [RESOLVED] Can you use sendmessage to change datepicker value in external app?

    I've just realised that I got type mismatch because i used both " and # now it's ok. Although it assumes the date is in American format when I put it this way:
    Code:
    SetDTPTime chld, #9/7/2013#
    i.e. in the UK 1 July looks like this: "01/07/2013", when I use #1/7/2013# it counts as 7 January. Yes I use VBA indeed, and posted my questions under VB6, because I didn't know that there was any difference and I didn't find any VBAforums. It works now. First time I commented out the wrong line. The changed code is like this:
    Code:
    Private Const DTM_SETSYSTEMTIME    As Long = &H1002
    Private Const GDT_VALID            As Long = 0
    Private Const MEM_COMMIT           As Long = &H1000
    Private Const MEM_RELEASE          As Long = &H8000&
    Private Const MEM_RESERVE          As Long = &H2000
    Private Const PAGE_READWRITE       As Long = &H4
    Private Const PROCESS_VM_OPERATION As Long = &H8
    Private Const PROCESS_VM_WRITE     As Long = &H20
    
    Private Type SYSTEMTIME
        wYear         As Integer
        wMonth        As Integer
        wDayOfWeek    As Integer
        wDay          As Integer
        wHour         As Integer
        wMinute       As Integer
        wSecond       As Integer
        wMilliseconds As Integer
    End Type
    
    Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As Long) As Long
    Private Declare Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hWnd As Long, Optional ByRef lpdwProcessId As Long) As Long
    Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    Private Declare Function SendMessageW Lib "user32.dll" (ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    'Private Declare Function TzSpecificLocalTimeToSystemTime Lib "kernel32.dll" (ByRef lpTimeZoneInformation As Any, ByRef lpLocalTime As SYSTEMTIME, ByRef lpUniversalTime As SYSTEMTIME) As Long
    Private Declare Function VariantTimeToSystemTime Lib "oleaut32.dll" (ByVal vtime As Date, ByRef lpSystemTime As SYSTEMTIME) As Long
    Private Declare Function VirtualAllocEx Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
    Private Declare Function VirtualFreeEx Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
    Private Declare Function WriteProcessMemory Lib "kernel32.dll" (ByVal hProcess As Long, ByVal lpBaseAddress As Long, ByRef lpBuffer As Any, ByVal nSize As Long, Optional ByRef lpNumberOfBytesWritten As Long) As Long
    
    Private Sub SetDTPTime(ByVal hWndDTP As Long, ByVal dtmLocalTime As Date)
        Dim hProcess As Long, lpST As Long, PID As Long
        Dim RV As Long ', LT As SYSTEMTIME
        Dim ST As SYSTEMTIME
    
        If VariantTimeToSystemTime(dtmLocalTime, ST) Then
            'If TzSpecificLocalTimeToSystemTime(ByVal 0&, LT, ST) Then
                RV = GetWindowThreadProcessId(hWndDTP, PID)
                hProcess = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_WRITE, 0&, PID)
                If hProcess Then
                    lpST = VirtualAllocEx(hProcess, 0&, LenB(ST), MEM_COMMIT Or MEM_RESERVE, PAGE_READWRITE)
                    If lpST Then
                        RV = WriteProcessMemory(hProcess, lpST, ST, LenB(ST), PID):     Debug.Assert RV <> 0& And PID = LenB(ST)
                        RV = SendMessageW(hWndDTP, DTM_SETSYSTEMTIME, GDT_VALID, lpST): Debug.Assert RV
                        RV = VirtualFreeEx(hProcess, lpST, 0&, MEM_RELEASE):            Debug.Assert RV
                    End If
                    RV = CloseHandle(hProcess):                                         Debug.Assert RV
                End If
            'End If
        End If
    End Sub
    
    Private Sub changeDate()
    'In this part I use findwindow() and findwindowex() to find the date picker window and store it's handle in chld variable.
    
        SetDTPTime chld, "01/07/2013"
    
    'this line also works: SetDTPTime chld, DateAdd("ww", 2#, Now)
    End Sub
    I could make a simple conversion between American and British dates but it's more convenient to just leave it as it is, as long as it always works. Probably I'll just use an excel cell instead of a variable to store the date and then there's no complication.

    Thanks for your help I don't think I'd have worked this out if it weren't for your patience.
    Last edited by tahi.laci; Jul 2nd, 2013 at 11:25 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width