ScriptingBridge without sdef? (cocoa) - objective-c

I'd like to get properties of the currently active app. I understand that this should be possible with ScriptingBridge, however, this seems to require you generate an sdef file and import this in your project for the app you are trying to target. Since I want to target all apps, is there another way to do this?
Example of accessing system preferences:
SystemPreferencesApplication *systemPreferences =
[SBApplication
applicationWithBundleIdentifier:#"com.apple.systempreferences"];
If there's another way to access properties of any active app, please do share. (For example; window title)
Thanks.

I assume you want to run an applescript. The scripting bridge is good if you have a lot of applescript code to run. However if you only have a small amount then a simpler way is with NSApplescript.
For example if you wanted to run this applescript...
tell application "System Events"
set theProcesses to processes
repeat with aProcess in theProcesses
tell aProcess to get properties
end repeat
end tell
Then you can write it this way...
NSString* cmd = #"tell application \"System Events\"\nset theProcesses to processes\nrepeat with aProcess in theProcesses\ntell aProcess to get properties\nend repeat\nend tell";
NSAppleScript* theScript = [[NSAppleScript alloc] initWithSource:cmd];
NSDictionary* errorDict = nil;
NSAppleEventDescriptor* result = [theScript executeAndReturnError:&errorDict];
[theScript release];
if (errorDict) {
NSLog(#"Error:%# %#", [errorDict valueForKey:#"NSAppleScriptErrorNumber"], [errorDict valueForKey:#"NSAppleScriptErrorMessage"]);
return;
}
// do something with result
NSLog(#"result: %#", result);

You can get a list of every currently running Application with
NSWorkSpace.sharedWorkspace.runningApplications;
Each object in that array should be an NSRunningApplication, which you can query and manipulate freely.

Related

macOS accessibility API on WebKit applications with AXTextMarker

I need to access data from webkit applications such as Safari, Mail and maybe others. I can see in the Accessibility Inspector there is :AXTextMarker and AXTextMarkerForRange.
I tried the usual way to get this info :
AXUIElementRef systemWideElement = AXUIElementCreateSystemWide(); //creating system wide element
AXUIElementRef focussedElement = NULL;
AXError error = AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute, (CFTypeRef *)&focussedElement); //Copy the focused
if (error != kAXErrorSuccess){
NSLog(#"Could not get focused element");
}else{
AXValueRef marker = NULL;
AXError getTextValueError = AXUIElementCopyAttributeValue(focussedElement, kAXMarkerUIElementsAttribute , (CFTypeRef *)&marker);
}
kAXMarkerUIElementsAttribute is the only thing I can see with Marker but everything is empty each time.
I guess for security reasons, I cannot access them? Is there any way possible. I am developing an app for people with difficulties reading and it could really help.
Thanks
Tips and tricks:
Ask focussedElement for its supported attributes. Use the functions:
AXError AXUIElementCopyAttributeNames(AXUIElementRef element, CFArrayRef _Nullable *names);
Returns a list of all the attributes supported by the specified accessibility object.
and
AXError AXUIElementCopyParameterizedAttributeNames(AXUIElementRef element, CFArrayRef _Nullable *names);
Returns a list of all the parameterized attributes supported by the specified accessibility object.
Most undocumented attributes are self explanatory.
For example get the selected text as attributed string:
CFTypeRef markerRange = NULL;
AXError error = AXUIElementCopyAttributeValue(focussedElement, (CFStringRef)#"AXSelectedTextMarkerRange", &markerRange);
CFTypeRef result = NULL;
error = AXUIElementCopyParameterizedAttributeValue(focussedElement, (CFStringRef)#"AXAttributedStringForTextMarkerRange", markerRange, &result);
Try this, I haven't tested this but I found some research on the API and I think this may solve your problem.
It seems for the accessibility API to fully work the application needs to be trusted or basically have root access. Is the application running in root?
I found some apple script from a past project. seemed relevant
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:#"do shell script \"%# %#\" with administrator privileges", #"/usr/sbin/lsof",#"-c filecoord"];
NSLog(#"Running... %#",script);
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
NSAppleEventDescriptor * eventResult = [appleScript executeAndReturnError:&errorInfo];
This apple script isn't excatily what you need but its a start. I'm not sure how to fully request root access to an application from inside the application its self,
But for testing, you can always just run your application as root and continue your debugging
Please let me know if this helped you in any way or not
Accessibility apps need to be added in System Preferences in the following section:
System Preferences -> Security & Privacy -> Privacy tab -> Accessibility (on the left)
Note that during development you need to include Xcode in this list if you are developing/running within Xcode.
I didn't test out your exact code, but I tested some comparable swift code and that worked fine provided Xcode was added in that spot in system preferences.

OSAKit.framework usage for executing scripts

I am on XCode 9.2, Objective-C, MacOS
i am looking for any example on using osakit.framework to execute a script file(applescript = .scpt or .applescript) or a method in that file with parameters and how to get the response.
My own implementation of applescript works but leaks too much memory so i want to try osakit.framework but its documentation is bad.
The simple implementation is like this
OSAScript *scriptNAME= [[OSAScript alloc] initWithSource:#"tell application \"Firefox\" to return name of window 1"];
NSDictionary * errorDict = nil;
NSAppleEventDescriptor * returnDescriptor = [scriptNAME executeAndReturnError: &errorDict];
NSLog(#"%#", returnDescriptor);
But instead of the script as source in text form i want to load my script file.
Edit:
I tried it with
OSAScript *scriptNAME= [[OSAScript alloc] initWithContentsOfURL:myScriptURL error:&error];
The script gets loaded but i can't call any method.
I Have two simple methods
on test()
display dialog "Hello World"
end
on testWithArguments:arg
display dialog (arg's item 1)
end
The call for the second method i tried:
[scriptNAME executeHandlerWithName:#"testWithArguments" arguments:#[#"Hello World"] error:&errorDict];
-- doesnt work
[scriptNAME executeHandlerWithName:#"testWithArguments:" arguments:#[#"Hello World"] error:&errorDict];
-- doesnt work
Bug found.
It was a wrong configured applescript file. Was declared as a script object from a former test. As simple applescript file it works great.

NSAppleEventDescriptor huge memory leak, obj-c

NSDictionary *error = nil;
//AppleScript to get all running windows
NSAppleScript *appleScriptFindWindows = [[NSAppleScript alloc] initWithSource:
#"tell application \"System Events\" to get the title of every window of process \"TestWindow\" whose name contains \"Black\" end tell"];
while (true) {
#autoreleasepool {
//Execute and get the result of the OSAScript
NSAppleEventDescriptor *result = [appleScriptFindWindows executeAndReturnError:&error];
//Convert the result to a string
NSString *windowNames = [NSString stringWithFormat:#"%#", result];
error = nil;
sleep(0.25);
}
}
I know I am not currently doing anything with result but I will do once I have the issue fixed.
I will be monitoring various windows/files using applescript on a continuous loop, however I have noticed that when I run this code my memory usage skyrockets at 12mb/s and energy impact is high. I cannot release or de-alloc AppleEventDescriptor because of arc.
Is there a way to release the event descriptor or perhaps I am missing something in the applescript itself to correctly exit after execution?
I am a bit lost on this one and being new to obj-c I am wondering if there is a better way to execute applescript within obj-c if that is the issue.

Window Constantly Wants To Be On Top Of Others - Xcode

My app has buttons that open automator workflows like this:
- (IBAction)actionname:(id)sender {
NSTaskname = [[NSTask alloc] init];
[NSTaskname setLaunchPath:#"/usr/bin/automator"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects:#"/Applications/appname.app/Contents/Resources/workflowname.workflow", nil];
[NSTaskname setArguments:arguments];
[NSTaskname launch];
}
The only problem is, that every single one appears behind the window of my app. Also, one workflow launches another app which also appears behind the window.
How can I fix this?
You can probably use NSRunningApplication to bring your NSTask process to the front with its PID like this...
NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier:[NSTaskname processIdentifier]];
[app activateWithOptions: NSApplicationActivateAllWindows];
And if you need to activate a specific application, for example your workflow that launches another app, then you could do this using the application's bundle identifier. This example will activate Safari.
NSArray* apps = [NSRunningApplication runningApplicationsWithBundleIdentifier:#"com.apple.Safari"];
[(NSRunningApplication*)[apps objectAtIndex:0] activateWithOptions: NSApplicationActivateAllWindows];

Is there a way to programmatically connect to a remote server from Cocoa?

Is there an Coca/obj-C API call to mimic the "Connect to Server" action in Finder? It's possible with Automater, so it seems like Finder has a hook somewhere.
Turns out there's an old Carbon function (can't find a Cocoa equivalent) called FSMountServerVolumeSync which does what I was looking for. You can supply and smb:// URL and login credentials.
File Manager Reference
OSStatus FSMountServerVolumeSync (
CFURLRef url,
CFURLRef mountDir,
CFStringRef user,
CFStringRef password,
FSVolumeRefNum *mountedVolumeRefNum,
OptionBits flags
);
An easy way is to just run some applescript code. I'll show you 2 choices. This first one is the standard way to show that Finder window from applescript.
NSString* cmd = #"choose URL";
The resulting window is bare-bones though, so you can actually open the Finder's window with this command...
NSString* cmd = #"tell application \"Finder\" to activate\ndelay 0.2\ntell application \"System Events\" to keystroke \"k\" using command down";
After choosing either of the "cmd" strings, you can execute that applescript code with this...
NSAppleScript* theScript = [[NSAppleScript alloc] initWithSource:cmd];
[theScript executeAndReturnError:nil];
[theScript release];
This might not be the best way, but can't you just use mount?