none
Outlook AddIn System Exception in OnClose Event RRS feed

  • Question

  • I have been developing an Outlook AddIn in Delphi using the OutlookEvents unit contained in the Babelfish for Outlook program.

    If I create a new TOutlookItem

             FOutlookItem := TMyOutlookItem.Create(FMailItem)

    and hook up to various events such as OnClose

            FOutlookItem.OnClose := TMyOutlookItemEvents.DoItemClose;

               ...

            procedure DoItemClose (Sender : TOutlookItem; var Cancel : Boolean);

            var

            OutlookItem : OleVariant;

            begin

                   if (MailItem(Sender.Intf).Class_ > 0)

    begin OutlookItem := Sender.Intf;

                   {do something with OutlookItem}

            end;

    it generally works fine.  If I open and close an email, MailItem(Sender.Intf).Class_ evaluates to 43 (olMail) , and my code then runs as expected.

    However, if I create an email and send it, I get a fatal crash as soon as I try to access MailItem(Sender.Intf).Class_, although bizarrely, I can evaluate it without a problem in the Evaulate / Modify debug window - it evaluates to 0 in this situation.  Or if I miss out that line, then I get the fatal crash as soon as I try to access OutlookItem.

    The error message I get is

    “...outlook.exe faulted with message system exception (code 0x000041d) at 0x77bc3e28.  Process stopped. Use Step or Run to continue.”

    I do not need to process any code in this situation, but I do need to handle the problem so that Outlook does not crash!

    Is there any way to avoid this problem?  How can I determine if Sender.Intf is a valid reference?  Can I handle the exception without Outlook crashing?

    Any help / suggestions would be most gratefully received.


    Andrew 


    Friday, November 20, 2015 10:27 PM

All replies

  • I have been developing an Outlook AddIn in Delphi using the OutlookEvents unit contained in the Babelfish for Outlook program.

    If I create a new TOutlookItem

             FOutlookItem := TMyOutlookItem.Create(FMailItem)

    and hook up to various events such as OnClose

            FOutlookItem.OnClose := TMyOutlookItemEvents.DoItemClose;

               ...

            procedure DoItemClose (Sender : TOutlookItem; var Cancel : Boolean);

            var

            OutlookItem : OleVariant;

            begin

                   if (MailItem(Sender.Intf).Class_ > 0)

    begin OutlookItem := Sender.Intf;

                   {do something with OutlookItem}

            end;

    it generally works fine.  If I open and close an email, MailItem(Sender.Intf).Class_ evaluates to 43 (olMail) , and my code then runs as expected.

    However, if I create an email and send it, I get a fatal crash as soon as I try to access MailItem(Sender.Intf).Class_, although bizarrely, I can evaluate it without a problem in the Evaulate / Modify debug window - it evaluates to 0 in this situation.  Or if I miss out that line, then I get the fatal crash as soon as I try to access OutlookItem.

    The error message I get is

    “...outlook.exe faulted with message system exception (code 0x000041d) at 0x77bc3e28.  Process stopped. Use Step or Run to continue.”

    I do not need to process any code in this situation, but I do need to handle the problem so that Outlook does not crash!

    Is there any way to avoid this problem?  How can I determine if Sender.Intf is a valid reference?  Can I handle the exception without Outlook crashing?

    Any help / suggestions would be most gratefully received.


    Andrew 


    It sounds like MailItem(Sender.Intf) or Sender.Intf is NULL (0) after sending. The fix would be to check for NULL before trying to call a function/method on the NULL reference. Either .Class_ or MailItem() is faulting.  I'm not familiar with the language you are using or babelfish (fair warning).

    The reason why the reference MailItem(Sender.Intf).Class_ can be evaluated in the debugger is because the code isn't executing at that point. Its showing you the value of the reference in memory which is 0 or NULL.



    if (0 != Sender.Intf)
    begin
    OutlookItem := Sender.Intf;
    {do something with OutlookItem}
    end;
    

    • Edited by Joel_Z Saturday, November 21, 2015 2:10 AM
    Saturday, November 21, 2015 1:59 AM
  • Thanks for the reply, but it does not help. In Delphi, Sender.Intf is of type 

    Pointer($xxxxxxx) as IInterface

    and it cannot be compared with 0 or null.

    Being a Delphi programmer, I have never had to worry very much about pointers, so it may be that the solution to my problem is something to do with pointer functionality that I do not understand.

    P.S.  I appear to have found a workaround by:-

    try

      ClassValue := MailItem(Sender.Intf).Class_

    except

    exit // exit the procedure

    end;

    It was in a try except construct before, but not a dedicated one.  Not sure why this works when the other one didn't, but at least it works.  


    Andrew Lockwood



    Saturday, November 21, 2015 2:29 PM
  • Thanks for the reply, but it does not help. In Delphi, Sender.Intf is of type 

    Pointer($xxxxxxx) as IInterface

    and it cannot be compared with 0 or null.

    Being a Delphi programmer, I have never had to worry very much about pointers, so it may be that the solution to my problem is something to do with pointer functionality that I do not understand.

    P.S.  I appear to have found a workaround by:-

    try

      ClassValue := MailItem(Sender.Intf).Class_

    except

    exit // exit the procedure

    end;

    It was in a try except construct before, but not a dedicated one.  Not sure why this works when the other one didn't, but at least it works.  


    Andrew Lockwood



    if (0 != MailItem(Sender.Intf))
    begin
    OutlookItem := Sender.Intf;
    {do something with OutlookItem}
    end;

    I wasn't sure if you should check for null on Sender.Intf or MailItem(Sender.Intf). Did you try the latter? Its good practice to prevent exceptions. 
    Saturday, November 21, 2015 6:40 PM
  • You can compare the value with nil: if Sender.Intf <> nil then...

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!


    Sunday, November 22, 2015 6:35 PM
  • If can compare the value with nil: if Sender.Intf <> nil then...

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Dmitry

    Thanks for the suggestion.  I have tried it, but it does not work.  My current workaround is ...


    function IsInterfaceValid (Intf : IUnknown; var OutlookItem : OleVariant) : boolean;
      var
        ClassType : integer;

      begin
        if (Intf = nil) then
          begin
            Result := False;
            exit;
          end

        else
          Result := True;

        try
          ClassType := MailItem(Intf).Class_;
          case ClassType of
            olAppointment:
              OutlookItem := AppointmentItem(Intf);

            olMail:
              OutlookItem := MailItem(Intf);

            olTask:
              OutlookItem := TaskItem(Intf);

            else
              ShowMessage(IntToStr(ClassType));
          end;

        except
          begin
            Result := False;
            OutlookItem := Unassigned;
          end;
        end;
      end;


    Intf does not equate to nil, but the exception occurs when I try to evaluate MailItem(Intf).Class_ which has a value of 0 according to the debugger.

    Obviously, this is a solution of sorts, but I wanted to see if I could prevent the exception happening, rather than trapping it when it does.  But maybe there are just some times when you cannot do this in COM programming?


    Andrew Lockwood 


    Tuesday, November 24, 2015 12:25 AM
  • Firstly, do not cast - you need to call QueryInterface (which is what the "as" operator in Delphi will do).

    Secondly, use late binding (IDispatch.GetIDsOfNames/Invoke)to read a property. You can do that easily in Delphi by casting to OleVariant

    function IsInterfaceValid (Intf : IUnknown; var OutlookItem : OleVariant) : boolean;
       var
         ClassType : integer;
    
       begin
         if (Intf = nil) then
           begin
             Result := False;
             exit;
           end
    
         else
           Result := True;
    
         try
           ClassType := OleVariant(Intf as IDispatch).Class;
           case ClassType of
             olAppointment:
               OutlookItem := Intf as AppointmentItem;
    
             olMail:
               OutlookItem := Intf as MailItem;
    
             olTask:
               OutlookItem := Intf as TaskItem;
    
             else
               ShowMessage(IntToStr(ClassType));
           end;
    
         except
           begin
             Result := False;
             OutlookItem := Unassigned;
           end;
         end;


    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!


    Tuesday, November 24, 2015 12:57 AM
  • Tried that, but it still throws an exception at:-

    ClassType := OleVariant(Intf as IDispatch).Class


    Andrew Lockwood

    Tuesday, November 24, 2015 1:43 AM
  • What is the exception?

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Tuesday, November 24, 2015 5:39 AM
  • Using System.SysUtils.Exception, the error is:

    Class EOleException

    Message The item has been moved or deleted



    Andrew Lockwood

    Friday, November 27, 2015 12:34 AM
  • Are you sure the exception is raised when you access the Class property? Is it called from the OnClose event? AfterWrite event can be problematic, but OnClose should be fine.

    Dmitry Streblechenko (MVP)
    http://www.dimastr.com/redemption
    Redemption - what the Outlook
    Object Model should have been
    Version 5.5 is now available!

    Friday, November 27, 2015 12:52 AM
  • Tried that, but it still throws an exception at:-

    ClassType := OleVariant(Intf as IDispatch).Class


    Andrew Lockwood

    Check Intf for null first before casting to IDispatch

    Friday, November 27, 2015 1:16 AM