vba 确定应用程序是否正在使用 Excel 运行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29807691/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 09:33:32  来源:igfitidea点击:

Determine if application is running with Excel

excelvba

提问by Alex

Goal

目标

Have an Excel file with a "Search" button that opens a custom program. This program is used for researches. If the program is already opened when the user clicks on the button, make it popup and focus on that given program.

有一个带有“搜索”按钮的 Excel 文件,可以打开一个自定义程序。该程序用于研究。如果用户单击按钮时程序已经打开,则使其弹出并专注于该给定程序。

Current Situation

现在的情况

Here's the code I'm trying to use to make it work:

这是我试图用来使其工作的代码:

Search Button

搜索按钮

Private Sub btnSearch_Click()
    Dim x As Variant
    Dim Path As String

    If Not IsAppRunning("Word.Application") Then
        Path = "C:\Tmp\MyProgram.exe"
        x = Shell(Path, vbNormalFocus)
    End If
End Sub

IsAppRunning()

IsAppRunning()

Function IsAppRunning(ByVal sAppName) As Boolean
    Dim oApp As Object
    On Error Resume Next
    Set oApp = GetObject(, sAppName)
    If Not oApp Is Nothing Then
        Set oApp = Nothing
        IsAppRunning = True
    End If
End Function

This code will work only when I put "Word.Application" as the executable. If I try to put "MyProgram.Application" the function will never see the program is running. How can I find that "MyProgram.exe" is currently opened?

仅当我将“Word.Application”作为可执行文件时,此代码才有效。如果我尝试放置“MyProgram.Application”,该函数将永远不会看到程序正在运行。如何找到当前打开的“MyProgram.exe”?

Further more, I'd need to put the focus on it...

此外,我需要把重点放在它上面......

回答by enderland

You can check this more directly by getting a list of open processes.

您可以通过获取打开的进程列表来更直接地检查这一点。

This will search based on the process name, returning true/false as appropriate.

这将根据进程名称进行搜索,并根据需要返回 true/false。

Sub exampleIsProcessRunning()  
    Debug.Print IsProcessRunning("MyProgram.EXE")
    Debug.Print IsProcessRunning("NOT RUNNING.EXE")

End Sub

Function IsProcessRunning(process As String)
    Dim objList As Object

    Set objList = GetObject("winmgmts:") _
        .ExecQuery("select * from win32_process where name='" & process & "'")

    If objList.Count > 0 Then
        IsProcessRunning = True
    Else
        IsProcessRunning = False
    End If

End Function

回答by Alex

Here's how I brought the search window to front:

以下是我将搜索窗口置于最前面的方法:

Private Const SW_RESTORE = 9

Private Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long

Private Sub btnSearch_Click()
    Dim x As Variant
    Dim Path As String

    If IsProcessRunning("MyProgram.exe") = False Then
        Path = "C:\Tmp\MyProgram.exe"
        x = Shell(Path, vbNormalFocus)
    Else
        Dim THandle As Long
        THandle = FindWindow(vbEmpty, "Window / Form Text")
        Dim iret As Long
        iret = BringWindowToTop(THandle)
        Call ShowWindow(THandle, SW_RESTORE)
    End If
End Sub

Now if the window was minimized and the user clicks the search button again, the window will simply pop up.

现在,如果窗口被最小化并且用户再次单击搜索按钮,窗口将简单地弹出。

回答by typedef Miles_Williams MILO

Just want to point out that the Window Text may change when documents are open in the application instance.

只是想指出,在应用程序实例中打开文档时,窗口文本可能会发生变化。

For example, I was trying to bring CorelDRAW to focus and everything would work fine so long as there wasn't a document open in Corel, if there was, I would need to pass the complete name to FindWindow()including the open document.

例如,我试图让 CorelDRAW 成为焦点,只要没有在 Corel 中打开文档,一切都会正常工作,如果有,我需要传递完整名称以FindWindow()包含打开的文档。

So, instead of just:

所以,而不仅仅是:

FindWindow("CorelDRAW 2020 (64-Bit)")

FindWindow("CorelDRAW 2020 (64-Bit)")

It would have to be:

它必须是:

FindWindow("CorelDRAW 2020 (64-Bit) - C:\CompletePath\FileName.cdr")

FindWindow("CorelDRAW 2020 (64-Bit) - C:\CompletePath\FileName.cdr")

As that is what would be returned from GetWindowText()

因为那是返回的内容 GetWindowText()

Obviously this is an issue as you don't know what document a user will have open in the application, so for anyone else who may be coming here, years later, who may be experiencing the same issue, here's what I did.

显然这是一个问题,因为您不知道用户将在应用程序中打开什么文档,因此对于可能在多年后来到这里的其他人,他们可能遇到同样的问题,这就是我所做的。

Option Explicit
Private Module

Private Const EXE_NAME As String = "CorelDRW.exe"
Private Const WINDOW_TEXT As String = "CorelDRAW 2020" ' This is common with all opened documents

Private Const GW_HWNDNEXT = 2
Private Const SW_RESTORE = 9

Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Public Sub FocusIfRunning(parAppName as String, parWindowText as String)

    Dim oProcs As Object
    Dim lWindowHandle As Long
    Dim sWindowText As String
    Dim sBuffer As String

    ' Create WMI object and execute a WQL query statement to find if your application
    ' is a running process. The query will return an SWbemObjectSet.

    Set oProcs = GetObject("winmgmts:").ExecQuery("SELECT * FROM win32_process WHERE " & _
                            "name = '" & parAppName & "'")

    ' The Count property of the SWbemObjectSet will be > 0 if there were
    ' matches to your query.

    If oProcs.Count > 0 Then

        ' Go through all the handles checking if the start of the GetWindowText()
        ' result matches your WindowText pre-file name.
        ' GetWindowText() needs a buffer, that's what the Space(255) is.

        lWindowHandle = FindWindow(vbEmpty, vbEmpty)

        Do While lWindowHandle

            sBuffer = Space(255)
            sWindowText = Left(sBuffer, GetWindowText(lWindowHandle, sBuffer, 255))

            If Mid(sWindowText, 1, Len(parWindowText)) Like parWindowText Then Exit Do

            ' Get the next handle. Will return 0 when there are no more.

            lWindowHandle = GetWindow(lWindowHandle, GW_HWNDNEXT)

        Loop

        Call ShowWindow(lWindowHandle , SW_RESTORE)

    End If

End Sub

Private Sub btnFocusWindow_Click()
    Call FocusIfRunning(EXE_NAME, WINDOW_TEXT)
End Sub

Hopefully somebody gets use from this and doesn't have to spend the time on it I did.

希望有人能从中受益,而不必花时间在我所做的事情上。

回答by Rodney Cuthbertson

Just wanted to say thank you for this solution. Only just started playing around with code and wanted to automate my job a bit. This code will paste current selection in excel sheet into an already open application with as single click. Will make my life so much easier!!

只是想说谢谢你的这个解决方案。才刚刚开始玩代码,并希望让我的工作自动化一点。此代码将通过单击将 Excel 工作表中的当前选择粘贴到已打开的应用程序中。会让我的生活变得更轻松!!

Thanks for sharing

感谢分享

Public Const SW_RESTORE = 9

Public Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Public Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long

Public Sub updatepart()
'
' updatepart Macro
' copies current selection
' finds and focuses on all ready running Notepad application called Test
' pastes value into Notepad document
' Keyboard Shortcut: Ctrl+u
'
Dim data As Range
Set data = Application.Selection
If data.Count <> 1 Then
    MsgBox "Selection is too large"
    Exit Sub
End If

Selection.Copy


If IsProcessRunning("Notepad.EXE") = False Then
    MsgBox "Notepad is down"
Else
    Dim THandle As Long
    THandle = FindWindow(vbEmpty, "Test - Notepad")
    Dim iret As Long
    iret = BringWindowToTop(THandle)
    Call ShowWindow(THandle, SW_RESTORE)
End If
waittime (500)
'Call SendKeys("{F7}")
Call SendKeys("^v", True) '{F12}
Call SendKeys("{ENTER}")

End Sub

Function waittime(ByVal milliseconds As Double)
    Application.Wait (Now() + milliseconds / 24 / 60 / 60 / 1000)
End Function

Function IsProcessRunning(process As String)
Dim objList As Object

Set objList = GetObject("winmgmts:") _
    .ExecQuery("select * from win32_process where name='" & process & "'")

If objList.Count > 0 Then
    IsProcessRunning = True
Else
    IsProcessRunning = False
End If

End Function