4

I was wondering if it is possible to pass arguments between Mac applications and if it possible, how.

I know that in Java is possible using the following syntax from the command line:

java JavaApp arg1 arg2 arg3 arg4

And that is possible to access those through the main's args[] array.

public static void main(String[] args) {
        System.out.println("d");
        for (int i = 0; i < args.length; i++)
            System.out.println(args[i]);
}

Edit: I want to pass the arguments from a command line Mac application to a Cocoa Mac application

Eimantas
  • 48,927
  • 17
  • 132
  • 168
Alex Salom
  • 3,074
  • 4
  • 22
  • 33
  • Only URL schemes and notifications via `NSDistributedNotificationCenter` comes to mind. – Eimantas May 15 '12 at 14:09
  • 2
    The example code you show simply prints out the command line arguments of the java program - this can be done in any language on any platform. But your question mentions passing arguments from a command line Mac program to a cocoa Mac program. However cocoa is just a framework for writing apps .. so it is not clear what you actually want to do. Are you writing both programs? Or are you trying to control an existing program? Can you give a more specific example of what you want to do under OSX? – Peter M May 15 '12 at 14:28
  • I am working at a company that has some command line tool to talk to a database. That command line tool is very big and very crappy. At some conditions it justs launches an exception which I catch but I don't seem to be able to set the program into a proper state. They want a very fast fix so I built a temporary Restarter app that is called from the `@catch` statement at the command line and justs stops the command line tool, fixes the database then restarts the command line tool. From the command line tool I need to send the name of a database's field ID for the Restarter to fix the database. – Alex Salom May 15 '12 at 14:47

4 Answers4

3

It's not clear to me why your Restarter app needs to be a Cocoa application. However, on the assumption that it does, command line arguments are available in the argument domain of the NSUserDefaults. This link shows how to invoke the program from the command line and how to specify the default name.

The Cocoa way to kick off a separate process is to use NSTask.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
1

If both the command line and GUI applications are written in Objective-C you could potentially use NSDistributedNotificationCenter to send notifications between the processes. Documentation for NSDistributedNotificationCenter can be found in the Notification Programming Guide.

Alternatively, Cocoa GUI applications accept command line parameters in the same way as any other C program, i.e. the argv and argc parameters of the main subroutine.

mttrb
  • 8,297
  • 3
  • 35
  • 57
  • Both mttrb and JeremyP answers helped me. Since my bosses wanted a very fast fix for their command line tool (I didn't write that damn tool), I was forced to build an external program today while I rewrite all the tool's code bit by bit. As for tonight, I will leave it working with the `NSDistributedNotificationCenter` solution but tomorrow I plan to look deeper into [JeremyP's](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/UserDefaults/AboutPreferenceDomains/AboutPreferenceDomains.html#//apple_ref/doc/uid/10000059i-CH2-96930) solution. Thanks again both of you. – Alex Salom May 15 '12 at 15:51
1

The following is a category I wrote on NSWorkspace that allows for an argv type array of arguments to be passed to the resulting app.

@interface NSWorkspace (MDAdditions)
- (BOOL)launchApplicationAtPath:(NSString *)path
                      arguments:(NSArray *)argv
                          error:(NSError **)outError;
@end

@implementation NSWorkspace (MDAdditions)

- (BOOL)launchApplicationAtPath:(NSString *)path
                      arguments:(NSArray *)argv
                          error:(NSError **)outError {
    NSParameterAssert(path != nil);
    BOOL success = YES;
    if (outError) *outError = nil;
    FSRef itemRef;
    OSStatus status = FSPathMakeRef((const UInt8 *)[path UTF8String], &itemRef, NULL);
    if (status != noErr) {
        if (anError) *anError = [NSError errorWithDomain:NSOSStatusErrorDomain
                        code:status userInfo:nil];
        return NO;
    }
    LSApplicationParameters appParameters = {0, kLSLaunchDefaults, &itemRef,
         NULL, NULL, (argv ? (CFArrayRef)argv : NULL), NULL };
    status = LSOpenApplication(&appParameters, NULL);
    if (status != noErr) {
         NSLog(@"LSOpenApplication() returned %d for %@", (int)status, path);
         if (outError) *outError = [NSError errorWithDomain:NSOSStatusErrorDomain
                        code:status userInfo:nil];
         return NO;
    }
    return YES;
}
@end

UPDATED:

As mentioned in the answers to Accessing command-line arguments in Objective-C, the "_" prefix of the _NSGetArgv() and _NSGetArgc() functions is usually a sign that they are private and should be avoided if an alternative is available (which there is).

To get the args passed to the executable, you can use NSProcessInfo's -arguments method as in the following code:

NSArray *argv = [[NSProcessInfo processInfo] arguments];
NSArray *args = [argv subarrayWithRange:NSMakeRange(1, argv.count - 1)];
NSLog(@"args == %@", args);
Community
  • 1
  • 1
NSGod
  • 22,699
  • 3
  • 58
  • 66
  • Is there a way to get the params in -- (void)applicationDidBecomeActive:(NSNotification *)aNotification ? I am getting the param in - (void)applicationDidFinishLaunching:(NSNotification *)aNotification but not in applicationDidBecomeActive. If it is windows reopen scenario, we need the param in this event. – Ramaprasad Upadhyaya Mar 05 '14 at 15:34
0

All your answers worked properly for me, but I found another solution that suits better my needs. I needed to launch a Cocoa app from a Command Line Tool, which I achieved with the following line:

system("nohup /PATH/Arguments.app/Contents/MacOS/Arguments argument1 argument2 &");

nohup is unix service that allows you to attach processes to itself so if you close the terminal window, the process remains alive.

Next problem that came along was capturing the arguments from within the Cocoa app. "How would I get the arguments from AppDelegate.m if main.m is the one that receives them and just returns an int."

Among Apple's frameworks and libraries I found one that exactly solved the problem. This library is called crt_externs.h and contains two useful variables, one to learn the number of arguments, and another one to obtain the arguments themselves.

extern char ***_NSGetArgv(void);
extern int *_NSGetArgc(void);

So, inside at the AppDelegate's from the Cocoa app we would write the following code to parse the arguments into NSString's:

char **argv = *_NSGetArgv();
NSString *argument1 = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding];
NSString *argument2 = [NSString stringWithCString:argv[2] encoding:NSUTF8StringEncoding];

As we can see we skip directly to the position 1 of the arguments array since position 0 contains the path itself:

argv[0] = '/PATH/Arguments.app/Contents/MacOS/Arguments'
argv[1] = 'argument1'
argv[2] = 'argument2'

Thank you all for your time and help. I learned a lot from you guys. I also hope this answer helps someone else :)

Cheers and happy coding!

Alex Salom
  • 3,074
  • 4
  • 22
  • 33