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

debugging, delphi, ide, toolsapi

Solution

My guess would be that either

`BorlandIDEServices` itself is not nil but is also not valid anymore

`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;

Problem

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?

Original source