Returning NSWindow/ AXUIElement using its pid_t - cocoa

Is it possible to get a AXUIElementRef of an open application knowing its PID?
My project is opening a file/folder. Then I get its PID and I want to be able to get access to this open window, so that is why I want AXUIElementRef of it.

extern AXUIElementRef AXUIElementCreateApplication ( pid_t pid);
Creates and returns the top-level accessibility object for the application with the specified process ID.
AXUIElementCreateApplication

Here is my solution as below in Swift 5
let applicationRef = AXUIElementCreateApplication(pid)
var uiElementsValue: AnyObject?
AXUIElementCopyAttributeValue(applicationRef, kAXWindowsAttribute as CFString, &uiElementsValue)
guard let appElements = uiElementsValue as? [AXUIElement] else { return }
'appElements' are your opening windows.

Related

Use Quarz Window Services API in XPC service

I have an App which does screen scrape and window scrape, both are working pretty well in the application, later I decided to move both screen scrape and window scrape into the XPC service which belongs to this App, the display scrape works ok, but window scrape malfunction:
My first try is that the host app pass the CGWindowID to the XPC, in the XPC, I use
CGWindowListCreateImage and CGWindowListCreateImageFromArray, both them always return null.
Then I try to pass the pid of the window to be scrape from the host, and In the XPC
I use CGWindowListCopyWindowInfo to enumerate Windows to match the pid to find the related CGWindowID inside the XPC service process, anyway, CGWindowListCopyWindowInfo always return null
My next try is to use
auto findWindowIds = [](uint32_t pId) -> CFArrayRef
{
auto appRef = AXUIElementCreateApplication(pId);
CFMutableArrayRef idArray = CFArrayCreateMutable(0, 0, nullptr);
CFIndex count = 0;
CFArrayRef windowArray = NULL;
auto err = AXUIElementGetAttributeValueCount(appRef,
CFSTR("AXWindows"), &count);
if (err == kAXErrorSuccess && count)
{
AXUIElementCopyAttributeValues(appRef, CFSTR("AXWindows"), 0, count, &windowArray);
for (int idx = 0; idx < count; idx ++)
{
AXUIElementRef element = (AXUIElementRef)
CFArrayGetValueAtIndex(windowArray, idx);
CGWindowID temp = 0;
_AXUIElementGetWindow(element, &temp);
LOGEX("windowId: %u", temp);
CFArrayAppendValue(idArray, reinterpret_cast<void*>(temp));
}
SAFE_CFRELEASE(windowArray);
}
SAFE_CFRELEASE(appRef);
return idArray;
};
to reverse the Pid to WindowID, the above code works as expected (the CGWindowID is exactly the same as the host app) according what I debugged into the code, anyway,
CGWindowListCreateImage and CGWindowListCreateImageFromArray are still return null.
Since host app is using Quartz Window Services APIs to enumerate windows, the window server is running, and the XPC inherit the host App's GUI security session, beside that,
Quartz Display Streaming are working pretty well inside XPC service, don't why this is happening.
Could we use Quartz Window Service API inside XPC service?
It turns out I need to add joinExistingSession in XPC settings so that it could access Windows Server.

Getting process information of every process

I am trying to create a program that any normal user can run on windows and generate a process list of all processes, including the executable location. I have used CreateToolhelp32Snapshot() to get all process names, pid, ppid. But having issues getting the image path. Everything I do results in pretty much Access Denied.
I have tried ZwQueryInformationProcess, GetProcessImageFileName, etc. and also using OpenProcess to get the handle to each process. I can get the handle by using PROCESS_QUERY_LIMITED_INFORMATION, but any other option doesn't work. I am lost and have been at this for a few days. Can anyone point me in the right direction?
This is the code that works for non-admin user on Windows. Use the szExeFile member of PROCESSENTRY32 to get the path:
HANDLE hProcessSnap = NULL;
HANDLE hProcess = NULL;
PROCESSENTRY32 pe32;
DWORD dwPriorityClass = 0;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap); // clean the snapshot object
return;
}
// Now walk the snapshot of processes, and
// display information about each process in turn
do
{
// do something with the pe32 struct.
// pe32.szExeFile -> path of the file
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);

NSTask : Couldn't posix_spawn: error 13 when launching app

I have a sub-app in my main Swift app. I made it so it's copied automatically in the Resources folder of the main app when building it. That way, I want to be able to launch an instance of the sub-app from the main app.
The thing is, I'm having an error that is hard to debug/find answers about.
Here is my code :
let args = ["--args", "-admin_url", site.url, "-login", site.login, "-pass", site.password]
let helperPath = (NSBundle.mainBundle().pathForResource("App Helper", ofType: "app"))!
let task = NSTask.init()
task.launchPath = helperPath
task.arguments = args
task.launch()
And the error :
[56490:7218926] Couldn't posix_spawn: error 13
I have no idea where to look, what to search for. I don't know what I'm doing wrong.
I'm wondering if the issue is related to the sub-app itself. That sub-app is empty for now. I set Application is Agent to YES. And in MainMenu.xib, I set the Visible at launch option to no.
That sub-app needs to do some work in the background and doesn't need any UI at all.
Thanks !
Don't use NSTask for this, use NSWorkspace:
let helperAppURL = NSBundle.mainBundle().URLForResource("App Helper",
withExtension:"app")!
_ = try? NSWorkspace.sharedWorkspace().openURL(helperAppURL,
options:[.Default],
configuration:[NSWorkspaceLaunchConfigurationArguments :
["--args", "-admin_url", site.url, "-login",
site.login, "-pass", site.password]])
In the above code, for brevity, I ignored the result of the openURL() command, but in reality it can return an instance of NSRunningApplication which represents the task.
To keep track of the instances of your helper app you launch, you could keep references to this NSRunningApplication in an appropriate kind of collection class, and when the time comes, call its terminate() method.
the launch() function is deprecated, using run()
func shell(_ command: String) -> String {
let task = Process()
task.launchPath = "/usr/bin/"
task.arguments = ["-c", command]
let pipe = Pipe()
task.standardOutput = pipe
if #available(macOS 10.13, *) {
try? task.run()
} else {
task.launch()
}
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
return output
}
or using swift-commands
import Commands
Commands.Bash.run("say hello")

how to get windowid from applescript

I am trying to get window id of of every window.
set r to {}
tell application "System Events"
repeat with t in windows of processes
set sid to id of t
set end of r to {title:title of t, id:sid}
end repeat
end tell
r
The above code returns
error "System Events got an error: Can’t get id of item 1 of every window of every process." number -1728 from id of item 1 of every window of every process
How to get the window id of every window?
I wrote a little Objective-C program for you that gets the window owners, window names and window ids:
////////////////////////////////////////////////////////////////////////////////
// windowlist.m
// Mark Setchell
//
// Get list of windows with their WindowOwnerNames, WindowNames and WindowNumbers
//
// Compile with:
// clang windowlist.m -o windowlist -framework coregraphics -framework cocoa
//
// Run with:
// ./windowlist
//
// You can then run "screencapture" to capture that window:
//
// screencapture -l<windowid> -x someFile.[png|jpg|tif]
////////////////////////////////////////////////////////////////////////////////
#include <Cocoa/Cocoa.h>
#include <CoreGraphics/CGWindow.h>
int main(int argc, char **argv)
{
NSArray *windows = (NSArray *)CGWindowListCopyWindowInfo(kCGWindowListExcludeDesktopElements|kCGWindowListOptionOnScreenOnly,kCGNullWindowID);
for(NSDictionary *window in windows){
int WindowNum = [[window objectForKey:(NSString *)kCGWindowNumber] intValue];
NSString* OwnerName = [window objectForKey:(NSString *)kCGWindowOwnerName];
NSString* WindowName= [window objectForKey:(NSString *)kCGWindowName];
printf("%s:%s:%d\n",[OwnerName UTF8String],[WindowName UTF8String],WindowNum);
}
}
Output
./windowlist
Preview:(null):300
Safari:(null):48
Terminal:(null):231
VirtualBox:(null):212
Mail:(null):150
Dropbox:(null):181
Finder:(null):118
Notification Center:(null):83
Google Drive:(null):73
Copy:(null):68
InkServer:(null):49
iTerm:(null):44
Google Drive::69
Copy::66
Dropbox::63
Creative Cloud::57
Spotlight::41
SystemUIServer::33
SystemUIServer:(null):36
SystemUIServer::31
Window Server:Menubar:3
Dock:Dock:23
iTerm:2. bash:190
iTerm:1. bash:336
This is not possible with this code.
In the processes array of System Events the property id of a window is not required to be available in AppleScript, that's the reason why you get the error.
If an application has an AppleScript dictionary and the windows element is provided then all windows have an id property, but not all applications support AppleScript and the non-document based applications don't provide the windows element by default.

Casting struct ProcessSerialNumber to UnsafePointer<Void>

I have a ProcessSerialNumber and want to create a NSAppleEventDescriptor from it, the same way as shown in issue 14 of objc.io. However the constructor expects an UnsafePointer<Void>.
let psn = ProcessSerialNumber(highLongOfPSN: UInt32(0), lowLongOfPSN: UInt32(kCurrentProcess))
let target = NSAppleEventDescriptor(
descriptorType: typeProcessSerialNumber,
bytes: &psn, // <-- this fails
length: sizeof(ProcessSerialNumber)
)
What am I missing to convert it correctly?
Yet another glorious swift error message failure, the real problem is that typeProcessSerialNumber is an Int and the initializer expects a DescType. Use:
let target = NSAppleEventDescriptor(descriptorType: DescType(typeProcessSerialNumber), bytes:&psn, length:sizeof(ProcessSerialNumber))

Resources