解决网页模态窗口导致VBA程序等待的问题。主要思路就是在弹窗前用VBA创建一个VBS,通过VBS调用此excel中的宏或者直接由VBS处理弹窗。实践中发现office会把这种操作检测为病毒?…..但是WPS是可以正常使用。示例代码见下:
' 需要引用: "Microsoft HTML Object Library" '模拟鼠标点击,用来模拟点击窗口使窗口激活并获得焦点(不是必须的) Public Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, _ ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long) Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long Public Declare Function SetCursorPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long Public Const MOUSEEVENTF_MOVE = &H1 Public Const MOUSEEVENTF_LEFTDOWN = &H2 Public Const MOUSEEVENTF_LEFTUP = &H4 Public Const MOUSEEVENTF_RIGHTDOWN = &H8 Public Const MOUSEEVENTF_RIGHTUP = &H10 Public Const MOUSEEVENTF_MIDDLEDOWN = &H20 Public Const MOUSEEVENTF_MIDDLEUP = &H40 Public Const MOUSEEVENTF_ABSOLUTE = &H8000 Type POINTAPI x As Long y As Long End Type '用于前置窗口,以便鼠标点击(不是必须的) Private Declare Function SetWindowPos Lib "user32" ( _ ByVal hwnd As Long, _ ByVal hWndInsertAfter As Long, _ ByVal x As Long, _ ByVal y As Long, _ ByVal cx As Long, _ ByVal cy As Long, _ ByVal wFlags As Long) As Long Private Const SMTO_ABORTIFHUNG = &H2 Private Const GW_CHILD = 5 Private Const GW_HWNDNEXT = 2 Private Type GUID Data1 As Long Data2 As Integer Data3 As Integer Data4(0 To 7) As Byte End Type Private Declare Function ShowWindow Lib "user32" _ (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _ (ByVal lpClassName As String, ByVal lpWindowName As String) 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 Declare Function GetWindow Lib "user32" _ (ByVal hwnd As Long, ByVal wCmd As Long) As Long Private Declare Function IsWindowVisible Lib "user32" _ (ByVal hwnd As Long) As Boolean Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _ (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long Private Declare Function RegisterWindowMessage Lib "user32" _ Alias "RegisterWindowMessageA" (ByVal lpString As String) As Long Private Declare Function EnumChildWindows Lib "user32" _ (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, lParam As Long) As Long Private Declare Function SendMessageTimeout Lib "user32" _ Alias "SendMessageTimeoutA" ( _ ByVal hwnd As Long, _ ByVal msg As Long, _ ByVal wParam As Long, _ lParam As Any, _ ByVal fuFlags As Long, _ ByVal uTimeout As Long, _ lpdwResult As Long) As Long Private Declare Function ObjectFromLresult Lib "oleacc" ( _ ByVal lResult As Long, _ riid As GUID, _ ByVal wParam As Long, _ ppvObject As Any) As Long 'Given an IE dialog window title, find the window and return a reference ' to the embedded HTML document object Function GetIEDialogDocument(dialogTitle As String) As IHTMLDocument Dim lhWndP As Long, lhWndC As Long, doc As IHTMLDocument 'find the IE dialog window given its title If GetHandleFromPartialCaption(lhWndP, dialogTitle) Then Debug.Print "Found dialog window - " & dialogTitle & "(" & TheClassName(lhWndP) & ")" Debug.Print , "getting the document..." Set doc = IEDOMFromhWnd(lhWndP) '让子窗口前端显示,如果子窗口可以前端显示下面的内容就没有必要 Const HWND_TOPMOST = -1 Const HWND_NOTOPMOST = -2 Const SWP_NOMOVE = &H2 Const SWP_NOSIZE = &H1 Const SWP_NOZORDER = &H4 Const SWP_NOACTIVATE = &H10 Const SWP_SHOWWINDOW = &H40 Const SWP_HIDEWINDOW = &H80 Const SWP_ASYNCWINDOWPOS = &H4000 '设置窗口位置 posX = 600 posY = 300 '最前端显示 SetWindowPos lhWndP, HWND_TOPMOST, posX, posY, 0, 0, SWP_NOSIZE Or SWP_SHOWWINDOW Or SWP_ASYNCWINDOWPOS '模拟鼠标点击页面 Dim Cp As POINTAPI GetCursorPos Cp SetCursorPos posX, posY mouse_event &H2, 0, 0, 0, 0 mouse_event &H4, 0, 0, 0, 0 '取消前端显示,取消激活 SetWindowPos lhWndP, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE Or SWP_SHOWWINDOW Or SWP_NOACTIVATE Else Debug.Print "Window '" & dialogTitle & "' not found!" End If Set GetIEDialogDocument = doc End Function ' 函数:IEDOMFromhWnd。 ' ' 返回:一个 WebBrowser 窗口的 IHTMLDocument 对象接口。 ' ' hWnd 参数:WebBrowser 控件的句柄或 WebBrowser 控件所在窗口的句柄。 ' Function IEDOMFromhWnd(ByVal hwnd As Long) As IHTMLDocument Dim IID_IHTMLDocument As GUID Dim hWndChild As Long Dim lRes As Long Dim lMsg As Long Dim hr As Long If hwnd <> 0 Then If Not IsIEServerWindow(hwnd) Then ' 查找一个 WebBrowser 控件。 EnumChildWindows hwnd, AddressOf EnumChildProc, hwnd End If ' 注册消息。 lMsg = RegisterWindowMessage("WM_HTML_GETOBJECT") ' 获取对象的指针。 Call SendMessageTimeout(hwnd, lMsg, 0, 0, _ SMTO_ABORTIFHUNG, 1000, lRes) If lRes Then ' 初始化接口 ID。 With IID_IHTMLDocument .Data1 = &H626FC520 .Data2 = &HA41E .Data3 = &H11CF .Data4(0) = &HA7 .Data4(1) = &H31 .Data4(2) = &H0 .Data4(3) = &HA0 .Data4(4) = &HC9 .Data4(5) = &H8 .Data4(6) = &H26 .Data4(7) = &H37 End With ' 利用指针 lRes 获取 IHTMLDocument 对象。 hr = ObjectFromLresult(lRes, IID_IHTMLDocument, 0, IEDOMFromhWnd) End If End If End Function 'utilty function for getting the classname given a window handle Function TheClassName(lhWnd As Long) Dim strText As String, lngRet As Long strText = String$(100, Chr$(0)) lngRet = GetClassName(lhWnd, strText, 100) TheClassName = Left$(strText, lngRet) End Function Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, _ ByVal sCaption As String) As Boolean Dim lhWndP As Long, sStr As String GetHandleFromPartialCaption = False lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW Do While lhWndP <> 0 sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0)) GetWindowText lhWndP, sStr, Len(sStr) sStr = Left$(sStr, Len(sStr) - 1) If Len(sStr) > 2 Then If UCase(sStr) Like "*ARG*" Then Debug.Print sStr End If If InStr(1, sStr, sCaption) > 0 Then GetHandleFromPartialCaption = True lWnd = lhWndP Exit Do End If lhWndP = GetWindow(lhWndP, GW_HWNDNEXT) Loop End Function Private Function IsIEServerWindow(ByVal hwnd As Long) As Boolean Dim lRes As Long Dim sClassName As String ' 初始化缓冲区大小。 sClassName = String$(255, 0) ' 获取 hWnd 句柄拥有者的类名称。 lRes = GetClassName(hwnd, sClassName, Len(sClassName)) sClassName = Left$(sClassName, lRes) IsIEServerWindow = StrComp(sClassName, _ "Internet Explorer_Server", _ vbTextCompare) = 0 End Function Function EnumChildProc(ByVal hwnd As Long, lParam As Long) As Long If IsIEServerWindow(hwnd) Then lParam = hwnd Else EnumChildProc = 1 End If End Function '通过创建vbs来运行表格里面的宏,用于解决网页模态窗口导致宏暂停问题 Private Sub vbsRunMacro(xlFullName As String, xlMacroName As String, parameter As String) Dim vbsFullName As String, res As String vbsFullName = Environ("temp") & "\temp.vbs" Open vbsFullName For Output As #1 Print #1, "Set xlApp = GetObject(, ""Excel.Application"")" 'GetObject直接调用已经打开的thisworkbook里的宏 Print #1, "xlApp.Visible = True" Print #1, "For Each xlBook In xlApp.Workbooks" Print #1, "If xlBook.FullName = """ & xlFullName & """ Then" Print #1, "Set oWorkbooks = xlBook" Print #1, "xlApp.Run """ & xlMacroName & """, """ & parameter & """" '根据需要调整宏的名称" ' Print #1, "xlApp.Quit" Print #1, "End If" Print #1, "Next" Print #1, "Set oWorkbooks= nothing" Print #1, "Set xlApp= nothing" Close #1 ' ShellAndWait vbsFullName Shell "wscript.exe " & vbsFullName ' '延时等待bat执行后删除文件 Application.Wait (Now + TimeValue("0:00:01")) Kill vbsFullName End Sub '通过创建vbs来发送按键,用于解决网页alert窗口导致宏暂停问题 '解决ASP.net从服务器端向客户端弹出alert对话框,这类弹窗无法通过重写js alert来处理 Private Sub vbsSendKeys(oKeys As String) Dim vbsFullName As String vbsFullName = Environ("temp") & "\temp.vbs" Open vbsFullName For Output As #1 Print #1, "set wshshell = createobject(""wscript.shell"")" Print #1, "wscript.sleep 3500" Print #1, "wshshell.sendkeys " & oKeys Print #1, "Set wshshell= nothing" Close #1 ' ShellAndWait vbsFullName Shell "wscript.exe " & vbsFullName '延时等待bat执行后删除文件 Application.Wait (Now + TimeValue("0:00:01")) Kill vbsFullName End Sub Private Sub DialogPerson(vendor_person As String) Const DLG_TITLE = "窗口title" '<< the dialog title Dim doc As IHTMLDocument Application.Wait (Now + TimeValue("0:00:01")) '等待弹窗 Set doc = GetIEDialogDocument(DLG_TITLE) If Not doc Is Nothing Then doc.getElementById("txtName").Value = vendor_person '<<填写子页面弹窗内的表单 ...... vbsSendKeys """{enter}""" '调用vbs发送回车键关闭子窗口的alert弹窗 doc.getElementById("btnSave").Click '点击按钮,调出alert弹窗 Else Debug.Print "Dialog Window '" & DLG_TITLE & "' was not found!", vbOKOnly + vbExclamation End If Set doc = Nothing End Sub '填写表单的程序 Private Sub enterPage(vendor_person As String, ...) myURL = "http://...." With CreateObject("internetexplorer.application") .Visible = True .navigate myURL Do Until .readyState = 4: DoEvents: Loop Application.Wait (Now + TimeValue("0:00:01")) '等待确保页面完全打开 vbsRunMacro ThisWorkbook.FullName, "DialogPerson", vendor_person '调用vbs打开子页面弹窗 ...... End With End With End Sub ’主程序 Sub main() enterPage Cells(i, "A")... End Sub
发表回复