我尝试编写一封通过MS Outlook(2010)发送的简单邮件,以允许用户进行内容预览,如果用户取消了发送,我的内存泄漏1x TServerEventDispatch
。使用来自SO附加事件的回复_MAILITEM
泄漏用户操作:发送和/或取消电子邮件。
完全调试模式下来自FastMM的堆栈跟踪(两种情况几乎相同):
4068DE [IB_Intf.pas][System][@GetMem$qqri][2097]
4090C3 [System][TObject.NewInstance$qqrv]
409826 [System][@ClassCreate$qqrpvzc]
633060 [Vcl.OleServer][Oleserver.TServerEventDispatch.$bctr$qqrp24Vcl.Oleserver.TOleServer]
6333D2 [Vcl.OleServer][Oleserver.TOleServer.$bctr$qqrp25System.Classes.TComponent]
B026D7 [Outlook2000][TOutlookApplication.$bctr$qqrp25System.Classes.TComponent]
B066F0 [DU_OutlookLeakTests.pas][DU_OutlookLeakTests][TestOutlookMemoryLeak.Item_OnSendMemoryLeakTest$qqrv][44]
622140 [TestFramework.pas][TestFramework][TTestCase.Invoke$qqrynpqqrv$v][2780]
622367 [TestFramework.pas][TestFramework][TTestCase.RunTest$qqrp25Testframework.TTestResult][2810]
61D0EA [TestFramework.pas][TestFramework][TTestResult.RunTestRun$qqr47System.%DelphiInterface$t19Testframework.ITest%][1473]
40F0AC [System][TInterfacedObject.QueryInterface$qqsrx5_GUIDpv]
这是DUnit测试,展示了我和Sertac's solution:
unit DU_OutlookLeakTests;
{$TYPEINFO ON} // Introspection is used
interface
uses
TestFramework, Classes, Windows, SysUtils, Outlook2000;
type
TestOutlookMemoryLeak = class(TTestCase)
strict private
FSendCnt: integer;
strict private
procedure HandleSend(Sender: TObject; var Cancel: WordBool);
procedure HandleItemSend(Sender: TObject; const Item: IDispatch;
var Cancel: WordBool);
published
procedure App_OnItemSendMemoryLeakTest;
procedure Item_OnSendMemoryLeakTest;
end;
implementation
/// <summary>
/// My original attempt to detect whether email has been sent or cancelled.
/// Leaks TServerEventDispatch if user cancels sending.
/// </summary>
procedure TestOutlookMemoryLeak.App_OnItemSendMemoryLeakTest;
var
Outlook: TOutlookApplication;
Mail: variant;
begin
Outlook := TOutlookApplication.Create(nil);
try
Outlook.OnItemSend := HandleItemSend;
Mail := Outlook.CreateItem(olMailItem);
try
Mail.Subject := 'A Subject';
Mail.Recipients.Add('petr.fejfar@xxx.yy');
Mail.Body := 'A Body';
FSendCnt := 0;
Mail.Display(True);
CheckTrue(FSendCnt > 0, 'Mail has been cancelled by user');
finally
VarClear(Mail);
end;
finally
Outlook.Free;
end;
end;
/// <summary>
/// Another attempt following Sertac Akyuz's answer here:
/// https://stackoverflow.com/questions/5507342/outlook-object-model-detecting-if-email-has-been-sent
/// Leaks TServerEventDispatch if user sends and/or cancels email.
/// </summary>
procedure TestOutlookMemoryLeak.Item_OnSendMemoryLeakTest;
var
Outlook: TOutlookApplication;
MailItem: _MailItem;
Mail: TMailItem;
begin
Outlook := TOutlookApplication.Create(nil);
try
MailItem := Outlook.CreateItem(olMailItem) as _MailItem;
try
Mail := TMailItem.Create(nil);
try
Mail.ConnectTo(MailItem);
Mail.OnSend := HandleSend;
Mail.Subject := 'A Subject';
Mail.Recipients.Add('petr.fejfar@xxx.yy');
Mail.Body := 'A Body';
FSendCnt := 0;
Mail.Display(True);
CheckTrue(FSendCnt > 0, 'Mail has been cancelled by user');
finally
Mail.Free;
end;
finally
MailItem := nil;
end;
finally
Outlook.Free;
end;
end;
procedure TestOutlookMemoryLeak.HandleItemSend(Sender: TObject;
const Item: IDispatch; var Cancel: WordBool);
begin
inc(FSendCnt);
end;
procedure TestOutlookMemoryLeak.HandleSend(Sender: TObject;
var Cancel: WordBool);
begin
inc(FSendCnt);
end;
initialization
RegisterTest(TestOutlookMemoryLeak.Suite);
end.
有没有人有想法,上述两种方法有什么问题?