简体   繁体   中英

Opening Mail.app From An External Application With An Attachment and Subject

I'd like to open Mail.app and specify a Subject and a File to attach. I can do either independently but not both.

To set the subject I can just form a mailto: string and NSWorkspace openURL that.

To set an attachment I can use

[[NSWorkspace sharedWorkspace] openFile:resolvedPath withApplication:@"Mail"];

I'm not aware of a equivalent to iOS's MFMailComposeViewController for the Mac. What are my options?

NSString* subject = @"mail subject";
NSString* body = @"mail body";
NSString* to = @"recipient@example.org";

NSString *encodedSubject = [NSString stringWithFormat:@"SUBJECT=%@", [subject stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSString *encodedBody = [NSString stringWithFormat:@"BODY=%@", [body stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSString *encodedTo = [to stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *encodedURLString = [NSString stringWithFormat:@"mailto:%@?%@&%@", encodedTo, encodedSubject, encodedBody];
NSURL *mailtoURL = [NSURL URLWithString:encodedURLString];

[[NSWorkspace sharedWorkspace] openURL:mailtoURL];

It can be done fairly easily via AppleScript, the "interesting" part being how to call your AppleScript from your app. This doc should give you what you need: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptingBridgeConcepts/Introduction/Introduction.html

The nice part about AppleScript is that you can use the script editor to find out how you want to control the external app, then code it up in app once that is done.

Edit: The original code this linked to has been removed, so I've copied some of it to this answer instead.

You can use AppleScript (or Apple Events) to drive Mail.app. This has the advantage that there is no limit to the size of attachments you can pass around, and avoids constructing huge URLs.

In our software, we use the following to compile a suitable AppleScript:

- (NSAppleScript *)script
{
  if (script)
    return script;

  NSDictionary *errorInfo = nil;

  /* This AppleScript code is here to communicate with Mail.app */
  NSString *ourScript =
    @"on build_message(sendr, recip, subj, ccrec, bccrec, msgbody,"
    @"                 attachfiles)\n"
    @"  tell application \"Mail\"\n"
    @"    set mailversion to version as string\n"
    @"    set msg to make new outgoing message at beginning of"
    @"      outgoing messages\n"
    @"    tell msg\n"
    @"      set the subject to subj\n"
    @"      set the content to msgbody\n"
    @"      if (sendr is not equal to \"\") then\n"
    @"        set sender to sendr\n"
    @"      end if\n"
    @"      repeat with rec in recip\n"
    @"        make new to recipient at end of to recipients with properties {"
    @"          name: |name| of rec, address: |address| of rec }\n"
    @"      end repeat\n"
    @"      repeat with rec in ccrec\n"
    @"        make new to recipient at end of cc recipients with properties {"
    @"          name: |name| of rec, address: |address| of rec }\n"
    @"      end repeat\n"
    @"      repeat with rec in bccrec\n"
    @"        make new to recipient at end of bcc recipients with properties {"
    @"          name: |name| of rec, address: |address| of rec }\n"
    @"      end repeat\n"
    @"      tell content\n"
    @"        repeat with attch in attachfiles\n"
    @"          make new attachment with properties { file name: attch } at "
    @"            after the last paragraph\n"
    @"        end repeat\n"
    @"      end tell\n"
    @"    end tell\n"
    @"    return msg\n"
    @"  end tell\n"
    @"end build_message\n"
    @"\n"
    @"on deliver_message(sendr, recip, subj, ccrec, bccrec, msgbody,"
    @"                   attachfiles)\n"
    @"  set msg to build_message(sendr, recip, subj, ccrec, bccrec, msgbody,"
    @"                           attachfiles)\n"
    @"  tell application \"Mail\"\n"
    @"    send msg\n"
    @"  end tell\n"
    @"end deliver_message\n"
    @"\n"
    @"on construct_message(sendr, recip, subj, ccrec, bccrec, msgbody,"
    @"                     attachfiles)\n"
    @"  set msg to build_message(sendr, recip, subj, ccrec, bccrec, msgbody,"
    @"                           attachfiles)\n"
    @"  tell application \"Mail\"\n"
    @"    tell msg\n"
    @"      set visible to true\n"
    @"      activate\n"
    @"    end tell\n"
    @"  end tell\n"
    @"end construct_message\n";

  script = [[NSAppleScript alloc] initWithSource:ourScript];

  if (![script compileAndReturnError:&errorInfo]) {
    NSLog (@"Unable to compile script: %@", errorInfo);
    [script release];
    script = nil;
  }

  return script;
}

then you can trigger the functions in the script using code like this

- (NSAppleEventDescriptor *)descriptorForThisProcess
{
  ProcessSerialNumber thePSN = { 0, kCurrentProcess };

  return [NSAppleEventDescriptor descriptorWithDescriptorType:typeProcessSerialNumber
                            bytes:&thePSN
                               length:sizeof (thePSN)];
}

- (BOOL)doHandler:(NSString *)handler
       forMessage:(NSAttributedString *)messageBody
      headers:(NSDictionary *)messageHeaders
{
  NSMutableString *body = [NSMutableString string];
  NSDictionary *errorInfo = nil;
  NSAppleEventDescriptor *target = [self descriptorForThisProcess];
  NSAppleEventDescriptor *event
    = [NSAppleEventDescriptor appleEventWithEventClass:'ascr'
                           eventID:kASSubroutineEvent
                      targetDescriptor:target
                          returnID:kAutoGenerateReturnID
                     transactionID:kAnyTransactionID];
  NSAppleEventDescriptor *params = [NSAppleEventDescriptor listDescriptor];
  NSAppleEventDescriptor *files = [NSAppleEventDescriptor listDescriptor];
  NSString *tmpdir = NSTemporaryDirectory ();
  NSString *attachtmp = nil;
  NSFileManager *fileManager = [NSFileManager defaultManager];
  NSString *sendr = @"", *subj = @"";
  NSAppleEventDescriptor *recip
    = [self recipientListFromString:[messageHeaders objectForKey:@"To"]];
  NSAppleEventDescriptor *ccrec
    = [self recipientListFromString:[messageHeaders objectForKey:@"Cc"]];
  NSAppleEventDescriptor *bccrec
    = [self recipientListFromString:[messageHeaders objectForKey:@"Bcc"]];
  unsigned numFiles = 0;
  NSUInteger length = [messageBody length];
  NSUInteger pos = 0;
  NSRange range;

  if ([messageHeaders objectForKey:@"Subject"])
    subj = [messageHeaders objectForKey:@"Subject"];
  if ([messageHeaders objectForKey:@"Sender"])
    sendr = [messageHeaders objectForKey:@"Sender"];

  /* Find all the attachments and replace them with placeholder text */
  while (pos < length) {
    NSDictionary *attributes = [messageBody attributesAtIndex:pos
                           effectiveRange:&range];
    NSTextAttachment *attachment
      = [attributes objectForKey:NSAttachmentAttributeName];

    if (attachment && !attachtmp) {
      /* Create a temporary directory to hold the attachments (we have to do this
     because we can't get the full path from the NSFileWrapper) */
      do {
    attachtmp = [tmpdir stringByAppendingPathComponent:
                  [NSString stringWithFormat:@"csmail-%08lx",
                    random ()]];
      } while (![fileManager createDirectoryAtPath:attachtmp
                       withIntermediateDirectories:NO
                                        attributes:nil
                                             error:NULL]);
    }

    if (attachment) {
      NSFileWrapper *fileWrapper = [attachment fileWrapper];
      NSString *filename = [attachtmp stringByAppendingPathComponent:
                 [fileWrapper preferredFilename]];
      NSURL *url = [NSURL fileURLWithPath:filename isDirectory:NO];

      [fileWrapper writeToURL:url
                      options:NSFileWrapperWritingAtomic
          originalContentsURL:nil
                        error:NULL];

      [body appendFormat:@"<%@>\n", [fileWrapper preferredFilename]];

      [files insertDescriptor:
    [NSAppleEventDescriptor descriptorWithString:filename]
              atIndex:++numFiles];

      pos = range.location + range.length;

      continue;
    }

    [body appendString:[[messageBody string] substringWithRange:range]];

    pos = range.location + range.length;
  }

  /* Replace any CF/LF pairs with just LF; also replace CRs with LFs */
  [body replaceOccurrencesOfString:@"\r\n" withString:@"\n"
    options:NSLiteralSearch range:NSMakeRange (0, [body length])];
  [body replaceOccurrencesOfString:@"\r" withString:@"\n"
    options:NSLiteralSearch range:NSMakeRange (0, [body length])];

  [event setParamDescriptor:
    [NSAppleEventDescriptor descriptorWithString:handler]
         forKeyword:keyASSubroutineName];
  [params insertDescriptor:
    [NSAppleEventDescriptor descriptorWithString:sendr]
           atIndex:1];
  [params insertDescriptor:recip atIndex:2];
  [params insertDescriptor:
    [NSAppleEventDescriptor descriptorWithString:subj]
           atIndex:3];
  [params insertDescriptor:ccrec atIndex:4];
  [params insertDescriptor:bccrec atIndex:5];
  [params insertDescriptor:
    [NSAppleEventDescriptor descriptorWithString:body]
           atIndex:6];
  [params insertDescriptor:files atIndex:7];

  [event setParamDescriptor:params forKeyword:keyDirectObject];

  if (![[self script] executeAppleEvent:event error:&errorInfo]) {
    NSLog (@"Unable to communicate with Mail.app.  Error was %@.\n",
       errorInfo);
    return NO;
  }

  return YES;
}

- (BOOL)deliverMessage:(NSAttributedString *)messageBody
           headers:(NSDictionary *)messageHeaders
{
  return [self doHandler:@"deliver_message"
          forMessage:messageBody
         headers:messageHeaders];
}

- (BOOL)constructMessage:(NSAttributedString *)messageBody
         headers:(NSDictionary *)messageHeaders
{
  return [self doHandler:@"construct_message"
          forMessage:messageBody
         headers:messageHeaders];
}

The -constructMessage:headers: method, above, will cause Mail to open a new e-mail with everything already filled in, while the -deliverMessage:headers: method will create and send the e-mail in one hit.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM