目標
# フォーカスされているアプリを取得
get_active_app_path() # "アプリケーションのパス"
# 開いている全てのアプリを取得
get_open_apps() # {"アプリケーション名": "アプリケーションのパス", ...}
環境構築
Windows
pip install pywin32 psutil
Mac
pip install pyobjc
まとめてインストールするにはrequirements.txtを作成します。
requirements.txt
pyobjc; sys_platform == 'darwin'
pywin32; sys_platform == 'win32'
psutil; sys_platform == 'win32'
pip install -r requirements.txt
コード
フォーカスされているアプリ
import sys
if sys.platform == "win32":
import win32gui
import win32process
from psutil import Process, NoSuchProcess
def get_active_app_path() -> str | None:
hwnd = win32gui.GetForegroundWindow()
_, pid = win32process.GetWindowThreadProcessId(hwnd)
if pid <= 0:
return None
try:
path = Process(pid).exe()
except NoSuchProcess:
return None
return path
elif sys.platform == "darwin":
from AppKit import NSWorkspace
def get_active_app_path() -> str:
apps = NSWorkspace.sharedWorkspace().activeApplication()
path = apps["NSApplicationPath"]
return path
開いているアプリ一覧
import sys
if sys.platform == "darwin":
from AppKit import NSWorkspace
def get_open_apps() -> dict[str, str]:
apps = NSWorkspace.sharedWorkspace().launchedApplications()
data = {app["NSApplicationName"]: app["NSApplicationPath"] for app in apps} # 因みに、app["NSApplicationProcessIdentifier"] でプロセスIDを取得できる
return data
elif sys.platform == "win32":
import win32gui
import win32process
import win32con
import ctypes
from ctypes.wintypes import DWORD, RECT
import pythoncom
from win32com.client import GetObject
from psutil import Process
class Win32Window:
def __init__(self, hWnd: int):
self._hWnd = hWnd
@property
def title(self) -> str:
name = win32gui.GetWindowText(self._hWnd)
if isinstance(name, bytes):
name = name.decode()
return name or ""
@property
def path(self) -> str:
_, pid = win32process.GetWindowThreadProcessId(self._hWnd)
return Process(pid).exe()
def getHandle(self) -> int:
return self._hWnd
def _find_window_handles(parent: int | None = None, window_class: str | None = None, title: str | None = None, onlyVisible: bool = True) -> list[int]:
handle_list: list[int] = []
def findit(hwnd: int, _) -> bool:
if window_class and window_class != win32gui.GetClassName(hwnd):
return True
if title and title != win32gui.GetWindowText(hwnd):
return True
if not onlyVisible or (onlyVisible and win32gui.IsWindowVisible(hwnd)):
handle_list.append(hwnd)
return True
if not parent:
parent = win32gui.GetDesktopWindow()
win32gui.EnumChildWindows(parent, findit, None)
return handle_list
def _findMainWindowHandles() -> list[tuple[int, int]]:
class TITLEBARINFO(ctypes.Structure):
_fields_ = [
("cbSize", DWORD),
("rcTitleBar", RECT),
("rgstate", DWORD * 6)
]
def win_enum_handler(hwnd: int, ctx: any):
if not win32gui.IsWindowVisible(hwnd):
return
title_info = TITLEBARINFO()
title_info.cbSize = ctypes.sizeof(title_info)
ctypes.windll.user32.GetTitleBarInfo(hwnd, ctypes.byref(title_info))
isCloaked = ctypes.c_int(0)
DWMWA_CLOAKED = 14
ctypes.windll.dwmapi.DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, ctypes.byref(isCloaked), ctypes.sizeof(isCloaked))
title = win32gui.GetWindowText(hwnd)
if win32gui.IsWindowVisible(hwnd) and title != "" and isCloaked.value == 0:
if not (title_info.rgstate[0] & win32con.STATE_SYSTEM_INVISIBLE):
handle_list.append((hwnd, win32process.GetWindowThreadProcessId(hwnd)[1]))
handle_list: list[tuple[int, int]] = []
win32gui.EnumWindows(win_enum_handler, None)
return handle_list
def _get_all_apps():
pythoncom.CoInitialize() # メインスレッド外で実行する場合のエラー回避
WMI = GetObject("winmgmts:")
mainWindows = [w[1] for w in _findMainWindowHandles()]
return [(p.Properties_("ProcessID").Value, p.Properties_("Name").Value) for p in WMI.InstancesOf("Win32_Process") if p.Properties_("ProcessID").Value in mainWindows]
def __remove_bad_windows(windows: list[int] | None) -> list[Win32Window]:
outList = []
if windows is not None:
for window in windows:
try:
outList.append(Win32Window(window))
except:
pass
return outList
def get_open_apps() -> dict[str, str]:
process_list = _get_all_apps()
result: dict[str, str] = {}
for win in __remove_bad_windows(_find_window_handles()):
pID = win32process.GetWindowThreadProcessId(win.getHandle())
for item in process_list:
appPID = item[0]
appName = str(item[1])
if appPID == pID[1]:
if not appName in result:
result[appName] = win.path # 因みに、win.title でウィンドウのタイトルを取得できる
break
return result
解説
割愛(リクエストがあれば書きます)
謝辞
上記のコードの中には私が書いたものだけではなく、ネットを彷徨って見つけたコードのコピペも紛れています。それらのページを見つけることができなかったので出典がありません。原作者の方ごめんなさい。
もしコピペ元だと思われるページを見つけた方は教えていただけるとありがたいです。