如何识别哪个进程正在运行 Mac OS X 中的哪个窗口?

Nac*_*gil 41 window process macos

我想知道是否可以确定哪个进程负责在 Mac OS X 中创建/管理窗口。

例如,当启动一个应用程序的多个实例时,如何获取一个特定窗口对应的进程 ID(PID)?或者,如果有一个没有标题的模态对话框窗口,我如何获得其所有者的 PID?

我知道在 Windows 中可以使用Sysinternals Suite工具,该工具提供了一种搜索正在运行某些数据的库的方法。

我正在寻找一种类似于本博文中出现的机制。

在这种情况下,使用 Sysinternals Suite(和 Process Explorer),他们通过搜索 DLL 或子字符串(在这种情况下,使用设备的物理名称)找到了哪个 DLL/程序正在使用网络摄像头。

那么是否有任何机制或程序,或者您是否知道如何为 Mac OS X 搜索类似的东西?如何识别哪个进程启动了一个窗口?

ech*_* on 30

我用过这个 Python 2 脚本。这不是万无一失的,但对我来说效果很好。

下面是它的作用摘要:它使用CGWindowListCopyWindowInfo从 导入的Quartz来从系统收集窗口信息,然后要求用户移动所需的窗口,然后再次收集窗口信息,并显示更改的窗口信息。转储的信息包括进程 ID,如kCGWindowOwnerPID.

这是代码:

#!/usr/bin/env python

import time
from Quartz import CGWindowListCopyWindowInfo, kCGWindowListExcludeDesktopElements, kCGNullWindowID
from Foundation import NSSet, NSMutableSet

wl1 = CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements, kCGNullWindowID)
print 'Move target window'
time.sleep(5)
wl2 = CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements, kCGNullWindowID)

w = NSMutableSet.setWithArray_(wl1)
w.minusSet_(NSSet.setWithArray_(wl2))
print '\nList of windows that moved:'
print w
print '\n'
Run Code Online (Sandbox Code Playgroud)

该脚本打印在 5 秒间隔内改变位置的窗口的信息。所以输出看起来像这样:

List of windows that moved:
{(
        {
        kCGWindowAlpha = 1;
        kCGWindowBounds =         {
            Height = 217;
            Width = 420;
            X = 828;
            Y = 213;
        };
        kCGWindowIsOnscreen = 1;
        kCGWindowLayer = 8;
        kCGWindowMemoryUsage = 406420;
        kCGWindowName = "";
        kCGWindowNumber = 77;
        kCGWindowOwnerName = UserNotificationCenter;
        kCGWindowOwnerPID = 481;
        kCGWindowSharingState = 1;
        kCGWindowStoreType = 2;
    }
)}
Run Code Online (Sandbox Code Playgroud)


小智 24

我做了一个名为的工具 lswin

$ python lswin.py

    PID WinID  x,y,w,h                  [Title] SubTitle
------- -----  ---------------------    -------------------------------------------
    169  1956 {0,-38,1280,25        }   [Window Server] Backstop Menubar
    169  1955 {0,-60,1280,22        }   [Window Server] Menubar
    169   396 {0,-38,1280,25        }   [Window Server] Backstop Menubar
    169   395 {0,-60,1280,22        }   [Window Server] Menubar
    169     6 {0,0,0,0              }   [Window Server] Cursor
    169     4 {0,22,1280,25         }   [Window Server] Backstop Menubar
    169     3 {0,0,1280,22          }   [Window Server] Menubar
    169     2 {0,0,1280,800         }   [Window Server] Desktop
    262   404 {0,-38,1280,38        }   [Google Chrome] 
    262   393 {0,0,1280,800         }   [Google Chrome] 
    262   380 {100,100,1,1          }   [Google Chrome] Focus Proxy
    ... ...
Run Code Online (Sandbox Code Playgroud)

然后您可以使用 grep 来查找您的窗口的 pid。

这是脚本的源代码:

#!/usr/bin/env python

import Quartz

#wl = Quartz.CGWindowListCopyWindowInfo( Quartz.kCGWindowListOptionOnScreenOnly | Quartz.kCGWindowListExcludeDesktopElements, Quartz.kCGNullWindowID)
wl = Quartz.CGWindowListCopyWindowInfo( Quartz.kCGWindowListOptionAll, Quartz.kCGNullWindowID)

wl = sorted(wl, key=lambda k: k.valueForKey_('kCGWindowOwnerPID'))

#print wl

print 'PID'.rjust(7) + ' ' + 'WinID'.rjust(5) + '  ' + 'x,y,w,h'.ljust(21) + ' ' + '\t[Title] SubTitle'
print '-'.rjust(7,'-') + ' ' + '-'.rjust(5,'-') + '  ' + '-'.ljust(21,'-') + ' ' + '\t-------------------------------------------'

for v in wl:
    print ( \
        str(v.valueForKey_('kCGWindowOwnerPID') or '?').rjust(7) + \
        ' ' + str(v.valueForKey_('kCGWindowNumber') or '?').rjust(5) + \
        ' {' + ('' if v.valueForKey_('kCGWindowBounds') is None else \
            ( \
                str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('X')))     + ',' + \
                str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('Y')))     + ',' + \
                str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('Width'))) + ',' + \
                str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('Height'))) \
            ) \
            ).ljust(21) + \
        '}' + \
        '\t[' + ((v.valueForKey_('kCGWindowOwnerName') or '') + ']') + \
        ('' if v.valueForKey_('kCGWindowName') is None else (' ' + v.valueForKey_('kCGWindowName') or '')) \
    ).encode('utf8')
Run Code Online (Sandbox Code Playgroud)

  • 有一个 [pull request](https://github.com/sjitech/mac_list_windows_pids/pull/5) 将代码引入 Python 3。 PR 中的原始文件可以在 [此处](https://raw.githubusercontent .com/sjitech/mac_list_windows_pids/e0dd8a3510d3447f843c7ab8aa229d75c7f99b2f/lswin.py),安装依赖项使用`pip3 install pyobjc-framework-Quartz` (3认同)

小智 13

有一个直接用户友好的操作系统本机工具用于检查每个窗口,除了相关过程之外,它还提供全面的信息,例如原始代码层次结构:它可以在 Xcode 开发人员工具下找到,并称为Accessibility Inspector

  • 聚光灯“辅助功能检查器”将我带到了它。用于选择感兴趣窗口的十字准线。谢谢你!! (3认同)

小智 12

@kenorb 我结合了你的两个版本的脚本,基本上它像第一个版本一样工作,显示差异但格式来自第二个版本。此外,如果窗口不在屏幕上 - 它不会被打印,否则会产生太多垃圾

import Quartz
import time
from Foundation import NSSet, NSMutableSet
def transformWindowData(data):
    list1 = []
    for v in data:
        if not v.valueForKey_('kCGWindowIsOnscreen'):
            continue


        row = ( \
            str(v.valueForKey_('kCGWindowOwnerPID') or '?').rjust(7) + \
            ' ' + str(v.valueForKey_('kCGWindowNumber') or '?').rjust(5) + \
            ' {' + ('' if v.valueForKey_('kCGWindowBounds') is None else \
                ( \
                    str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('X')))     + ',' + \
                    str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('Y')))     + ',' + \
                    str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('Width'))) + ',' + \
                    str(int(v.valueForKey_('kCGWindowBounds').valueForKey_('Height'))) \
                ) \
                ).ljust(21) + \
            '}' + \
            '\t[' + ((v.valueForKey_('kCGWindowOwnerName') or '') + ']') + \
            ('' if v.valueForKey_('kCGWindowName') is None else (' ' + v.valueForKey_('kCGWindowName') or '')) \
        ).encode('utf8')
        list1.append(row)

    return list1;

def printBeautifully(dataSet):
    print 'PID'.rjust(7) + ' ' + 'WinID'.rjust(5) + '  ' + 'x,y,w,h'.ljust(21) + ' ' + '\t[Title] SubTitle'
    print '-'.rjust(7,'-') + ' ' + '-'.rjust(5,'-') + '  ' + '-'.ljust(21,'-') + ' ' + '\t-------------------------------------------'

    # print textList1
    for v in dataSet:
        print v;

#grab initial set
wl = Quartz.CGWindowListCopyWindowInfo( Quartz.kCGWindowListOptionAll, Quartz.kCGNullWindowID)
wl = sorted(wl, key=lambda k: k.valueForKey_('kCGWindowOwnerPID'))

#convert into readable format
textList1 = transformWindowData(wl);

#print everything we have on the screen
print 'all windows:'
printBeautifully(textList1)

print 'Move target window'
time.sleep(5)

#grab window data the second time
wl2 = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListOptionAll, Quartz.kCGNullWindowID)
textList2 = transformWindowData(wl2)

#check the difference
w = NSMutableSet.setWithArray_(textList1)
w.minusSet_(NSSet.setWithArray_(textList2))

#print the difference
printBeautifully(w)
Run Code Online (Sandbox Code Playgroud)

  • 用一点点`pip install pyobjc-framework-Quartz` (2认同)

ccp*_*zza 10

您可以使用Automator.app来执行此操作:

\n
    \n
  1. 启动 Automator(聚光灯类型Automator.app+ Enter
  2. \n
  3. 创建新的工作流程。
  4. \n
  5. 在菜单上选择“工作流程” > “录制”(或单击 工具栏上的“录制”按钮)。
  6. \n
  7. 与您想要检查的窗口进行交互,然后在完成后单击录制工具栏中的\xe2\xac\x9b\xef\xb8\x8f [停止]按钮。(\xe2\x9d\x97\xef\xb8\x8f如果系统提示您缺少辅助功能访问权限,请在“系统设置”中授予它
  8. \n
  9. 将记录的感兴趣的步骤从“Watch Me Do”部分拖到下面的空白面板(拖动过程中会出现一个加号按钮)。提示:您还可以选择所有步骤并将它们全部拖到一起以创建单个脚本。
  10. \n
  11. 检查生成的 AppleScript 以获取您需要的详细信息。
  12. \n
\n
\n

注意您可以使用此方法生成 AppleScript 代码,用于定位窗口、对话框、设置文本、单击按钮等。

\n
\n

这是一个用于记录 UI 操作的随机示例视频。

\n