WinAPI Sendmessage()

hatman

Well-known Member
Joined
Apr 8, 2005
Messages
2,664
After exhausting all avenues to get direct access to our vaulted drawing database, I have been told that the only access that anyone needs is through the online Search Engine. They just don't want to acknowledge that anyone might want to automate the process. That leaves me with the option of letting people do this manually or automating a session of Explorer to perform all of the navigations.

Trying to automate IE...

I got most of it to work, until I added the curve ball of "What if the user is not logged into the system already?" The answer is that IE gets directed to a login screen. So I figured out how to detect that possibility. I built a userform that gets the login info, and populates the fields in the IE Form. I figured out how to Click the Submit Button.

What I can't figure out is how to handle the Security Alert dialog that pops up. It's a standard internet security warning dialog telling the user thet they are about to send secure information that could be hijacked, blah, blah, blah. This window does not seem to be the active IE window... in fact I don't think it is an IEFrame class, so I can't manipulate it with the IE object in my code. Sendkeys won't work because by this point the active window is Excel again (becaus eof the userform that popped up). So I fell back on SendMEssage() from the WinAPI. I figure I can either send the "Enter" key or the "Y" key, but niether seems to work.

I'm stumped. Here is my code up to the SendMessage call... HELP!

Code:
Global flag As Boolean

Private Const VK_RETURN = &HD

Private Declare Function SendMessage _
    Lib "user32" Alias "SendMessageA" _
    (ByVal hwnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    lParam As Any) As Long
    
    
Private Declare Function FindWindow _
    Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long

Sub Doc_Search()

    'reference: Microsoft Internet Controls
    'reference: Microsoft HTML Object Library
    
    Dim mIE As InternetExplorer
    Dim Item_link As HTMLLinkElement
    Dim Item_form As HTMLFormElement
    Dim doc As HTMLDocument
    
        
    Set mIE = New InternetExplorer
    
    mIE.Visible = True
    
    mIE.Navigate "http://none of your business"

    Do Until mIE.readyState = READYSTATE_COMPLETE
    
    Loop
    
    Set doc = mIE.Document
    
    For Each Item_form In doc.forms

        If Item_form.Name = "login" Then
        
            UserForm1.Show
            
            If flag Then
            
                Set doc = Nothing
                mIE.Quit
                Set mIE = Nothing
                Exit Sub
                
            End If
        
            For Each Item In Item_form
            
                If Item.Name = "USER" Then
                
                    Item.Value = UserForm1.TextBox1.Value
                    
                ElseIf Item.Name = "PASSWORD" Then
                
                    Item.Value = UserForm1.TextBox2.Value
                    
                ElseIf Item.Name = "login" Then
                
                    Item.Click
                    
                    Exit For
                    
                End If
                
            Next Item
                        
            a = mIE.hwnd
            
            b = FindWindow(vbNullString, "Security Alert")
            
            SendMessage b, VK_RETURN, 0, 0
 

Excel Facts

Using Function Arguments with nested formulas
If writing INDEX in Func. Arguments, type MATCH(. Use the mouse to click inside MATCH in the formula bar. Dialog switches to MATCH.
Hi Hatman. Work with this. The function is only for flexibility. If you know the exact caption, you could replace the vbNullString argument with the button's caption and accomplish this in several lines. I would just stick with the function. Also, you may want to include code that distinguishes a specific dialog amongst other that may be showing simultaneously. You could do this by searching the text in the Static class and or the window text and or simply using the GetForegroundWindow API function if you are certain that the dialog will be the foreground window.

Code:
Option Explicit

Private Declare Function SendMessage _
    Lib "user32" Alias "SendMessageA" _
    (ByVal hwnd As Long, _
    ByVal wMsg As Long, _
    ByVal wParam As Long, _
    lParam As Any) As Long
    
Private Declare Function FindWindow _
    Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, _
    ByVal lpWindowName As String) As Long


Private Declare Function FindWindowEx _
    Lib "user32" Alias "FindWindowExA" _
    (ByVal hWnd1 As Long, _
    ByVal hWnd2 As Long, _
    ByVal lpsz1 As String, _
    ByVal lpsz2 As String) As Long
    
Private Declare Function SetForegroundWindow _
    Lib "user32" Alias "" _
    (ByVal hwnd As Long) As Long
    
Private Declare Function GetWindowText _
    Lib "user32" Alias "GetWindowTextA" _
    (ByVal hwnd As Long, _
    ByVal lpString As String, _
    ByVal cch As Long) As Long
    
Private Declare Function GetWindowTextLength _
    Lib "user32" Alias "GetWindowTextLengthA" _
    (ByVal hwnd As Long) As Long

Private Const BM_CLICK = &HF5
Private Const BM_SETSTATE = &HF3
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Const MK_LBUTTON = &H1

Sub ExampleUsage()
    CloseDialog "Yes"
End Sub

Function CloseDialog(ButtonText As String) As Boolean
    Dim DialogHwnd As Long
    Dim ButtonHwnd As Long
    Dim ControlText As String
    
    DialogHwnd = FindWindow("#32770", vbNullString)
    
    'get the handle of the first button
    ButtonHwnd = FindWindowEx(DialogHwnd, 0, "Button", vbNullString)
    ControlText = String(GetWindowTextLength(ButtonHwnd) + 1, Chr$(0))
    GetWindowText ButtonHwnd, ControlText, Len(ControlText)
    
    'loop in zorder
    Do Until InStr(ControlText, ButtonText) <> 0
        ButtonHwnd = FindWindowEx(DialogHwnd, ButtonHwnd, "Button", vbNullString)
        ControlText = String(GetWindowTextLength(ButtonHwnd) + 1, Chr$(0))
        GetWindowText ButtonHwnd, ControlText, Len(ControlText)
        If ButtonHwnd = 0 Then Exit Function
    Loop
    
    'special considerations when clicking "Yes" or "Ok"
    'a BM_CLICK alone may not close the dialog when clicking on "Yes" or "Ok"
    'also, the dialog window may need to have focus in order for this to work
    'this is built in security to prevent code from automatically closing
    'dialogs.  Because of this behavior, you will not be able to step through
    'your code to test this.  If the VBA IDE has focus, the dialog may not
    'close.  The behavior will depend on your version of Windows and IE
    SetForegroundWindow DialogHwnd
    SendMessage ButtonHwnd, BM_CLICK, 0, 0
    SendMessage ButtonHwnd, BM_SETSTATE, 1, 0
    SendMessage ButtonHwnd, WM_LBUTTONDOWN, MK_LBUTTON, 0
    SendMessage ButtonHwnd, WM_LBUTTONUP, MK_LBUTTON, 0
End Function
 
Upvote 0
Thank you Right_Click: that looks promising. I'll need to chew on it for a bit and get back to you. Of highest importance is UNDERSTANDING what your code is doing... a couple of Function Calls I haven't used before (man, I feel like a kid in a candy store... )
 
Upvote 0
SLICK!

I had to modify the declaration of SetForegroundWindow to eliminate the Alias "" as it generated a n Error 453: Function not found in library.

I also had to take your advice about distiguishing the dialog from others... apparently the Userform gets caught in this net for some reason. I have a few theories, but quite frankly, I don't care, as I have other ways to distinguish the proper dialog.

The SetForegroundWindow actually came in handy elsewhere also. So I guess I should thank you TWICE!

Thanx! This was just EXACTLY what I needed. My biggest hang-up making it work is that the code runs faster than the window refresh, so I found the need to add some Do..Loops to slow the code until various windows were created... until I figured that one out, I had a tough time figuring out why this wasn't working (can't manipulate a window if FindWindow returns 0 because no windows exist yet that satisfy the parameters).

The thing that pleases me MOST about this is that this all works even if I leave the .visible property of the IE Window set to false... so this is all invisible to the user until I set the property to True on the final "screen". I was a little bit worried that the SetForegroundWindow would either not work with invisible windows, or would toggle the property so the user would be able to see it. Not the case (he he he he!!)
 
Upvote 0

Forum statistics

Threads
1,213,560
Messages
6,114,304
Members
448,564
Latest member
ED38

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top