Docstoc

Python and COM

Document Sample
Python and COM Powered By Docstoc
					Python and COM

    Greg Stein
  Mark Hammond
            Tutorial Agenda
•   Introduction to COM
•   PythonCOM Framework
•   Using Python as a COM client
•   Using Python as a COM server
•   Advanced topics
•   Futures
Introduction to COM
      COM/OLE and ActiveX
• Component Object Model
  – Specification for implementing and defining
    objects
• OLE is the old name, COM is the official
  name, ActiveX is the marketing name
  – All the same - the Microsoft Marketing
    Machine
     What is a COM interface?
• A technique for implementing objects
  – Uses a “vtable” to define methods
  – Does not have properties
• Basically a C++ object with only public
  methods
  – But not C++ specific - just borrowed
    implementation technique
  – Allows implementation from other languages
  – All interfaces have a unique ID
                IUnknown
• Base class of all interfaces
  – Every COM object must implement
• Defines object lifetimes
  – Reference counted using “AddRef” and
    “Release” methods
• Defines techniques for querying the object
  for something useful
  – QueryInterface method
• No other functionality - simply defines
  these 3 methods
           Custom interfaces
• Any interface derived from IUnknown
  – Therefore must follow IUnknown rules for
    lifetimes
• Derived?
  – Means object can be used as its base. Simple
    implementation using vtables
• All interfaces are custom interfaces
  – Not a standard part of COM per-se, just follow
    its rules
        Objects vs. Interfaces
• Interfaces simply define functionality
• Objects, once instantiated, implement the
  interface
  – Object class also has unique ID
• Objects always provide multiple interfaces
  – At least IUnknown and some other functional
    interface
  CLSIDs, GUIDs, UUIDs, IIDs
• COM defines 128 bit identifier, and API for
  creating them
  – “High degree of certainty” that these are
    globally unique
• All use the same type and implementation,
  acronym reflects different usage
  – IID == Interface ID, GUID == Globally Unique
    Identifier, CLSID == Class ID, UUID ==
    Universally Unique Identifier, etc
  – API for converting to and from Strings
         Registering Objects
• Objects register themselves in the Windows
  Registry
  – Register with their Unique CLSID
• Also register a name for the object
  – COM provides API for translating, but names
    are not guaranteed unique
  – Many objects self register
             Creating Objects
• Standard COM API for creation
  – CoCreateInstance, passing:
     • CLSID identifying the object to create
     • IID identifying the initial interface requested
         C++ Pseudo Code
// Create an object, but only get back
// IUnknown pointer, with new reference
IUnknown *pUnk =
  CoCreateInstance(“MyObject”, ...)
// Ask the object for a pointer to a
  useful implementation!
pUseful = pUnk->QueryInterface(
  IID_IUseful, ...)
pUnk->Release(); // finished with this.
pUseful->DoSomethingUseful();
…
pUseful->Release(); // Object now dies…
    Custom Interface Example
                    1 of 2
• Native Interfaces using Word
  – You would be unlikely to use Word this way
  – Demonstrative purposes only!
• >>> import ni, win32com, pythoncom
  >>> o=pythoncom.CoCreateInstance \
     ("Word.Application", None,
     pythoncom.CLSCTX_ALL,
     pythoncom.IID_IUnknown)
  >>> o
  <PyIUnknown at 0x834b04 with obj at
  0x423474>
    Custom Interface Example
                      2 of 2
• >>> o.QueryInterface( \
             pythoncom.IID_IPersist)
  <PyIPersist at 0x85dd54 with obj at
  0x423464>
• Almost identical to the pseudo code above
  – In fact, Python is far better than C++, as long as
    we support the required interfaces natively
• No AddRef or Release required, or even
  exposed
  – Release() currently exposed, but not for long!
   IDispatch - poor man’s COM
                     1 of 2
• Also known as “Automation”
• Derived from IUnknown
• Defines vtable methods to determine
  “dispatch methods and properties” at
  runtime
  – Perfect for scripting languages which have no
    compile step, or which are not C++!
• Optionally uses Type Libraries so
  optimizations can be made at compile time
  IDispatch - poor man’s COM
                      2 of 2
• What many people know as COM
  – Microsoft marketing machine
  – In reality, a small, but somewhat useful part of
    COM
• Many useful COM interfaces do not support
  IDispatch
  – Native MAPI, Active Scripting/Debugging,
    ActiveX Controls, Speech Recognition, etc
• Very hard from C/C++, very easy from VB
             Core Interfaces
              Introduction
• COM tends to use interfaces for everything.
  Example:
  – Instead of using a file pointer/handle, a
    “Stream” interface is used, which provides file
    like semantics
  – Anyone free to implement the stream interface
    using any technique they choose
  – Such interfaces not necessarily part of COM
    per-se, but we consider them “core interfaces”
            Core Interfaces
             Enumerators
• Enumerators provide access into a list of
  values
• Provides Next, Skip, Reset and Clone
  methods
• Different enumerator interfaces for different
  types:
  – IEnumGUID - Enumerate list of GUID’s
  – IEnumFooBar - Enumerate list of FooBars!
             Core Interfaces
              Collections
• Alternative technique for accessing lists
• Usually only used via IDispatch
  – Uses “tricks” only IDispatch has available, such
    as properties!
  – Therefore not a real interface
• Used to provide array like semantics for
  VB, etc
  – Methods include Count() and Item(). Count
    often implied by len(), Item() often omitted.
            Core Interfaces
         Streams and Storage
• IStream provides file like semantics
• IStorage provides file system like semantics
• Programs can write to this specification
  without needing to know the destination of
  the data
• COM provides implementations of these for
  “structured storage files”
             Core Interfaces
               Monikers
• Provide file name to object mapping
  semantics
• Fundamental concept is to provide an
  indirection level to an underlying object,
  and a program neutral way of accessing the
  underlying object
  – File and URL Monikers do just that
  – Pointer monikers allow anyone to implement an
    abstract indirection to an object (e.g., into a
    message store, etc)
           Core Interfaces
          ConnectionPoints
• Provides simple callback functionality
• Client sets up connection point object
• Object passed to Connection Point
  Container object
• Container calls methods on the Connection
  Point when appropriate
• Typically used as an event mechanism (e.g.,
  ActiveX Controls). This is how VB finds
  the list of events for an object.
            Core Interfaces
             And the Rest
• Plenty of others not listed here
• Anything in core PythonCOM is considered
  core
  – By us, anyway - YMMV :-)
• Check out the sources, Help Files, or
  forthcoming documentation
  – Who was going to write that?
  – Extensions to PythonCOM - present and future
             Error Handling
• All methods use HRESULT return code
  – Multiple “success” codes, and many failure
    codes
  – ISupportErrorInfo interface for richer error
    information
• IDispatch uses EXCEP_INFO structure
• PythonCOM transparently maps these
• More detail in Server section
PythonCOM Framework
      PythonCOM Framework
• Supports use of Python for both COM
  servers and COM clients
• Easy for the Python programmer
• Dispatch friendly with core support for
  most common vtable interfaces
• Easily extended for new vtable interfaces
      PythonCOM Extensions
• Model allows for COM extension DLLs
  – Once loaded, looks like native support to the
    Python programmer
• MAPI, ActiveX Scripting and Debugging
  all use this technique
  – Import them once, and PythonCOM will serve
    up their interfaces
• Makes for stable core, with more frequent
  extension releases
Using Python as a COM client
         Python COM Clients
             The Problem
• Calling a COM object from Python
• COM = vtable = C++ (not Python)
• IDispatch removes vtable requirement
  – Imposes coding burden on client
  – IDispatch is still vtable based, so core problem
    remains
         Python COM Clients
             The Answer
• We need an intermediary between a Python
  object and COM’s vtables
  – These are called “interfaces” (c.f. “gateways”
    for the server side - poor choice of terminology,
    but this is what we use!)
      PythonCOM Interfaces
                     1 of 3
• Very similar to standard Python extension
  modules
• Conceptually identical to wrapping any C++
  object in Python
  – 1:1 mapping between the COM pointer and
    Python object
  – Pulls apart arguments using PyArg_ParseTuple
  – Makes call on underlying pointer
  – Handles errors, exceptions, and return values
   PythonCOM Interfaces
                        2 of 3


                      Interface                         Server
Client                Interface
                      Interface




                                    v-table interface
                        PythonCOM




         Python C++
         PythonCOM Interfaces
                     3 of 3


Client     Wrapper           Interface                              Server
                                  IDispatch




                                              IDispatch interface
                              PythonCOM




                Python C++
         IDispatch vs. vtable
• IDispatch implemented in PythonCOM.dll
  like any other interface
  – No Dynamic logic implemented in DLL
  – Only GetIDsOfNames and Invoke exposed
• win32com.client Python code implements
  all IDispatch logic
• Calls the above 2 methods dynamically to
  obtain method and property information
     IDispatch Implementation
• 2 modes of IDispatch usage
  – Dynamic, where no information about an object
    is known at runtime
     • All determination of methods and properties made at
       runtime
  – Static, where full information about an object is
    known before hand
     • Information comes from a “Type Library”
     • Not all objects have Type Libraries (including
       Python objects!)
Dynamic IDispatch Implementation
                     1 of 5
• Implemented by win32com.client.dynamic
  – Also makes use of win32com.client.build
  – Uses __getattr__ and __setattr__ methods in
    Python to implement its magic
Dynamic IDispatch Implementation
                      2 of 5
• Not perfect solution as
  – __getattr__ has no idea if the attribute being
    requested is a property reference or a method
    reference
  – No idea if the result of a method call is required
    (i.e., is it a sub or a function)
  – Python must guess at the variable types
  – Big problem tends to be “byref” params - by
    default these are not handled
Dynamic IDispatch Implementation
                    3 of 5
• win32com.client.Dispatch kicks it all off
• Demo
>>>   import ni
>>>   from win32com.client import Dispatch
>>>   w=Dispatch(“Word.Application”)
>>>   w.Visible = 1
• Starts Winword, and makes it visible
Dynamic Dispatch Implementation
                        4 of 5
• Pros
  – No setup steps - just works
  – Provides quick scripting access to components
• Cons
  – Relatively slow
  – You need to know the object model of the
    target. Not self documenting.
     • Actually, Python can make many objects self
       documenting, but this is beyond the scope of this
Dynamic Dispatch Implementation
                        5 of 5
• Smart Dispatch vs. Dumb Dispatch
  – To overcome some potential problems, Python
    attempts to use Type Info even for dynamic
    objects
  – Slows down considerably for certain objects
  – win32com.client.DumbDispatch provides
    alternative implementation which does not
    attempt to locate type information
     • For many servers, will provide excellent results and
       speed
 Static Dispatch Implementation
                    1 of 4
• Generates .py file from Type Information
  – win32com.client.makepy does this
• Python code then imports this module
• Python knows everything about the object
  – No confusion between methods and properties
  – Byref args handled correctly
  – No dynamic lookups - much faster
 Static Dispatch Implementation
                  2 of 4
• Demo
C:\> cd “\Program Files\Microsoft
  Office\Office”
C:\> \python\python
  \python\win32com\client\makepy.py
  msword8.olb > \python\msword8.py
...
C:> start python
>>> import msword8 # grind, grind :-)
>>> w = msword8.Application()
>>> w.Visible = 1
 Static Dispatch Implementation
                        3 of 4
• Pros
  – ByRef args handled correctly
     • Result becomes a tuple in that case
  – All types handled correctly
     • Python knows the type required, so doesnt have to
       guess. More scope to coerce
  – Significantly faster
  – Python source file documents methods and
    properties available
 Static Dispatch Implementation
                         4 of 4
• Cons
  – Need to hunt down type library
  – Need to enter cryptic command to generate
    code
  – No standard place to put generated code
  – Compiling code may take ages
     • Not real problem, as this is a once only step
  – Type library may not be available
• Many Cons listed are not permanent - help
  would be appreciated!
Dispatch, VARIANTs and Python
            Types
• VARIANT
 – COM concept for IDispatch interface
    • Just a C union, with a flag for the type, and an API
      for manipulating and converting
 – IDispatch always uses VARIANT objects
    • In reality, COM is not typeless - most servers
      assume a particular type in the variant
 – Most (only!) complex code in PythonCOM
   deals with VARIANT conversions
Dispatch, VARIANTs and Python
            Types
• Python has 2 modes of conversion
  – Python type drives VARIANT type
     • Python knows no better
     • Creates VARIANT based on type of Python object
  – Known type drives VARIANT type
     • For static IDispatch, Python often known exactly
       type required
     • Attempt to coerce the Python object to VARIANT
       of this type
       win32com.client Files
                     1 of 2
• makepy.py, dynamic.py
  – Static and dynamic IDispatch implementations
    respectively
• build.py
  – Utility code used by both modules above
• CLSIDToClass.py
  – Manages dictionary of Python classes, mapped
    by CLSID. Code generated by makepy.py
    automatically populates this.
        win32com.client Files
                       2 of 2
• combrowse.py
  – Basic COM browser that requires Pythonwin.
    Simply double-click on it.
• tlbrowse.py
  – Basic Type Library browser that requires
    Pythonwin
• util.py
  – Utiility helpers
• connect.py
  – Connection point client base class
    Client Side Error Handling
                        1 of 2
• Client interfaces raise
  pythoncom.com_error exception
• Exception consists of:
  – HRESULT
     • 32 bit error code, defined by OLE
  – Error Message
     • Should be language independant
  – (cont.)
    Client Side Error Handling
                       2 of 2
• COM Exception Tuple
  – Tuple of (wcode, AppName, AppMessage,
    HelpFile, HelpContext, scode), all of which are
    application defined
  – Exception object itself, or any part of it, may be
    None
• Arg Error
  – Integer containing the argument number that
    caused the error
  – Often None if error does not relate to specific
    argument
SWIG and COM Client Interfaces
                      1 of 3
• Recent changes to SWIG allow it to
  generate client side native interfaces
  – ie, any custom interface not based on IDispatch
    can be generated
• Uses existing SWIG functionality and M.O.
  – ie, maintain .i files, and SWIG generates .c files
• Native MAPI support generated this way
  – Pretty serious API, and it works a treat!
SWIG and COM Client Interfaces
                     2 of 3
• Sample .i from MAPI
#define TABLE_SORT_DESCEND TABLE_SORT_DESCEND
  HRESULT MAPIInitialize( MAPIINIT_0 *INPUT);
  HRESULT MAPILogonEx(
      ULONG INPUT,
      TCHAR *inNullString,
      ...
      IMAPISession **OUTPUT
  );
SWIG and COM Client Interfaces
                           3 of 3
• Notes
  –   #defines are carried into module
  –   Many functions are completely trivial
  –   SWIG handles input/output params
  –   Scope for even better integration with COM
       • e.g., maybe the first cut at the .i could be generated
         from the Type Info.
  – More work and discussions with Dave Beazley
    needed!
Using Python as a COM server
        Python COM Servers
            The Problem
• Exposing a Python object as a COM object
• COM = vtable = C++ (not Python)
• IDispatch removes vtable requirement
  – Imposes coding burden on client
  – Some interfaces are defined as a vtable
• Answer: we need an intermediary between
  COM’s vtables and a Python object
  – These are called “gateways”
                Gateways
                    1 of 2
• Gateways act as the intermediary
  – Hold reference to the Python object
  – Map C++ method calls into Python calls
  – Map parameters and return values
• A gateway is a C++ object implementing a
  particular COM interface
• Gateways are registered with the framework
  and instantiated as needed to support
  particular interfaces as they are requested
                Gateways
                    2 of 2
• The default gateway supports IDispatch
  – All Python COM servers automatically support
    IDispatch
• Default also supports ISupportErrorInfo, a
  standard interface for returning extended
  error information
                               Gateways
                                   3 of 3

                             Gateway      Wrapper       Server
Client                       Gateway
                             Gateway
         v-table interface




                                            PythonCOM




                                   C++ Python
      Calling Python Methods
• The Python COM framework defines an
  IDispatch-oriented protocol for how the
  gateways call into Python:
  – _QueryInterface_ : determine support for a
    particular COM interface
  – _GetIDsOfNames_ : look up a dispatch
    identifier (DISPID) for a given name
  – _Invoke_ : invoke a method with specified
    parameters
                   Policies
                      1 of 2
• “Features” of the gateway protocol:
  – Non-intuitive for a Python programmer
  – Usually requires support structures for the
    DISPID handling
  – Subtleties with some of the parameters and
    return values
• Result: hard for Python programmers to
  write a COM server
• Answer: “policy objects”
                  Policies
                     2 of 2
• A “policy” specifies how to implement a
  Python COM server
• The policy object maps the gateway
  protocol to the given implementation policy
• The default policy is usually sufficient
• Custom policies may be created and used
  – An advanced topic (discussed later)
               Instantiation
                     1 of 3
• The framework calls the CreateInstance
  function in the win32com.server.policy
  module
  – Hard-wired call to CreateInstance, but behavior
    can easily be hooked through custom policies
• When your COM object is registered, an
  additional registry key specifies the creator
  function
  – Typically “mymodule.MyClass”
               Instantiation
                     2 of 3
• The registry key is read by the default
  policy and used to instantiate your object
• COM does not provide additional
  parameters to your creator function (the
  __init__ method)
  – Make sure that any parameters have defaults
  – COM+ will provide this capability
• Registry can specify a custom policy
                     Instantiation
                            3 of 3


              PythonCOM                 policy.py
clsid, riid               clsid, riid
                                        CreateInstance(clsid, riid)
                           returned
returned

                                            creates

                                        Interface

                                                         pUnk
          The Default Policy
• Python server objects (instances) are
  annotated with special attributes
  – Typically specified as class attributes
  – Most are optional
• _public_methods_
  – A list of strings specifying the methods that
    clients are allowed to call
  – This is the only required attribute
          A Quick Example
class MyPythonServer:
     _public_methods_ = [ „SomeMethod‟ ]
     def SomeMethod(self, arg1, arg2):
           do_some_work(arg1)
           return whatever(arg2)

• Note that the only difference for the Python
  programmer is the addition of the
  _public_methods_ attribute
          Useful Attributes
• _public_attrs_ : what Python attributes
  should be exposed as COM Properties
• _readonly_attrs_ : which of the above
  should be considered read-only
• _com_interfaces_ : what COM interfaces
  beyond IDispatch are supported
                Wrapping
                    1 of 3
• The process of associating a gateway
  instance and a policy instance with a
  particular Python instance is known as
  “wrapping”
• Similarly, retrieving the Python instance is
  known as “unwrapping”
• Objects returned by Python COM servers
  must be wrapped (the framework does not
  automatically wrap)
               Wrapping
                      2 of 3
            Gateway

               Policy

                   Server



This diagram shows the organization of the
objects involved in a Python COM server and
where the “wrapping” term came from
                 Wrapping
                     3 of 3
• Wrapping an object that will be returned:
  from win32com.server import util
  ...
  def method(self, arg):
      ob = whatever(arg)
      return util.wrap(ob)

• Unwrapping (of an argument) is rare
  – You must know the object is a Python object
    (and what to do with it once unwrapped)
  – Usually used in relatively closed systems
            Error Handling
                   1 of 3
• COM defines simple result codes with the
  HRESULT type and associated constants
• Extended error information includes
  description, help file, context, etc
• Returned via EXCEP_INFO structure in
  IDispatch or through ISupportErrorInfo
• Framework maps Python exceptions to
  COM result codes and exceptions
              Error Handling
                       2 of 3
• If the Python exception is an instance, then
  framework looks for special attributes to fill
  in COM extended exception information
  – Just raise an instance with the right attributes
  – See exception.Exception utility class
• Otherwise, the framework does its best
             Error Handling
                    3 of 3
• When called via IDispatch, it returns the
  exception via EXCEP_INFO
• For non-Dispatch calls, the caller may
  follow up by using ISupportErrorInfo to
  retrieve the exception
• ISupportErrorInfo is part of the base
  gateway class and is always present
               Collections
                    1 of 3
• Collections are sequence-like objects that
  typically implement the Add, Remove, and
  Item methods and a Count property
• Some Collections (such as those provided
  natively by VB) can be indexed using
  numbers (acts as a sequence) or using
  strings (acts as a mapping)
• win32com.server.util.Collection is a simple
  numerically indexed Collection class
                Collections
                      2 of 3
• The Item method is special
  – It should be the “default” method, meaning that
    VB can implicitly call it without using its name
  – The predefined DISPID_VALUE value refers
    to the default method
  – Item can be called with one parameter (the
    index) or with two parameters (an index and a
    new value to place at that index)
  – This duality is not handled well by the default
    policy nor server.util.Collection
  – Also beware the numeric vs. string indexing
                 Collections
                        3 of 3
• The Python COM framework defines
  returning a list or tuple to mean returning a
  SAFEARRAY of VARIANT values
  – This means your object must explicitly return
    an object that obeys the Collection protocol
  – A custom policy could be used to automatically
    wrap sequences with a Collection
  – Only recognizes list and tuple
     • avoids treating a string as a sequence
     • instances are not checked for sequence behavior
              Enumerators
                     1 of 3
• Enumerators are used by clients to
  enumerate a collection (sequence)
  – VBScript automatically fetches an enumerator
    for script code such as:
           for each item in collection

• Standard COM protocol uses the predefined
  DISPID_NEWENUM value and calls
  IDispatch::Invoke()
  – Default policy calls your _NewEnum method
              Enumerators
                    2 of 3
• IEnumVARIANT is the interface used by
  Automation clients (such as VB)
  – Your returned enumerator must implement the
    IEnumVARIANT interface
  – Values returned from Next() are VARIANTs
    (see client section for discussion of COM
    enumerator interfaces)
  – Support for IEnumVARIANT part of core
  – Python datatypes are easily coerced into
    VARIANTs by the IEnumVARIANT gateway
              Enumerators
                    3 of 3
• win32com.server.util.NewEnum(seq) will
  return an enumerator for a given sequence
• Custom enumerators are easily written:
  – Dynamic sequences
  – Special handling of enumerated values
  – Subclass from server.util.ListEnumerator,
    ListEnumeratorGateway, or write from scratch
• New gateways needed for interfaces other
  than IEnumVARIANT
                Server Utilities
• Various functionality available in
  win32com.server.*
  –   ...connect : connection points
  –   ...exception : exception handling
  –   ...policy : framework support
  –   ...register : server object registration
  –   ...util : miscellaneous utilities
     win32com.server.connect
• Supplies utilities and classes for the server
  side of connection points
    win32com.server.exception
• Exports a single class: Exception
• Constructor has keyword arguments for the
  status code, description, help file, etc.
• The Exception class places these values into
  instance variables
• The Python COM framework picks up the
  values for returning to the caller
      win32com.server.policy
• Framework knows about this file
  – Hard-coded reference, so it must exist
• Provides CreateInstance for the framework
  – Provides hooks for custom policies and
    dispatchers
• Defines various standard policies and
  dispatchers
  – Future: the policies and dispatchers will move
    out to separate files for easier maintainability
      win32com.server.register
• Utilities for registering your COM servers
• Two primary functions:
   – RegisterServer()
   – UnregisterServer()
• Typically, registration for servers in a file is
  performed when the file is run from the
  command line (e.g. “python myservers.py”)
• RegisterServer() has many options; see its
  doc string for more information
  win32com.server.register Example
class MyClass:
       _public_methods_ = [ “MyMethod” ]
       # … class definition


if __name__ == “__main__”:
       import sys
       from win32com.server import register
       if len(sys.argv) > 1 and sys.argv[1] == “--unregister”:
                 register.UnregisterServer(“{…}”, “The.ProgID”)
       else:
                 register.RegisterServer(“{…}”, “MyModule.MyClass”,
                        progID=“The.ProgID”)
        win32com.server.util
• wrap()
• unwrap()
• NewEnum()
  – ListEnumerator class
  – ListEnumeratorGateway class
• Collection class
          win32com.makegw
• Tool for interfaces and gateways
  – SWIG now my preference for interfaces -
    gateways somewhat harder
• Generate once, and never again
  – compare with SWIG, which allows multiple
    generations - particularly useful as support is
    added after initial generation
• Better than hand-coding
  – Active Scripting, Debugging and some others
    done this way
Advanced Topics
       Advanced: Dispatchers
                       1 of 2
• Debugging and tracing utility
  – Almost identical to policies; simply delegate to
    actual policy
• Only used during development, so zero
  runtime overhead in release
• Implementation is not for speed, but for
  assistance in debugging
  – e.g., IIDs translated if possible to names, often
    using the registry or dictionary lookups, etc
       Advanced: Dispatchers
                     2 of 2
• Log information about your server
  – All method calls made
  – All IDispatch mapping
  – All QueryInterface requests
• Dispatchers available that send to various
  debugging “terminals”
  – win32dbg debugger, win32trace utility, existing
    stdout, etc.
  – Easy to write your own if you have specific
    debugging requirements
        Advanced: Wrapping
                      1 of 5
• All Python server objects are wrapped with
  at least two objects: the policy and the
  gateway
  – Caveat: a custom policy may implement the
    actual server rather than using another object
    (see win32com.servers.dictionary)
  – Dispatchers can actually add a third layer into
    this group
        Advanced: Wrapping
                      2 of 5
• Since a gateway is referenced with a C++
  interface pointer, Python cannot hold the
  reference
  – Wrap once more with a framework “interface”
    (we’ll call it a PyInterface to distinguish from
    COM interfaces)
  – The PyInterface is removed by the framework
    (exposing the C++ pointer) when a PyInterface
    is returned by a server
                   Advanced: Wrapping
                                     3 of 5

                Interface                     Gateway
                                                 Policy
       Python




                                    C++
                                                     Server


Returned to COM client
(the framework removes the PyInterface)
        Advanced: Wrapping
                      4 of 5
• win32com.pythoncom.WrapObject() wraps
  a Python object with a gateway and a
  PyInterface
  – Pass it the policy object (which is wrapping the
    Python server object)
  – Optional parameter specifies the IID of a
    gateway to use for the wrapping (the gateway
    must be registered with the COM framework)
        Advanced: Wrapping
                   5 of 5
• Unwrapping is performed through a special
  COM interface: IUnwrapPythonObject
• pythoncom.UnwrapObject() queries for this
  interface on the COM object held within the
  PyInterface object that is passed
• The base gateway class implements this
  COM interface (so all gateways have it)
• The interface’s single method returns the
  gateway’s underlying Python object
   Advanced: Custom Policies
                      1 of 7
• Why use a custom policy?
  – Special error handling, creation, calling
    mechanisms, validation, etc
  – Write the policy class and enter the appropriate
    information into the registry so that the
    framework will use your policy
  – Be sure to use your custom policy when
    wrapping your objects
    Advanced: Custom Policies
                     2 of 7
• Other provided policies
  – BasicWrapPolicy : handy base class
  – MappedWrapPolicy : low level mapping-based
    handling of names and properties and methods
  – DesignatedWrapPolicy : build onto the Mapped
    policy a way for objects to easily specify the
    properties and methods
  – DynamicPolicy : determine methods and
    properties dynamically
    Advanced: Custom Policies
                      3 of 7
• Example: custom instantiation
  – A single Python COM server was used to
    represent multiple COM objects
  – At instantiation time, it used the CLSID passed
    to the policy to look in the registry for more
    detailed information
  – A child/sub object was created, based on the
    registered information; the COM server
    provided some generic behavior for all of the
    child objects
   Advanced: Custom Policies
                     4 of 7
• Example: error handling
  – Returning “KeyError” or other Python
    exceptions to callers was undesirable
  – Wrap all Invokes with an exception handler that
    would map Python errors into a generic error
  – Let through pythoncom.com_error unchanged
  – If a “magic” registry value was present, then a
    full traceback was placed into the exception
    (rather than simply “internal error”)
    Advanced: Custom Policies
                      5 of 7
• Example: data validation
  – If the server object had a _validation_map_
    attribute, then a custom validation method
    would be called for all Property “puts”
  – _validation_map_ would map a Property name
    to a type signature that the _validate_() method
    would test against
  – The Invoke method was hooked to call the
    _validate_() method
    Advanced: Custom Policies
                     6 of 7
• Example: functions for Property get/put
  – The Item() method in Collections is really
    treated as a parameterized property
  – Using a custom policy, get_Item() can be
    differentiated from put_Item()
  – Allowed for get_Count() and the absence of
    put_Count() implied read-only
    Advanced: Custom Policies
                     7 of 7
• Example: alter mechanism for specifying
  the available properties
  – Using the _validation_map_ from a previous
    example, the available properties are easily
    derived (simply the keys of the mapping)
  – Avoided duplication of property specification
    (one in _validation_map_ and one in
    _public_attrs_)
        Advanced: Threading
                    1 of 2
• Python is normally “single threaded”; the
  least capable COM threading model
• With care, it could be possible to mark an
  object as “free threaded” to fool how COM
  handles the object, but Python will continue
  to allow only one thread per process to run
• This behavior is fine for many applications
  where Python is a COM client, but it breaks
  down for some server scenarios
        Advanced: Threading
                     2 of 2
• The problem can be reduced by applying
  patches with allow Python to be truly free-
  threaded
  – Slows down single thread case
  – Applies mainly to multiprocessor use
• More work on threading is needed and is in
  progress on the Thread-SIG
Futures
           Future Directions
•   Auto wrap and unwrap
•   COM+
•   SWIG
•   makepy
       Future: Auto Wrapping
• This could be done today, but wasn’t:
  – Leaving it to Python increased flexibility
  – Complexity involved with needing a way to
    specify two things during any wrapping
    process: the policy and the gateway
• Moving to COM+ will be an opportune time
  to change
• Annotation through attributes will control
  wrapping process
             Future: COM+
                     1 of 3
• What is COM+ ?
  – Upcoming revision of COM
  – Runtime services: memory management,
    interceptors, object model changes, language
    independence, etc
  – see: http://www.microsoft.com/cominfo
             Future: COM+
                     2 of 3
• Python already has most of COM+’s
  facilities and matches its model strongly
• Huge win for Python:
  – Simplify COM programming even more
  – Will reduce the framework and associated
    overheads
  – Better language compatibilities
  – Major reduction in dependence on vtables
  – Better type handling
             Future: COM+
                     3 of 3
• Any Python object can be a COM+ server,
  provided it is registered appropriately
  – Note that COM+ registration will be easier
• Auto wrapping
• “import somedll” will load the “metadata”
  from the DLL and automatically make its
  classes and constants available
              Future: SWIG
• Depends largely on what Dave Beazley is
  willing to support!
• Interface support needs more work
  – Framework is OK
  – Mainly adding all interfaces and types to SWIG
    library
• Gateways still a long way off
  – Future here quite uncertain
• Some sort of IDL parsing highly desirable
            Future: makepy
• Functionally quite complete
  – Some cleanup desirable, but works well
• Architectural issues outstanding
  – Where does generated code go?
• Utilities for automatic generation
  – From program ID
  – Integration with COM browser
  – Integration with some GUI interface

				
DOCUMENT INFO
Shared By:
Categories:
Stats:
views:72
posted:3/26/2011
language:English
pages:112