views:

512

answers:

2

hi, is there a send api to access a menu command in another application? For example,I am trying to access the View menu of a notepad. How am I going to do this? I already got the menu using GetSystemMenu but I can't access it. I think this has an API already but I don't know.

+2  A: 

The following code in Python activates the View / Status Bar menu item. You should have no trouble converting it to Delphi as it looks like pseudocode anyway. It selects the 4th menu items across ("View") and the 1st item down ("Status Bar"). If you wanted, you could change it to search for the desired item by text by walking through the items and using GetMenuString. See the MSDN for details.

Note that it doesn't do any error checking. Note also that it expects Notepad's title to be 'Untitled - Notepad'. (You could change that to None to search for anything; I guess that would be nil in Delphi.)

from win32gui import *
from win32con import *
hwnd = FindWindow('Notepad', 'Untitled - Notepad') # use Winspector Spy to find window class name and title
hmenu = GetMenu(hwnd)
hviewmenu = GetSubMenu(hmenu, 3)                   # 3rd menu item across, starting from 0
id = GetMenuItemID(hviewmenu, 0)                   # 0th menu item down ("Status Bar")
PostMessage(hwnd, WM_COMMAND, id, 0)
dangph
+1  A: 

And here some Delphi code.
Be aware that this would not work if you don't have true Menus.
From the help: "GetMenu does not work on floating menu bars. Floating menu bars are custom controls that mimic standard menus; they are not menus. To get the handle on a floating menu bar, use the Active Accessibility APIs."

For instance, it would not work with Delphi itself...

// Grab sub menu for a Window (by handle), given by (0 based) indices in menu hierarchy
function GetASubmenu(const hW: HWND; const MenuInts: array of Integer): HMENU;
var
  hSubMenu: HMENU;
  I: Integer;
begin
  Result := 0;
  if Length(MenuInts) = 0 then
    Exit;

  hSubMenu := GetMenu(hW);
  if not IsMenu(hSubMenu) then
    Exit;

  for I in MenuInts do
  begin
    Assert(I < GetMenuItemCount(hSubMenu), format('GetASubmenu: tried %d out of %d items',[I, GetMenuItemCount(hSubMenu)]));
    hSubMenu := GetSubMenu(hSubMenu, I);
    if not IsMenu(hSubMenu) then
      Exit;
  end;

  Result := hSubMenu;
end;

// Get the caption for MenuItem ID
function GetMenuItemCaption(const hSubMenu: HMENU; const Id: Integer): string;
var
  MenuItemInfo: TMenuItemInfo;
begin
  MenuItemInfo.cbSize := 44;           // Required for Windows 95. not sizeof(AMenuInfo)
  MenuItemInfo.fMask := MIIM_STRING;
  // to get the menu caption, 1023 first chars should be enough
  SetLength(Result, 1023 + 1);
  MenuItemInfo.dwTypeData := PChar(Result);
  MenuItemInfo.cch := Length(Result)-1;
  if not GetMenuItemInfo(hSubMenu, Id, False, MenuItemInfo) then
    RaiseLastOSError;
  // real caption's size. Should call GetMenuItemInfo again if was too short
  SetLength(Result, MenuItemInfo.cch);
  {$WARN SYMBOL_PLATFORM OFF}
  if DebugHook > 0 then
    OutputDebugString(MenuItemInfo.dwTypeData);
end;

procedure Test;
var
  hwnd, hSubMenu: Cardinal;
  id : Integer;
begin
//  hwnd := FindWindow('Afx:00400000:8:00010013:00000000:03F61829', nil); // UltraEdit
//  hSubMenu := GetASubmenu(hwnd, [5,0]);
  hwnd := FindWindow('Notepad', nil); // get the 1st instance of Notepad...
  hSubMenu := GetASubmenu(hwnd, [3]); // 4th submenu Menu aka &View

  if hSubMenu > 0 then
  begin
    id := GetMenuItemID(hSubMenu, 0); // 1st Item in that sub menu (must not be a submenu itself)
    if id > -1 then
    begin
      PostMessage(hwnd, WM_COMMAND, id, 0); 
      ShowMessage('Done: ' + GetMenuItemCaption(hSubMenu, id));
    end
    else
      RaiseLastOSError;
  end
  else
    RaiseLastOSError;
end;
François