You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将TTMSFNCCloudMicrosoftOutlookMail异步调用转为Delphi阻塞模式?

阻塞式SendWait方法实现方案

你的核心问题是TTMSFNCCloudMicrosoftOutlookMail组件的回调通过Synchronize切换到主线程,但主线程被TEvent.WaitFor阻塞,导致回调无法执行、事件永远无法触发。以下是两种可行的解决思路:

方案一:利用组件自带的同步控制属性(优先推荐)

检查TTMSFNCCloudMicrosoftOutlookMail组件是否存在SynchronizeEvents(或类似命名)的属性,将其设置为False后,组件的回调事件会直接在后台线程触发,不再通过Synchronize切换到主线程。

修改你的SendCloudMail调用逻辑,在调用前添加:

// 假设你的Outlook组件实例名为FOutlookMail
FOutlookMail.SynchronizeEvents := False;

之后你的原有代码就能正常工作——回调中的oEvent.SetEvent会在后台线程执行,主线程的WaitFor不会阻塞回调流程,自然不会出现死锁。

方案二:用MsgWaitForMultipleObjects处理消息+自定义消息

如果组件没有上述属性,就需要让主线程在等待期间处理消息,确保Synchronize的代码能正常执行:

1. 在TATMail类中添加消息定义与处理方法

type
  TATMail = class(TComponent)
  private
    const
      WM_EMAIL_COMPLETE = WM_USER + 1;
    var
      FWaitEvent: TEvent;
      FSuccess: Boolean;
      FErrorMessage: string;
    procedure WMEmailComplete(var Msg: TMessage); message WM_EMAIL_COMPLETE;
    // ... 其他已有成员
  public
    function SendWait(const aReceiver, aSender, aReplyTo, aCc, aSubject,
      aBody, aAttachments: string; out AErrorMessage: string): Boolean;
    // ... 其他已有方法
  end;

procedure TATMail.WMEmailComplete(var Msg: TMessage);
begin
  FErrorMessage := PChar(Msg.WParam);
  FSuccess := Boolean(Msg.LParam);
  FWaitEvent.SetEvent;
end;

2. 重写SendWait方法

function TATMail.SendWait(const aReceiver, aSender, aReplyTo, aCc, aSubject,
  aBody, aAttachments: string; out AErrorMessage: string): Boolean;
var
  WaitResult: DWORD;
  Msg: TMsg;
begin
  FSuccess := False;
  FErrorMessage := '';
  FWaitEvent := TEvent.Create(nil, True, False, '');
  try
    if GetSystemConfig.SendMailUsingSendGridRestApi then
      SendMailUsingSendGridApi(aReceiver, aSender, aReplyTo, aCc, aSubject, aBody, aAttachments,
        procedure(const AMessage: string; ASuccess: Boolean)
        begin
          PostMessage(Self.Handle, WM_EMAIL_COMPLETE, NativeInt(PChar(AMessage)), NativeInt(ASuccess));
        end)
    else if GetSystemConfig.SendMailUsingMSOutlook365 then
    begin
      SendCloudMail(aReceiver, aReplyTo, aCc, aSubject, aBody, aAttachments,
        procedure(const AMessage: string; ASuccess: Boolean)
        begin
          PostMessage(Self.Handle, WM_EMAIL_COMPLETE, NativeInt(PChar(AMessage)), NativeInt(ASuccess));
        end)
    end
    else
      SendMail(aReceiver, aSender, aReplyTo, aCc, aSubject, aBody, aAttachments,
        procedure(const AMessage: string; ASuccess: Boolean)
        begin
          FErrorMessage := AMessage;
          FSuccess := ASuccess;
          FWaitEvent.SetEvent;
        end);

    // 循环等待事件,同时处理消息避免Synchronize卡死
    repeat
      WaitResult := MsgWaitForMultipleObjects(1, FWaitEvent.Handle, False, 50000, QS_ALLINPUT);
      case WaitResult of
        WAIT_OBJECT_0:
          Break; // 事件触发,结束等待
        WAIT_OBJECT_0 + 1:
          begin
            // 处理所有待处理消息
            while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
            begin
              TranslateMessage(Msg);
              DispatchMessage(Msg);
            end;
          end;
        WAIT_TIMEOUT:
          begin
            FErrorMessage := 'Timeout occurred while sending email.';
            Break;
          end;
      end;
    until False;

    AErrorMessage := FErrorMessage;
    Result := FSuccess;

    // 显示提示
    if Result then
      GuiDialog.ShowMessage('Email sent successfully!')
    else if FErrorMessage <> '' then
      GuiDialog.ShowMessage('Failed to send email: ' + FErrorMessage)
    else
      GuiDialog.ShowMessage('Timeout occurred while sending email.');
  finally
    FWaitEvent.Free;
  end;
end;

注意:如果TATMail不是窗口类(没有Handle),需要用AllocateHWnd创建临时窗口句柄来接收消息,替换代码中的Self.Handle即可。


内容的提问来源于stack exchange,提问作者Roland Bengtsson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 09:37:06