如何确定Mac OS X中哪个进程正在运行哪个窗口?


28

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

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

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

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

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

那么,有什么机制或程序吗?或者您对如何在Mac OS X中搜索类似内容有任何想法吗?如何确定哪个进程启动了窗口?


“ ...哪个进程正在显示哪个窗口...”与Windows示例“ ...哪个DLL /程序正在通过搜索DLL或子字符串使用网络摄像头的Windows”示例相比,这令人困惑。您能否编辑您的问题以弄清楚。
JakeGould

1
一些进程在没有任何窗口的情况下运行,甚至可能没有控制终端。
Basile Starynkevitch 2015年

Answers:


22

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

我不会在未经许可的情况下重新发布完整脚本,但这是一个摘要:它使用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'

该脚本会在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;
    }
)}

好吧,这并不是我一直在寻找的东西,但这是一个很好的起点。谢谢@echo!
I.Cougil

@echo on-我不确定如何应用该网站显示的内容,您能否详细说明?
C_K 2015年

似乎python脚本的链接已死。我相信,我发现了一个新的博客网站一样张贴在这里:cadebaba.blogspot.com/2014/04/...
马克Ebbert

这是一个了不起的脚本。它帮助我找到了我无法识别的讨厌软件。
Samvel Avanesov

不错!! 实际上,这是识别和删除弹出警报窗口的恶意软件的干净,正确的方法。比安装和运行防病毒程序要好得多,后者知道它本身可能就是恶意软件。
杰里·克里诺克

15

我做了一个名为 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
    ... ...

然后,您可以使用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')

完美运作。感谢您分享@ osexp2003!
干草

10

@kenorb我结合了您的脚本的2个版本,基本上它的工作方式与第一个相似,但显示出差异,但格式来自第二个。另外,如果窗口不在屏幕上-不会被打印,否则会产生过多的垃圾

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)

太棒了 距Mac的xkill更近了一步!
迈克尔·福克斯

2
与一点pip install pyobjc-framework-Quartz
CupawnTae

请注意,该脚本无法在多显示器设置下100%正常运行。如果在一个屏幕上的终端中运行此窗口,然后在另一个屏幕上移动窗口,则差异中将列出许多窗口。它们似乎都是系统窗口和菜单栏中的图标,等等。最好在运行之前将终端和神秘窗口移至同一屏幕。
DaveBurns
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.