如何将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
相关产品推荐
相关产品推荐

