SendMessage WM_GETTEXT will not get the text of the 'File name:' edit box on the 'save as' dialog box

velcrow
velcrow used Ask the Experts™
on
In VBA I'm using the SendMessage function of the user32.dll windows API with the WM_GETTEXT parameter as follows to retrieve the text from the 'File name' edit box on the windows 'Save As' dialog box.  I can successfully post a file name to the box and get the length of the text in the box, but I cannot for the life of me figure out why I can't get the text.

comboBox32win = FindWindowEx(SaveAsDialog, 0, "ComboBoxEx32", vbNullString)
ComboBoxwin = FindWindowEx(comboBox32win, 0, "ComboBox", vbNullString)
EditBox = FindWindowEx(ComboBoxwin, 0, "Edit", vbNullString)
txtlen = SendMessage(EditBox, WM_GETTEXTLENGTH, vbNullString, vbNullString)
txtlen = txtlen + 1
txt = Space$(txtlen)
retval = SendMessage(EditBox, WM_GETTEXT, txtlen, ByVal txt)
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
In this line, get rid of the "ByVal":

    retval = SendMessage(EditBox, WM_GETTEXT, txtlen, ByVal txt)

So it's:

    retval = SendMessage(EditBox, WM_GETTEXT, txtlen, txt)

Can we see your declaration for SendMessage()?

Author

Commented:
I had already tried that, and it does not work.  Here is my declaration:

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

Author

Commented:
The purpose for wanting the read the ‘File name’ text box is because I’m sending the file name to that text box using    SendMessage(EditBox, WM_SETTEXT, vbNullString, fileName), but for some reason the ‘save as’ dialog box is found using FindWindow("#32770", "Save As") before it as actually finished loading and so when I send the file name it succeeds but it hasn’t really posted the file name to the dialog box and so I’ve created a loop to continue sending the file name until it has actually been posted.  So, if you know of another way to delay after finding the ‘Save as’ dialog until it has actually finished loading then that would work as well.
Success in ‘20 With a Profitable Pricing Strategy

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

Author

Commented:
Here is the code for the loop I've described above:

    Do
        retval = SendMessage(EditBox, WM_SETTEXT, vbNullString, fileName)
        DoEvents
        txtlen = SendMessage(EditBox, WM_GETTEXTLENGTH, vbNullString, vbNullString)
        txtlen = txtlen + 1
        txt = Space$(txtlen)
        Call SendMessage(EditBox, WM_GETTEXT, txtlen, txt)
    Loop While txt <> fileName
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
*Shot in the dark*

Try using PostMessage() instead of SendMessage().

Author

Commented:
tried that too

Author

Commented:
just for clarification, the 'save as' dialog in this case is the one that appears when downloading a file from a website using internet explorer.
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Is the actual edit field getting updated with your desired value?  (...and it's just not getting returned back?)

Author

Commented:
that is correct.
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Ohhh...I remember a similar issue cropped up when I was helping another expert awhile back.

We ended up sending the desired filename char by char with PostMessage() and then following it up with the Enter key to make it "update" itself.

This is VB.Net code so it'll need tweaking for VB6:

                            For Each c As Char In TargetFileName
                                PostMessage(EditHandle, WM_CHAR, Microsoft.VisualBasic.AscW(c), IntPtr.Zero)
                            Next
                            PostMessage(EditHandle, WM_KEYDOWN, Keys.Enter, &H0)

The PAQ it came from: http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_24693216.html#25223150
(read point #1 in my post at the very top too)

Author

Commented:
Back to my earlier post; if you know how to determine when the dialog box has finished loading and its controls can be accessed, that would work too.  One would think that once the SaveAsDialog = FindWindow("#32770", "Save As") had received a handle it would be save to send file name and click save, but that is not the case.

Author

Commented:
I'm not having a problem posting to the edit box, I'm having a problem reading it.
Top Expert 2010

Commented:
The standard windows dialog should have the same control ID for the edit box so it would be easier to just get the dialog handle and then use the dialog ID in your case the edit control. The control ID is 0000047C
Top Expert 2010
Commented:
Well posting the example might have helped ... getting late I suppose anyway here is concept

Option Explicit

Private Const WM_GETTEXT = &HD

Private Declare Function FindWindowW Lib "user32" (ByVal lpClassName As Long, ByVal lpWindowName As Long) As Long
Private Declare Function SendDlgItemMessageW Lib "user32" (ByVal hDlg As Long, ByVal nIDDlgItem As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Public Function GetSavasFilename(ByVal szTitle As String) As String

  Dim hDlg As Long
  Dim cb As Long
  Dim Buffer(260 * 2) As Byte
  
  hDlg = FindWindowW(0, StrPtr(szTitle))
  If hDlg = 0 Then
    Exit Function
  End If
  cb = SendDlgItemMessageW(hDlg, &H47C, WM_GETTEXT, 260, VarPtr(Buffer(0)))
  GetSavasFilename = Left$(Buffer, cb)

End Function

Open in new window

Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
I understand...it ~LOOKS~ like your posting to it but the value being read back is different because the control is actually a ComboBox control.  You have to hit the Enter key to make it update itself...

Author

Commented:
eql1044,

Fantastic, your code worked.  Any idea why SendDlgItemMessageW works and SendMessage to the specific control does not?
Top Expert 2010

Commented:
I didn't look to much into your code but it's probrably just a minor issue of declerations but I wouldn't know unless we had your full code to test all your declerations and variables when posting helps solves these kinds of issues. I just tossed out a better approach for finding the control ID.
Can you post your full code with declerations maybe we can find out what the problem had been?

Author

Commented:
here is my declaration for SendMessage

Author

Commented:
ooops, forgot the code

Public Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" _
        (ByVal hwnd As Long, ByVal Msg As Long, wParam As Any, ByVal lParam As String) As Long
Top Expert 2010

Commented:
I know I seen that one but everything like your constants and FindWindow() + variable :)

Author

Commented:
   Dim hwndDialog As Long  ' handle to the dialog box
    Dim hwndButton As Long  ' handle to the  button
    Dim retval As Long      ' return value
    SaveAsDialog = FindWindow("#32770", "Save As")
    comboBox32win = FindWindowEx(SaveAsDialog, 0, "ComboBoxEx32", vbNullString)
    ComboBoxwin = FindWindowEx(comboBox32win, 0, "ComboBox", vbNullString)
    EditBox = FindWindowEx(ComboBoxwin, 0, "Edit", vbNullString)
    retval = SendMessage(comboBox32win, WM_SETTEXT, vbNullString, "fileName")
    txtlen = SendMessage(EditBox, WM_GETTEXTLENGTH, vbNullString, vbNullString)
    txtlen = txtlen + 1
'    txt = Space$(txtlen)
    Call SendMessageGETTEXT(EditBox, WM_GETTEXT, 260, txt)

Author

Commented:
the last line of the code I just should have been
Call SendMessage(EditBox, WM_GETTEXT, 260, txt)
the SendMessageGETTEXT was just another test i was trying

here are my constants and other declarations, but I don't think there is a problem here because everything else worked fine

Public Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" _
        (ByVal hwnd As Long, ByVal Msg As Long, wParam As Any, ByVal lParam As String) As Long
       
Public Declare Function SendMessageGETTEXT Lib "user32.dll" Alias "SendMessageA" _
        (ByVal hwnd As Long, ByVal Msg As Long, wParam As Any, ByVal lParam As Long) As Long
       
Public Declare Function SetActiveWindow Lib "user32.dll" (ByVal hwnd As Long) As Long
Public Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal _
    lpClassName As Any, ByVal lpWindowName As Any) As Long
Public Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal _
    hwndParent As Long, ByVal hwndChildAfter As Long, ByVal lpszClass As Any, _
    ByVal lpszWindow As Any) As Long
Public Declare Function GetActiveWindow Lib "user32" () As Long
Public Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Public Declare Function GetWindowInfo Lib "user32.dll" Alias "GetWindowInfoA" (ByVal hwnd, ByVal PWINDOWINFO)
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Public Declare Function WaitForSingleObject Lib "Kernel32.dll" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function SendDlgItemMessageW Lib "user32" (ByVal hDlg As Long, ByVal nIDDlgItem As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long


Const GWL_ID As Long = -12
Public Const WM_GETTEXTLENGTH = &HE
Public Const CB_INSERTSTRING = &H14A
Public Const WM_GETTEXT = &HD
Private Const WM_SETTEXT = &HC
Public Const BM_CLICK = &HF5
Private Const BM_SETSTATE = &HF3
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Const GW_HWNDFIRST = 0
Private Const GW_HWNDNEXT = 2
Top Expert 2010

Commented:
Okay thats a start and it looks like your issue is SendMessage() decleration which I suspected already but had to see other parts. You passing wParam as Any but you need to pass it Byval your problem is the defined buffer size was always zero so no data is returned.

Author

Commented:
egl1044,
I'm going to accept your solution and award points because it did accomplish what I was trying to do (read the text box), but I've just discovered that what I was trying to do will still not solve my problem... so, I'm hoping you can help me with that as well.
Top Expert 2010

Commented:
Sure but only if you give Idle_Mind some points. Share the wealth :)D
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Haha...no worries bud.  Lots of fish in the sea...  =)

Author

Commented:
I changed to Byval, but I still get nothing.
Top Expert 2010

Commented:
velcrow,
In your code you posted you had the buffer marked as a comment only thing I did was uncommect txt = Space$(txtlen) and change the call : Call SendMessage(EditBox, WM_GETTEXT, ByVal 260, txt)

Author

Commented:
Idle_Mind:
I'm new at this.  I intended on assigning you some ponts but I guess I screwed it up.

eql1044:
Call SendMessage(EditBox, WM_GETTEXT, ByVal 260, txt)  working for you?  it does not work for me.
Top Expert 2010

Commented:
Yup working for me I had to declare some missing variables but this is what I just copied into VB. BTW make sure that the title is correct
Top Expert 2010

Commented:

Option Explicit

Public Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" _
        (ByVal hwnd As Long, ByVal Msg As Long, wParam As Any, ByVal lParam As String) As Long
        
Public Declare Function SendMessageGETTEXT Lib "user32.dll" Alias "SendMessageA" _
        (ByVal hwnd As Long, ByVal Msg As Long, wParam As Any, ByVal lParam As Long) As Long
        
Public Declare Function SetActiveWindow Lib "user32.dll" (ByVal hwnd As Long) As Long
Public Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal _
    lpClassName As Any, ByVal lpWindowName As Any) As Long
Public Declare Function FindWindowEx Lib "user32.dll" Alias "FindWindowExA" (ByVal _
    hwndParent As Long, ByVal hwndChildAfter As Long, ByVal lpszClass As Any, _
    ByVal lpszWindow As Any) As Long
Public Declare Function GetActiveWindow Lib "user32" () As Long
Public Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Public Declare Function GetWindowInfo Lib "user32.dll" Alias "GetWindowInfoA" (ByVal hwnd, ByVal PWINDOWINFO)
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Public Declare Function WaitForSingleObject Lib "Kernel32.dll" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function SendDlgItemMessageW Lib "user32" (ByVal hDlg As Long, ByVal nIDDlgItem As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long


Const GWL_ID As Long = -12
Public Const WM_GETTEXTLENGTH = &HE
Public Const CB_INSERTSTRING = &H14A
Public Const WM_GETTEXT = &HD
Private Const WM_SETTEXT = &HC
Public Const BM_CLICK = &HF5
Private Const BM_SETSTATE = &HF3
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Const GW_HWNDFIRST = 0
Private Const GW_HWNDNEXT = 2


Public Sub Test()
Dim hwndDialog As Long  ' handle to the dialog box
    Dim hwndButton As Long  ' handle to the  button
    Dim retval As Long      ' return value
    
' Added this stuff...
    Dim SaveAsDialog As Long
    Dim comboBox32win As Long
    Dim ComboBoxwin As Long
    Dim txtlen As Long
    Dim txt As String
    Dim EditBox As Long
     
    SaveAsDialog = FindWindow("#32770", "Save As")
    comboBox32win = FindWindowEx(SaveAsDialog, 0, "ComboBoxEx32", vbNullString)
    ComboBoxwin = FindWindowEx(comboBox32win, 0, "ComboBox", vbNullString)
    EditBox = FindWindowEx(ComboBoxwin, 0, "Edit", vbNullString)
    Debug.Print EditBox
    
    retval = SendMessage(comboBox32win, WM_SETTEXT, vbNullString, "eeName")
    txtlen = SendMessage(EditBox, WM_GETTEXTLENGTH, vbNullString, vbNullString)
    Debug.Print txtlen
    txtlen = txtlen + 1
    txt = Space$(txtlen)
    Call SendMessage(EditBox, WM_GETTEXT, ByVal 260, txt)
    Debug.Print txt
End Sub

Open in new window

Author

Commented:
Ok, the problem was I had not declared the txt as String, because I though VBA was automatically changing it to a string upon assigning the value Space$(txtlen).  I should stop being a lazy programmer.  : )
Anyway, I was able to successfully get the text box value by both methods, but now back to the original problem.  If I post another question will you be available to help me with that?

Author

Commented:
egl1044:

The original problem was that the calls I was making to the ‘save as’ dialog box were occurring immediately after getting the handle to the dialog, box through SaveAsDialog = FindWindow("#32770", "Save As") but apparently before it had completely load.  Such that when it does appear on the screen it did not have the filename I sent and upon clicking save by retval = SendMessage(hwndButton, BM_CLICK, ByVal CLng(0), ByVal CLng(0)) it crashes and closes the browser that opened it.
Therefore, I was attempting to loop until the return value equaled the filename being sent;  but, just as the filename was being accepted before the dialog had finished loading, so to does the return value come back as the filename that was sent and when the dialog does appear it still has the default filename.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial