Sign in to follow this  
Followers 0
PaulIA

Menus and WM_SYSCOMMAND

17 posts in this topic

I'm not sure if this is something similar to the ListView question that I raised, but it is curious none the least, so I thought I would ask. If you run the code below, you will see that you can't press Alt+F to open the File menu. You must press and release the Alt key and then press the "F" key to open the menu. I put in a message capture to see if I could spot what is happening and compared this to the messages from a menu created using GUICtrlCreateMenu. Normally, when you press Alt+F, you get messages like:

WM_SYSCOMMAND

WM_INITMENU

However, in the example below, I get:

WM_SYSKEYDOWN

WM_SYSKEYUP

If I press and release the Alt key and then press the "F" key, I get:

WM_SYSKEYDOWN

WM_SYSKEYUP

WM_SYSCOMMAND

WM_INITMENU

Again, this is more of a curiosity thing than anything else. It would be nice if AutoIt was compatible with controls created with CreateWindowEx, but if it's too much of a pain, I understand. I thought I would raise this up to see if this was a bug or if that's just how things need to work.

-Paul

CODE
#include <GUIConstants.au3>

; =================================================================================================
Global Const $MFT_SEPARATOR                     = 0x00000800
Global Const $MFT_STRING                        = 0x00000000

Global Const $MENUITEMINFO                      = "int;int;int;int;int;int;int;int;int;ptr;int"
Global Const $MII_CBSIZE                        = 1
Global Const $MII_FMASK                         = 2
Global Const $MII_FTYPE                         = 3
Global Const $MII_HSUBMENU                      = 6
Global Const $MII_DWTYPEDATA                    = 10

Global Const $MIIM_ID                           = 0x0002
Global Const $MIIM_SUBMENU                      = 0x0004
Global Const $MIIM_TYPE                         = 0x0010
Global Const $MIIM_DATA                         = 0x0020
Global Const $MIIM_STRING                       = 0x0040

Global Const $WM_SYSKEYDOWN                     = 0x0104
Global Const $WM_SYSKEYUP                       = 0x0105
Global Const $WM_SYSCOMMAND                     = 0x0112
Global Const $WM_INITMENU                       = 0x0116

; =================================================================================================

Global $hGUI, $hFile, $hHelp, $hMain

; Create GUI
$hGUI  = GUICreate("Menu Test", 400, 200)

; Create File menu
$hFile = _Menu_CreateMenu()
_Menu_AddMenuItem($hFile, 0, "&New"  )
_Menu_AddMenuItem($hFile, 1, "&Open" )
_Menu_AddMenuItem($hFile, 2, "&Close")
_Menu_AddMenuItem($hFile, 3, "&Save" )
_Menu_AddMenuItem($hFile, 4, ""      )
_Menu_AddMenuItem($hFile, 5, "E&xit" )

; Create Help menu
$hHelp = _Menu_CreateMenu()
_Menu_AddMenuItem($hHelp, 0, "&About")

; Create Main menu
$hMain = _Menu_CreateMenu()
_Menu_AddMenuItem($hMain, 0, "&File", $hFile)
_Menu_AddMenuItem($hMain, 1, "&Help", $hHelp)

; Set the window menu
_Menu_SetMenu($hGUI, $hMain)

GUISetState()

; Set up message capture
GUIRegisterMsg($WM_SYSKEYDOWN, "WM_CAPTURE")
GUIRegisterMsg($WM_SYSKEYUP  , "WM_CAPTURE")
GUIRegisterMsg($WM_SYSCOMMAND, "WM_CAPTURE")
GUIRegisterMsg($WM_INITMENU  , "WM_CAPTURE")

do
  Sleep(10)
until GUIGetMsg() = $GUI_EVENT_CLOSE

; =================================================================================================
Func WM_CAPTURE($hWnd, $iMsg, $iwParam, $ilParam)
  Switch $iMsg
    case $WM_SYSKEYDOWN
      ConsoleWrite("WM_SYSKEYDOWN: " & Hex($iwParam) & " " & Hex($ilParam) & @CR)
    case $WM_SYSKEYUP
      ConsoleWrite("WM_SYSKEYUP .: " & Hex($iwParam) & " " & Hex($ilParam) & @CR)
    case $WM_SYSCOMMAND
      ConsoleWrite("WM_SYSCOMMAND: " & Hex($iwParam) & " " & Hex($ilParam) & @CR)
    case $WM_INITMENU
      ConsoleWrite("WM_INITMENU .: " & Hex($iwParam) & " " & Hex($ilParam) & @CR)
  EndSwitch
  Return $GUI_RUNDEFMSG
EndFunc

; =================================================================================================
Func _Menu_AddMenuItem($hMenu, $iPosition, $sText, $hSubMenu=0)
  Local $iCmdID, $iMask, $iSize, $rMII, $pBuffer, $rBuffer

  $iMask  = BitOR($MIIM_TYPE, $MIIM_ID, $MIIM_SUBMENU)
  $rMII   = DllStructCreate($MENUITEMINFO)
  $iSize  = DllStructGetSize($rMII)
  DllStructSetData($rMII, $MII_CBSIZE  , $iSize   )
  DllStructSetData($rMII, $MII_FMASK   , $iMask   )
  DllStructSetData($rMII, $MII_HSUBMENU, $hSubMenu)
  if $sText = "" then
    DllStructSetData($rMII, $MII_FTYPE, $MFT_SEPARATOR)
  else
    $iSize   = StringLen($sText)
    $rBuffer = DllStructCreate("char[" & $iSize + 1 & "]")
    $pBuffer = DllStructGetPtr($rBuffer)
    DllStructSetData($rBuffer, 1              , $sText     )
    DllStructSetData($rMII   , $MII_FTYPE     , $MFT_STRING)
    DllStructSetData($rMII   , $MII_DWTYPEDATA, $pBuffer   )
  endif
  _Menu_InsertMenuItem($hMenu, $iPosition, True, $rMII)
EndFunc

; =================================================================================================
Func _Menu_CreateMenu()
  Local $aResult

  $aResult = DllCall("User32.dll", "hwnd", "CreateMenu")
  Return $aResult[0]
EndFunc

; =================================================================================================
Func _Menu_InsertMenuItem($hMenu, $iItem, $bByPosition, $rMII)
  Local $aResult

  $aResult = DllCall("User32.dll", "int", "InsertMenuItem", "hwnd", $hMenu, "int", $iItem, "int", _
                     $bByPosition, "ptr", DllStructGetPtr($rMII))
  Return ($aResult[0] <> 0)
EndFunc

; =================================================================================================
Func _Menu_SetMenu($hWnd, $hMenu)
  Local $aResult

  $aResult = DllCall("User32.dll", "int", "SetMenu", "hwnd", $hWnd, "hwnd", $hMenu)
  Return $aResult[0]
EndFunc

Auto3Lib: A library of over 1200 functions for AutoIt

Share this post


Link to post
Share on other sites



You will not believe it but if you add a button it works. at least for the main menu acceleration.

I will have a look.

I notice you are reimplementing GUICtrlCreateMenu and GUICtrlCreateMenuItem. I don't know why.

for the CreateWindowEx I don't know why you really need it as the GUICtrlCreate is using it, but I did a fix (I hope) for the crash in 3.2.1.14

Share this post


Link to post
Share on other sites

@PaulIA

As an API extremist, can you help me to understand why adding the button allow the acceleration key to activate the menu.

Thanks for your knowledge ;)

PS I add a file working as yours but with GUICtrlCreatMenu

Share this post


Link to post
Share on other sites

I spent some time this morning working on this. Here are some additional observations:

It appears that you can create any control (including a label) and the accelerator keys will work. This is true with the script that you attached using the GUICtrlCreate methods and also using Auto3Lib to create the controls using CreateWindowEx.

In a normal application, when you hold down the Alt key, you do not get any messages until you press an accelerator key. After you press the accelerator key, you then get various menu commands. The message sequence normally looks something like:

WM_SYSCOMMAND
WM_INITMENU
WM_MENUSELECT
WM_INITMENUPOPUP
WM_MENUSELECT
WM_ENTERIDLE

In AutoIt (without a control), when you hold down the Alt key, you immediately start getting WM_SYSKEYDOWN messages. You receive these as long as you hold the Alt key down. If you press an accelerator key while holding the Alt key down, you get a WM_SYSKEYDOWN message for the accelerator. When you release the Alt key, you get a WM_SYSKEYUP message. The message sequence looks something like:

WM_SYSKEYDOWN
WM_SYSKEYDOWN
WM_SYSKEYDOWN
WM_SYSKEYDOWN
WM_SYSKEYUP

In AutoIt (without a control), if you press the Alt key and immediately release it, you get the following message sequence:

WM_SYSKEYDOWN
WM_SYSKEYUP
WM_SYSCOMMAND
WM_INITMENU
WM_MENUSELECT
WM_INITMENUPOPUP
WM_MENUSELECT
WM_ENTERIDLE

This highlights the main menu and you can then press the accelerator key to select the menu item.

As I do not have access to the source, any speculation on my part as to where the problem lies would be worthless. However, my instinct would be to start looking at the AutoIt message handlers and see if there is anything that would short circuit the generation of the WM_SYSCOMMAND.

I will try to find some time to investigate further and will let you know if I find anything else.


Auto3Lib: A library of over 1200 functions for AutoIt

Share this post


Link to post
Share on other sites

One thing to consider is TranslateAccelerator(). I don't think AutoIt calls it since there isn't a built-in accelerator table. But it could be that a "normal" application does use that function, which could theoretically affect the messages. I believe that even wizard-generated Windows applications use an accelerator table. It may be that TranslateAccelerator() does nothing for this particular case, but on the other hand, I think it's a variable that needs looked at so the exact impact can be determined.

Share this post


Link to post
Share on other sites

One thing to consider is TranslateAccelerator(). I don't think AutoIt calls it since there isn't a built-in accelerator table. But it could be that a "normal" application does use that function, which could theoretically affect the messages. I believe that even wizard-generated Windows applications use an accelerator table. It may be that TranslateAccelerator() does nothing for this particular case, but on the other hand, I think it's a variable that needs looked at so the exact impact can be determined.

How does that relate to the fact that adding a button control make the menu selection OK?

Share this post


Link to post
Share on other sites

Reply to the first post:

Absolutely normal behaviour! ;)

Just do this to see/compare it:

- make a copy of the Windows tool "Soundrecorder" (sndrec32.exe) into a temp. folder

- open the copy into ResourceHacker and remove all controls from the main dialog (100).

- recompile it and save this

- open the changed exe

-> same messages from this dialog

-> where is the problem?

With the TranslateAccelerator() things -> good question.

Maybe there is also something with "wm_command" - just a nightly idea... :lmao:

Greets

Holger

Share this post


Link to post
Share on other sites

Reply to the first post:

Absolutely normal behaviour! ;)

Just do this to see/compare it:

- make a copy of the Windows tool "Soundrecorder" (sndrec32.exe) into a temp. folder

- open the copy into ResourceHacker and remove all controls from the main dialog (100).

- recompile it and save this

- open the changed exe

-> same messages from this dialog

-> where is the problem?

With the TranslateAccelerator() things -> good question.

Maybe there is also something with "wm_command" - just a nightly idea... :lmao:

Greets

Holger

I'm not sure I understand what you are calling "normal behaviour". Are you saying that if I build a GUI with only a menu on it that I should not be able to select the menu unless I have a control on the GUI?

Auto3Lib: A library of over 1200 functions for AutoIt

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Did you test it with my steps?

Do you know an application which has only a menu so we could compare it?

Greets

Holger

Here's a quick one that I built in Delphi. It only has a menu on it and I can select the menu.

Edit: Used resource hacker to make sure there are NO controls except the menu and menu items.

Edited by PaulIA

Auto3Lib: A library of over 1200 functions for AutoIt

Share this post


Link to post
Share on other sites

Would be nice to see the source to it ;)

But only later...have to go sleep...

Here you go:

program Test;
uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Menus;
type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Exit1: TMenuItem;
    Help1: TMenuItem;
    About1: TMenuItem;
  private
  public
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
end.

object Form1: TForm1
  Left = 319
  Top = 172
  Width = 191
  Height = 183
  Caption = 'Menu Test'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  Menu = MainMenu1
  Position = poDesktopCenter
  PixelsPerInch = 96
  TextHeight = 13
  object MainMenu1: TMainMenu
    Left = 42
    Top = 14
    object File1: TMenuItem
      Caption = '&File'
      object Exit1: TMenuItem
        Caption = 'E&xit'
      end
    end
    object Help1: TMenuItem
      Caption = '&Help'
      object About1: TMenuItem
        Caption = '&About'
      end
    end
  end
end

Auto3Lib: A library of over 1200 functions for AutoIt

Share this post


Link to post
Share on other sites

I would prefer something other than Delphi, something using the Windows API directly. A C program would be ideal because I don't trust that Delphi isn't doing stuff outside the norm for the Windows API.

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

I would prefer something other than Delphi, something using the Windows API directly. A C program would be ideal because I don't trust that Delphi isn't doing stuff outside the norm for the Windows API.

You have the Dev-C++?

if so there is a WinMenu example.

Not sure if that will help or not.

Edit: don't think that one will help, it's only using WM_COMMAND.

Edited by gafrost

SciTE for AutoItDirections for Submitting Standard UDFs

 

Don't argue with an idiot; people watching may not be able to tell the difference.

 

Share this post


Link to post
Share on other sites

You have the Dev-C++?

if so there is a WinMenu example.

Not sure if that will help or not.

Edit: don't think that one will help, it's only using WM_COMMAND.

however, it behaves exactly like the AU3 code of PaulIA. Press ALT, release it and then F to get the File Menu...

Cheers

Kurt


__________________________________________________________(l)user: Hey admin slave, how can I recover my deleted files?admin: No problem, there is a nice tool. It's called rm, like recovery method. Make sure to call it with the "recover fast" option like this: rm -rf *

Share this post


Link to post
Share on other sites

#16 ·  Posted (edited)

however, it behaves exactly like the AU3 code of PaulIA. Press ALT, release it and then F to get the File Menu...

Cheers

Kurt

if one adds TranslateMessage(&messages) to the message loop, the DEV-C++ sample works as expected. As an alternative one can install an accelerator table - see TranslateAccelerator(). I guess, "simple" Windows applications call TranslateMessage() if they have not installed an accelerator table. According to MSDN TranslateMessage() and TranslateAccelerator() should not be used together...

MSDN LINK

Cheers

Kurt

Edited by /dev/null

__________________________________________________________(l)user: Hey admin slave, how can I recover my deleted files?admin: No problem, there is a nice tool. It's called rm, like recovery method. Make sure to call it with the "recover fast" option like this: rm -rf *

Share this post


Link to post
Share on other sites

I think TranslateMessage() was the function I was trying to think of earlier when I found TranslateAccelerator() on MSDN.

Share this post


Link to post
Share on other sites

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  
Followers 0