Pythonic control of VMware -- ctypes does not provide access to 'menu' gui-control.

zapazap zapazap at yahoo.com
Mon Mar 1 03:24:19 EST 2004


Dear Snake Charming Gurus,

(Was: http://mail.python.org/pipermail/python-list/2004-January/204454.html)

First, a thank you to Tim Golden, Thomas Heller, and Mark Hammond 
for your earlier help with this problem.  I am uncertain about what 
etiquette calls for, but more on that later.

My Objective:
  I am trying to control the _VMWare Desktop_ application
  pythonically on WinXP Professional OS, with hopes of doing 
  the same later on Linux.  (If it can be done easily on Linux 
  I might quit the WinXP work now, but I have never done any
  automation like this on Linux before.)

My Progress:
  I have been trying to extend Simon Brunning's _winGuiAuto_
  module to deal with tabs listviews.  This was the subject
  of my previous thread.  I am facing a more fundamental 
  problem.  I am unable even to exercise the application's
  main menu, which is something I thought winGuiAuto in its
  present form should have been able to do.

My Test:
  I have a unittest that successfully opens and closes both
  _Notepad_ and _Freecell_, but fails to close _VMware_.

My Suspicion:
  I suspect that there is a different "path" (so to speak) to
  the menu control in the VMWare application than exists in
  the other two applications.

Question #1 (My Etiquette):
  Having said "Much Thanks!!" at the end of my first post, should  
  I have written back in thanks, privately or publicly?  I greatly 
  appreciated the help (and helpful it was), but have long thought 
  "thanks in advance" covered the "friction" of followup thanks.  
  But I recently read ESR's "How To Ask Questions The Smart Way" 
  and am not so sure.

Question #2:
  Is my suspicion correct?  Might I have to go through several
  nested controls before I get to the menu control of some apps?
  If so, I think may need to search for some interactive browser
  and/or debugger for windows app to reverse engineer the app's
  structure.  But I fear how much windows arcana I will need to
  learn if I am to do this :-(  -- advice of any sort would sure
  be encouraging.

Question #3:
  Perhaps life would be much easier for me if I do it in Linux?
  (Since most of my work will eventually be done inside the VMs,
  the host OS does not matter so greatly to me.)

Let me say that my goal is for a user to be able to see ONLY the
virtual machine, but be able to have snapshots taken and restored,
devices reassigned, etc, from within the VM without seeing the 
host.  I will just have the VM talk to the Host via sockets, that
is no problem to hack on.  My only difficulty is having a process
on the host actually drive the VMWare application in a way that 
does not require the VM to leave full-screen mode.  Perhaps this
description might help someone in their helping me?

My failing unittest follows.  It was run under Python 2.3.2.

Thank you again!
- Bryan Hann (zapazap AT yahoo DOT com)

---------------snip-------------------------------------------------
# Warning: close all instances of NOTEPAD, FREECELL
# and VMWARE before running this test

import os
import sys
import time
import unittest
import ctypes
import win32con
import win32gui

##########################################
#  begin user configurable values
##########################################

# path to vmware folder
VMWARE_HOME = 'D:\\Progra~1\\VMware\\VMware~1'
# number of seconds to wait for the OS to respond to a change request.
DELAY = 2.0
# command prefix
CMD = 'start cmd /c '    

##########################################
#  end user configurable values
##########################################


class Test_CloseViaMenu(unittest.TestCase):

    def test_freecell(self):
        
        # I document this method; the others are similar.
        
        # text to be found in the title of a freecell application        
        appname = 'FreeCell'           
        command = CMD + appname
        # ensure no currently running instance of the application
        assert not get_hwnds(appname)
        # launch the application
        os.system(command)
        # give it time  
        time.sleep(DELAY)
        # get the (unique) hwnd for the application's main window
        [hwnd] = get_hwnds(appname)    

        # find the id for the Game|Exit menu entry
        hmenu = ctypes.windll.user32.GetMenu(hwnd)
        assert hmenu, 'Application %s has no menu!' % appname
        assert menu_name(hmenu,0)=='&Game'        
        hmenu = ctypes.windll.user32.GetSubMenu(hmenu, 0)
        assert menu_name(hmenu,9)=='E&xit'
        ExitID = ctypes.windll.user32.GetMenuItemID(hmenu, 9)

        # try to close the application
        win32gui.PostMessage(hwnd, win32con.WM_COMMAND, ExitID, 0)

        # give it time  
        time.sleep(DELAY)
        # it should be gone now
        assert not get_hwnds(appname)  

    def test_notepad(self):
        
        appname = 'Notepad'
        command = CMD + appname
        assert not get_hwnds(appname)
        os.system(command)
        time.sleep(DELAY)
        [hwnd] = get_hwnds(appname)
        
        # find the id for the File|Exit menu entry
        hmenu = ctypes.windll.user32.GetMenu(hwnd)
        assert hmenu, 'Application %s has no menu!' % appname
        assert menu_name(hmenu,0) == '&File'        
        hmenu = ctypes.windll.user32.GetSubMenu(hmenu, 0)
        assert menu_name(hmenu,8) == 'E&xit'        
        ExitID = ctypes.windll.user32.GetMenuItemID(hmenu, 8)
        
        win32gui.PostMessage(hwnd, win32con.WM_COMMAND, ExitID, 0)

        time.sleep(DELAY)
        assert not get_hwnds(appname)

    def test_vm(self):
        
        appname ='VMware'
        command = CMD + appname 
        assert not get_hwnds(appname)
        os.system(command)
        time.sleep(DELAY)
        [hwnd] = get_hwnds(appname)

        # find the id for the File|Exit menu entry
        hmenu = ctypes.windll.user32.GetMenu(hwnd)
        assert hmenu, 'Application %s has no menu!' % appname
        assert menu_name(hmenu,0)=='&File'        
        hmenu = ctypes.windll.user32.GetSubMenu(hmenu, 0)
        assert menu_name(hmenu,12)=='E&xit'        
        ExitID = ctypes.windll.user32.GetMenuItemID(hmenu, 8)
        
        win32gui.PostMessage(hwnd, win32con.WM_COMMAND, ExitID, 0)

        time.sleep(DELAY)
        assert not get_hwnds(self.appname)

def menu_name(hMenu,nn):
    """
    Given an hMeny and index nn, return the name of
    the nn-th item in the menu.
    """
    dummy = ctypes.c_buffer("\000" * 32)
    ctypes.windll.user32.GetMenuStringA(
        ctypes.c_int(hMenu),
        ctypes.c_int(nn),
        dummy,
        ctypes.c_int(len(dummy)),
        win32con.MF_BYPOSITION
        )
    return dummy.value

def get_hwnds(text):
    """
    Return list of all top level hwnds with specified
    text in the title.
    """
    fn = win32gui.GetWindowText
    list = []
    win32gui.EnumWindows( (lambda hwnd,acc: acc.append(hwnd)), list)
    return [ hwnd for hwnd in list if text in fn(hwnd) ]
        
if __name__=='__main__':
    fn = sys.getwindowsversion
    win_ver = {4: "NT", 5: "2K", 6: "XP"}[fn()[0]]
    assert win_ver in ["2K","XP"]
    if not win_ver == "2K":
        print >> sys.stderr, 'warning: tested only on windows 2K'
    os.environ['PATH'] = os.environ['PATH'] + ';' + VMWARE_HOME
    unittest.main()


##########################################
# The following is the output I got
##########################################

"""
..F
======================================================================
FAIL: test_vm (__main__.Test_CloseViaMenu)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "U:\test.py", line 95, in test_vm
    assert hmenu, 'Application %s has no menu!' % appname
AssertionError: Application VMware has no menu!

----------------------------------------------------------------------
Ran 3 tests in 5.137s

FAILED (failures=1)
"""



More information about the Python-list mailing list