2015-03-26 2 views
-1

J'ai joué avec du code pour envoyer du courrier en utilisant MAPI dans mon programme Delphi XE7. J'ai réussi à faire fonctionner To, Cc et Bcc avec fileattachment, mais quand le mail est envoyé par le client (dans ce cas Outlook 2010) le programme plante et je n'arrive pas à comprendre où je suis. mal - je pourrais être aveugle sur le code. J'ai fait un petit exemple avec le code dans un fichier dans mon dropbox dl.dropboxusercontent.com/u/65392149/Mapi_MCVE.ZIPMAPI dans Delphi - de travail mais plantage après que le courrier est envoyé

La fonction MAPI ressemble à ceci

function SendMailMAPI(const aFrom, aTo, aCc, aBcc, aSubject, aBody: string; aMailFiles: TStringList; aReceipt: boolean): boolean; 
var 
    MapiStatus: DWord; 
    MapiMessage: TMapiMessage; 
    MapiOrigin: TMapiRecipDesc; 
    MapiRecipient: array of TMapiRecipDesc; 
    MapiFiles: PMapiFileDesc; 

    RecipientsTo: TStringlist; 
    RecipientsCc: TStringlist; 
    RecipientsBcc: TStringlist; 
    RecipientsCount: integer; 
    FilesCount: Integer; 
    i: integer; 
    Filename: string; 
begin 
    MapiStatus := SUCCESS_SUCCESS; 
    Result := True; 
    MapiFiles := nil; 

    FillChar(MapiMessage, Sizeof(TMapiMessage), 0); 
    if aReceipt then 
    MapiMessage.flFlags := MAPI_RECEIPT_REQUESTED; 

    MapiMessage.lpszSubject := PAnsiChar(AnsiString(aSubject)); 
    MapiMessage.lpszNoteText := PAnsiChar(AnsiString(aBody)); 

    FillChar(MapiOrigin, Sizeof(TMapiRecipDesc), 0); 
    MapiOrigin.lpszName := PAnsiChar(AnsiString(aFrom)); 
    MapiOrigin.lpszAddress := PAnsiChar(AnsiString(aFrom)); 
    MapiMessage.lpOriginator := nil; 

    FilesCount := aMailFiles.Count; 
    if FilesCount > 0 then 
    begin 
     GetMem(MapiFiles, SizeOf(TMapiFileDesc) * FilesCount); 
     for i := 0 to FilesCount - 1 do 
     begin 
      FileName := aMailfiles[i]; 
      MapiFiles[i].ulReserved := 0; 
      MapiFiles[i].flFlags := 0; 
      MapiFiles[i].nPosition := ULONG($FFFFFFFF); 
      MapiFiles[i].lpszPathName := PAnsiChar(AnsiString(FileName)); 
      MapiFiles[i].lpszFileName := PAnsiChar(AnsiString(ExtractFileName(FileName))); 
      MapiFiles[i].lpFileType := nil; 
     end; 
     MapiMessage.nFileCount := FilesCount; 
     MapiMessage.lpFiles := @MapiFiles^; 
    end; 

    RecipientsCount := 0; 
    RecipientsTo := TStringlist.Create; 
    RecipientsCc := TStringlist.Create; 
    RecipientsBcc := TStringlist.Create; 
    RecipientsTo.Delimiter := ';'; 
    RecipientsCc.Delimiter := ';'; 
    RecipientsBcc.Delimiter := ';'; 
    try 
    if aTo <> '' then 
     begin 
     RecipientsTo.DelimitedText := aTo; 
     RecipientsCount := RecipientsCount + RecipientsTo.Count; 
     end; 
    if aCc <> '' then 
     begin 
     RecipientsCc.DelimitedText := aCc; 
     RecipientsCount := RecipientsCount + RecipientsCc.Count; 
     end; 
    if aBcc <> '' then 
     begin 
     RecipientsBcc.DelimitedText := aBcc; 
     RecipientsCount := RecipientsCount + RecipientsBcc.Count; 
     end; 

    FillChar(MapiRecipient, Sizeof(TMapiRecipDesc) * RecipientsCount, 0); 
    SetLength(MapiRecipient, RecipientsCount); 
    RecipientsCount := 0; 
    if RecipientsTo.Count > 0 then 
     begin 
     MapiRecipient[RecipientsCount].ulRecipClass := MAPI_TO; 
     for i := 0 to RecipientsTo.Count - 1 do 
      begin 
      MapiRecipient[RecipientsCount].lpszName := PAnsiChar(AnsiString(RecipientsTo[i])); 
      MapiRecipient[RecipientsCount].lpszAddress := PAnsiChar(AnsiString(RecipientsTo[i])); 
      Inc(RecipientsCount); 
      end; 
     end; 
    if RecipientsCc.Count > 0 then 
     begin 
     MapiRecipient[RecipientsCount].ulRecipClass := MAPI_CC; 
     for i := 0 to RecipientsCc.Count - 1 do 
      begin 
      MapiRecipient[RecipientsCount].lpszName := PAnsiChar(AnsiString(RecipientsCc[i])); 
      MapiRecipient[RecipientsCount].lpszAddress := PAnsiChar(AnsiString(RecipientsCc[i])); 
      Inc(RecipientsCount); 
      end; 
     end; 
    if RecipientsBcc.Count > 0 then 
     begin 
     MapiRecipient[RecipientsCount].ulRecipClass := MAPI_BCC; 
     for i := 0 to RecipientsBcc.Count - 1 do 
      begin 
      MapiRecipient[RecipientsCount].lpszName := PAnsiChar(AnsiString(RecipientsBcc[i])); 
      MapiRecipient[RecipientsCount].lpszAddress := PAnsiChar(AnsiString(RecipientsBcc[i])); 
      Inc(RecipientsCount); 
      end; 
     end; 
    MapiMessage.nRecipCount := RecipientsCount; 
    MapiMessage.lpRecips:= Pointer(MapiRecipient); 
    finally 
    RecipientsTo.Free; 
    RecipientsCc.Free; 
    RecipientsBcc.Free; 
    end; 

    try 
    MapiStatus := MapiSendMail(0, Application.MainForm.Handle, MapiMessage, MAPI_LOGON_UI + MAPI_DIALOG, 0); 
    except 
    on E:Exception do 
     ShowMessage('U_Mailing.Mapi.SendMailMAPI: ' + E.Message); 
    end; 

    for i := 0 to FilesCount - 1 do 
    begin 
     System.AnsiStrings.StrDispose(MapiFiles[i].lpszPathName); 
     System.AnsiStrings.StrDispose(MapiFiles[i].lpszFileName); 
    end; 
    for i := 0 to RecipientsCount - 1 do 
    begin 
     System.AnsiStrings.StrDispose(MapiRecipient[i].lpszName); 
     System.AnsiStrings.StrDispose(MapiRecipient[i].lpszAddress); 
    end; 

    case MapiStatus 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.'); 
    else 
     Showmessage('MAPISendMail failed with an unknown error code.'); 
    Result := False; 
    end; 
end; 

Répondre

0

Je peux voir deux erreurs évidentes. Premièrement, vous utilisez beaucoup de variables temporaires implicites AnsiString dont la durée de vie est incertaine. Cela se produit chaque fois que vous faites

PAnsiChar(AnsiString(...)) 

Le compilateur fait une AnsiString temporaire variable locale. Vous comptez sur le compilateur en faire assez d'entre eux, un pour chaque chaîne distincte. Lorsque vous faites cela à l'intérieur d'une boucle, le compilateur aura une variable locale implicite partagée entre toutes les itérations. Ce n'est pas assez. Vous pourriez vous en tirer parce que le gestionnaire de mémoire n'arrive pas à réutiliser la mémoire. Mais votre code est toujours faux. Je voudrais créer un TList<AnsiString> pour contenir les nombreuses variables AnsiString. Chaque fois que vous avez besoin pour un PAnsiChar faire ceci:

astr := AnsiString(...); 
TempAnsiStrings.Add(astr); 
... := PAnsiChar(astr); 

TempAnsiStrings est votre liste qui contient des objets AnsiString temporaires. Emballez ceci dans une fonction imbriquée pour faciliter l'utilisation. De toute évidence, vous devez instancier la liste et la détruire, mais je crois que vous savez déjà comment procéder.

Votre autre problème sont les appels à StrDispose. Vous devez les supprimer car le compilateur gère la durée de vie. Appelez StrDispose pour libérer de la mémoire allouée avec StrNew ou StrAlloc. Vous n'allouez pas de cette façon donc il n'y a pas de place pour StrDispose. Au lieu d'utiliser l'allocation de mémoire explicite pour la liste des fichiers, un tableau dynamique serait plus propre. Cela aurait l'avantage d'éviter la possibilité de fuite que votre code a actuellement en raison de son bloc finalement manquant.

+0

J'ai cherché des exemples de MAPI et je dois dire qu'il y a plusieurs façons de le coder. J'ai trouvé un moyen sur http://www.delphipraxis.net/157337-email-per-mapi-mit-mehreren-cc-bcc-und-anhaengen.html et j'ai écrit un 'wrapper' pour cela et cela fonctionne pour le moment. – OZ8HP

+1

Avez-vous compris ce que j'ai dit dans la réponse? –

+0

La plupart - je vais devoir travailler avec quand j'aurai plus de temps – OZ8HP