Error in pythonic InternetExplorer hta application

Alex Martelli aleaxit at yahoo.com
Thu Jan 11 11:19:06 EST 2001


"Christof Pastors" <christof_n at pastors.de> wrote in message
news:93k5cs$hc5$1 at papyrus.erlm.siemens.de...
    [snip]
> I get a NameError exception because the name "TheAddress" is not found.
> In which object can I find "TheAddress"???
> I guessed "window.TheAddress" and "document.TheAddress" without success.

It's actually:
TheAddress = document.all('TheAddress')

Javascript does peculiar things this way!  Note that TheAddress is
the ID attribute of one tag:

> <input type=text value="http://www.python.org" id=TheAddress style="width:
> expression(document.body.clientWidth - AddText.offsetWidth -
> AddGo.offsetWidth - 45)">

I notice (with curiosity) that the expressions with which the width
attribute is computed ARE evaluated in Javascript (so they can use,
e.g., AddText and AddGo "as if" they were objects!), but that's
normal I guess, since so far nobody's said that Python is the default
language for this HTA, so the default remains Javascript.  Fine,
just be *careful* -- you can't use *Python* expressions here (not
without specifying the language!), it MUST be JavaScript.  The
'general expressions to determine/recompute attributes transparently
at runtime' feature of IE is new enough (5.5?) that I've never
digged deep into it, so I can't yet be of much help about it.


I guess that, for a rich and complex HTA, an OK solution might
be to wrap the real 'document' object into a utility class:

class docWrapper:
    def __init__(self, doc):
        self.__doc = doc
    def __getattr__(self, name):
        try: return getattr(self.__doc, name)
        except AttributeError: return self.__doc.all(name)

for example, with something like

document = docWrapper(document)

at the start, if one insists on using document.Whatever for
BOTH real attributes of the document object AND descendant
objects thereof which have a certain ID (personally, I find
it clearer to distinguish, but, whatever floats your boat).


Or, to be able to just use TheAddress AS IF it was a global
variable, one would have to use black-magic on the module
object -- substituting it with a class-instance with the
required __getattr__ (shades of a very recent thread...) --
I doubt this would work, though (perhaps a good thing:
*explicit is better than implicit*!-).


There's a worse problem, though, which I've met before, but
to which I don't yet know a solution.  Once you've fixed
the 'TheAddress' problem, the script will break again,
right here...:

> TheAddress.onkeypress = clickShortcut

Specifically, a large messagebox with a traceback will
explain to you (it's win32com talking, I think) that
"Object of type 'function' can not be converted to a
COM VARIANT".

And it's right -- there's no normal Python (win32com)
conversion to take a function (clickShortcut) and make
a VARIANT out of it -- and a Variant is what you need
to be able to set a COM object's property, like, here,
the 'onkeypress' property.  I *THINK* that what MS's
HTML DOM expects here is a dispatch-pointer, i.e. in
Python terms a wrapped COM object -- but, I'm not sure,
nor do I know exactly *what* would be expected of
this object.  I've tried finding out experimentally,
by setting such onwhatever properties to wrapped Python
objects that would trace the names and/or ID's of
methods being queried for and/or called, etc, but I've
never been able to find out more.

If anybody *does* know what's supposed to go into the
various .onfoobar properties, when scripting the HTML
DOM from Python, *please* let us all know...!!!  Any
pointers to docs, no matter how obscure, will also be
very welcome -- I'm an old hand at squeezing some bits
of info out of obscure MS docs, it's just that in this
case I've been able to found no shreds thereof.


So, anyway, until we manage to crack the secret of
the .onplok properties, we'll have to catch the events
in other ways.  Note that the same issue applies to
attributes set inline, such as the onclick="navigate()"
which you set in the button element of id AddGo: the
property is apparently just a string, and it's NOT
clear what happens to it when the button is clicked
(even if one sets the further attribute to specify
the language -- language=Python, for example); the
function appears to never execute -- it would be hard
to tell in your version, since 'print' goes nowhere,
but it's easy to add something like:

import win32api

class OdsFile:
    def write(self, what):
        win32api.OutputDebugString(what)
import sys
sys.stderr = sys.stdout = OdsFile()

so that 'print' statements become useful for debugging
(this needs dbmon.exe, or some other ways to watch for
outputdebugstrings being slinged around by processes
not under a debugger -- I guess you could easily use
win32traceutility for the same purpose, I'm just not
used to it since dbmon-plus-OutputDebugString are not
language-dependent so I can use them for multi-language
debugging &c).


There are several other approaches to handling HMTL
DOM events.  win32com's general approaches to event
handling can be used.  Also, the attachEvent method
of HTML DOM elements helps -- the COM class instance'
thus 'attached' is supposed to get its DISPID_VALUE
method (method of DISPID 0 -- the 'default' one --
should be mapped to a method named _value_ according
to DesignatedWrapPolicy) called when the event occurs
(with the event-object as an argument).

The latter doesn't *quite* work, because that policy
only uses _value_ to set the attributes-to-dispids
map, and the 'call' from the HTML DOM is looking more
specifically for a *method*.  Workaround is easy,
though (that is one great thing of Python: while a
very high level language, it DOES 'expose its internals'
so you can, at need, work around issues, problems, bugs,
limitations; just try doing that for most others...:-).


So, anyway, here's a version of your HTA which should
work as (I believe) you intended it to...:

<html>

<head>
  <TITLE>HTML Application Example</TITLE>
  <HTA:APPLICATION ID="HTAEx" APPLICATIONNAME="HTAEx"
    ICON="e.ico" WINDOWSTATE="normal">
</head>

<body>
<span id=AddressBar style="overflow: none">
<span id=AddText>Address</span>
<input type=text value="http://www.python.org" id=TheAddress
 style="width: expression(document.body.clientWidth
   - AddText.offsetWidth - AddGo.offsetWidth - 45)">
<input type=button value="Go" id=AddGo><br>
<span>
<br>
<iframe src="http://www.python.org" id=TheFrame
style="width: 100%; height: 85%"></iframe></span>

<script language=Python>
#
# COM & HTML-DOM infrastructure
#
import win32com.server.util as wsu, win32com.server.dispatcher as wsd
import win32com.server.policy as wsp, win32com.client.gencache as wcg

# redirect out & err as OutputDebugString
import win32api, sys
class OdsFile:
    def write(self, what):
        win32api.OutputDebugString(what)
sys.stderr = sys.stdout = OdsFile()
print "Hta example: Starting"

#
# attach a Python function to handle an HTML DOM event
#
class Handler:
    _public_methods_ = []
    def __init__(self, func):
        self.__func = func
    def _value_(self, evtobj):
        return self.__func(wcg.EnsureDispatch(evtobj))

basepol = wsp.DesignatedWrapPolicy
class MyPolicy(basepol):
    def _wrap_(self, ob):
        basepol._wrap_(self, ob)
        if self._dispid_to_get_.has_key(0):
            self._dispid_to_func_[0] = self._dispid_to_get_[0]
# dsp = wsd.DispatcherOutputDebugString
dsp = None

def handleWith(function):
    return wsu.wrap(Handler(function), usePolicy=MyPolicy,
useDispatcher=dsp)

print "Infrastructure OK"

# stuff specific to this HTA

TheAddress = document.all('TheAddress')

def navigate(event):
    last = "Hello, " + TheAddress.value
    print last

def clickShortcut(event):
    if window.event.keyCode == 13:
        navigate(event)

print "Defs OK, attaching events"
try:
    TheAddress.attachEvent("onkeypress", handleWith(clickShortcut))
    print "Done onkeypress on TheAddress"
    document.all("AddGo").attachEvent("onclick", handleWith(navigate))
    print "Done onclick on AddGo"
except:
    import traceback
    traceback.print_exc()

print "Init done"

</script>
</body>
</html>


Note that all of the output is based on OutputDebugString;
dbmon can be downloaded as a sample application from
http://msdn.microsoft.com/library/devprods/vs6/visualc/vcsample/vcsmpdbmon.h
tm
it's also part of the win32 sdk, and you can also get several
equivalent utilities such as dbwin32 at
http://www.winsite.com/info/pc/win95/programr/dbwin32.zip/
etc, etc.  It shouldn't be hard to change it to use win32trace
instead, if you wish to, since the actual OutputDebugString
call is, after all, in just one place!-)


Alex





More information about the Python-list mailing list