-
Jun 25th, 2013, 02:31 PM
#1
Thread Starter
Junior Member
[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?
-
Jun 25th, 2013, 07:58 PM
#2
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.
-
Jun 25th, 2013, 09:14 PM
#3
Re: Can you use sendmessage to change datepicker value in external app?
Have you tried just setting it via WM_SETTEXT?
-
Jun 26th, 2013, 12:17 AM
#4
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)
-
Jun 26th, 2013, 03:32 PM
#5
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)
-
Jun 26th, 2013, 10:16 PM
#6
Thread Starter
Junior Member
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
-
Jun 26th, 2013, 11:00 PM
#7
Re: Can you use sendmessage to change datepicker value in external app?
Originally Posted by tahi.laci
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:
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)
-
Jun 27th, 2013, 10:12 PM
#8
Thread Starter
Junior Member
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.
-
Jun 28th, 2013, 02:20 AM
#9
Re: Can you use sendmessage to change datepicker value in external app?
Originally Posted by tahi.laci
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)
-
Jun 29th, 2013, 07:15 PM
#10
Thread Starter
Junior Member
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
-
Jun 30th, 2013, 12:43 AM
#11
Re: [RESOLVED] Can you use sendmessage to change datepicker value in external app?
Originally Posted by tahi.laci
I got type mismatch after putting "#".
Can you show the date literal and/or the whole line of code?
Originally Posted by tahi.laci
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)
-
Jul 2nd, 2013, 10:59 PM
#12
Thread Starter
Junior Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|