Jump to content
Sign in to follow this  
Tommi Prami

How to get position of Top-level TMenuItem of Main menu

Recommended Posts

I would need to get position of MenuItem, of top level TMenuItem, so always visible.

 

I would like to show balloonhint on top of that, to tell user that, you should go in this menu, to process stuff, pretty much now, if suints your timetable 😉

 

-Tee-

Edited by Tommi Prami

Share this post


Link to post

GetMenuBarInfo API should help.

 

I have no Delphi at hand at the moment but the following quick test works in Lazarus:


function GetWindowMenuItemRect(Handle: THandle; Index: Integer): TRect;
var
  Info: MENUBARINFO;
begin
  FillChar(Info, SizeOf(Info), 0);
  Info.cbSize := SizeOf(Info);
  Win32Check(GetMenuBarInfo(Handle, $FFFFFFFD, Index, @Info));
  Result := Info.rcBar;
end;

function GetScreenCanvas: TCanvas;
begin
  Result := TCanvas.Create;
  try
    Result.Handle := GetDC(0);
    Result.Pen.Style := psSolid;
    Result.Pen.Color := clRed;
    Result.Brush.Style := bsClear;
  except
    Result.Free;
    raise;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  C: TCanvas;
  I: Integer;
begin
  C := GetScreenCanvas;
  try
    for I := 0 to MainMenu1.Items.Count - 1 do
      C.Rectangle(GetWindowMenuItemRect(Handle, I + 1));
  finally
    C.Free;
  end;
end;

 

windowmenuitemrect.png

Edited by Ondrej Kelle

Share this post


Link to post

Thanks man, I'll check...

Gort to make couple of changes to make it compile, weirdly it did not allow me to assign Returned rect to result.


Type in Delphi seems to be TMenuBarInfom and last parameter of GetMenuBarInfo is defined as Var-parameter. So I think I'll have to pass the whole record, not just pointer.

 

 

function GetWindowMenuItemRect(const AHandle: THandle; const AIndex: Integer): TRect;
var
  LMenuBarInfo: TMenuBarInfo;
begin
  FillChar(LMenuBarInfo, SizeOf(LMenuBarInfo), 0);
  LMenuBarInfo.cbSize := SizeOf(LMenuBarInfo);
  {$WARN SYMBOL_PLATFORM OFF}
  Win32Check(GetMenuBarInfo(AHandle, Integer($FFFFFFFD), AIndex, LMenuBarInfo));
  {$WARN SYMBOL_PLATFORM ON}

  Result.Top := LMenuBarInfo.rcBar.Top;
  Result.Left := LMenuBarInfo.rcBar.Left;
  Result.Height := LMenuBarInfo.rcBar.Height;
  Result.Width := LMenuBarInfo.rcBar.Width;
end;

 

It just gives me System Error.  Code: 1400. Invalid window handle
 

Tried to pass Windows Handle, MenuITems handle, did not Accept it... 😞

Weird. 

 

-Tee-

Share this post


Link to post

Tried all combinations of different handles and indexes, I could think of... Damnation. Now have to go tortured by the Dentist, and not event the Delphi-coding one 😞

Share this post


Link to post

Your fix looks fine to me. Apart from the var parameter declaration the rest should be the same. Pass it the form's Handle (like in my example, I'm passing it Self.Handle where Self is the instance of the form).

 

Good luck with the Dentist! 😉

Edited by Ondrej Kelle

Share this post


Link to post

I've checked it with Delphi XE. The declaration of TMenuBarInfo in Windows unit seems to be wrong (packed record, 29 bytes). Simply redeclare it in your unit:

 

type
  {$A8} // default quadword-alignment
  PMenuBarInfo = ^TMenuBarInfo;
  tagMENUBARINFO = record
    cbSize: DWORD;
    rcBar: TRect;
    hMenu: HMENU;
    hwndMenu: HWND;
    { fBarFocused:1: BOOL;} { bar, popup has the focus }
    { fFocused:1: BOOL; }  { item has the focus }
    FocusedBits: BYTE;
  end;
  TMenuBarInfo = tagMENUBARINFO;

function GetMenuBarInfo(hend: HWND; idObject, idItem: Longint;
  var pmbi: TMenuBarInfo): BOOL; stdcall; external user32 name 'GetMenuBarInfo';

 

which makes  the record size 32 bytes, then it works:

 

menuitemrect.png

 

BTW, it's already fixed in Rio.

 

Edited by Ondrej Kelle
  • Like 1

Share this post


Link to post

Thanks,

 

I see nothing wrong in that, but I just can't see why Compiler does not like it. It might be something trivial, but I just can't figure it out.

 

TMenuBarInfo = tagMENUBARINFO; // <- [dcc32 Error] : E2029 '=' expected but identifier 'TMenuBarInfo' found

 

 

Share this post


Link to post
1 hour ago, Tommi Prami said:

Thanks,

 

I see nothing wrong in that, but I just can't see why Compiler does not like it. It might be something trivial, but I just can't figure it out.

 

TMenuBarInfo = tagMENUBARINFO; // <- [dcc32 Error] : E2029 '=' expected but identifier 'TMenuBarInfo' found

 

 

Sorry, I've been vague. Here's the full code which works in Delphi XE, I hope it helps:

GetWindowMenuItemRect example

Cheers!

  • Like 1

Share this post


Link to post

Thanks,

 

Can't see any difference but if I copy record and external method declarations from github, it compiles. weirdest thing ever 😄

Edited by Tommi Prami

Share this post


Link to post
30 minutes ago, Tommi Prami said:

Thanks,

 

Can't see any difference but if I copy record and external method declarations from github, it compiles. weirdest thing ever 😄

Just a thought... Sounds like the units in your uses clause are listed in a different order so the type declarations resolve differently.

Share this post


Link to post

type
  {$A8} // default quadword-alignment
  PSTMenuBarInfo = ^TSTMenuBarInfo;
  tagSTMENUBARINFO = record
    cbSize: DWORD;
    rcBar: TRect;
    hMenu: HMENU;
    hwndMenu: HWND;
    { fBarFocused:1: BOOL;} { bar, popup has the focus }
    { fFocused:1: BOOL; }  { item has the focus }
    FocusedBits: BYTE;
  end;
  TSTMenuBarInfo = tagSTMENUBARINFO;

function STGetMenuBarInfo(hend: HWND; idObject, idItem: Longint; var pmbi: TSTMenuBarInfo): BOOL;
  stdcall; external user32 name 'GetMenuBarInfo';

function GetWindowMenuItemRect(const AHandle: THandle; const AIndex: Integer): TRect;
var
  LMenuBarInfo: TSTMenuBarInfo;
begin
  FillChar(LMenuBarInfo, SizeOf(TSTMenuBarInfo), 0);
  LMenuBarInfo.cbSize := SizeOf(TSTMenuBarInfo);
  {$RANGECHECKS OFF}
  {$OVERFLOWCHECKS OFF}
  {$WARN SYMBOL_PLATFORM OFF}
  Win32Check(STGetMenuBarInfo(AHandle, Longint($FFFFFFFD), AIndex, LMenuBarInfo));
  {$WARN SYMBOL_PLATFORM ON}
  {$OVERFLOWCHECKS ON}
  {$RANGECHECKS ON}
  Result := LMenuBarInfo.rcBar;
end;
Changed types and method name to make sure it uses different types, added some far fetched compiler defines. Same code works using rest of your code. But failed on app that woulöd actually need to use it :). Have an idea tough... Gives me System Error.  Code: 1400. Invalid window handle

 

LEt me see... 

 

btw... Thanks for help!

  • Like 1

Share this post


Link to post

Could this be messed up with visual form inheritance.

 

It works in clean demo app, basically your code, with those name changes I've made. Have to typecast the constant because otherwise I'll get an Constant violates subrange bounds error, but not even that matter/happen in a clean new App.

 

-Tee-

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
Sign in to follow this  

×