3

Some of our applications which work fine with different ways of email integration, using mailto:, simulated "Send To...", and SMTP in Windows 2000 and 2003 environments, now move to a new Windows 2008 system with Exchange 2010 and Outlook 2010 clients.

We have one use case where the application creates a new mail, sets recipient(s) and subject, adds one or more attachments and then opens it in the default mail client so it can be edited by the user before sending.

Do you know a solution which works in the new environment? Should we use a third party library? Or is there some OLE automation code available which is known to work, using Outlook.Application?

mjn
  • 36,362
  • 28
  • 176
  • 378
  • Is there a reason why you do not want to let you app handle the edit by the user before you send the message using SMTP? Unless you need all the fancy stuff Outlook provides when editing a message, it would probably be a much simpeler solution? – Marjan Venema Feb 02 '11 at 17:09
  • Editing capabilities of the Outlook mail client are required, adding of email addresses from the addressbook for example. We do not use LDAP / AD integration yet. – mjn Feb 02 '11 at 17:19

5 Answers5

7

We use JclSimpleBringUpSendMailDialog from the jclMapi unit in the Jedi JCL library.

I once had an app where we built in a user option to specify whether they wanted to use SMTP or MAPI and then all sorts of mail server settings but the Jedi library call makes life so much easier. If end users have gone to the trouble of setting up all their settings in a MAPI client then why would they want to set them all up again in my/our software.

The trouble with the mailto:// stuff is that it's often not quite configurable enough or the mail client doesn't handle the parameters in the same/standard way - then users think your software's rubbish rather than believe they have a dodgy mail client.

So we just use the MAPI interface. Easy.

shunty
  • 3,699
  • 1
  • 22
  • 27
  • 1
    This looks great, it would be my favorite solution. Thanks! and +1 – mjn Feb 02 '11 at 17:44
  • I tried it with the current JCL release and get the errors described above. – mjn Feb 03 '11 at 10:57
  • Hmm. Dunno. Works well for us in normal client scenarios and seems ok on a win 2003 server remote desktop. No one has reported it doesn't work but I suspect it isn't used massively. The error report you posted mentions dhcpsvc.dll - maybe there's some kind of name resolution problem going on. As an aside - my code has the jcl call wrapped in a try...except that does nothing(!) with a comment saying that JCL usually brings up an error dialog of its own. So it appears exceptions should be handled ok. Maybe it's a permissions issue on the TS. – shunty Feb 03 '11 at 11:29
  • Also... Do you need to install MAPI components on the server? I know Win 7 doesn't install a lot of the email stuff by default - you have to add stuff from the 'essentials' pack - perhaps this is the case on the server? And... since when did Windows 2010 come out? – shunty Feb 03 '11 at 11:39
  • MAPI is installed, and we have two terminal servers which sometimes succeed in opening the Outlook client and the GUI works fine - and the next try ends with a frozen app and the error above appears. Looks like a network driver (dhcp) problem. Same JCL code, on Windows 7 / 64 Bit, works without errors. – mjn Feb 03 '11 at 16:13
1

I use this unit - it credits Brian Long ages ago...

unit UArtMAPI;


interface



procedure ArtMAPISendMail(
            const Subject, MessageText, MailFromName, MailFromAddress,
                  MailToName, MailToAddress: String;
            const AttachmentFileNames: array of String);


implementation



uses
  SysUtils,
  Windows,
  UArtLibrary,
  Dialogs,
  Forms,
  MAPI;

procedure ArtMAPISendMail(
            const Subject, MessageText, MailFromName, MailFromAddress,
                  MailToName, MailToAddress: String;
            const AttachmentFileNames: array of String);
//Originally by Brian Long: The Delphi Magazine issue 60 - Delphi And Email
var
  MAPIError: DWord;
  MapiMessage: TMapiMessage;
  Originator, Recipient: TMapiRecipDesc;
  Files, FilesTmp: PMapiFileDesc;
  FilesCount: Integer;
begin
   FillChar(MapiMessage, Sizeof(TMapiMessage), 0);

   MapiMessage.lpszSubject := PAnsiChar(AnsiString(Subject));
   MapiMessage.lpszNoteText := PAnsiChar(AnsiString(MessageText));

   FillChar(Originator, Sizeof(TMapiRecipDesc), 0);

   Originator.lpszName := PAnsiChar(AnsiString(MailFromName));
   Originator.lpszAddress := PAnsiChar(AnsiString(MailFromAddress));
//   MapiMessage.lpOriginator := @Originator;
   MapiMessage.lpOriginator := nil;


   MapiMessage.nRecipCount := 1;
   FillChar(Recipient, Sizeof(TMapiRecipDesc), 0);
   Recipient.ulRecipClass := MAPI_TO;
   Recipient.lpszName := PAnsiChar(AnsiString(MailToName));
   Recipient.lpszAddress := PAnsiChar(AnsiString(MailToAddress));
   MapiMessage.lpRecips := @Recipient;

   MapiMessage.nFileCount := High(AttachmentFileNames) - Low(AttachmentFileNames) + 1;
   Files := AllocMem(SizeOf(TMapiFileDesc) * MapiMessage.nFileCount);
   MapiMessage.lpFiles := Files;
   FilesTmp := Files;
   for FilesCount := Low(AttachmentFileNames) to High(AttachmentFileNames) do
   begin
     FilesTmp.nPosition := $FFFFFFFF;
     FilesTmp.lpszPathName := PAnsiChar(AnsiString(AttachmentFileNames[FilesCount]));
     Inc(FilesTmp)
   end;

   try
     MAPIError := MapiSendMail(
       0,
       Application.MainForm.Handle,
       MapiMessage,
       MAPI_LOGON_UI {or MAPI_NEW_SESSION},
       0);
   finally
     FreeMem(Files)
   end;

   case MAPIError of
     MAPI_E_AMBIGUOUS_RECIPIENT:
      Showmessage('A recipient matched more than one of the recipient descriptor structures and MAPI_DIALOG was not set. No message was sent.');
     MAPI_E_ATTACHMENT_NOT_FOUND:
      Showmessage('The specified attachment was not found; no message was sent.');
     MAPI_E_ATTACHMENT_OPEN_FAILURE:
      Showmessage('The specified attachment could not be opened; no message was sent.');
     MAPI_E_BAD_RECIPTYPE:
      Showmessage('The type of a recipient was not MAPI_TO, MAPI_CC, or MAPI_BCC. No message was sent.');
     MAPI_E_FAILURE:
      Showmessage('One or more unspecified errors occurred; no message was sent.');
     MAPI_E_INSUFFICIENT_MEMORY:
      Showmessage('There was insufficient memory to proceed. No message was sent.');
     MAPI_E_LOGIN_FAILURE:
      Showmessage('There was no default logon, and the user failed to log on successfully when the logon dialog box was displayed. No message was sent.');
     MAPI_E_TEXT_TOO_LARGE:
      Showmessage('The text in the message was too large to sent; the message was not sent.');
     MAPI_E_TOO_MANY_FILES:
      Showmessage('There were too many file attachments; no message was sent.');
     MAPI_E_TOO_MANY_RECIPIENTS:
      Showmessage('There were too many recipients; no message was sent.');
     MAPI_E_UNKNOWN_RECIPIENT:
       Showmessage('A recipient did not appear in the address list; no message was sent.');
     MAPI_E_USER_ABORT:
       Showmessage('The user canceled the process; no message was sent.');
     SUCCESS_SUCCESS:
       Showmessage('MAPISendMail successfully sent the message.');
   else
     Showmessage('MAPISendMail failed with an unknown error code.');
   end;
end;




end.
Brian Frost
  • 13,334
  • 11
  • 80
  • 154
  • From the code I can not see if it only sends, or shows the draft mail in Outlook? ... And what is the UArtLibrary unit for? – mjn Feb 03 '11 at 16:10
0

Maybe this is the easiest way to open an email window

System("mailto:someone@example.com?Subject=Hello%20again&body=your%20textBody%20here")
rodolfoprado
  • 126
  • 7
  • This does not file attachments as required in the question: *", adds one or more attachments ..."* – mjn Aug 14 '13 at 11:00
0

Maybe this can be useful for you. But I can't test it, because I'm thunderbird user.

  • I had this code snippet already and tried this code already, on a Windows 7 development system with Outlook 2010 installed. It failed with an error message which I'll post here later. Anyway thanks for the link, it might help others (and I will also try it on the windows 2003 system) – mjn Feb 02 '11 at 16:24
  • Hm, in that case it can't be useful, when there's at least one exception. –  Feb 02 '11 at 16:29
  • As I can see from the code it sends the message - we need a way to display the message editor first, so that the user can modify body, recipients, etc. first before sending. ... mabe the Save method is a workaround, but the user needs to open Outlook and navigate to the draft folder manually then. – mjn Feb 03 '11 at 16:15
  • 1
    @mjn - Instead of `Send` method call the `Display` method of the mail item to display the message before sending. – Sertac Akyuz Feb 12 '11 at 14:09
0

I dug up this example. It's from 2002, so uses an older outlook version, but the idea should still be the same. It was used in an application to send newly registered users their userid and password (don't start me on that, it was just a way to restrict access to a site, nothing sensitive there). The messages are saved in the Outbox, not opened, but if you dig through the OleServer and Outlook8 (number will be higher by now), you should find ways to open the mail for user attention instead of saving it straight to the outbox.

The entire Outlook object model can be found on msdn: http://msdn.microsoft.com/en-us/library/aa221870%28v=office.11%29.aspx

Another good source for information about Office Automation are Deborah Pate's pages: http://www.djpate.freeserve.co.uk/Automation.htm

function TStapWerkt_data.GenerateMailsOutlook(SendDBF: boolean): boolean;
var
  Outlook: TOutlookApplication;
  olNameSpace: NameSpace;
  MailIt: TMailItem;
  AttachedFile: OleVariant;
  i: integer;
  emailaddress: string;
begin
  Result := true;
  Outlook := TOutlookApplication.Create( nil );
  try
    Outlook.ConnectKind := ckNewInstance;
    try
      Outlook.Connect;
      try
        olNameSpace := Outlook.GetNamespace('MAPI');
        olNameSpace.Logon('', '', False, False);
        try

          if SendDBF then begin
            MailIt := TMailItem.Create( nil );
            MailIt.ConnectTo( Outlook.CreateItem( olMailItem ) as MailItem );
            try
              MailIt.Recipients.Add( 'info@bjmsoftware.com' );
              MailIt.Subject := 'StapWerk.dbf';
              MailIt.Body := 'Stap'#13#10;
              AttachedFile := IncludeTrailingBackslash( ExtractFilePath(
                  Stap_db.Stap_Database.DatabaseName ) ) + 'stapwerk.dbf';
              MailIt.Attachments.Add( AttachedFile, EmptyParam, EmptyParam, EmptyParam );
              MailIt.Save;
            finally
              MailIt.Free;
            end;
          end;

          for i := 0 to FNewUsers.Count - 1 do begin
            MailIt := TMailItem.Create( nil );
            MailIt.ConnectTo( Outlook.CreateItem( olMailItem ) as MailItem );
            try
              emailaddress := TStapper( FNewUsers.Items[i] ).Email;
              if emailaddress = '' then begin
                emailaddress := C_MailUnknownAddress;
              end;
              MailIt.Recipients.Add( emailaddress );
              MailIt.Subject := C_MailSubject;
              MailIt.Body := Format( C_MailBody,
                  [TStapper( FNewUsers.Items[i] ).UserId,
                  TStapper( FNewUsers.Items[i] ).Password] );
              MailIt.Save;
            finally
              MailIt.Free;
            end;
          end;

        finally
          olNameSpace.Logoff;
        end;
      finally
        Outlook.Disconnect;
        // We kunnen ondanks ckNewInstance quit niet gebruiken
        // Outlook lijkt de connectkind te negeren en hoe dan ook te koppelen
        // naar een bestaande instantie. En die gooi je dan dus ook dicht met quit.
//        Outlook.quit;
      end;
    finally
      Outlook.free;
    end;
  except
    on E: Exception do begin
      Result := false;
    end;
  end;
end;
Marjan Venema
  • 19,136
  • 6
  • 65
  • 79