Как определить, какой процесс запущен, какое окно в Mac OS X?

Я хотел бы знать, возможно ли определить, какой процесс отвечает за создание / управление окном в Mac OS X.

Например, когда запускается несколько экземпляров приложения, как я могу получить идентификатор процесса (PID), соответствующий одному конкретному окну? Или, если есть модальное диалоговое окно без заголовка, как я могу получить PID его владельца?

Я знаю, что в Windows это возможно с помощью инструмента Sysinternals Suite, который предоставляет способ поиска библиотеки, работающей с некоторыми данными.

Я ищу механизм, похожий на тот, который появляется в этом посте.

В этом случае, используя Sysinternals Suite (и Process Explorer), они обнаружили, какая DLL/ программа использовала веб-камеру, выполнив поиск DLL или подстроки (в данном случае, используя физическое имя устройства).

Так есть ли какой-нибудь механизм или программа, или у вас есть идеи о том, как искать что-то подобное для Mac OS X? Как я могу определить, какой процесс запустил окно?

7 ответов

Я использовал скрипт Python. Это не надежно, но для меня это очень хорошо работает.

Я не буду публиковать полный сценарий без разрешения, но вот резюме: он использует CGWindowListCopyWindowInfo, который импортируется из Quartz, чтобы собрать информацию об окне из системы, затем попросит пользователя переместить желаемое окно, затем снова соберет информацию об окне и покажет информацию для тех, которые изменились. Дамп информации включает в себя идентификатор процесса, как 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;
    }
)}

Я сделал инструмент под названием 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')

Существует прямой удобный для пользователя собственный инструмент ОС для проверки каждого окна, который предоставляет исчерпывающую информацию в дополнение к связанному процессу, например, иерархию исходного кода: он находится в инструментах разработчика Xcode и называется Accessibility Inspector

@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)

Для этого вы можете использовать Automator.app:

  1. Start Automator (в центре внимания) Automator.app+ Enter)
  2. Создайте новый рабочий процесс.
  3. В меню выберите Рабочий процесс > Запись (или нажмите кнопку [Запись] на панели инструментов).
  4. Взаимодействуйте с окном, которое вы хотите проверить, а затем нажмите кнопку ⬛️ [Стоп] на панели инструментов записи, когда закончите. (❗️ Если система выводит сообщение об отсутствии доступа специальных возможностей, предоставьте его в системных настройках)
  5. Перетащите записанные интересующие шаги из раздела Watch Me Do на пустую панель справа (во время перетаскивания появится кнопка с плюсом). ПОДСКАЗКА: Вы также можете выбрать все шаги и перетащить их все вместе, чтобы создать единый сценарий.
  6. Просмотрите сгенерированный AppleScript, чтобы получить необходимые сведения.

ПРИМЕЧАНИЕ. Этот метод можно использовать для создания кода AppleScript для поиска окон, диалогов, установки текста, нажатия кнопок и т. Д.

Вот пример видео для записи действий мыши.

Возможно, это не совсем надежно, но, вероятно, это самое простое решение, если и когда оно сработает. Никакого программного обеспечения для установки.

  1. Откройте «Монитор активности» на вкладке «ЦП», отсортированной по убыванию «% ЦП».
  2. Нажмите и удерживайте Tabили нажмите несколько раз.
  3. Следите за тем, какое приложение поднимается наверх списка, и запишите его PID.

Я не уверен, что этот параметр для переключения между элементами управления в разделе «Системные настройки» → «Клавиатура»«Ярлыки» будет учитываться здесь. Я не думаю, что это произойдет, но, как бы то ни было, мой в настоящее время не проверен.

Сценарий echo on обновлен до Python3 для тех, кто в этом нуждается. Я нашел это чрезвычайно полезным. Обратите внимание, что вам необходимо установить Quartz с помощьюpip install pyobjc-framework-Quartzзаранее.

      #!/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')
Другие вопросы по тегам