2

How can I add some context menu to a specific page of Inno Setup?

For example in installing page, if the user right-clicks on-page, he can see "Cancel" or "Pause" menu items which can do some action.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Inside Man
  • 4,194
  • 12
  • 59
  • 119

1 Answers1

3

Inno Setup does not have an API how context menus, not even for handling mouse clicks. So you need to implement a custom Windows message handler and handle WM_CONTEXTMENU.

[Code]

const
  GWL_WNDPROC = -4;
  WM_CONTEXTMENU = $007B;
  WM_COMMAND = $0111;

type
  WPARAM = UINT_PTR;
  LPARAM = LongInt;
  LRESULT = LongInt;

const
  MF_BYPOSITION = $0400;
  MF_STRING = $0000;

const
  ID_MUTE = 0;
  ID_STOP = 1;

function CallWindowProc(
  lpPrevWndFunc: LongInt; hWnd: HWND; Msg: UINT; wParam: WPARAM;
  lParam: LPARAM): LRESULT; external 'CallWindowProcW@user32.dll stdcall';  
function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: LongInt): LongInt;
  external 'SetWindowLongW@user32.dll stdcall';    
function CreatePopupMenu: THandle; external 'CreatePopupMenu@User32.dll stdcall';
function InsertMenu(
  hMenu: THandle; uPosition: Cardinal; uFlags: Cardinal; uIDNewItem: Cardinal;
  lpNewItem: string): Boolean;
  external 'InsertMenuW@User32.dll stdcall';
function TrackPopupMenu(
  hMenu: THandle; uFlags: Cardinal; x: Integer; y: Integer; nReserved: Integer;
  hWnd: THandle; Rect: Integer): Boolean; 
  external 'TrackPopupMenu@User32.dll stdcall';
function ClientToScreen(hWnd: HWND; var lpPoint: TPoint): Boolean;
  external 'ClientToScreen@user32.dll stdcall';
  
var
  OldPageWndProc: LongInt;
  Page: TWizardPage;

function GET_X_LPARAM(dw: DWORD): WORD; // aka LOWORD
begin
  Result := WORD(dw);
end;

function GET_Y_LPARAM(dw: DWORD): WORD; // aka HIWORD
begin
  Result := WORD((dw shr 16) and $FFFF);
end;

function PageWndProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
var
  PopupMenu: THandle;
  X, Y: Integer;
begin
  if uMsg = WM_CONTEXTMENU then
  begin
    X := GET_X_LPARAM(lParam); 
    Y := GET_Y_LPARAM(lParam); 

    PopupMenu := CreatePopupMenu();
    InsertMenu(PopupMenu, -1, MF_BYPOSITION or MF_STRING, ID_MUTE, 'Mute');
    InsertMenu(PopupMenu, -1, MF_BYPOSITION or MF_STRING, ID_STOP, 'Stop');
    TrackPopupMenu(PopupMenu, 0, X, Y, 0, Page.Surface.Handle, 0);
  end
    else
  if uMsg = WM_COMMAND then
  begin
    if wParam = ID_MUTE then
    begin
      MsgBox('Muting', mbInformation, MB_OK);
      Result := 0;
    end
      else
    if wParam = ID_STOP then
    begin
      MsgBox('Stopping', mbInformation, MB_OK);
      Result := 0;
    end;
  end
    else
  begin
    Result := CallWindowProc(OldPageWndProc, hwnd, uMsg, wParam, lParam);
  end;
end;

procedure InitializeWizard();
begin
  Page := CreateCustomPage(wpWelcome, 'Page with context menu', '');

  OldPageWndProc :=
    SetWindowLong(Page.Surface.Handle, GWL_WNDPROC, CreateCallback(@PageWndProc));
end;

procedure DeinitializeSetup;
begin
  SetWindowLong(Page.Surface.Handle, GWL_WNDPROC, OldPageWndProc);
end;

The GWL_WNDPROC code is based on Inno Setup - How to edit the "About Setup" dialog text box.

If you want to add icons, see Adding an icon image to context menu in Inno Setup.

For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library.

enter image description here

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992