There is workaround, but it is very, very dirty: Use a cracker class to obtain access to the FHandle private member of the TPopupMenu.Items menu item property.
A cracker class involves reproducing the private storage layout of the target class up to and including the private member of interest, and using a type-cast to "overlay" that type onto an instance of the target type in a context that then allows you to access the internal storage of the target.
In this case, the target object is the Items property of TPopupMenu which is an instance of TMenuItem. TMenuItem derives from TComponent so the cracker class to provide access to FHandle for a TMenuItem is:
type
// Here be dragons...
TMenuItemCracker = class(TComponent)
private
FCaption: string;
FChecked: Boolean;
FEnabled: Boolean;
FDefault: Boolean;
FAutoHotkeys: TMenuItemAutoFlag;
FAutoLineReduction: TMenuItemAutoFlag;
FRadioItem: Boolean;
FVisible: Boolean;
FGroupIndex: Byte;
FImageIndex: TImageIndex;
FActionLink: TMenuActionLink;
FBreak: TMenuBreak;
FBitmap: TBitmap;
FCommand: Word;
FHelpContext: THelpContext;
FHint: string;
FItems: TList;
FShortCut: TShortCut;
FParent: TMenuItem;
FMerged: TMenuItem;
FMergedWith: TMenuItem;
FMenu: TMenu;
FStreamedRebuild: Boolean;
FImageChangeLink: TChangeLink;
FSubMenuImages: TCustomImageList;
FOnChange: TMenuChangeEvent;
FOnClick: TNotifyEvent;
FOnDrawItem: TMenuDrawItemEvent;
FOnAdvancedDrawItem: TAdvancedMenuDrawItemEvent;
FOnMeasureItem: TMenuMeasureItemEvent;
FAutoCheck: Boolean;
FHandle: TMenuHandle;
end;
NOTE: Since this technique relies on an exact reproduction of the internal storage layout of the target class, the cracker declaration may need to include $IFDEF variations to cater for changes in that internal layout between different Delphi versions. The declaration above is correct for Delphi XE4 and should be checked against the TMenuItem source for correctness w.r.t other Delphi versions.
With that cracker class we can then provide a utility proc to wrap up the nasty tricks we are then going to perform using the access this provides. In this case, we can clear the menu items as usual, but also call DestroyMenu() ourselves using the cracker cast to overwrite the FHandle member variable with 0 since it is now invalid and needs to be 0 to force the TPopupMenu to recreate the menu when next needed:
procedure ResetPopupMenu(const aMenu: TPopupMenu);
begin
aMenu.Items.Clear;
// Here be dragons...
DestroyMenu(aMenu.Items.Handle);
TMenuItemCracker(aMenu.Items).FHandle := 0;
end;
In your sample code simply replace your call to PopupMenu1.Items.Clear in your Button2Click handler with a call to ResetPopupMenu(PopupMenu1).
It goes without saying that this is dangerous in the extreme. Quite apart from the sheer lunacy of hacking around inside the private storage of a class, no account is taken in this specific case for unmerging merged menus, for example.
But you asked if there was a workaround, and here is at least one. :)
Whether you consider this more or less practical or desirable than simply destroying and recreating the TPopupMenu is up to you. Class cracking is a technique which can be useful for getting you out of a jam which might otherwise be impossible to resolve but should definitely be considered a "last resort" !