Jump to content
Updated RadStudio RoadMap Read more... ×
Neutral General

[OpenToolsAPI] Project menu item - Access violation after Uninstall

Recommended Posts

Hi,

 

I'm trying to write an IDE plugin which extends the popup menu of a project.

This is a rough overview of my code:

 

TMyProjectMenuItemNotifier = class(TNotifierObject, IOTANotifier, IOTAProjectMenuItemCreatorNotifier)
// ...
TMyProjectMenuItem = class(TInterfacedObject, IOTANotifier, IOTALocalMenu, IOTAProjectManagerMenu)
// ...

{ TMyProjectMenuItemNotifier }

procedure TMyProjectMenuItemNotifier.Initialize;
var manager: IOTAProjectManager;
begin
  if Supports(BorlandIDEServices, IOTAProjectManager, manager) then
    FProjectNotifierId := manager.AddMenuItemCreatorNotifier(Self);
end;

procedure TMyProjectMenuItemNotifier.Finalize;
var manager: IOTAProjectManager;
begin
  if (FProjectNotifierId > -1) and (Supports(BorlandIDEServices, IOTAProjectManager, manager)) then
    manager.RemoveMenuItemCreatorNotifier(FProjectNotifierId);
end;

procedure TMyProjectMenuItemNotifier.AddMenu(const Project: IOTAProject;
  const IdentList: TStrings; const ProjectManagerMenuList: IInterfaceList;
  IsMultiSelect: Boolean);
var item: TMyProjectMenuItem;
begin
  item := TMyProjectMenuItem.Create(Project);
  item.Caption := 'Test123';
  item.Enabled := true;
  item.Position := 0;

  ProjectManagerMenuList.Add(item);
end;

{ TMyProjectMenuItem }

procedure TMyProjectMenuItem.Execute(const MenuContextList: IInterfaceList);
begin
  ShowMessage('Click!');
end;

// Unit which creates and registers all the classes

var
   projectItemNotifier: TMyProjectMenuItemNotifier;

procedure Register;
begin
  projectItemNotifier := TMyProjectMenuItemNotifier.Create;
  projectItemNotifier.Initialize;
end;

initialization

finalization
  projectItemNotifier.Finalize;

 

In general all this works well so far. There's just one problem:

It seems like the TMyProjectMenuItem-Objects are not destroyed on closing the Popup-Menu but on opening the next one.

This somehow ends in the following order of events.

 

1. Build MyPlugin.bpl => Success

2. Install MyPlugin.bpl => Success

3. Right click on my project 

       a) TMyProjectMenuItem is created

       b) Popup menu shows and my menu item works as expected

4. I close the popup menu 

5. I right click on my project (again)

       a) Prior TMyProjectMenuItem is destroyed

       b) New TMyProjectMenuItem is created

6. I choose "Uninstall" 

      a) TMyProjectMenuItemNotifier.Finalize is called

      b) TMyProjectMenuItemNotifier is destroyed

7. I right click on my project (again!)

          image.png.ee42f227109de0c6f245286c96ac8151.png

8. I right click on my project (one last time) => Success. The popup menu with the default items shows

 

The TMyProjectMenuItem from my last click before the uninstall never gets destroyed and it seems like the IDE doesn't know the item is gone either and I get this access violation when the IDE tries to access (and destroy) the last TMyProjectMenuItem.

 

And at this point I'm clueless. I don't know how I'm supposed to fix that. There is no Interface or Method to manually unregister/destroy the object so that the IDE knows it's gone.

I'm using Delphi 10.0 Seattle on Windows 10

Edited by Neutral General

Share this post


Link to post

I have not worked with menu notifiers before, but I think the principle should be the same as with other OTA notifiers. You do not need to store a Notifier object. All you have to store is its index, to remove your notifier when your package is finalized.

var
  MenuItemNotifierInd: Integer;

procedure Register;
var
  manager: IOTAProjectManager;
  projectItemNotifier: IOTAProjectMenuItemCreatorNotifier;
begin
  MenuItemNotifierInd := -1;
  if not Supports(BorlandIDEServices, IOTAProjectManager, manager) then
    Exit;
  projectItemNotifier := TMyProjectMenuItemNotifier.Create;
  MenuItemNotifierInd := manager.AddMenuItemCreatorNotifier(projectItemNotifier);
end;
...
finalization
  if MenuItemNotifierInd >= 0 then
    //... remove notifier
end.

For the menu item use an interface instead of an object too:

var
  item: IOTAProjectManagerMenu;
begin
  item := TMyProjectMenuItem.Create(Project);
  item.Caption := 'Test123';
  item.Enabled := true;
  item.Position := 0;
  ProjectManagerMenuList.Add(item);

 

Share this post


Link to post

I've tried using an interface for the menu item, but it didn't change anything and every code about this I found also always used object variables so I assumed that this would be okay.

Currently my menu notifier also has some other functionality. Implementing the Notifier interface is just one thing it does.

But maybe I should use a dedicated notifier object and include my main plugin logic in another class?

Share this post


Link to post

In my IDE plugins I have Data module with main logic and notifiers and other objects, which are created and deleted by the module (DataModuleCreate, DataModuleDestroy). The data module is created and destroyed automatically:

initialization
  DataModule1 := TDataModule1.Create(nil);
finalization
  DataModule1.Free;
end.
Quote

I found also always used object variables so I assumed that this would be okay.

When we create an interfaced object and store a reference to it in a variable, its count of references increases. Are you sure that your menu items are really released after use?

 

I followed this instruction when I created my plugins. It recommends to use a Data Module: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Extending_the_IDE_Using_the_Tools_API

Edited by Kryvich

Share this post


Link to post
2 hours ago, Kryvich said:

Are you sure that your menu items are really released after use?

Yes. I set a breakpoint and before that a ShowMessage in the destructor of the menu items. The old ones get destroyed if a new popup menu is created.

I'll take a look at the link, thanks! :)

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×