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