Link to home
Start Free TrialLog in
Avatar of velcrow
velcrow

asked on

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

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)
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

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()?
Avatar of velcrow
velcrow

ASKER

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
Avatar of velcrow

ASKER

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.
Avatar of velcrow

ASKER

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
*Shot in the dark*

Try using PostMessage() instead of SendMessage().
Avatar of velcrow

ASKER

tried that too
Avatar of velcrow

ASKER

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.
Is the actual edit field getting updated with your desired value?  (...and it's just not getting returned back?)
Avatar of velcrow

ASKER

that is correct.
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: https://www.experts-exchange.com/questions/24693216/Urgent-FindWindow-issue-can't-find-the-handle-of-the-2nd-popup-that-is-triggered-from-1st.html#25223150
(read point #1 in my post at the very top too)
Avatar of velcrow

ASKER

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.
Avatar of velcrow

ASKER

I'm not having a problem posting to the edit box, I'm having a problem reading it.
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
ASKER CERTIFIED SOLUTION
Avatar of nffvrxqgrcfqvvc
nffvrxqgrcfqvvc

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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...
Avatar of velcrow

ASKER

eql1044,

Fantastic, your code worked.  Any idea why SendDlgItemMessageW works and SendMessage to the specific control does not?
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?
Avatar of velcrow

ASKER

here is my declaration for SendMessage
Avatar of velcrow

ASKER

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
I know I seen that one but everything like your constants and FindWindow() + variable :)
Avatar of velcrow

ASKER

   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)
Avatar of velcrow

ASKER

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
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.
Avatar of velcrow

ASKER

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.
Sure but only if you give Idle_Mind some points. Share the wealth :)D
Haha...no worries bud.  Lots of fish in the sea...  =)
Avatar of velcrow

ASKER

I changed to Byval, but I still get nothing.
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)
Avatar of velcrow

ASKER

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.
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

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

Avatar of velcrow

ASKER

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?
Avatar of velcrow

ASKER

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.