通过OLE的MS Outlook:用户检测到电子邮件发送/取消时内存泄漏

时间:2014-04-01 10:42:26

标签: delphi outlook-2010 delphi-xe3

我尝试编写一封通过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.

有没有人有想法,上述两种方法有什么问题?

0 个答案:

没有答案