Part 1: Reviving Microsoft Agent using PyWin32 - How to Make It Live!

 

I just found an MS Agent characters I haven't seen before, and though to share how to control them from within Python

I just found an MS Agent characters I haven't seen before, and though to share how to control them from within Python

I think this article was driven by the fact that this April, Microsoft discontinued Windows XP. And why is that related? Because if you remember Windows XP, somehow I think you will remember --maybe in annoyance-- this cute little dog called Rover from Windows XP Find Files bar:

Howdy, Rover?

Howdy, Rover? Not even you, now your master was also dead. So sad.. Frown

Long story short, Microsoft did not gain success with this MS Agent stuff. Without even realizing it, one of the first thing I do when re-installing Windows XP is ... get rid of Rover from Windows Explorer! And yeah, unfortunately, I am not the only one.

But still, in this article we are going to use it as a study case on how to properly use PyWin32, an amazing Python package from Mark Hammond, that let your Python application integrate tightly with Windows. For example, do you know that Dropbox client application was built with Python? Laughing

Great, lets dig more on this matter!

Three Different Routes for Windows Integration

There are three different routes you can take to integrate Python application seamlessly to Windows environment, which are:

  1. Using PyWin32 package from Mark Hammond.
    For casual application, this well-maintained and well-supported package is your first bet for a successful Windows integration. Download its latest build here (build 218 as the time of this writing), and chose the correct version for your Python distribution. When I say casual, it means that your application is satisfied with the ability to call Win32 API and automate IDispatch based COM Objects (COM/Active-X Objects that can be automated using scripting environment such as JavaScript/VBScript). A clear example would be this blog, that shows you how to use Python in Excel.
    PS: I love seeing people use Python in many diverse computing environment like that!
  2. Using comtypes package from Thomas Heller
    When you want to automate particular COM Objects that are derived from IUnknown instead of IDispatch, this will be the time for you to use comtypes. It really is amazing knowing what you can do with comtypes. For example, do you know that DropBox client application is a Python application? This article from DropBox Tech Blog may shed some light for you! Or, this codeproject article that demonstrate how to work with custom COM interface, will help you in your next step in Windows integration.
  3. Using ctypes package, also authored by Thomas Heller
    When your Windows integration necessity are beyond custom COM interface, in which you need to call specific DLLs either as part of Windows distribution (such as those reside in kernel32.dll) or wrap your own C function, you will going to need ctypes.

Quick note regarding COM/Active-X: Although it's not dead, COM (Component Object Model) is technically superseded by .NET Framework. In its essence, COM is a software component architecture that let diverse application reuse components from each others to build a working application. As you know that Windows exposed its functionality through Windows API (e.g creating system tray application using Shell_NotifyIcon function), it also exposed other parts of its system using COM object (e.g. INetCfg is an IUnknown based COM Object that let you configure Windows networking configuration). Later on .NET Framework was introduced and at the current moment a new API is come to live: Windows RT. I love hearing those jargon! Laughing

In this article, we are going to look on how to create a better Python application that integrate well in Windows, using PyWin32 packages, specifically its win32com package to automate Microsoft Agent component. We are going to make a talking application that will obediently sit low and still in System Tray, and will speak up the time every each hour. Lets get down to it!

Installing Microsoft Agent

I you are still using Windows XP (woops), then nothing else you should do. MS Agents was right there alright. But if you happen to use Windows 7, go ahead to this page from Microsoft and install a hot-fix to bring back MS Agent. Users who use Windows 8/8.1 may find this support page or this page useful. Another interesting collection of MS Agent characters can be found here.

Through out the rest of the article, I am using James characters as depicted in the above figure. You may want to use another characters of your liking. Be sure to check their supported animation though! For example, James supported animation can be found here

PS: Just found out this open source replacement for MS Agent also support Windows 7 (not sure about Windows 8 though). You may try it, although currently I am using that MS Agent Hot-Fix from Microsoft.

It's alive!

The next question is, "This MS Agent things is a COM object, right? But how do I use it in my Python application?". Thanks to great work by Mark Hammond, we got this awesome PyWin32 package that let us use native Windows API and COM services from within Python environment. Download an install an appropriate distribution for your Python environment from this Sourceforge page.

Let's test our newly installed James agents, and see if we truly bring it to life:

1
2
3
4
5
6
import win32com.client
ag=win32com.client.Dispatch('Agent.Control')
ag.Connected=1
ag.Characters.Load('James')
ag.Characters('James').Show()
ag.Characters('James').Speak('Hi there Pythonist. I see that you brought me back to this world. Thank you!')

A warning though: don't try to save the above code into a *.py file and run it with either python command line or your favorit e IDE. Why? It's because the main thread that start your application will exit immediately and the character won't have time to show itself. Paste the above code into an interactive python REPL session or even ipython if you like, and you'll see a character pop up and speak using a digital speech synthesizer. Pretty cool for a thrown away product, right?

Make it always run: stick it in a System Tray application

This is another example of how awesome PyWin32 package is: create a system tray icon for Python application. As the above code need a main thread that will keep the character showing, we are going to create a Python application that put an icon in the system tray. For the System Tray functionality, I am going to use Simon Brunning's SysTrayIcon class, which is a rip off from Mark Hammond's win32gui_taskbar.py and win32gui_menu.py demos from PyWin32. In this article, I use it without changes. So, credit goes to those guys..

Below is a new Python class that wrapped a single MS Agent characters of your liking. Observe that the code is pretty much straightforward to understand.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
__author__ = 'Eko Wibowo'
 
import systray
import win32com.client
import datetime
import threading
 
class MsAgent(object):
    '''
    Construct an MS Agent object, display it and let it says the time exactly every hour
    '''
    def __init__(self):
        self.agent=win32com.client.Dispatch('Agent.Control')
        self.charId = 'James'
        self.agent.Connected=1
        self.agent.Characters.Load(self.charId)
 
    def say_the_time(self, sysTrayIcon):
        '''
        Speak up the time!
        '''
        now = datetime.datetime.now()
        str_now = '%s:%s:%s' % (now.hour, now.minute, now.second)
        self.agent.Characters(self.charId).Show()
        self.agent.Characters(self.charId).Speak('The time is %s' % str_now)
        self.agent.Characters(self.charId).Hide()
 
    def bye(self, sysTrayIcon):
        '''
        Unload msagent object from memory
        '''
 
        self.agent.Characters.Unload(self.charId)
        self.thread.cancel()
 
if __name__ == '__main__':
    import itertools, glob
 
    icons = itertools.cycle(glob.glob('*.ico'))
    hover_text = "What can I do for you Sir?"
 
    agent = MsAgent()
    menu_options = (('Say the time', icons.next(), agent.say_the_time),)
 
    systray.SysTrayIcon(icons.next(), hover_text, menu_options, on_quit=agent.bye, default_menu_index=1)

Put an *.ico file in the same folder as the application (of course, together with systray.py module). Run it, and you will a see a new icon in the Windows System tray. Right click it, and choose "Say the time". James will obediently follow your command :) 

How to Run This Application?

I saved the main application in a file named oclockreminder.pyw. By using this extension, if this file was double clicked, it will be executed by pythonw.exe, making a non-console application (it's similar to javaw.exe). You can later create a shortcut for this file in Windows Start Menu, and  having it run automatically. Actually, the best way would be to prepare an *.exe installer for this application. We are going to explore this option later on this blog.

Conclusion

Realizing that Python can integrate well in a particular Operating System, bring a window to a whole new level of possibilities. The topic discuss in this article still only touch the surface of what you can do with Python. But it I hope it gives enough foundation to get you started. 

Download the application source directly from this link, or browse through Pythonthusiast public dropbox folder here.

Or, follow its Github Repository: pythonthusiast/oclockreminder.

Stay tuned for my next article!




Leave comments

authimage
  • Well, if you put it that way.... :D
    Wait, I am going to make this creature do more important stuff here!

    • eko
  • This is akin to demon summoning. You are doing the devil's work!

    • RJ

Copyright(c) 2014 - PythonBlogs.com
By using this website, you signify your acceptance of Terms and Conditions and Privacy Policy
All rights reserved