当 Delphi 32 位应用程序的相同代码工作时,从 Delphi 64 位应用程序调用 MAPI 电子邮件不起作用?

Posted

技术标签:

【中文标题】当 Delphi 32 位应用程序的相同代码工作时,从 Delphi 64 位应用程序调用 MAPI 电子邮件不起作用?【英文标题】:Calling MAPI email from Delphi 64 bit app does not work when same code for Delphi 32 bit app works? 【发布时间】:2015-06-20 04:34:03 【问题描述】:

我正在构建一个 64 位 Delphi XE2 应用程序来处理通过 Outlook 发送 MAPI 邮件。这适用于 Outlook 2010、2013 等的 64 位安装,因为您无法从 32 位应用程序调用 64 位 MAPI。

我的 Delphi 代码在编译为 32 位应用程序时可与 32 位版本的 Outlook 客户端完美配合。 32 位应用程序在 Win7 64 上运行。

当将相同的代码重新编译到 64 位应用程序中时,该代码总是失败并显示 MAPI_E_FAILURE 错误代码为 2。 64 位应用程序在安装了 Outlook 2013 64 位客户端的 Win 8 上运行。

知道为什么 64 位应用程序会失败吗?对于 64 位代码,我是否正在做一些明显的事情?

干杯! TJ

代码如下:

function SendEMailByMAPI(SenderName, SenderAddress, Subject, Body: Ansistring; Recipients, Attachments: TStringList): Integer;
var i: Integer;
var EmailMessage: TMapiMessage;
var lpSender: TMapiRecipDesc;
var MAPI_Session : Cardinal;
var RecipientArray : array of TMapiRecipDesc;
var AttachmentsArray : array of TMapiFileDesc;
begin
  FillChar(EmailMessage, SizeOf(EmailMessage), 0);

  //add sender address if not blank.
  if (SenderAddress <> '') then begin
    lpSender.ulRecipClass := MAPI_ORIG;
    if (SenderName <> '') then begin
      lpSender.lpszName := PAnsiChar(SenderAddress);
    end
    else begin
      lpSender.lpszName := PAnsiChar(SenderName);
    end;
    lpSender.lpszAddress := PAnsiChar(SenderAddress);
    lpSender.ulReserved := 0;
    lpSender.ulEIDSize := 0;
    lpSender.lpEntryID := nil;
    EmailMessage.lpOriginator := @lpSender;
  end;

  EmailMessage.lpszSubject := PAnsiChar(Subject);
  EmailMessage.lpszNoteText := PAnsiChar(Body);

  EmailMessage.nFileCount := Attachments.Count;
  SetLength(AttachmentsArray, Attachments.Count);
  //Loop and add file path and name
  for i := 0 to Attachments.Count-1 do begin
    AttachmentsArray[i].nPosition := Cardinal(-1);
    AttachmentsArray[i].lpszPathName := PAnsiChar(AnsiString(Attachments[i]));
    AttachmentsArray[i].lpszFileName := PAnsiChar(AnsiString(ExtractFileName(Attachments[i])));
  end;

  if EmailMessage.nFileCount > 0 then begin
    EmailMessage.lpFiles := Pointer(AttachmentsArray);
  end
  else begin
    EmailMessage.lpFiles := nil;
  end;

  SetLength(RecipientArray, Recipients.Count);
  for i := 0 to Recipients.Count-1 do begin
    RecipientArray[i].ulReserved := 0;
    RecipientArray[i].ulRecipClass := MAPI_TO;
    RecipientArray[i].lpszName := StrNew(PAnsiChar(AnsiString(Recipients[i])));
    RecipientArray[i].lpszAddress := StrNew(PAnsiChar('SMTP:' + AnsiString(Recipients[i])));
    RecipientArray[i].lpEntryID := nil;
    RecipientArray[i].ulEIDSize := 0;
  end;

  EmailMessage.nRecipCount := Recipients.Count;
  EmailMessage.lpRecips := Pointer(RecipientArray);

  //Send the message
  Result:= MapiLogon(Application.Handle, PAnsiChar(''), PAnsiChar(''), MAPI_LOGON_UI or MAPI_NEW_SESSION, 0, @MAPI_Session);
  if (Result = SUCCESS_SUCCESS) then begin
    ShowMessage('Before calling MAPISendMail');//used as a timing device to see how long it takes for Outlook to respond to request and show email dialog
    Result := MAPISendMail(MAPI_Session, Application.Handle, EmailMessage, MAPI_DIALOG or MAPI_LOGON_UI , 0);
    if Result <> 0 then begin
      case result of
        MAPI_E_AMBIGUOUS_RECIPIENT : ShowMessage('Receiver is not unique.');
        MAPI_E_ATTACHMENT_NOT_FOUND : ShowMessage('File for appending not found');
        MAPI_E_ATTACHMENT_OPEN_FAILURE : ShowMessage('File could not be opened for appending.');
        MAPI_E_BAD_RECIPTYPE : ShowMessage('Type of receiver not MAPI_TO, MAPI_CC or MAPI_BCC.');
        MAPI_E_FAILURE : ShowMessage('Unknown Error.');
        MAPI_E_INSUFFICIENT_MEMORY : ShowMessage('Not enough memory.');
        MAPI_E_LOGIN_FAILURE : ShowMessage('User Login failed.');
        MAPI_E_TEXT_TOO_LARGE : ShowMessage('Text too large.');
        MAPI_E_TOO_MANY_FILES : ShowMessage('Too many file attachments.');
        MAPI_E_TOO_MANY_RECIPIENTS : ShowMessage('Too many recipients specified.');
        MAPI_E_UNKNOWN_RECIPIENT : ShowMessage('Receiver not found in the address book.');
        MAPI_E_USER_ABORT : ShowMessage('User canceled or MAPI Send not installed.');
      else
        ShowMessage('Error sending email. Error code: ' + inttostr(Result));
      end;
    end;
  end
  else begin
    ShowMessage('Failure to get MAPI call handle: ' + inttostr(Result));
  end;
end;

这个函数是这样调用的:

procedure TForm1.ButtonClick(Sender: TObject);
var SenderName: Ansistring;
var SenderAddress: Ansistring;
var Subject: Ansistring;
var Body: Ansistring;
var Recipients: TStringList;
var Attachments: TStringList;
begin
  Recipients := TStringList.Create;
  Attachments := TStringList.Create;

  Recipients.Add('test@test.com');
  Attachments.Add('c:\temp\test.xlsx');

  SenderName := 'TJ Asher';
  SenderAddress := 'sender@test.com';
  Subject := 'email test 64 bit subject';
  Body := 'Email test 64 bit body';
  SendEMailByMAPI(SenderName, SenderAddress, Subject, Body, Recipients, Attachments);
end;

【问题讨论】:

您的记录可能翻译错误。我们看不到它们。您对StrNew 的使用是错误的。它泄漏。创建一个TList&lt;AnsiString&gt; 来存储可以使用PAnsiChar 强制转换的临时字符串值。在 finally 块中销毁列表以进行整理。 这里发布的代码可能是好是坏,这取决于我们看不到的代码。如果您遇到问题甚至无法发布您可能正在工作的 MAPI 包装器,那么没有人可以帮助您。 @David Heffernan - 不确定您所说的错误翻译记录是什么意思?没有记录。我会按照您的建议查看StrNew 位。 @Warren P - 这是整个代码。按钮单击代码调用的一个函数。 MAPISendMail 调用是所有现有的 Delphi VCL 代码。如果需要,我可以发布 Delphi VCL 代码。 XE2 WinApi MAPI 包装单元在 64 位目标中使用时可能不正确。 【参考方案1】:

在 Windows 8 64 位上可能不支持 MAPISendMail (A) 接口:

https://msdn.microsoft.com/is-is/library/dd296721.aspx 建议使用宽字符界面。

也有可能 MAPISendMail 不是 Outlook 2013 64 位支持的接口:它已经贬值了一段时间。我无法在 Outlook 2013/64 上使用该界面,但存在(与 MAPI 一样)复杂的因素。

【讨论】:

以上是关于当 Delphi 32 位应用程序的相同代码工作时,从 Delphi 64 位应用程序调用 MAPI 电子邮件不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

Delphi 的 64 位 DLL c/c++ 接口

Delphi:如何确定应用程序是不是在 Win32 / Win64 下运行并在 64 位上自动启动 64 位版本?

delphi下如何制作纯dos下能够运行的程序,并非是控制台程序

Delphi 中的 Windows 7 登录屏幕保护程序

Delphi XE6 在安装 bpl 时崩溃

通过 64 位 Delphi 发送 MIDI 控制消息