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

From: zapazap (zapazap_at_yahoo.com)
Date: 03/01/04

  • Next message: ..:: sjf ::..: "Re: Reading selected data from text files"
    Date: 1 Mar 2004 00:24:19 -0800
    
    

    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)
    """


  • Next message: ..:: sjf ::..: "Re: Reading selected data from text files"