【问题标题】:Programmatically enter text into any application以编程方式将文本输入任何应用程序
【发布时间】:2014-09-15 23:32:52
【问题描述】:

是否有一个概念验证的 Objective-C 可执行文件,它使用 Apple 事件而不是 AppleScript 将一些文本输入应用程序,然后单击鼠标?

例如AppleScript 等价于

tell application "System Events"
 tell process "Safari"
  keystroke "Hello World"
  click
 end tell 
end tell

它应该可以在 Mac OS X 10.9 上运行,最好是面向未来的(向后兼容性无关紧要)。上下文是我将从另一种语言调用 Objective-C 代码。

我这么说是因为我读到了:

从 Mac OS X 10.7 开始,低级 Cocoa API (NSAppleEventDescriptor) 仍然缺乏基本功能(例如发送 Apple 事件),而高级 Cocoa API(脚本桥)缺陷太大, 仅限于作为 appscript 样式包装器的可行基础。

和:

NSAppleScript 只能在主线程上安全使用

所以,我的目标是:

  1. 任何应用程序(按名称或当前)
  2. 任何键盘输入或鼠标
  3. 来自 C 或 Objective-C
  4. 几百毫秒内

谢谢!

【问题讨论】:

    标签: objective-c cocoa scripting-bridge appleevents nsappleeventdescriptor


    【解决方案1】:

    CoreGraphics 框架中的 CGEvent API https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html> 允许您将低级鼠标和键盘事件发布到窗口服务器,而不是使用 AppleEvents。

    #include <CoreGraphics/CoreGraphics.h>
    
    NSArray *launchedApplications = [[NSWorkspace sharedWorkspace] launchedApplications]; // depreciated but I couldn't find a modern way to get the Carbon PSN
    NSPredicate *filter = [NSPredicate predicateWithFormat:@"NSApplicationName = \"TextEdit\""];
    NSDictionary *appInfo = [[launchedApplications filteredArrayUsingPredicate:filter] firstObject];
    ProcessSerialNumber psn;
    psn.highLongOfPSN = [[appInfo objectForKey:@"NSApplicationProcessSerialNumberHigh"] unsignedIntValue];
    psn.lowLongOfPSN = [[appInfo objectForKey:@"NSApplicationProcessSerialNumberLow"] unsignedIntValue];
    
    CGEventRef event1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true); // 'z' key down
    CGEventRef event2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false); // 'z' key up
    
    CGEventPostToPSN(&psn, event1);
    CGEventPostToPSN(&psn, event2);
    

    您还可以考虑编写一个服务https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/SysServices/introduction.html>,它允许您通过应用程序菜单中的服务菜单向其他应用程序提供功能。请注意,您甚至可以为服务菜单项分配键盘快捷键。服务通过系统粘贴板工作;如果您只需要能够将一些固定或生成的数据粘贴到另一个应用程序中,这种方法可能比处理原始窗口服务器事件更容易。

    【讨论】:

    • 谢谢,我开始运行了。这似乎是正确的 API。如何发送鼠标点击?
    • 使用CGEventCreateMouseEvent()。请参阅CGEvent.h(文件菜单 > 快速打开...是您的朋友)或上面链接的文档。
    【解决方案2】:

    实现结果的最佳方法是使用 Automator,

    https://developer.apple.com/library/mac/documentation/AppleApplications/Conceptual/AutomatorConcepts/AutomatorConcepts.pdf

    如果你想通过ObjectiveC实现这一点,你需要了解“分布式对象架构”。 通过配对 NSPort 和 NSInvocation,你可以做一些令人惊奇的事情,比如跨进程和跨机器的方法调用。

    这是一个指南
    https://developer.apple.com/librarY/prerelease/mac/documentation/Cocoa/Conceptual/DistrObjects/Concepts/architecture.html

    【讨论】:

      【解决方案3】:

      我不确定这是否是您要查找的内容,但您可能对设置 NSInvocation 对象感兴趣:

      - (void)invokeWithTarget:(id)anObject
      

      如果您希望运行一些代码并“模拟”一个 UX 环境,保存调用并运行它可能很有价值。

      (自动机?)

      【讨论】:

      • 对 Objective-C 来说还是个新手,但是你将如何使用它向某个应用程序对象发送一些文本消息?
      • 这里的答案对于查看调用是否适合您的情况非常有帮助:stackoverflow.com/questions/313400/nsinvocation-for-dummies
      • 是的,我读到了,因此我发表了评论。似乎是一般的消息传递,而不是在其他一些过程中将特定的 Apple 事件消息发送到特定的应用程序对象。
      • 你看过 Automator 吗?我想我会问。
      • 是的,但我想以编程方式构建这些操作,并直接访问 Objective-C。
      猜你喜欢
      • 1970-01-01
      • 2013-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多