• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1617
  • Last Modified:

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

0
rfwoolf
Asked:
rfwoolf
  • 3
  • 2
1 Solution
 
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
 
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

Featured Post

Receive 1:1 tech help

Solve your biggest tech problems alongside global tech experts with 1:1 help.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now