如何在 Powershell 中获取新创建的 Firefox 窗口的正确 PID?

发布于 2025-01-10 05:33:15 字数 555 浏览 0 评论 0原文

这是我遇到的问题的一个非常简单的示例:

$process = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru

Write-Host $process.Id

firefox 窗口将启动并按预期工作,它将返回一个进程 ID,但当我实际检查正在运行的进程时,我看不到该 PID 的结果。

我尝试添加这个只是为了看看,

while (-not $process.HasExited){
    Write-Host "..."
}

Write-Host $process.HasExited

看起来该进程在退出之前确实运行了几毫秒。

我认为这可能与 Firefox 处理其自身进程的方式有关。因为我用其他一些随机应用程序测试了类似的设置,它们都按预期工作。

关于如何解决 Firefox 的这个问题,您有什么想法吗?

Here is a very simple example of the problem I am experiencing:

$process = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru

Write-Host $process.Id

The firefox window will start and work as expected, it will return a process id, but when I actually check the running processes, I see no results for that PID.

I tried adding this just to see,

while (-not $process.HasExited){
    Write-Host "..."
}

Write-Host $process.HasExited

And it looks like the process does run for maybe a couple milliseconds before it exits.

I'm thinking this may have something to do with how Firefox handles it's own processes. Because I tested a similar setup with some other random apps and they all worked as expected.

Any ideas on how to work around this when it comes to Firefox?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

分開簡單 2025-01-17 05:33:15

有几个挑战需要克服:

  • 最终呈现实际浏览器窗口的 firefox 进程与最初启动的进程不同。也就是说,正如您所观察到的,启动的进程会生成其他进程,并且本身会快速退出。

  • 正如 Olaf 指出的那样,现代浏览器通常会启动多个非瞬态进程,因此挑战在于如何识别代表浏览器窗口的窗口。

  • 浏览器可能重用现有进程,因此单个进程可以呈现多个窗口/选项卡,并且关闭其中一个窗口/选项卡不会终止整个进程。

    • 如果您需要确保使用专用的新流程,您有两种选择:

      • (a) 确保没有任何预先存在的 Firefox 实例正在运行,方法是出错,或者 - 如果您的用例可以接受 - 首先强制终止所有现有实例 (Stop-Process -Name firefox).
      • (b) 付出更多努力,创建一个专用的临时 Firefox 配置文件,您可以使用 -new-instance 选项启动该配置文件,该选项允许多个独立的 Firefox 实例并发运行,并且可以单独跟踪其生命周期。

下面这个繁琐的解决方案实现了选项(b):

  • 如果没有找到 firefox 进程,则不用担心创建独立实例,Firefox 可以正常启动。

  • 否则,将创建一个临时配置文件,并通过 -new-instance-profile 启动 选项,以确保使用进程来呈现新的浏览器窗口。

  • 启动初始进程后,循环直到出现 稍后启动的 firefox 进程并具有非空窗口标题,然后被认为是真正感兴趣的过程。

  • 然后,您可以等待该进程终止,以了解专用浏览器窗口何时关闭。如果必须创建临时配置文件,则会在之后将其清除。

# Comment this statement out to silence the verbose messages below.
$VerbosePreference = 'Continue'

$now = Get-Date
$url = 'https://example.org'  # URL to open.

# Launch a (new) Firefox instance.
if ($alreadyRunning = [bool] (Get-Process -ErrorAction Ignore firefox)) {
  # Determine the path for a temporary profile with a unique name.
  $tempProfilePath = Join-Path ([IO.Path]::GetTempPath()) ([datetime]::utcnow.tostring('o') -replace '\D')
  Write-Verbose  "Firefox is already running. Creating temp. profile $tempProfilePath..."
  # Note: Creating an empty directory for the profile is seemingly enough.
  $null = New-Item -Type Directory $tempProfilePath
  Write-Verbose "and starting a new instance with it..."
  Start-Process firefox "-new-instance -profile $tempProfilePath $url"
} else {
  Write-Verbose 'Firefox isn''t running. Starting normally...'
  Start-Process firefox $url
}

# Find the newly launched process that is the actual browser window.
Write-Verbose 'Waiting for a recently launched Firefox process with a nonempty window title to appear...'
while (-not (
  $ps = Get-Process firefox | 
    Where-Object StartTime -gt $now |
      Where-Object MainWindowTitle
)) {
  Write-Host -NoNewLine . 
  Start-Sleep -MilliSeconds 500 
}
Write-Host

Write-Verbose "Found. Waiting for process to exit..."
$ps.WaitForExit()

Write-Verbose 'Process has exited.'

if ($alreadyRunning) {
  Write-Verbose "Cleaning up temporary profile $tempProfilePath..."
  do {
    # The profile dir. is typically held on to for a little while longer by associated processes that may not have terminated yet.
    Start-Sleep -MilliSeconds 200
    Remove-Item -ErrorAction Ignore -Literalpath $tempProfilePath -Recurse -Force
  }
  while (Test-Path -LiteralPath $tempProfilePath)
}

There are several challenges to overcome:

  • The firefox process that ends up presenting the actual browser window is different from the one that is initially launched. That is, as you've observed, the launched process spawns other processes and itself exits quickly.

  • As Olaf points out, modern browsers typically launch multiple non-transient processes, so the challenge is how to identify the one that represent the browser window.

  • Browsers may reuse existing processes, so a single process can present multiple windows / tabs, and closing one of them won't terminate the process as a whole.

    • If you need to ensure that a dedicated, new process is used, you have two options:

      • (a) Make sure that no preexisting Firefox instance is running, either by erroring out, or - if acceptable for your use case - by forcefully terminating all existing instances first (Stop-Process -Name firefox).
      • (b) With significantly more effort, create a dedicated, temporary Firefox profile that you can launch with the -new-instance option, which allows multiple independent Firefox instances to run concurrently and whose lifetime can be tracked separately.

The following - cumbersome - solution implements option (b):

  • If no firefox process is found, there is no concern about creating independent instances, and Firefox can be launched normally.

  • Otherwise, a temporary profile is created, and launched via the -new-instance and -profile options to ensure that a new process will be used to present the new browser window.

  • After launching the initial process, loop until a firefox process appears that was launched later and has a nonempty window title, which is then presumed to be the real process of interest.

  • You can then wait for the termination of this process to know when the dedicated browser window has been closed. If a temporary profile had to be created, it is cleaned up afterwards.

# Comment this statement out to silence the verbose messages below.
$VerbosePreference = 'Continue'

$now = Get-Date
$url = 'https://example.org'  # URL to open.

# Launch a (new) Firefox instance.
if ($alreadyRunning = [bool] (Get-Process -ErrorAction Ignore firefox)) {
  # Determine the path for a temporary profile with a unique name.
  $tempProfilePath = Join-Path ([IO.Path]::GetTempPath()) ([datetime]::utcnow.tostring('o') -replace '\D')
  Write-Verbose  "Firefox is already running. Creating temp. profile $tempProfilePath..."
  # Note: Creating an empty directory for the profile is seemingly enough.
  $null = New-Item -Type Directory $tempProfilePath
  Write-Verbose "and starting a new instance with it..."
  Start-Process firefox "-new-instance -profile $tempProfilePath $url"
} else {
  Write-Verbose 'Firefox isn''t running. Starting normally...'
  Start-Process firefox $url
}

# Find the newly launched process that is the actual browser window.
Write-Verbose 'Waiting for a recently launched Firefox process with a nonempty window title to appear...'
while (-not (
  $ps = Get-Process firefox | 
    Where-Object StartTime -gt $now |
      Where-Object MainWindowTitle
)) {
  Write-Host -NoNewLine . 
  Start-Sleep -MilliSeconds 500 
}
Write-Host

Write-Verbose "Found. Waiting for process to exit..."
$ps.WaitForExit()

Write-Verbose 'Process has exited.'

if ($alreadyRunning) {
  Write-Verbose "Cleaning up temporary profile $tempProfilePath..."
  do {
    # The profile dir. is typically held on to for a little while longer by associated processes that may not have terminated yet.
    Start-Sleep -MilliSeconds 200
    Remove-Item -ErrorAction Ignore -Literalpath $tempProfilePath -Recurse -Force
  }
  while (Test-Path -LiteralPath $tempProfilePath)
}
旧城空念 2025-01-17 05:33:15

感谢 @mklement0 的工作,在您的情况下,您可以使用父进程 ID。

我使用 WMI 来获取父进程,但它适用于第一次启动。

$parentProcess = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru
$childProcess = get-process -id $(Get-CimInstance -Class Win32_Process -Filter "Name = 'firefox.exe'"  | where {$_.ParentProcessId -eq $parentProcess.id}).ProcessId
# effectively stop the child
$childProcess | Stop-Process

Thanks to @mklement0 work, in your case you can use the parent Process ID.

I use WMI to get the parent process, but it works for the very first launch.

$parentProcess = Start-Process 'C:\Program Files\Mozilla Firefox\firefox.exe' -argumentlist "-new-window https://google.com -foreground" -PassThru
$childProcess = get-process -id $(Get-CimInstance -Class Win32_Process -Filter "Name = 'firefox.exe'"  | where {$_.ParentProcessId -eq $parentProcess.id}).ProcessId
# effectively stop the child
$childProcess | Stop-Process
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文