【问题标题】:Use FindWindow to hide a WScriptExec window in VBA使用 FindWindow 在 VBA 中隐藏 WScriptExec 窗口
【发布时间】:2013-06-14 18:21:56
【问题描述】:

是的。复杂。
前提:
我正在运行一个需要通过 FTP 检索信息的 Access 数据库。它使用 WScriptExec 对象运行 ftp.exe 并读取 stdOut 以确定创建目录的日期和时间(名称是格式为 'd.yymmdd.hhmmss' 的日期和时间,所以我只需发送 ls d.* 到服务器)。该代码有效,但我希望窗口不显示或至少隐藏得更快。
目标:
通过查找 WScriptExec 窗口的句柄来查找和操作 WScriptExec 窗口(我天生就有 ProcessID,这显然毫无价值)。不,我不想/不能在此应用程序中使用 .Run 并输出到文件。我以后可能需要像这样操作一个窗口,并且想知道如何在没有“使用其他方法”之类的变通方法的情况下进行操作。
我尝试过的:
- FindWindow("Console,MSDOS,pretty much any made up class I could think of since I don't know class types", "C:\WINDOWS\system32\ftp.exe, C:\WINDOWS\system32\cmd.exe, ftp.exe, cmd.exe, pretty much every window title you can imagine") 所有这些都在另一个参数中带有 vbNullString。我尝试过的一切都返回 0。
-FindWindowLike from this link。我尝试过的一切都返回 0。我对其进行了修改以弹出它找到的每个窗口,但没有看到听起来正确的窗口标题。所以我假设标题不是 WScriptExec 命令提示符窗口中显示的标题。
-GetForegroundWindow。返回我的 Access DB 窗口,即使在 AppActivate objExec.ProcessID 之后也是如此。

仅供参考,我是如何调用 WScriptExec 对象的:
Set objExec = objShell.Exec("cmd /c ftp -n ftp.server.location")(我也尝试过不使用cmd /c;两者都有效)

【问题讨论】:

  • moh :( 无法在工作中安装。我会在家里尝试。还有其他建议吗?
  • 我可以使用 vbe 对象在它创建的窗口中循环吗?
  • 你试过了吗:FindWindowEx(0, 0, "ConsoleWindowClass", vbNullString)
  • 刚试过。返回 0。得到 proc explorer 并尝试查看创建的进程树,但我不确定那里有哪些有用的信息。

标签: vba wsh findwindow


【解决方案1】:

FindWindow("ConsoleWindowClass", vbNullString) 如果有足够的延迟以允许窗口加载,则可以工作。

以下代码允许用户隐藏 WScriptExec 对象:

Public Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Dim objShell As Object, objExec As Object
Dim lngWnd As Long

Set objShell = CreateObject("Wscript.Shell")
Set objExec = objShell.Exec("ftp -n server.location.com")

Pause 0.01
lngWnd = FindWindow("ConsoleWindowClass", vbNullString)
ShowWindow lngWnd, 0
MsgBox lngWnd

Pause 是我为延迟程序而编写的代码,值以秒为单位。您可以改用 windows API Sleep 命令,obvi。

此方法的成功测试是是否弹出消息并且控制台窗口不再可见。感谢@mehow 的帮助:)

我看到很多人试图在我的搜索中隐藏他们的 WScriptExec 对象窗口。如果您可以将他们指向此解决方案,它至少会帮助那些希望隐藏窗口以进行长时间活动的人。 那个问题的真正答案是使用 API 手动加载控制台进程。

【讨论】:

    【解决方案2】:

    这个问题可能有点老了,但我认为这个答案可能仍然可以提供帮助。 (用 Excel VBA 测试,用 Access 无法测试)

    与 usncahill 的答案非常相似,但它不会休眠或等待窗口加载,而是会继续循环并查找 hwnd 并在找到后立即执行。

    我下面的脚本从 Exec 对象中获取 ProcessID 来查找窗口的 Hwnd。然后,您可以使用 Hwnd 设置窗口的显示状态。

    根据我对 Excel 2007 VBA 的测试,在大多数情况下,我什至看不到窗口...在某些情况下,它可能会在几毫秒内可见,但只会出现快速闪烁或闪烁...注意:我有使用 SW_MINIMIZE 的结果比我使用 SW_HIDE 的效果更好,但你可以尝试一下。

    我添加了 TestRoutine Sub 以展示如何使用“HideWindow”功能的示例。 'HideWindow' 函数使用 'GetHwndFromProcess' 函数从 ProcessID 中获取窗口 hwnd。

    将以下内容放入模块中...

    Option Explicit
    '   ShowWindow() Commands
    Public Const SW_HIDE = 0
    Public Const SW_MINIMIZE = 6
    'GetWindow Constants
    Public Const GW_CHILD = 5
    Public Const GW_HWNDFIRST = 0
    Public Const GW_HWNDLAST = 1
    Public Const GW_HWNDNEXT = 2
    Public Const GW_HWNDPREV = 3
    Public Const GW_OWNER = 4
    '   API Functions
    Public Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
    Public Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
    Public Declare Function GetDesktopWindow Lib "user32" () As Long
    Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
    
    
    Sub TestRoutine()
        Dim objShell As Object
        Dim oExec As Object
        Dim strResults As String
    
        Set objShell = CreateObject("WScript.Shell")
        Set oExec = objShell.Exec("CMD /K")
        Call HideWindow(oExec.ProcessID)
    
        With oExec
            .StdIn.WriteLine "Ping 127.0.0.1"
            .StdIn.WriteLine "ipconfig /all"
            .StdIn.WriteLine "exit"
            Do Until .StdOut.AtEndOfStream
                strResults = strResults & vbCrLf & .StdOut.ReadLine
                DoEvents
            Loop
        End With
        Set oExec = Nothing
        Debug.Print strResults
    End Sub
    
    
    Function HideWindow(iProcessID)
        Dim lngWinHwnd As Long
        Do
            lngWinHwnd = GetHwndFromProcess(CLng(iProcessID))
            DoEvents
        Loop While lngWinHwnd = 0
        HideWindow = ShowWindow(lngWinHwnd, SW_MINIMIZE)
    End Function
    
    Function GetHwndFromProcess(p_lngProcessId As Long) As Long
        Dim lngDesktop As Long
        Dim lngChild As Long
        Dim lngChildProcessID As Long
        On Error Resume Next
        lngDesktop = GetDesktopWindow()
        lngChild = GetWindow(lngDesktop, GW_CHILD)
        Do While lngChild <> 0
            Call GetWindowThreadProcessId(lngChild, lngChildProcessID)
            If lngChildProcessID = p_lngProcessId Then
                GetHwndFromProcess = lngChild
                Exit Do
            End If
            lngChild = GetWindow(lngChild, GW_HWNDNEXT)
        Loop
        On Error GoTo 0
    End Function
    

    显示窗口功能: http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx

    GetWindow 函数: http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515%28v=vs.85%29.aspx

    GetDesktopWindow 函数: http://msdn.microsoft.com/en-us/library/windows/desktop/ms633504%28v=vs.85%29.aspx

    GetWindowThreadProcessId 函数: http://msdn.microsoft.com/en-us/library/windows/desktop/ms633522%28v=vs.85%29.aspx

    如果您需要有关 API 工作原理的更多信息,可以通过 google 快速搜索为您提供大量信息。

    我希望这可以帮助...谢谢。

    【讨论】:

    • 如果您设法使窗口不出现,即使在某些情况下,响应也比我的要好得多。很好的发现!
    • 很好的答案!我做了一个小调整,以确保最小化的窗口也不会通过将常量 SW_MINIMIZE 更改为 SW_SHOWMINNOACTIVE (7) 来激活。
    猜你喜欢
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 2011-07-08
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多