delphidebuggingidetoolsapi

Why `as` operator might throw nasty EAccessViolation instead of normal EIntfCastError?


I've made highly experimental and unstable IDE add-in, which causes extremely nasty A/V at IDE shutdown (breaks recent projects feature, grr!). I finally narrowed it down to the specific destructor:

destructor TMyAddIn.Destroy;
begin
  {$IFDEF DEBUG}
  { BUG is here, causes A/V at shutdown }
  (BorlandIDEServices as IOTAMessageServices).AddTitleMessage('Goodbye');
  {$ENDIF}

  { ... }
  { finalizing stuff }
  { ... }

   inherited;
end;

A/V exception happens on read of address 0x00000008.

I've added more defences to the problematic statement:

  if Assigned(BorlandIDEServices) then    { passes }
    if Supports(BorlandIDEServices, IOTAMessageServices) then   { fails }
     (BorlandIDEServices as IOTAMessageServices).AddTitleMessage('Goodbye');

... and figured out what (1) pointer is still not nil (2) QueryInterface still works (3) desired interface no longer present. Given that everything looks normal, I expect friendly EIntfCastError. But why I've got A/V instead?


Solution

  • My guess would be that either

    1. BorlandIDEServices itself is not nil but is also not valid anymore

    2. BorlandIDEServices is valid, but its internal IOTAMessageServices implementation is not.

    Those could account for a read of address 0x00000008 error.

    What you should do instead is obtain the IOTAMessageServices interface earlier in your add-in's lifetime and hold on to it, that way it is still valid in your destructor due to reference counting, eg:

    {$IFDEF DEBUG}
    private
      MsgSvc: IOTAMessageServices;
    {$ENDIF}
    
    constructor TMyAddIn.Create;
    begin
      inherited;
    
      { ... }
      { initializing stuff }
      { ... }
    
      {$IFDEF DEBUG}
      MsgSvc := BorlandIDEServices as IOTAMessageServices;
      MsgSvc.AddTitleMessage('Hello');
      {$ENDIF}
    end;
    
    destructor TMyAddIn.Destroy;
    begin
      {$IFDEF DEBUG}
      MsgSvc.AddTitleMessage('Goodbye');
      {$ENDIF}
    
      { ... }
      { finalizing stuff }
      { ... }
    
       inherited;
    end;