【问题标题】:Controlling OSX windows控制 OSX 窗口
【发布时间】:2010-12-16 09:29:49
【问题描述】:

我正在尝试从我的应用程序控制外部 OSX 应用程序的窗口。我想要 到 1.移动屏幕上的窗口 2.调整屏幕上的窗口大小 3.更改应用程序当前活动的窗口 4. 获取当前活动窗口。

(我想通过 ObjC/C/C++ api 来实现)。

考虑到我有想要控制的窗口的 CGWindowID,我应该寻找哪些 API 调用?也就是说,我希望找到具有以下签名的函数:MoveWindow(CGWindowID winId, int x, int y)ResizeWindow(CGWindowID winId, int width, int height)Activatewindow(CGWindowID winId)CGWindowID GetCurrentlyActivatedWindow()

对于 3,我已经在使用 SetFrontProcess 将一个进程拉到前面,但这并不能让我选择一个进程的特定窗口(如果它有多个)。

【问题讨论】:

    标签: objective-c cocoa macos macos-carbon


    【解决方案1】:

    我认为您应该解释为什么要这样做。据我所知,移动所有其他应用程序窗口的唯一实用程序是 Spaces 和 Expose,它们都是由 Apple 提供的。如果你想占据整个屏幕,有公共 API 可以解决这个问题,但移动另一个应用的窗口听起来很可疑。

    【讨论】:

    • 我想要自动化第 3 方应用程序。为此,我需要能够获取应用程序的当前状态并向其发送鼠标/键盘事件。 ResizeWindow 将帮助我通过屏幕抓取获取当前状态,并且需要 ActivateWindow 能够可靠地将鼠标/键盘事件发送到应用程序。 GetCurrentlyActivatedWindow 将用于带回原始活动窗口,因此自动化工作不会尽可能多地干扰用户。 MoveWindow 只是开发它的“好东西”。
    • 好的,既然如此,您应该查看 Accessibility/Scripting API。任何 Cocoa 应用程序都具有一定程度的可脚本性,这可能与您的需要一样多。
    【解决方案2】:

    根据您的评论,您可以使用 OS X 辅助功能 API 执行此操作,但我认为必须在用户的辅助功能首选项中打开“辅助设备访问权限”。

    现在有一个第三方共享软件应用程序,它的名字让我无法理解,它可以让您使用键盘命令移动任何窗口(我认为可以调整它的大小)。

    【讨论】:

    • 是的,就是 MercuryMover。 :-)
    【解决方案3】:

    我是新来的,因此必须将其作为答案而不是评论提交。我编写了一个 .NET 应用程序 (PlaceMint) 在 Windows 中正是这样做的,而且一点也不可疑。 PlaceMint 帮助您以用户定义的结构化方式组织窗口。它所做的只是根据用户定义的规则移动或调整窗口大小。有人问我是否可以移植到 OSX,但我从来没有走得太远,因为我找不到像 Windows 中提供的 API 定义(像 SetWindowPos() 一样编组对 user32.dll 的 dll 调用)。

    【讨论】:

      【解决方案4】:

      这样做的一种方法确实是使用可访问性 API。

      我正在开发的一个应用程序就是这样做来获取前窗、前窗的文档路径和许多其他属性。

      我这样做的方式是通过 AppleScript。它有时可能很笨拙,但似乎相当可靠。我使用AppScript 从我的 Cocoa 应用程序中发送 AppleScript。它是线程安全的,并且比其他替代方案更稳定 - Scripting Bridge 或 NSAppleScript。

      难点在于使用 AppleScript 中的窗口 ID 来识别窗口 - AppleScript 似乎没有与 CGWindowID 匹配的窗口 ID 属性。但是,您可以使用 AppleScript 获得任何您想要的窗口。

      1. 将最前面的窗口移动到 100、100

        tell application "System Events"
         set theprocess to the first process whose frontmost is true
         set thewindow to the value of attribute "AXFocusedWindow" of theprocess
            set position of thewindow to {100, 100}
        end tell
        
      2. 将最前面的窗口大小调整为 200、300

        tell application "System Events"
          set theprocess to the first process whose frontmost is true
          set thewindow to the value of attribute "AXFocusedWindow" of theprocess
             set size of thewindow to {200, 300}
        end tell
        
      3. 更改最前面应用程序的当前窗口

        tell application "System Events"
          set theprocess to the first process whose frontmost is true
          set thewindow to the value of attribute "AXFocusedWindow" of theprocess
             set size of thewindow to {200, 300}
             windows of theprocess
             -- Code to get ID of window you want to activate
             tell window 2 of theprocess -- assuming it's window 2
                   perform action "AXRaise"
             end tell
        end tell
        
      4. 处于活动状态的窗口

        tell application "System Events"
         set theprocess to the first process whose frontmost is true
         set thewindow to the value of attribute "AXFocusedWindow" of theprocess
        end tell
        

      有一个可用于 AppScript 的应用程序,名为 ASTranslate,它将将此 AppleScript 转换为调用 AppScript 中相关命令的 Objective C 代码。

      有关如何获取窗口大小和边界的更多信息(据我所知,这些是只读的)请参阅Son of Grab 示例应用程序。

      【讨论】:

      • 这些代码示例在几个方面帮助了我,因为我想实现与 Sami 相同的目标。 :) 非常感谢!
      • 没问题。很高兴能提供帮助 - 这个社区为我的问题提供了很多很好的答案,很高兴能够提供一些反馈。
      • 有没有一种方法可以只聚焦窗口,而不需要辅助功能?我的用例是,我的应用程序具有焦点,它列出了所有正在运行的应用程序,在该列表中选择一个应用程序将使它成为焦点。目前,如果我使用activateWithOptions:,它不会取消最小化最小化的窗口,所以如果所有窗口都最小化它什么都不做。所以我试图找到另一种集中注意力的方法(最小化)
      【解决方案5】:

      需要在系统偏好设置中启用辅助功能才能正常工作。它是applescript,但可以在objective-c 中与脚本桥一起使用。

      -- Moves safari window by deltaposition
      tell application "System Events"
          tell application "Safari"
              set win to first window
              set b to bounds of win
              set deltaposition to {50, 0}
              set bounds of first window to {(item 1 of b) + (item 1 of deltaposition), (item 2 of b) + (item 2 of deltaposition), (item 3 of b) + (item 1 of deltaposition), (item 4 of b) + (item 2 of deltaposition)}
          end tell
      end tell
      

      【讨论】:

        【解决方案6】:

        使用 CGWindowListCopyWindowInfo 获取每个窗口的 kCGWindowOwnerPID。然后,如果你想访问 ObjectiveC 的东西,你可以使用“分布式对象”,或者使用 Mach RPC 来访问其他的东西。所有这些都记录在http://developer.apple.com

        想要向其他应用程序发送消息有很多很好的理由 - 例如,在开发既不是鼠标也不是键盘的新颖用户界面时。

        【讨论】:

        • 嗨,bgri,请您提供一些示例代码。我使用CGWindowListCopyWindowInfo 来获取所有kCGWindowOwnerPIDs,但我不知道如何通过它来聚焦窗口。
        猜你喜欢
        • 2021-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-30
        • 1970-01-01
        相关资源
        最近更新 更多