using MAPI to send to a BCC address

This is a question that has been asked at least 3 times on EE, but the answer has been so vague that future idiots like me cannot understand them:
http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_20625310.html

I am trying to add a BCC field to an email generated using MAPI.


Below you will find the code used to add a recipient to the MAPI_TO part of the email.
I need to keep this, and ADD a recipient to the MAPI_BCC part of the email.

I can't seem to do both.

if (RecipientEmail <> '') then
    begin
    //to field
      lpRecipient.ulRecipClass := MAPI_TO;
      if (RecipientName = '') then
        lpRecipient.lpszName := PChar(RecipientEMail)
      else
        lpRecipient.lpszName := PChar(RecipientName);
      lpRecipient.lpszAddress := PChar(RecipientEmail);
      lpRecipient.ulReserved := 0;
      lpRecipient.ulEIDSize := 0;
      lpRecipient.lpEntryID := nil;
      nRecipCount := 1;
      lpRecips := @lpRecipient;

Open in new window

LVL 13
rfwoolfAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

rfwoolfAuthor Commented:
For your reference here is the full SendMail (MAPI) procedure:

function SendMail(const Subject, Body, FileName,
                  SenderName, SenderEMail,
                  RecipientName, RecipientEMail: string): Integer;
var
  Message: TMapiMessage;
  lpSender, lpRecipient: TMapiRecipDesc;
  FileAttach: TMapiFileDesc;
 
  SM: TFNMapiSendMail;
  MAPIModule: HModule;
begin
  FillChar(Message, SizeOf(Message), 0);
  with Message do
  begin
    if (Subject <> '') then
      lpszSubject := PChar(Subject);
 
    if (Body <> '') then
      lpszNoteText := PChar(Body);
 
    if (SenderEmail <> '') then
    begin
      lpSender.ulRecipClass := MAPI_ORIG;
      if (SenderName = '') then
        lpSender.lpszName := PChar(SenderEMail)
      else
        lpSender.lpszName := PChar(SenderName);
      lpSender.lpszAddress := PChar(SenderEmail);
      lpSender.ulReserved := 0;
      lpSender.ulEIDSize := 0;
      lpSender.lpEntryID := nil;
      lpOriginator := @lpSender;
    end;
 
    if (RecipientEmail <> '') then
    begin
    //to field
      lpRecipient.ulRecipClass := MAPI_TO;
      if (RecipientName = '') then
        lpRecipient.lpszName := PChar(RecipientEMail)
      else
        lpRecipient.lpszName := PChar(RecipientName);
      lpRecipient.lpszAddress := PChar(RecipientEmail);
      lpRecipient.ulReserved := 0;
      lpRecipient.ulEIDSize := 0;
      lpRecipient.lpEntryID := nil;
      nRecipCount := 2;
      lpRecips := @lpRecipient;
    end
    else
      lpRecips := nil;
 
    if (FileName = '') then
    begin
      nFileCount := 0;
      lpFiles := nil;
    end
    else
    begin
      FillChar(FileAttach, SizeOf(FileAttach), 0);
      FileAttach.nPosition := Cardinal($FFFFFFFF);
      FileAttach.lpszPathName := PChar(FileName);
 
      nFileCount := 1;
      lpFiles := @FileAttach;
    end;
  end;
 
  MAPIModule := LoadLibrary(PChar(MAPIDLL));
  if MAPIModule = 0 then
    Result := -1
  else
    try
      @SM := GetProcAddress(MAPIModule, 'MAPISendMail');
      if @SM <> nil then
      begin
        Result := SM(0, Application.Handle, Message, MAPI_DIALOG or MAPI_LOGON_UI, 0);
      end
      else
        Result := 1;
    finally
      FreeLibrary(MAPIModule);
    end;
 
  if Result <> 0 then
    MessageDlg('Error sending mail (' + IntToStr(Result) + ').', mtError,
[mbOK], 0);
end;

Open in new window

0
Russell LibbySoftware Engineer, Advisory Commented:

What you should keep in mind is that the Message.lpRecips is not really a pointer to a single recipient, but to an array of recipients. Most of the time you will see the @TMapiRecipDesc variable being set (in which case the nRecipCount should be ONE, not two), and thats fine but it is only setting a single recipient description. To handle more recipients (cc and bcc only differ in the value set for ulRecipClass), you can do the following:

var
 MyRecips:  Array [0..5] of TMapiRecipDesc;

which is fine if the quantity is known, or:
 
type
  PRecipDescArray   =  ^TRecipDescArray;
  TRecipDescArray   =  Array [0..MaxWord-1] of TMapiRecipDesc;

var
 lpRecips:   PRecipDescArray;

and then allocate memory for lpRecips at runtime. Please note that the same holds for the lpFiles field of the message, which is a pointer to an array of TMapiFileDesc.

Anyways, below is a demo class that wraps all this up and allows you to set multiple recipients as well as files. Let me know if you have questions.

Russell

---

unit SendMailObj;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  SendMailObj
//   Author      :  rllibby
//   Date        :  02.06.2008
//   Description :  Object wrapper to simplify the building and sending of an
//                  email.
//
////////////////////////////////////////////////////////////////////////////////
interface

{ Example usage

  with TSendMail.Create do
  begin
     try
        SenderName:='Foo Bar';
        SenderEmail:='foo@bar.com';
        Subject:='This is a test';
        Body:='Hello world!';
        AddRecipient('Jane Doe', 'jane.doe@somewhere.com');
        AddRecipient('John Smith', 'john.smith@somewhere.com', rtCC);
        AddRecipient('John Doe', 'john.doe@somewhere.com', rtBCC);
        AddFile('c:\somefile.txt');
        ShowMessage(IntToStr(Send));
     finally
        Free;
     end;
  end;

}

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes, Forms, Mapi;

////////////////////////////////////////////////////////////////////////////////
//   Data types
////////////////////////////////////////////////////////////////////////////////
type
  PRecipDescArray   =  ^TRecipDescArray;
  TRecipDescArray   =  Array [0..MaxWord-1] of TMapiRecipDesc;
  PFileDescArray    =  ^TFileDescArray;
  TFileDescArray    =  Array [0..MaxWord-1] of TMapiFileDesc;

////////////////////////////////////////////////////////////////////////////////
//   TSendMail
////////////////////////////////////////////////////////////////////////////////
type

  // Recipient types
  TRecipientType    =  (rtNormal, rtCC, rtBCC);

  // Send mail object
  TSendMail         =  class(TObject)
  private
     // Private declarations
     FSenderName:   String;
     FSenderEmail:  String;
     FSubject:      String;
     FBody:         String;
     FPointers:     TList;
     FFiles:        TStringList;
     FRecipNames:   TStringList;
     FRecipEmails:  TStringList;
  protected
     // Protected declarations
     function       AddRefMem(P: Pointer): Pointer;
     procedure      ClearRefMem;
  public
     // Public declarations
     constructor    Create;
     destructor     Destroy; override;
     function       Send(ReceiptRequest: Boolean = False): Integer;
     procedure      AddFile(FileName: String);
     procedure      AddRecipient(RecipientName, RecipientEmail: String; RecipientType: TRecipientType = rtNormal);
     procedure      ClearFiles;
     procedure      ClearRecipients;
     property       Body: String read FBody write FBody;
     property       SenderName: String read FSenderName write FSenderName;
     property       SenderEmail: String read FSenderEmail write FSenderEmail;
     property       Subject: String read FSubject write FSubject;
  end;

// function SendMail(const Subject, Body, FileName, SenderName, SenderEMail, RecipientName, RecipientEMail: String): Integer;

implementation

//// TSendMail /////////////////////////////////////////////////////////////////
function TSendMail.Send(ReceiptRequest: Boolean = False): Integer;
var  lpMessage:     TMapiMessage;
     lpSender:      TMapiRecipDesc;
     lpRecipients:  PRecipDescArray;
     lpFiles:       PFileDescArray;
     dwIndex:       Integer;
begin

  // Resource protection
  try
     // Clear message structure
     FillChar(lpMessage, SizeOf(lpMessage), 0);
     // Clear sender structure
     FillChar(lpSender, SizeOf(lpSender), 0);
     // Sender
     lpMessage.lpszSubject:=Pointer(FSubject);
     // Body
     lpMessage.lpszNoteText:=Pointer(FBody);
     // Set flags
     if ReceiptRequest then lpMessage.flFlags:=MAPI_RECEIPT_REQUESTED;
     // Check sender email
     if (Length(FSenderEmail) > 0) then
     begin
        // Fill in sender information
        lpSender.ulRecipClass:=MAPI_ORIG;
        // Substitute email for name if name was not given
        if (Length(FSenderName) > 0) then
           lpSender.lpszName:=Pointer(FSenderName)
        else
           lpSender.lpszName:=Pointer(FSenderEmail);
        lpSender.lpszAddress:=Pointer(FSenderEmail);
        // Set message sender
        lpMessage.lpOriginator:=@lpSender;
     end;
     // Check count of recipients
     if (FRecipNames.Count > 0) then
     begin
        // Allocate memory for array of recipients
        lpRecipients:=AddRefMem(AllocMem(FRecipNames.Count * SizeOf(TMapiRecipDesc)));
        // Add recipients
        for dwIndex:=0 to Pred(FRecipNames.Count) do
        begin
           // Fill in recipient
           lpRecipients^[dwIndex].ulRecipClass:=Cardinal(FRecipNames.Objects[dwIndex]);
           lpRecipients^[dwIndex].lpszName:=AddRefMem(StrPCopy(AllocMem(Succ(Length(FRecipNames[dwIndex]))), FRecipNames[dwIndex]));
           lpRecipients^[dwIndex].lpszAddress:=AddRefMem(StrPCopy(AllocMem(Succ(Length(FRecipEmails[dwIndex]))), FRecipEmails[dwIndex]));
        end;
        // Set message recipients
        lpMessage.nRecipCount:=FRecipNames.Count;
        lpMessage.lpRecips:=@lpRecipients^[0];
     end;
     // Check count of files
     if (FFiles.Count > 0) then
     begin
        // Allocate memory for array of files
        lpFiles:=AddRefMem(AllocMem(FFiles.Count * SizeOf(TMapiFileDesc)));
        // Add files
        for dwIndex:=0 to Pred(FFiles.Count) do
        begin
           // Fill in file information
           lpFiles^[dwIndex].nPosition:=Cardinal($FFFFFFFF);
           lpFiles^[dwIndex].lpszPathName:=AddRefMem(StrPCopy(AllocMem(Succ(Length(FFiles[dwIndex]))), FFiles[dwIndex]));
        end;
        // Set message attachments
        lpMessage.nFileCount:=FFiles.Count;
        lpMessage.lpFiles:=@lpFiles^[0];
     end;
     // Send the message
     result:=MapiSendMail(0, Application.Handle, lpMessage, MAPI_DIALOG or MAPI_LOGON_UI, 0);
  finally
     // Clear all allocated memory blocks
     ClearRefMem;
  end;

end;

procedure TSendMail.ClearRefMem;
var  dwIndex:       Integer;
begin

  // Resource protection
  try
     // Walk the list of pointers
     for dwIndex:=Pred(FPointers.Count) downto 0 do
     begin
        // Free memory
        if Assigned(FPointers[dwIndex]) then FreeMem(FPointers[dwIndex]);
     end;
  finally
     // Clear pointer list
     FPointers.Clear;
  end;

end;

function TSendMail.AddRefMem(P: Pointer): Pointer;
begin

  // Resource protection
  try
     // Add to list
     FPointers.Add(P);
  finally
     // Return pointer
     result:=P;
  end;

end;

procedure TSendMail.AddFile(FileName: String);
begin

  // Check that file actually exists, add to list
  if FileExists(FileName) then FFiles.Add(FileName);

end;

procedure TSendMail.AddRecipient(RecipientName, RecipientEmail: String; RecipientType: TRecipientType = rtNormal);
begin

  // Check email address, which must be set
  if (Length(RecipientEmail) > 0) then
  begin
     // Check recipient name
     if (Length(RecipientName) = 0) then
        // Add recipient name (use email addr) and email address (as well as type)
        FRecipNames.AddObject(RecipientEmail, TObject(Succ(Ord(RecipientType))))
     else
        // Add recipient name and email address (as well as type)
        FRecipNames.AddObject(RecipientName, TObject(Succ(Ord(RecipientType))));
     // Add email address
     FRecipEmails.Add(RecipientEmail);
  end;

end;

procedure TSendMail.ClearFiles;
begin

  // Clear the files
  FFiles.Clear;

end;

procedure TSendMail.ClearRecipients;
begin

  // Clear recipients
  FRecipNames.Clear;
  FRecipEmails.Clear;

end;

constructor TSendMail.Create;
begin

  // Perform inherited
  inherited Create;

  // Set defaults
  SetLength(FSenderName, 0);
  SetLength(FSenderEmail, 0);
  SetLength(FSubject, 0);
  SetLength(FBody, 0);
  FPointers:=TList.Create;
  FFiles:=TStringList.Create;
  FRecipNames:=TStringList.Create;
  FRecipEmails:=TStringList.Create;

end;

destructor TSendMail.Destroy;
begin

  // Resource protection
  try
     // Free lists
     FPointers.Free;
     FFiles.Free;
     FRecipNames.Free;
     FRecipEmails.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

end.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
rfwoolfAuthor Commented:
Amazing stuff, thank you!! Sorry for the late acceptance.
0
rfwoolfAuthor Commented:
Thank you for the excellent solution!
0
Russell LibbySoftware Engineer, Advisory Commented:
No problem on the delay, glad you found it useful

Russell
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.