Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,104 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>Generated Python COM Support</TITLE>
<META NAME="Version" CONTENT="8.0.3410">
<META NAME="Date" CONTENT="10/11/96">
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\html.dot">
</HEAD>
<BODY TEXT="#000000" LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff">
<P><IMG SRC="image/pycom_blowing.gif" WIDTH=549 HEIGHT=99 ALT="Python and COM - Blowing the others away"></P>
<H1>Generated Python COM Support</H1>
<P>This file describes how the Python COM extensions support "generated files". The information contained here is for expert Python users, and people who need to take advantage of the advanced features of the support. More general information is available in the <A HREF="QuickStartClientCom.html">Quick Start to Client Side COM</A> documentation.</P>
<H2>Introduction</H2>
<P>Generated Python COM support means that a .py file exists behind a particular COM object. This .py file is created by a generation process from a COM type library.</P>
<P>This documentation talks about the process of the creation of the .py files.</P>
<H2>Design Goals</H2>
<P>The main design goal is that the Python programmer need not know much about the type library they wish to work with. They need not know the name of a specific Python module to use a type library. COM uses an IID, version and LCID to identify a type library. Therefore, the Python programmer only need know this information to obtain a Python module.</P>
<H2>How to generate support files</H2>
<P>Support files can be generated either "off-line" by the makepy utility, or in custom Python code.</P>
<P>Using makepy is in many ways far simpler - you simply pick the type library and you are ready to go! The <A HREF="QuickStartClientCom.html">Quick Start to Client Side COM</A> documentation describes this process.</P>
<P>Often however, you will want to use code to ensure the type library has been processed. This document describes that process.</P>
<H2>Usage</H2>
<P>The win32com.client.gencache module implements all functionality. As described above, if you wish to generate support from code, you need to know the IID, version and LCID of the type library.</P>
<P>The following functions are defined. The best examples of their usage is probably in the Pythonwin OCX Demos, and the COM Test Suite (particularly testMSOffice.py)</P>
<P>Note that the gencache.py file supports being run from the command line, and provides some utilities for managing the cache. Run the file to see usage options.</P>
<H2>Using makepy to help with the runtime generation</H2>
<P>makepy supports a "-i" option, to print information about a type library. When you select a type library, makepy will print out 2 lines of code that you cant paste into your application. This will then allow your module to generate the makepy .py file at runtime, but will only take you a few seconds!</P>
<H2>win32com.client.gencache functions</H2>
<H3>def MakeModuleForTypelib(typelibCLSID, lcid, major, minor, progressInstance = None):</H3>
<P>Generate support for a type library.</P>
<P>Given the IID, LCID and version information for a type library, generate and import the necessary support files.</P>
<B><P>Returns</P>
</B><P>The Python module. No exceptions are caught.</P>
<B><P>Params</P>
</B><I><P>typelibCLSID</I><BR>
IID of the type library.</P>
<I><P>major</I><BR>
Integer major version.</P>
<I><P>minor</I><BR>
Integer minor version.</P>
<I><P>lcid</I><BR>
Integer LCID for the library.</P>
<I><P>progressInstance</I><BR>
A class instance to use as the progress indicator, or None to use the default GUI one.&nbsp;</P>
<H3>def EnsureModule(typelibCLSID, lcid, major, minor, progressInstance = None):</H3>
<P>Ensure Python support is loaded for a type library, generating if necessary.</P>
<P>Given the IID, LCID and version information for a type library, check and if necessary generate, then import the necessary support files.</P>
<P>Returns:</P>
<P>The Python module. No exceptions are caught during the generate process.</P>
<B><P>Params</P>
</B><I><P>typelibCLSID</I><BR>
IID of the type library.</P>
<I><P>major</I><BR>
Integer major version.</P>
<I><P>minor</I><BR>
Integer minor version.</P>
<I><P>lcid</I><BR>
Integer LCID for the library.</P>
<I><P>progressInstance</I><BR>
A class instance to use as the progress indicator, or None to use the default GUI one.&nbsp;</P>
<P>&nbsp;</P>
<H3>def GetClassForProgID(<I>progid</I>):</H3>
<P>Get a Python class for a Program ID</P>
<P>Given a Program ID, return a Python class which wraps the COM object</P>
<B><P>Returns</P>
</B><P>The Python class, or None if no module is available.</P>
<B><P>Params</P>
</B><I><P>progid<BR>
</I>A COM ProgramID or IID (eg, "Word.Application")</P>
<P>&nbsp;</P>
<H3>def GetModuleForProgID(progid):</H3>
<P>Get a Python module for a Program ID</P>
<P>Given a Program ID, return a Python module which contains the class which wraps the COM object.</P>
<B><P>Returns</P>
</B><P>The Python module, or None if no module is available.</P>
<B><P>Params:</P>
</B><I><P>progid <BR>
</I>A COM ProgramID or IID (eg, "Word.Application")</P>
<P>&nbsp;</P>
<H3>def GetModuleForCLSID(clsid):</H3>
<P>Get a Python module for a CLSID</P>
<P>Given a CLSID, return a Python module which contains the class which wraps the COM object.</P>
<B><P>Returns</P>
</B><P>The Python module, or None if no module is available.</P>
<B><P>Params</P>
</B><I><P>progid<BR>
</I>A COM CLSID (ie, not the description)</P>
<P>&nbsp;</P>
<H3>def GetModuleForTypelib(typelibCLSID, lcid, major, minor):</H3>
<P>Get a Python module for a type library ID</P>
<B><P>Returns</P>
</B><P>An imported Python module, else None</P>
<B><P>Params</B>:</P>
<I><P>typelibCLSID</I><BR>
IID of the type library.</P>
<I><P>major</I><BR>
Integer major version.</P>
<I><P>minor</I><BR>
Integer minor version</P>
<I><P>lcid</I><BR>
Integer LCID for the library.</P></BODY>
</HTML>

View file

@ -0,0 +1,90 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>Untitled</TITLE>
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\html.dot">
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080">
<H1><IMG SRC="image/pycom_blowing.gif" WIDTH=549 HEIGHT=99 ALT="Python and COM - Blowing the others away"></H1>
<H1>Python and COM - Implementation Details </H1>
<H2>Introduction </H2>
<P>This document describes the technical implementation of the COM support in Python. It is primarily concerned with the underlying C++ interface to COM, although general Python issues are touched. </P>
<P>This document is targeted at people who wish to maintain/enhance the standard COM support (typically by writing extension modules). For information on using Python and COM from a Python programmers perspective, please see the <A HREF="docindex.html">documentation index</A>. </P>
<H2>General COM Support. </H2>
<P>COM support in Python can be broken into 2 general areas - C++ support, and Python support. C++ support exists in the core PythonCOM module (plus any PythonCOM extension modules). Python support exists in the .py files that accompany the core module. </P>
<H2>Naming Conventions </H2>
<P>The naming conventions used by Python code will be: </P>
<UL>
<LI>The Python "New Import" (ni) module will be used, allowing packages, or nested modules. </LI>
<LI>The package name will be "win32com". </LI>
<LI>The core module name will be "pythoncom" (ie, "win32com.pythoncom") </LI></UL>
<P>The rest of the naming conventions are yet to be worked out. </P>
<H2>Core COM support. </H2>
<P>This section is involved with the core C++ support in "pythoncom". </P>
<P>The organisation of PythonCOM support falls into 3 discrete areas. </P>
<H3>COM Client Support </H3>
<P>This is the ability to manipulate other COM objects via their exposed interface. This includes use of IDispatch (eg using Python to start Microsoft Word, open a file, and print it.) but also all client side IUnknown derived objects fall into this category, including ITypeLib and IConnectionPoint support. </P>
<H3>COM Server Support </H3>
<P>This is ability for Python to create COM Servers, which can be manipulated by another COM client. This includes server side IDispatch (eg, Visual Basic starting a Python interpreter, and asking it to evaluate some code) but also all supported server side IUnknown derived classes. </P>
<H3>Python/COM type and value conversion </H3>
<P>This is internal code used by the above areas to managed the conversion to and from Python/COM types and values. This includes code to convert an arbitrary Python object into a COM variant, manages return types, and a few other helpers. </P>
<H2>COM Structures and Python Types </H2>
<P>OLE supports many C level structures for the COM API, which must be mapped to Python. </P>
<H3>VARIANT </H3>
<P>Variants are never exposed as such to Python programs. The internal framework always converts all variants to and from Python types. In some cases, type descriptions may be used, which force specific mappings, although in general the automatic conversion works fine. </P>
<H3>TYPEDESC </H3>
<P>A tuple, containing the elements of the C union. This union will be correctly decoded by the support code. </P>
<H3>ELEMDESC </H3>
<P>A tuple of TYPEDESC and PARAMDESC objects. </P>
<H3>FUNCDESC </H3>
<P>A funcdesc is a large and unwieldy tuple. Documentation to be supplied. </P>
<H3>IID/CLSID </H3>
<P>A native IID in Python is a special type, defined in pythoncom. Whenever a CLSID/IID is required, typically either an object, a tuple of type "iii(iiiiiiii)" or string can be used. </P>
<P>Helper functions are available to convert to and from IID/CLSID and strings. </P>
<H2>COM Framework </H2>
<P>Both client and server side support have a specific framework in place to assist in supporting the widest possible set of interfaces. The framework allows external extension DLLs to be written, which extend the interfaces available to the Python user. </P>
<P>This allows the core PythonCOM module to support a wide set of common interfaces, and other extensions to support anything obscure. </P>
<H3>Client Framework </H3>
<H4>QueryInterface and Types </H4>
<P>When the only support required by Python is IDispatch, everything is simple - every object returned from QueryInterface is a PyIDispatch object. But this does not extend to other types, such as ITypeLib, IConnectionPoint etc., which are required for full COM support. </P>
<P>For example, consider the following C++ psuedo-code: </P>
<CODE><P>IConnectionPoint *conPt;<BR>
someIDispatch-&gt;QueryInterface(IID_IConnectionPoint, (void **)&amp;conPt);<BR>
// Note the IID_ and type of the * could be anything!</CODE> </P>
<P>This cast, and knowledge of a specific IID_* to type must be simulated in Python. </P>
<P>Python/COM will therefore maintain a map of UID's to Python type objects. Whenever QueryInterface is called, Python will lookup this map, to determine if the object type is supported. If the object is supported, then an object of that type will be returned. If the object is not supported, then a PyIUnknown object will be returned. </P>
<P>Note that PyIDispatch will be supported by the core engine. Therefore: </P>
<CODE><P>&gt;&gt;&gt; disp=someobj.QueryInterface(win32com.IID_Dispatch) </P>
</CODE><P>will return a PyIDispatch object, whereas </P>
<CODE><P>&gt;&gt;&gt; unk=someobj.QueryInterface(SomeUnknownIID) # returns PyIUnknown<BR>
&gt;&gt;&gt; disp=unk.QueryInterface(win32com.IID_Dispatch) <BR>
&gt;&gt;&gt; unk.Release() # Clean up now, rather than waiting for unk death.</CODE> </P>
<P>Is needed to convert to an IDispatch object. </P>
<H4>Core Support </H4>
<P>The core COM support module will support the IUnknown, IDispatch, ITypeInfo, ITypeLib and IConnectionPointContainer and IConnectionPoint interfaces. This implies the core COM module supports 6 different OLE client object types, mapped to the 6 IID_*'s representing the objects. (The IConnection* objects allow for Python to repsond to COM events) </P>
<P>A psuedo-inheritance scheme is used. The Python types are all derived from the Python IUnknown type (PyIUnknown). Therefore all IUnknown methods are automatically available to all types, just as it should be. The PyIUnknown type manages all object reference counts and destruction. </P>
<H4>Extensibility </H4>
<P>To provide the above functionality, a Python map is provided, which maps from a GUID to a Python type object. </P>
<P>The advantage of this scheme is an external extension modules can hook into the core support. For example, imagine the following code: </P>
<CODE><P>&gt;&gt;&gt; import myextracom # external .pyd supporting some interface.<BR>
# myextracom.pyd will do the equivilent of</CODE> </P>
<CODE><P># pythoncom.mapSupportedTypes(myextracom.IID_Extra, myextracom.ExtraType) <BR>
&gt;&gt;&gt; someobj.QueryInterface(myextracom.IID_Extra)</CODE> </P>
<P>Would correctly return an object defined in the extension module. </P>
<H3>Server Framework </H3>
<H4>General Framework </H4>
<P>A server framework has been put in place which provides the following features: </P>
<P>All Interfaces provide VTBL support - this means that the Servers exposed by Python are callable from C++ and other compiled languages. </P>
<P>Supports full "inproc" servers. This means that no external .EXE is needed making Python COM servers available in almost all cases. </P>
<P>An extensible model which allows for extension modules to provide server support for interfaces defined in that module. A map is provided which maps from a GUID to a function pointer which creates the interface. </P>
<H3>Python and Variant Types Conversion </H3>
<P>In general, Python and COM are both "type-less". COM is type-less via the VARIANT object, which supports many types, and Python is type-less due to its object model. </P>
<P>There are a number of areas where Python and OLE clash. </P>
<H4>Parameters and conversions. </H4>
<P>For simple calls, there are 2 helpers available which will convert to and from PyObjects and VARIANTS. The call to convert a Python object to a VARIANT is simple in that it returns a VARIANT of the most appropriate type for the Python object - ie, the type of the Python object determines the resulting VARIANT type. </P>
<P>There are also more complex conversion routines available, wrapped in a C++ helper class. Typically, these helpers are used whenever a specific variant type is known (eg, when an ITypeInfo is available for the object being used). In this case, all efforts are made to convert the Python type to the requested variant type - ie, in this situation, the VARIANT type determines how the Python object is coerced. In addition, this code supports the use of "ByRef" and pointer paramaters, providing and freeing any buffers necessary for the call. </P></BODY>
</HTML>

View file

@ -0,0 +1,82 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>Quick Start to Client side COM and Python</TITLE>
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\html.dot">
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080">
<H1>Quick Start to Client side COM and Python</H1>
<H2>Introduction</H2>
<P>This documents how to quickly start using COM from Python. It is not a thorough discussion of the COM system, or of the concepts introduced by COM.</P>
<P>Other good information on COM can be found in various conference tutorials - please see <A HREF="http://starship.python.net/crew/mhammond/conferences">the collection of Mark's conference tutorials</A></P>
<P>For information on implementing COM objects using Python, please see <A HREF="http://www.python.org/windows/win32com/QuickStartServerCom.html">a Quick Start to Server side COM and Python</A></P>
<P>In this document we discuss the following topics:</P>
<UL>
<LI><A HREF="#Using">Using a COM object from Python</A> </LI>
<LI><A HREF="#WhatObjects">How do I know which objects are available?</A> </LI>
<LI><A HREF="#StaticDispatch">Static Dispatch/Type Safe objects (using the new improved makepy.py)</A></LI>
<LI><A HREF="#UsingComConstants">Using COM Constants with makepy.</A></LI></UL>
<H2>Quick Start</H2>
<H3><A NAME="Using">To use a COM object from Python</A></H3>
<CODE><P>import win32com.client<BR>
o = win32com.client.Dispatch("Object.Name")<BR>
o.Method()<BR>
o.property = "New Value"<BR>
print o.property</P>
</CODE><P>Example</P>
<CODE><P>o = win32com.client.Dispatch("Excel.Application")<BR>
o.Visible = 1<BR>
o.Workbooks.Add() # for office 97 95 a bit different!<BR>
o.Cells(1,1).Value = "Hello"</CODE> </P>
<P>And we will see the word "Hello" appear in the top cell. </P>
<H3><A NAME="WhatObjects">How do I know which methods and properties are available?</A></H3>
<P>Good question. This is hard! You need to use the documentation with the products, or possibly a COM browser. Note however that COM browsers typically rely on these objects registering themselves in certain ways, and many objects to not do this. You are just expected to know.</P>
<H4>The Python COM browser</H4>
<P>PythonCOM comes with a basic COM browser that may show you the information you need. Note that this package requires Pythonwin (ie, the MFC GUI environment) to be installed for this to work.</P>
<P>There are far better COM browsers available - I tend to use the one that comes with MSVC, or this one!</P>
<P>To run the browser, simply select it from the Pythonwin <I>Tools</I> menu, or double-click on the file <I>win32com\client\combrowse.py</I></P>
<H2><A NAME="StaticDispatch">Static Dispatch (or Type Safe) objects</A></H2>
<P>In the above examples, if we printed the '<CODE>repr(o)</CODE>' object above, it would have resulted in</P>
<CODE><P>&lt;COMObject Excel.Application&gt;</P>
</CODE><P>This reflects that the object is a generic COM object that Python has no special knowledge of (other than the name you used to create it!). This is known as a "dynamic dispatch" object, as all knowledge is built dynamically. The win32com package also has the concept of <I>static dispatch</I> objects, which gives Python up-front knowledge about the objects that it is working with (including arguments, argument types, etc)</P>
<P>In a nutshell, Static Dispatch involves the generation of a .py file that contains support for the specific object. For more overview information, please see the documentation references above.</P>
<P>The generation and management of the .py files is somewhat automatic, and involves one of 2 steps:</P>
<UL>
<LI>Using <I>makepy.py</I> to select a COM library. This process is very similar to Visual Basic, where you select from a list of all objects installed on your system, and once selected the objects are magically useable. </LI></UL>
<P>or</P>
<UL>
<LI>Use explicit code to check for, and possibly generate, support at run-time. This is very powerful, as it allows the developer to avoid ensuring the user has selected the appropriate type library. This option is extremely powerful for OCX users, as it allows Python code to sub-class an OCX control, but the actual sub-class can be generated at run-time. Use <I>makepy.py</I> with a </I>-i</I> option to see how to include this support in your Python code.</LI></UL>
<P>The <I>win32com.client.gencache</I> module manages these generated files. This module has <A HREF="GeneratedSupport.html">some documentation of its own</A>, but you probably don't need to know the gory details!</P>
<H3>How do I get at the generated module?</H3>
<P>You will notice that the generated file name is long and cryptic - obviously not designed for humans to work with! So how do you get at the module object for the generated code?</P>
<P>Hopefully, the answer is <I>you shouldn't need to</I>. All generated file support is generally available directly via <I>win32com.client.Dispatch</I> and <I>win32com.client.constants</I>. But should you ever really need the Python module object, the win32com.client.gencache module has functions specifically for this. The functions GetModuleForCLSID and GetModuleForProgID both return Python module objects that you can use in your code. See the docstrings in the gencache code for more details.</P>
<H3>To generate Python Sources supporting a COM object</H3>
<H4>Example using Microsoft Office 97.</H4>
<P>Either:</P>
<UL>
<LI>Run '<CODE>win32com\client\makepy.py</CODE>' (eg, run it from the command window, or double-click on it) and a list will be presented. Select the Type Library '<CODE>Microsoft Word 8.0 Object Library</CODE>' </LI>
<LI>From a command prompt, run the command '<CODE>makepy.py "Microsoft Word 8.0 Object Library"</CODE>' (include the double quotes). This simply avoids the selection process. </LI>
<LI>If you desire, you can also use explicit code to generate it just before you need to use it at runtime. Run <CODE>'makepy.py -i "Microsoft Word 8.0 Object Library"</CODE>' (include the double quotes) to see how to do this.</LI></UL>
<P>And that is it! Nothing more needed. No special import statements needed! Now, you simply need say</P>
<CODE><P>&gt;&gt;&gt; import win32com.client</P>
<P>&gt;&gt;&gt; w=win32com.client.Dispatch("Word.Application")</P>
<P>&gt;&gt;&gt; w.Visible=1</P>
<P>&gt;&gt;&gt; w</P>
<P>&lt;win32com.gen_py.Microsoft Word 8.0 Object Library._Application&gt;</P>
</CODE><P>Note that now Python knows the explicit type of the object.</P>
<H3><A NAME="UsingComConstants">Using COM Constants</A></H3>
<P>Makepy automatically installs all generated constants from a type library in an object called <I>win32com.clients.constants</I>. You do not need to do anything special to make these constants work, other than create the object itself (ie, in the example above, the constants relating to <I>Word</I> would automatically be available after the <CODE>w=win32com.client.Dispatch("Word.Application</CODE>") statement<CODE>.</P>
</CODE><P>For example, immediately after executing the code above, you could execute the following:</P>
<CODE><P>&gt;&gt;&gt; w.WindowState = win32com.client.constants.wdWindowStateMinimize</P>
</CODE><P>and Word will Minimize.</P></BODY>
</HTML>

View file

@ -0,0 +1,195 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>Quick Start to Server Side COM and Python</TITLE>
<META NAME="Version" CONTENT="8.0.3410">
<META NAME="Date" CONTENT="10/11/96">
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\html.dot">
</HEAD>
<BODY TEXT="#000000" LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff">
<H1>Quick Start to Server side COM and Python</H1>
<H2>Introduction</H2>
<P>This documents how to quickly start implementing COM objects in Python. It is not a thorough discussion of the COM system, or of the concepts introduced by COM.</P>
<P>For more details information on Python and COM, please see the <A HREF="http://www.python.org/windows/win32com/COMTutorial/index.htm">COM Tutorial given by Greg Stein and Mark Hammond at SPAM 6 (HTML format)</A> or download the same tutorial <A HREF="http://www.python.org/windows/win32com/COMTutorial.ppt">in PowerPoint format.</A></P>
<P>For information on using external COM objects from Python, please see <A HREF="QuickStartClientCom.html">a Quick Start to Client side COM and Python</A>.</P>
<P>In this document we discuss the <A HREF="#core">core functionality</A>, <A HREF="#Registering">registering the server</A>, <A HREF="#testing">testing the class</A>, <A HREF="#debugging">debugging the class</A>, <A HREF="#Exception">exception handling</A> and <A HREF="#Policies">server policies</A> (phew!)</P>
<H2><A NAME="core">Implement the core functionality</A></H2>
<H3><A NAME="Using">Implement a stand-alone Python class with your functionality</A></H3>
<CODE><P>class HelloWorld:</P><DIR>
<DIR>
<P>def __init__(self):</P><DIR>
<DIR>
<P>self.softspace = 1</P>
<P>self.noCalls = 0</P></DIR>
</DIR>
<P>def Hello(self, who):</P><DIR>
<DIR>
<P>self.noCalls = self.noCalls + 1</P>
<P># insert "softspace" number of spaces</P>
<P>return "Hello" + " " * self.softspace + who</P></DIR>
</DIR>
</DIR>
</DIR>
</CODE><P>This is obviously a very simple server. In particular, custom error handling would be needed for a production class server. In addition, there are some contrived properties just for demonstration purposes.</P>
<H3>Make Unicode concessions</H3>
<P>At this stage, Python and Unicode dont really work well together. All strings which come from COM will actually be Unicode objects rather than string objects.</P>
<P>To make this code work in a COM environment, the last line of the "Hello" method must become:</P><DIR>
<DIR>
<DIR>
<DIR>
<CODE><P>return "Hello" + " " * self.softspace + str(who)</P></DIR>
</DIR>
</DIR>
</DIR>
</CODE><P>Note the conversion of the "who" to "str(who)". This forces the Unicode object into a native Python string object.</P>
<P>For details on how to debug COM Servers to find this sort of error, please see <A HREF="#debugging">debugging the class</A></P>
<H3>Annotate the class with win32com specific attributes</H3>
<P>This is not a complete list of names, simply a list of properties used by this sample.</P>
<TABLE CELLSPACING=0 BORDER=0 CELLPADDING=7 WIDTH=637>
<TR><TD WIDTH="34%" VALIGN="TOP">
<P><B>Property Name</B></TD>
<TD WIDTH="66%" VALIGN="TOP">
<B><P>Description</B></TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP">
<P>_public_methods_</TD>
<TD WIDTH="66%" VALIGN="TOP">
<P>List of all method names exposed to remote COM clients</TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP">
<P>_public_attrs_</TD>
<TD WIDTH="66%" VALIGN="TOP">
<P>List of all attribute names exposed to remote COM clients</TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP" HEIGHT=5>
<P>_readonly_attrs_</TD>
<TD WIDTH="66%" VALIGN="TOP" HEIGHT=5>
<P>List of all attributes which can be accessed, but not set.</TD>
</TR>
</TABLE>
<P>We change the class header to become:</P>
<CODE><P>class HelloWorld:</P><DIR>
<DIR>
<P>_public_methods_ = ['Hello']</P>
<P>_public_attrs_ = ['softspace', 'noCalls']</P>
<P>_readonly_attrs_ = ['noCalls']</P>
<P>def __init__(self):</P>
<P>[Same from here…]</P></DIR>
</DIR>
</CODE><H3><A NAME="Registering">Registering and assigning a CLSID for the object</A></H3>
<P>COM requires that all objects use a unique CLSID and be registered under a "user friendly" name. This documents the process.</P>
<H4>Generating the CLSID</H4>
<P>Microsoft Visual C++ comes with various tools for generating CLSID's, which are quite suitable. Alternatively, the pythoncom module exports the function CreateGuid() to generate these identifiers.</P>
<CODE><P>&gt;&gt;&gt; import pythoncom<BR>
&gt;&gt;&gt; print pythoncom.CreateGuid()<BR>
{7CC9F362-486D-11D1-BB48-0000E838A65F}</P>
</CODE><P>Obviously the GUID that you get will be different than that displayed here.</P>
<H4>Preparing for registration of the Class</H4>
<P>The win32com package allows yet more annotations to be applied to a class, allowing registration to be effected with 2 lines in your source file. The registration annotations used by this sample are:</P>
<TABLE CELLSPACING=0 BORDER=0 CELLPADDING=7 WIDTH=636>
<TR><TD WIDTH="34%" VALIGN="TOP">
<P><B>Property Name</B></TD>
<TD WIDTH="66%" VALIGN="TOP">
<B><P>Description</B></TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP">
<P>_reg_clsid_</TD>
<TD WIDTH="66%" VALIGN="TOP">
<P>The CLSID of the COM object</TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP">
<P>_reg_progid_</TD>
<TD WIDTH="66%" VALIGN="TOP">
<P>The "program ID", or Name, of the COM Server. This is the name the user usually uses to instantiate the object</TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP" HEIGHT=5>
<P>_reg_desc_</TD>
<TD WIDTH="66%" VALIGN="TOP" HEIGHT=5>
<P>Optional: The description of the COM Server. Used primarily for COM browsers. If not specified, the _reg_progid_ is used as the description.</TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP" HEIGHT=5>
<P>_reg_class_spec_</TD>
<TD WIDTH="66%" VALIGN="TOP" HEIGHT=5>
<P>Optional: A string which represents how Python can create the class instance. The string is of format<BR>
[package.subpackage.]module.class</P>
<P>The portion up to the class name must be valid for Python to "import", and the class portion must be a valid attribute in the specified class.</P>
<P>This is optional from build 124 of Pythoncom., and has been removed from this sample.</TD>
</TR>
<TR><TD WIDTH="34%" VALIGN="TOP" HEIGHT=5>
<P>_reg_remove_keys_</TD>
<TD WIDTH="66%" VALIGN="TOP" HEIGHT=5>
<P>Optional: A list of tuples of extra registry keys to be removed when uninstalling the server. Each tuple is of format ("key", root), where key is a string, and root is one of the win32con.HKEY_* constants (this item is optional, defaulting to HKEY_CLASSES_ROOT)</TD>
</TR>
</TABLE>
<P>Note there are quite a few other keys available. Also note that these annotations are <I>not</I> required - they just make registration simple. Helper functions in the module <CODE>win32com.server.register</CODE> allow you to explicitly specify each of these attributes without attaching them to the class.</P>
<P>The header of our class now becomes:</P>
<CODE><P>class HelloWorld:</P><DIR>
<DIR>
<P>_reg_clsid_ = "{7CC9F362-486D-11D1-BB48-0000E838A65F}"</P>
<P>_reg_desc_ = "Python Test COM Server"</P>
<P>_reg_progid_ = "Python.TestServer"</P>
<P>_public_methods_ = ['Hello']</P>
<P>[same from here]</P></DIR>
</DIR>
</CODE><H4>Registering the Class</H4>
<P>The idiom that most Python COM Servers use is that they register themselves when run as a script (ie, when executed from the command line.) Thus the standard "<CODE>if __name__=='__main___':</CODE>" technique works well.</P>
<P>win32com.server.register contains a number of helper functions. The easiest to use is "<CODE>UseCommandLine</CODE>".</P>
<P>Registration becomes as simple as:</P>
<CODE><P>if __name__=='__main__':<BR>
&#9;# ni only for 1.4!<BR>
&#9;import ni, win32com.server.register <BR>
&#9;win32com.server.register.UseCommandLine(HelloWorld)</P>
</CODE><P>Running the script will register our test server.</P>
<H2><A NAME="testing">Testing our Class</A></H2>
<P>For the purposes of this demonstration, we will test the class using Visual Basic. This code should run under any version of Visual Basic, including VBA found in Microsoft Office. Any COM compliant package could be used alternatively. VB has been used just to prove there is no "smoke and mirrors. For information on how to test the server using Python, please see the <A HREF="QuickStartClientCom.html">Quick Start to Client side COM</A> documentation.</P>
<P>This is not a tutorial in VB. The code is just presented! Run it, and it will work!</P>
<H2><A NAME="debugging">Debugging the COM Server</A></H2>
<P>When things go wrong in COM Servers, there is often nowhere useful for the Python traceback to go, even if such a traceback is generated.</P>
<P>Rather than discuss how it works, I will just present the procedure to debug your server:</P>
<B><P>To register a debug version of your class</B>, run the script (as above) but pass in a "<CODE>--debug</CODE>" parameter. Eg, for the server above, use the command line "<CODE>testcomserver.py --debug</CODE>".</P>
<B><P>To see the debug output generated</B> (and any print statements you may choose to add!) you can simply select the "Remote Debug Trace Collector" from the Pythonwin Tools menu, or run the script "win32traceutil.py" from Windows Explorer or a Command Prompt.</P>
<H2><A NAME="Exception">Exception Handling </A></H2>
<P>Servers need to be able to provide exception information to their client. In some cases, it may be a simple return code (such as E_NOTIMPLEMENTED), but often it can contain much richer information, describing the error on detail, and even a help file and topic where more information can be found. </P>
<P>We use Python class based exceptions to provide this information. The COM framework will examine the exception, and look for certain known attributes. These attributes will be copied across to the COM exception, and passed back to the client. </P>
<P>The following attributes are supported, and correspond to the equivalent entry in the COM Exception structure:<BR>
<CODE>scode, code, description, source, helpfile and helpcontext</P>
</CODE><P>To make working with exceptions easier, there is a helper module "win32com.server.exception.py", which defines a single class. An example of its usage would be: </P>
<CODE><P>raise COMException(desc="Must be a string",scode=winerror.E_INVALIDARG,helpfile="myhelp.hlp",...)</CODE> </P>
<P>(Note the <CODE>COMException class supports (and translates) "desc" as a shortcut for "description", but the framework requires "description")</P>
</CODE><H2><A NAME="Policies">Server Policies</A></H2>
<P>This is information about how it all hangs together. The casual COM author need not know this. </P>
<P>Whenever a Python Server needs to be created, the C++ framework first instantiates a "policy" object. This "policy" object is the gatekeeper for the COM Server - it is responsible for creating the underlying Python object that is the server (ie, your object), and also for translating the underlying COM requests for the object. </P>
<P>This policy object handles all of the underlying COM functionality. For example, COM requires all methods and properties to have unique numeric ID's associated with them. The policy object manages the creation of these ID's for the underlying Python methods and attributes. Similarly, when the client whishes to call a method with ID 123, the policy object translates this back to the actual method, and makes the call. </P>
<P>It should be noted that the operation of the "policy" object could be dictated by the Python object - the policy object has many defaults, but the actual Python class can always dictate its operation. </P>
<H3>Default Policy attributes </H3>
<P>The default policy object has a few special attributes that define who the object is exposed to COM. The example above shows the _public_methods attribute, but this section describes all such attributes in detail. </P>
<H5>_public_methods_ </H5>
<P>Required list of strings, containing the names of all methods to be exposed to COM. It is possible this will be enhanced in the future (eg, possibly '*' will be recognised to say all methods, or some other ideas…) </P>
<H5>_public_attrs_ </H5>
<P>Optional list of strings containing all attribute names to be exposed, both for reading and writing. The attribute names must be valid instance variables. </P>
<H5>_readonly_attrs_ </H5>
<P>Optional list of strings defining the name of attributes exposed read-only. </P>
<H5>_com_interfaces_ </H5>
<P>Optional list of IIDs exposed by this object. If this attribute is missing, IID_IDispatch is assumed (ie, if not supplied, the COM object will be created as a normal Automation object.</P>
<P>and actual instance attributes: </P>
<P>_dynamic_ : optional method </P>
<P>_value_ : optional attribute </P>
<P>_query_interface_ : optional method </P>
<P>_NewEnum : optional method </P>
<P>_Evaluate : optional method </P></BODY>
</HTML>

View file

@ -0,0 +1,22 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>win32com Documentation Index</TITLE>
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\html.dot">
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080">
<H1><IMG SRC="image/pycom_blowing.gif" WIDTH=549 HEIGHT=99 ALT="Python and COM - Blowing the others away"></H1>
<H1>PythonCOM Documentation Index</H1>
<P>The following documentation is available</P>
<P><A HREF="QuickStartClientCom.html">A Quick Start to Client Side COM</A> (including makepy)</P>
<P><A HREF="QuickStartServerCom.html">A Quick Start to Server Side COM</A></P>
<P><A HREF="GeneratedSupport.html">Information on generated Python files (ie, what makepy generates)</A></P>
<P><A HREF="variant.html">An advanced VARIANT object which can give more control over parameter types</A></P>
<P><A HREF="package.html">A brief description of the win32com package structure</A></P>
<P><A HREF="PythonCOM.html">Python COM Implementation documentation</A></P>
<P><A HREF="misc.html">Misc stuff I dont know where to put anywhere else</A></P>
<H3>ActiveX Scripting</H3>
<P><A HREF="../../win32comext/axscript/demos/client/ie/demo.htm">ActiveX Scripting Demos</A></P></BODY>
</HTML>

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

View file

@ -0,0 +1,31 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>win32com</TITLE>
<META NAME="Template" CONTENT="C:\Program Files\Microsoft Office\Office\html.dot">
</HEAD>
<BODY TEXT="#000000" LINK="#0000ff" VLINK="#0000ff">
<DIR>
<P><!-- Enclose the entire page in UL, so bullets don't indent. --></P>
<H1><IMG SRC="image/pycom_blowing.gif" WIDTH=549 HEIGHT=99></H1>
<H2>Python and COM</H2>
<H3>Introduction</H3>
<P>Python has an excellent interface to COM (also known variously as OLE2, ActiveX, etc).</P>
<P>The Python COM package can be used to interface to almost any COM program (such as the MS-Office suite), write servers that can be hosted by any COM client (such as Visual Basic or C++), and has even been used to provide the core ActiveX Scripting Support. </P>
<UL>
<LI>Note that win32com is now released in the win32all installation package. The <A HREF="../win32all/win32all.exe">installation EXE can be downloaded</A>, or you <A HREF="../win32all/">can jump to the win32all readme</A> for more details. </LI>
<LI>Here is the <A HREF="win32com_src.zip">win32com source code</A> in a zip file. </LI></UL>
</DIR>
<DIR>
<H3>Documentation</H3>
<P><A HREF="ActiveXScripting.html">Preliminary Active Scripting and Debugging documentation</A> is available.</P>
<P>2 Quick-Start guides have been provided, which also contain other links. See the <A HREF="QuickStartClientCom.html">Quick Start for Client side COM</A> and the <A HREF="QuickStartServerCom.html">Quick Start for Server side COM</A> </P>
</P></DIR>
</DIR>
</BODY>
</HTML>

View file

@ -0,0 +1,18 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>Misc win32com Stuff</TITLE>
<META NAME="Version" CONTENT="8.0.3410">
<META NAME="Date" CONTENT="10/11/96">
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\HTML.DOT">
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#ffffff">
<H1>Misc stuff I dont know where to put anywhere else</H1>
<H4>Client Side Dispatch</H4>
<P>Using win32com.client.Dispatch automatically invokes all the win32com client side "smarts", including automatic usage of generated .py files etc.</P>
<P>If you wish to avoid that, and use truly "dynamic" objects (ie, there is generated .py support available, but you wish to avoid it), you can use win32com.client.dynamic.Dispatch</P>
<B><P>_print_details_() method</B><BR>
If win32com.client.dynamic.Dispatch is used, the objects have a _print_details_() method available, which prints all relevant knowledge about an object (for example, all methods and properties). For objects that do not expose runtime type information, _print_details_ may not list anything.</P></BODY>
</HTML>

View file

@ -0,0 +1,37 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>The win32com package</TITLE>
<META NAME="Template" CONTENT="D:\Program Files\Microsoft Office\Office\html.dot">
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080">
<H1><IMG SRC="image/pycom_blowing.gif" WIDTH=549 HEIGHT=99 ALT="Python and COM - Blowing the others away"></H1>
<H1>The win32com package </H1>
<FONT SIZE=2><P>This document describes the win32com package in general terms.</FONT> </P>
<FONT SIZE=2><P>The COM support can be thought of as existing in 2 main portions - the C++ support code (the core PythonCOM module), and helper code, implemented in Python. The total package is known as "win32com".</FONT> </P>
<FONT SIZE=2><P>The win32com support is stand-alone. It does not require Pythonwin.</FONT> </P>
<H2>The win32com package </H2>
<FONT SIZE=2><P>To facilitate an orderly framework, the Python "ni" module has been used, and the entire package is known as "win32com". As is normal for such packages, win32com itself does not provide any functionality. Some of the modules are described below:</FONT> </P>
<UL>
<B><FONT SIZE=2><LI>win32com.pythoncom - core C++ support</B>. <BR>
This module is rarely used directly by programmers - instead the other "helper" module are used, which themselves draw on the core pythoncom services.</FONT> </LI>
<B><FONT SIZE=2><LI>win32com.client package<BR>
</B>Support for COM clients used by Python. Some of the modules in this package allow for dynamic usage of COM clients, a module for generating .py files for certain COM servers, etc.</FONT> </LI>
<B><FONT SIZE=2><LI>win32com.server package<BR>
</B>Support for COM servers written in Python. The modules in this package provide most of the underlying framework for magically turning Python classes into COM servers, exposing the correct public methods, registering your server in the registry, etc. </LI>
<B><LI>win32com.axscript<BR>
</B>ActiveX Scripting implementation for Python.</FONT> </LI>
<B><FONT SIZE=2><LI>win32com.axdebug<BR>
</B>Active Debugging implementation for Python</FONT> </LI>
<B><FONT SIZE=2><LI>win32com.mapi<BR>
</B>Utilities for working with MAPI and the Microsoft Exchange Server</LI></UL>
</FONT><P>&nbsp;</P>
<H2>The pythoncom module </H2>
<FONT SIZE=2><P>The pythoncom module is the underlying C++ support for all COM related objects. In general, Python programmers will not use this module directly, but use win32com helper classes and functions. </P>
<P>This module exposes a C++ like interface to COM - there are objects implemented in pythoncom that have methods "QueryInterface()", "Invoke()", just like the C++ API. If you are using COM in C++, you would not call a method directly, you would use pObject-&gt;Invoke( …, MethodId, argArray…). Similarly, if you are using pythoncom directly, you must also use the Invoke method to call an object's exposed method.</FONT> </P>
<FONT SIZE=2><P>There are some Python wrappers for hiding this raw interface, meaning you should almost never need to use the pythoncom module directly. These helpers translate a "natural" looking interface (eg, obj.SomeMethod()) into the underlying Invoke call.</P></FONT></BODY>
</HTML>

View file

@ -0,0 +1,162 @@
<HTML>
<HEAD>
<TITLE>win32com.client.VARIANT</TITLE>
</HEAD>
<BODY>
<H2>Introduction</H2>
<p>
win32com attempts to provide a seamless COM interface and hide many COM
implementation details, including the use of COM VARIANT structures. This
means that in most cases, you just call a COM object using normal Python
objects as parameters and get back normal Python objects as results.
</p>
<p>
However, in some cases this doesn't work very well, particularly when using
"dynamic" (aka late-bound) objects, or when using "makepy" (aka early-bound)
objects which only declare a parameter is a VARIANT.
</p>
<p>
The <code>win32com.client.VARIANT</code> object is designed to overcome these
problems.
</p>
<h2>Drawbacks</h2>
The primary issue with this approach is that the programmer must learn more
about COM VARIANTs than otherwise - they need to know concepts such as
variants being <em>byref</em>, holding arrays, or that some may hold 32bit
unsigned integers while others hold 64bit signed ints, and they need to
understand this in the context of a single method call. In short, this is
a relatively advanced feature. The good news though is that use of these
objects should never cause your program to hard-crash - the worst you should
expect are Python or COM exceptions being thrown.
<h2>The VARIANT object</h2>
The VARIANT object lives in <code>win32com.client</code>. The constructor
takes 2 parameters - the 'variant type' and the value. The 'variant type' is
an integer and can be one or more of the <code>pythoncom.VT_*</code> values,
possibly or'd together.
<p>For example, to create a VARIANT object which defines a byref array of
32bit integers, you could use:
<pre>
>>> from win32com.client import VARIANT
>>> import pythoncom
>>> v = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_ARRAY | pythoncom.VT_I4,
... [1,2,3,4])
>>> v
win32com.client.VARIANT(24579, [1, 2, 3, 4])
>>>
</pre>
This variable can then be used whereever a COM VARIANT is expected.
<h2>Example usage with dynamic objects.</h2>
For this example we will use the COM object used for win32com testing,
<code>PyCOMTest.PyCOMTest</code>. This object defines a method which is
defined in IDL as:
<pre>
HRESULT DoubleInOutString([in,out] BSTR *str);
</pre>
As you can see, it takes a single string parameter which is also used as
an "out" parameter - the single parameter will be updated after the call.
The implementation of the method simply "doubles" the string.
<p>If the object has a type-library, this method works fine with makepy
generated support. For example:
<pre>
>>> from win32com.client.gencache import EnsureDispatch
>>> ob = EnsureDispatch("PyCOMTest.PyCOMTest")
>>> ob.DoubleInOutString("Hello")
u'HelloHello'
>>>
</pre>
However, if makepy support is not available the method does not work as
expected. For the next example we will use <code>DumbDispatch</code> to
simulate the object not having a type-library.
<pre>
>>> import win32com.client.dynamic
>>> ob = win32com.client.dynamic.DumbDispatch("PyCOMTest.PyCOMTest")
>>> ob.DoubleInOutString("Hello")
>>>
</pre>
As you can see, no result came back from the function. This is because
win32com has no type information available to use, so doesn't know the
parameter should be passed as a <code>byref</code> parameter. To work
around this, we can use the <code>VARIANT</code> object.
<p>The following example explicitly creates a VARIANT object with a
variant type of a byref string and a value 'Hello'. After making the
call with this VARIANT the value is updated.
<pre>
>>> import win32com.client.dynamic
>>> from win32com.client import VARIANT
>>> import pythoncom
>>> ob = win32com.client.dynamic.DumbDispatch("PyCOMTest.PyCOMTest")
>>> variant = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_BSTR, "Hello")
>>> variant.value # check the value before the call.
'Hello'
>>> ob.DoubleInOutString(variant)
>>> variant.value
u'HelloHello'
>>>
</pre>
<h2>Usage with generated objects</h2>
In most cases, objects with makepy support (ie, 'generated' objects) don't
need to use the VARIANT object - the type information means win32com can guess
the right thing to pass. However, in some cases the VARIANT object can still
be useful.
Imagine a poorly specified object with IDL like:
<pre>
HRESULT DoSomething([in] VARIANT value);
</pre>
But also imagine that the object has a limitation that if the parameter is an
integer, it must be a 32bit unsigned value - any other integer representation
will fail.
<p>If you just pass a regular Python integer to this function, it will
generally be passed as a 32bit signed integer and given the limitation above,
will fail. The VARIANT object allows you to work around the limitation - just
create a variant object <code>VARIANT(pythoncom.VT_UI4, int_value)</code> and
pass that - the function will then be called with the explicit type you
specified and will succeed.
<p>Note that you can not use a VARIANT object to override the types described
in a type library. If a makepy generated class specifies that a VT_UI2 is
expected, attempting to pass a VARIANT object will fail. In this case you
would need to hack around the problem. For example, imagine <code>ob</code>
was a COM object which a method called <code>foo</code> and you wanted to
override the type declaration for <code>foo</code> by passing a VARIANT.
You could do something like:
<pre>
>>> import win32com.client.dynamic
>>> from win32com.client import VARIANT
>>> import pythoncom
>>> dumbob = win32com.client.dynamic.DumbDispatch(ob)
>>> variant = VARIANT(pythoncom.VT_BYREF | pythoncom.VT_BSTR, "Hello")
>>> dumbob.foo(variant)
</pre>
The code above converts the makepy supported <code>ob</code> into a
'dumb' (ie, non-makepy supported) version of the object, which will then
allow you to use VARIANT objects for the problematic methods.
</BODY>
</HTML>

View file

@ -0,0 +1,30 @@
Unless stated in the specfic source file, this work is
Copyright (c) 1996-2008, Greg Stein and Mark Hammond.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
Neither names of Greg Stein, Mark Hammond nor the name of contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,120 @@
#
# Initialization for the win32com package
#
import win32api, sys, os
import pythoncom
# flag if we are in a "frozen" build.
_frozen = getattr(sys, "frozen", 1==0)
# pythoncom dumbly defaults this to zero - we believe sys.frozen over it.
if _frozen and not getattr(pythoncom, "frozen", 0):
pythoncom.frozen = sys.frozen
# Add support for an external "COM Extensions" path.
# Concept is that you can register a seperate path to be used for
# COM extensions, outside of the win32com directory. These modules, however,
# look identical to win32com built-in modules.
# This is the technique that we use for the "standard" COM extensions.
# eg "win32com.mapi" or "win32com.axscript" both work, even though they do not
# live under the main win32com directory.
__gen_path__ = ''
__build_path__ = None
### TODO - Load _all_ \\Extensions subkeys - for now, we only read the default
### Modules will work if loaded into "win32comext" path.
def SetupEnvironment():
HKEY_LOCAL_MACHINE = -2147483646 # Avoid pulling in win32con for just these...
KEY_QUERY_VALUE = 0x1
# Open the root key once, as this is quite slow on NT.
try:
keyName = "SOFTWARE\\Python\\PythonCore\\%s\\PythonPath\\win32com" % sys.winver
key = win32api.RegOpenKey(HKEY_LOCAL_MACHINE , keyName, 0, KEY_QUERY_VALUE)
except (win32api.error, AttributeError):
key = None
try:
found = 0
if key is not None:
try:
__path__.append( win32api.RegQueryValue(key, "Extensions" ))
found = 1
except win32api.error:
# Nothing registered
pass
if not found:
try:
__path__.append( win32api.GetFullPathName( __path__[0] + "\\..\\win32comext") )
except win32api.error:
# Give up in disgust!
pass
# For the sake of developers, we also look up a "BuildPath" key
# If extension modules add support, we can load their .pyd's from a completely
# different directory (see the comments below)
try:
if key is not None:
global __build_path__
__build_path__ = win32api.RegQueryValue(key, "BuildPath")
__path__.append(__build_path__)
except win32api.error:
# __build_path__ neednt be defined.
pass
global __gen_path__
if key is not None:
try:
__gen_path__ = win32api.RegQueryValue(key, "GenPath")
except win32api.error:
pass
finally:
if key is not None:
key.Close()
# A Helper for developers. A sub-package's __init__ can call this help function,
# which allows the .pyd files for the extension to live in a special "Build" directory
# (which the win32com developers do!)
def __PackageSupportBuildPath__(package_path):
# See if we have a special directory for the binaries (for developers)
if not _frozen and __build_path__:
package_path.append(__build_path__)
if not _frozen:
SetupEnvironment()
# If we don't have a special __gen_path__, see if we have a gen_py as a
# normal module and use that (ie, "win32com.gen_py" may already exist as
# a package.
if not __gen_path__:
try:
import win32com.gen_py
# hrmph - 3.3 throws: TypeError: '_NamespacePath' object does not support indexing
# attempting to get __path__[0] - but I can't quickly repro this stand-alone.
# Work around it by using an iterator.
__gen_path__ = next(iter(sys.modules["win32com.gen_py"].__path__))
except ImportError:
# If a win32com\gen_py directory already exists, then we use it
# (gencache doesn't insist it have an __init__, but our __import__
# above does!
__gen_path__ = os.path.abspath(os.path.join(__path__[0], "gen_py"))
if not os.path.isdir(__gen_path__):
# We used to dynamically create a directory under win32com -
# but this sucks. If the dir doesn't already exist, we we
# create a version specific directory under the user temp
# directory.
__gen_path__ = os.path.join(
win32api.GetTempPath(), "gen_py",
"%d.%d" % (sys.version_info[0], sys.version_info[1]))
# we must have a __gen_path__, but may not have a gen_py module -
# set that up.
if "win32com.gen_py" not in sys.modules:
# Create a "win32com.gen_py", but with a custom __path__
import imp
gen_py = imp.new_module("win32com.gen_py")
gen_py.__path__ = [ __gen_path__ ]
sys.modules[gen_py.__name__]=gen_py
del imp
gen_py = sys.modules["win32com.gen_py"]
# get rid of these for module users
del os, sys, win32api, pythoncom

View file

@ -0,0 +1,53 @@
"""Manages a dictionary of CLSID strings to Python classes.
Primary use of this module is to allow modules generated by
makepy.py to share classes. @makepy@ automatically generates code
which interacts with this module. You should never need to reference
this module directly.
This module only provides support for modules which have been previously
been imported. The gencache module provides some support for loading modules
on demand - once done, this module supports it...
As an example, the MSACCESS.TLB type library makes reference to the
CLSID of the Database object, as defined in DAO3032.DLL. This
allows code using the MSAccess wrapper to natively use Databases.
This obviously applies to all cooperating objects, not just DAO and
Access.
"""
mapCLSIDToClass = {}
def RegisterCLSID( clsid, pythonClass ):
"""Register a class that wraps a CLSID
This function allows a CLSID to be globally associated with a class.
Certain module will automatically convert an IDispatch object to an
instance of the associated class.
"""
mapCLSIDToClass[str(clsid)] = pythonClass
def RegisterCLSIDsFromDict( dict ):
"""Register a dictionary of CLSID's and classes.
This module performs the same function as @RegisterCLSID@, but for
an entire dictionary of associations.
Typically called by makepy generated modules at import time.
"""
mapCLSIDToClass.update(dict)
def GetClass(clsid):
"""Given a CLSID, return the globally associated class.
clsid -- a string CLSID representation to check.
"""
return mapCLSIDToClass[clsid]
def HasClass(clsid):
"""Determines if the CLSID has an associated class.
clsid -- the string CLSID to check
"""
return clsid in mapCLSIDToClass

View file

@ -0,0 +1,547 @@
# This module exists to create the "best" dispatch object for a given
# object. If "makepy" support for a given object is detected, it is
# used, otherwise a dynamic dispatch object.
# Note that if the unknown dispatch object then returns a known
# dispatch object, the known class will be used. This contrasts
# with dynamic.Dispatch behaviour, where dynamic objects are always used.
import pythoncom
from . import dynamic
from . import gencache
import sys
import pywintypes
_PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
def __WrapDispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, \
UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER,
WrapperClass = None):
"""
Helper function to return a makepy generated class for a CLSID if it exists,
otherwise cope by using CDispatch.
"""
assert UnicodeToString is None, "this is deprecated and will go away"
if resultCLSID is None:
try:
typeinfo = dispatch.GetTypeInfo()
if typeinfo is not None: # Some objects return NULL, some raise exceptions...
resultCLSID = str(typeinfo.GetTypeAttr()[0])
except (pythoncom.com_error, AttributeError):
pass
if resultCLSID is not None:
from . import gencache
# Attempt to load generated module support
# This may load the module, and make it available
klass = gencache.GetClassForCLSID(resultCLSID)
if klass is not None:
return klass(dispatch)
# Return a "dynamic" object - best we can do!
if WrapperClass is None: WrapperClass = CDispatch
return dynamic.Dispatch(dispatch, userName, WrapperClass, typeinfo, clsctx=clsctx)
def GetObject(Pathname = None, Class = None, clsctx = None):
"""
Mimic VB's GetObject() function.
ob = GetObject(Class = "ProgID") or GetObject(Class = clsid) will
connect to an already running instance of the COM object.
ob = GetObject(r"c:\blah\blah\foo.xls") (aka the COM moniker syntax)
will return a ready to use Python wrapping of the required COM object.
Note: You must specifiy one or the other of these arguments. I know
this isn't pretty, but it is what VB does. Blech. If you don't
I'll throw ValueError at you. :)
This will most likely throw pythoncom.com_error if anything fails.
"""
if clsctx is None:
clsctx = pythoncom.CLSCTX_ALL
if (Pathname is None and Class is None) or \
(Pathname is not None and Class is not None):
raise ValueError("You must specify a value for Pathname or Class, but not both.")
if Class is not None:
return GetActiveObject(Class, clsctx)
else:
return Moniker(Pathname, clsctx)
def GetActiveObject(Class, clsctx = pythoncom.CLSCTX_ALL):
"""
Python friendly version of GetObject's ProgID/CLSID functionality.
"""
resultCLSID = pywintypes.IID(Class)
dispatch = pythoncom.GetActiveObject(resultCLSID)
dispatch = dispatch.QueryInterface(pythoncom.IID_IDispatch)
return __WrapDispatch(dispatch, Class, resultCLSID = resultCLSID, clsctx = clsctx)
def Moniker(Pathname, clsctx = pythoncom.CLSCTX_ALL):
"""
Python friendly version of GetObject's moniker functionality.
"""
moniker, i, bindCtx = pythoncom.MkParseDisplayName(Pathname)
dispatch = moniker.BindToObject(bindCtx, None, pythoncom.IID_IDispatch)
return __WrapDispatch(dispatch, Pathname, clsctx=clsctx)
def Dispatch(dispatch, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
"""Creates a Dispatch based COM object.
"""
assert UnicodeToString is None, "this is deprecated and will go away"
dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
return __WrapDispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
def DispatchEx(clsid, machine=None, userName = None, resultCLSID = None, typeinfo = None, UnicodeToString=None, clsctx = None):
"""Creates a Dispatch based COM object on a specific machine.
"""
assert UnicodeToString is None, "this is deprecated and will go away"
# If InProc is registered, DCOM will use it regardless of the machine name
# (and regardless of the DCOM config for the object.) So unless the user
# specifies otherwise, we exclude inproc apps when a remote machine is used.
if clsctx is None:
clsctx = pythoncom.CLSCTX_SERVER
if machine is not None: clsctx = clsctx & ~pythoncom.CLSCTX_INPROC
if machine is None:
serverInfo = None
else:
serverInfo = (machine,)
if userName is None: userName = clsid
dispatch = pythoncom.CoCreateInstanceEx(clsid, None, clsctx, serverInfo, (pythoncom.IID_IDispatch,))[0]
return Dispatch(dispatch, userName, resultCLSID, typeinfo, clsctx=clsctx)
class CDispatch(dynamic.CDispatch):
"""
The dynamic class used as a last resort.
The purpose of this overriding of dynamic.CDispatch is to perpetuate the policy
of using the makepy generated wrapper Python class instead of dynamic.CDispatch
if/when possible.
"""
def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
assert UnicodeToString is None, "this is deprecated and will go away"
return Dispatch(ob, userName, returnCLSID,None)
def CastTo(ob, target, typelib = None):
"""'Cast' a COM object to another interface"""
# todo - should support target being an IID
mod = None
if typelib is not None: # caller specified target typelib (TypelibSpec). See e.g. selecttlb.EnumTlbs().
mod = gencache.MakeModuleForTypelib(typelib.clsid, typelib.lcid, int(typelib.major, 16), int(typelib.minor, 16))
if not hasattr(mod, target):
raise ValueError("The interface name '%s' does not appear in the " \
"specified library %r" % (target, typelib.ver_desc))
elif hasattr(target, "index"): # string like
# for now, we assume makepy for this to work.
if "CLSID" not in ob.__class__.__dict__:
# Eeek - no makepy support - try and build it.
ob = gencache.EnsureDispatch(ob)
if "CLSID" not in ob.__class__.__dict__:
raise ValueError("Must be a makepy-able object for this to work")
clsid = ob.CLSID
# Lots of hoops to support "demand-build" - ie, generating
# code for an interface first time it is used. We assume the
# interface name exists in the same library as the object.
# This is generally the case - only referenced typelibs may be
# a problem, and we can handle that later. Maybe <wink>
# So get the generated module for the library itself, then
# find the interface CLSID there.
mod = gencache.GetModuleForCLSID(clsid)
# Get the 'root' module.
mod = gencache.GetModuleForTypelib(mod.CLSID, mod.LCID,
mod.MajorVersion, mod.MinorVersion)
# Find the CLSID of the target
target_clsid = mod.NamesToIIDMap.get(target)
if target_clsid is None:
raise ValueError("The interface name '%s' does not appear in the " \
"same library as object '%r'" % (target, ob))
mod = gencache.GetModuleForCLSID(target_clsid)
if mod is not None:
target_class = getattr(mod, target)
# resolve coclass to interface
target_class = getattr(target_class, "default_interface", target_class)
return target_class(ob) # auto QI magic happens
raise ValueError
class Constants:
"""A container for generated COM constants.
"""
def __init__(self):
self.__dicts__ = [] # A list of dictionaries
def __getattr__(self, a):
for d in self.__dicts__:
if a in d:
return d[a]
raise AttributeError(a)
# And create an instance.
constants = Constants()
# A helpers for DispatchWithEvents - this becomes __setattr__ for the
# temporary class.
def _event_setattr_(self, attr, val):
try:
# Does the COM object have an attribute of this name?
self.__class__.__bases__[0].__setattr__(self, attr, val)
except AttributeError:
# Otherwise just stash it away in the instance.
self.__dict__[attr] = val
# An instance of this "proxy" is created to break the COM circular references
# that exist (ie, when we connect to the COM events, COM keeps a reference
# to the object. Thus, the Event connection must be manually broken before
# our object can die. This solves the problem by manually breaking the connection
# to the real object as the proxy dies.
class EventsProxy:
def __init__(self, ob):
self.__dict__['_obj_'] = ob
def __del__(self):
try:
# If there is a COM error on disconnection we should
# just ignore it - object probably already shut down...
self._obj_.close()
except pythoncom.com_error:
pass
def __getattr__(self, attr):
return getattr(self._obj_, attr)
def __setattr__(self, attr, val):
setattr(self._obj_, attr, val)
def DispatchWithEvents(clsid, user_event_class):
"""Create a COM object that can fire events to a user defined class.
clsid -- The ProgID or CLSID of the object to create.
user_event_class -- A Python class object that responds to the events.
This requires makepy support for the COM object being created. If
this support does not exist it will be automatically generated by
this function. If the object does not support makepy, a TypeError
exception will be raised.
The result is a class instance that both represents the COM object
and handles events from the COM object.
It is important to note that the returned instance is not a direct
instance of the user_event_class, but an instance of a temporary
class object that derives from three classes:
* The makepy generated class for the COM object
* The makepy generated class for the COM events
* The user_event_class as passed to this function.
If this is not suitable, see the getevents function for an alternative
technique of handling events.
Object Lifetimes: Whenever the object returned from this function is
cleaned-up by Python, the events will be disconnected from
the COM object. This is almost always what should happen,
but see the documentation for getevents() for more details.
Example:
>>> class IEEvents:
... def OnVisible(self, visible):
... print "Visible changed:", visible
...
>>> ie = DispatchWithEvents("InternetExplorer.Application", IEEvents)
>>> ie.Visible = 1
Visible changed: 1
>>>
"""
# Create/Get the object.
disp = Dispatch(clsid)
if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
tlb, index = ti.GetContainingTypeLib()
tla = tlb.GetLibAttr()
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
# Get the class from the module.
disp_class = gencache.GetClassForProgID(str(disp_clsid))
except pythoncom.com_error:
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
else:
disp_class = disp.__class__
# If the clsid was an object, get the clsid
clsid = disp_class.CLSID
# Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class.
# XXX - we are still "classic style" classes in py2x, so we need can't yet
# use 'type()' everywhere - revisit soon, as py2x will move to new-style too...
try:
from types import ClassType as new_type
except ImportError:
new_type = type # py3k
events_class = getevents(clsid)
if events_class is None:
raise ValueError("This COM object does not support events.")
result_class = new_type("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_})
instance = result_class(disp._oleobj_) # This only calls the first base class __init__.
events_class.__init__(instance, instance)
if hasattr(user_event_class, "__init__"):
user_event_class.__init__(instance)
return EventsProxy(instance)
def WithEvents(disp, user_event_class):
"""Similar to DispatchWithEvents - except that the returned
object is *not* also usable as the original Dispatch object - that is
the returned object is not dispatchable.
The difference is best summarised by example.
>>> class IEEvents:
... def OnVisible(self, visible):
... print "Visible changed:", visible
...
>>> ie = Dispatch("InternetExplorer.Application")
>>> ie_events = WithEvents(ie, IEEvents)
>>> ie.Visible = 1
Visible changed: 1
Compare with the code sample for DispatchWithEvents, where you get a
single object that is both the interface and the event handler. Note that
the event handler instance will *not* be able to use 'self.' to refer to
IE's methods and properties.
This is mainly useful where using DispatchWithEvents causes
circular reference problems that the simple proxy doesn't deal with
"""
disp = Dispatch(disp)
if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
tlb, index = ti.GetContainingTypeLib()
tla = tlb.GetLibAttr()
gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
# Get the class from the module.
disp_class = gencache.GetClassForProgID(str(disp_clsid))
except pythoncom.com_error:
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
else:
disp_class = disp.__class__
# Get the clsid
clsid = disp_class.CLSID
# Create a new class that derives from 2 classes - the event sink
# class and the user class.
try:
from types import ClassType as new_type
except ImportError:
new_type = type # py3k
events_class = getevents(clsid)
if events_class is None:
raise ValueError("This COM object does not support events.")
result_class = new_type("COMEventClass", (events_class, user_event_class), {})
instance = result_class(disp) # This only calls the first base class __init__.
if hasattr(user_event_class, "__init__"):
user_event_class.__init__(instance)
return instance
def getevents(clsid):
"""Determine the default outgoing interface for a class, given
either a clsid or progid. It returns a class - you can
conveniently derive your own handler from this class and implement
the appropriate methods.
This method relies on the classes produced by makepy. You must use
either makepy or the gencache module to ensure that the
appropriate support classes have been generated for the com server
that you will be handling events from.
Beware of COM circular references. When the Events class is connected
to the COM object, the COM object itself keeps a reference to the Python
events class. Thus, neither the Events instance or the COM object will
ever die by themselves. The 'close' method on the events instance
must be called to break this chain and allow standard Python collection
rules to manage object lifetimes. Note that DispatchWithEvents() does
work around this problem by the use of a proxy object, but if you use
the getevents() function yourself, you must make your own arrangements
to manage this circular reference issue.
Beware of creating Python circular references: this will happen if your
handler has a reference to an object that has a reference back to
the event source. Call the 'close' method to break the chain.
Example:
>>>win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
<module 'win32com.gen_py.....
>>>
>>> class InternetExplorerEvents(win32com.client.getevents("InternetExplorer.Application.1")):
... def OnVisible(self, Visible):
... print "Visibility changed: ", Visible
...
>>>
>>> ie=win32com.client.Dispatch("InternetExplorer.Application.1")
>>> events=InternetExplorerEvents(ie)
>>> ie.Visible=1
Visibility changed: 1
>>>
"""
# find clsid given progid or clsid
clsid=str(pywintypes.IID(clsid))
# return default outgoing interface for that class
klass = gencache.GetClassForCLSID(clsid)
try:
return klass.default_source
except AttributeError:
# See if we have a coclass for the interfaces.
try:
return gencache.GetClassForCLSID(klass.coclass_clsid).default_source
except AttributeError:
return None
# A Record object, as used by the COM struct support
def Record(name, object):
"""Creates a new record object, given the name of the record,
and an object from the same type library.
Example usage would be:
app = win32com.client.Dispatch("Some.Application")
point = win32com.client.Record("SomeAppPoint", app)
point.x = 0
point.y = 0
app.MoveTo(point)
"""
# XXX - to do - probably should allow "object" to already be a module object.
from . import gencache
object = gencache.EnsureDispatch(object)
module = sys.modules[object.__class__.__module__]
# to allow us to work correctly with "demand generated" code,
# we must use the typelib CLSID to obtain the module
# (otherwise we get the sub-module for the object, which
# does not hold the records)
# thus, package may be module, or may be module's parent if demand generated.
package = gencache.GetModuleForTypelib(module.CLSID, module.LCID, module.MajorVersion, module.MinorVersion)
try:
struct_guid = package.RecordMap[name]
except KeyError:
raise ValueError("The structure '%s' is not defined in module '%s'" % (name, package))
return pythoncom.GetRecordFromGuids(module.CLSID, module.MajorVersion, module.MinorVersion, module.LCID, struct_guid)
############################################
# The base of all makepy generated classes
############################################
class DispatchBaseClass:
def __init__(self, oobj=None):
if oobj is None:
oobj = pythoncom.new(self.CLSID)
elif isinstance(oobj, DispatchBaseClass):
try:
oobj = oobj._oleobj_.QueryInterface(self.CLSID, pythoncom.IID_IDispatch) # Must be a valid COM instance
except pythoncom.com_error as details:
import winerror
# Some stupid objects fail here, even tho it is _already_ IDispatch!!??
# Eg, Lotus notes.
# So just let it use the existing object if E_NOINTERFACE
if details.hresult != winerror.E_NOINTERFACE:
raise
oobj = oobj._oleobj_
self.__dict__["_oleobj_"] = oobj # so we dont call __setattr__
# Provide a prettier name than the CLSID
def __repr__(self):
# Need to get the docstring for the module for this class.
try:
mod_doc = sys.modules[self.__class__.__module__].__doc__
if mod_doc:
mod_name = "win32com.gen_py." + mod_doc
else:
mod_name = sys.modules[self.__class__.__module__].__name__
except KeyError:
mod_name = "win32com.gen_py.unknown"
return "<%s.%s instance at 0x%s>" % (mod_name, self.__class__.__name__, id(self))
# Delegate comparison to the oleobjs, as they know how to do identity.
def __eq__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ == other
def __ne__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ != other
def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
return self._get_good_object_(
self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
user, resultCLSID)
def __getattr__(self, attr):
args=self._prop_map_get_.get(attr)
if args is None:
raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
return self._ApplyTypes_(*args)
def __setattr__(self, attr, value):
if attr in self.__dict__: self.__dict__[attr] = value; return
try:
args, defArgs=self._prop_map_put_[attr]
except KeyError:
raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
self._oleobj_.Invoke(*(args + (value,) + defArgs))
def _get_good_single_object_(self, obj, obUserName=None, resultCLSID=None):
return _get_good_single_object_(obj, obUserName, resultCLSID)
def _get_good_object_(self, obj, obUserName=None, resultCLSID=None):
return _get_good_object_(obj, obUserName, resultCLSID)
# XXX - These should be consolidated with dynamic.py versions.
def _get_good_single_object_(obj, obUserName=None, resultCLSID=None):
if _PyIDispatchType==type(obj):
return Dispatch(obj, obUserName, resultCLSID)
return obj
def _get_good_object_(obj, obUserName=None, resultCLSID=None):
if obj is None:
return None
elif isinstance(obj, tuple):
obUserNameTuple = (obUserName,) * len(obj)
resultCLSIDTuple = (resultCLSID,) * len(obj)
return tuple(map(_get_good_object_, obj, obUserNameTuple, resultCLSIDTuple))
else:
return _get_good_single_object_(obj, obUserName, resultCLSID)
class CoClassBaseClass:
def __init__(self, oobj=None):
if oobj is None: oobj = pythoncom.new(self.CLSID)
self.__dict__["_dispobj_"] = self.default_interface(oobj)
def __repr__(self):
return "<win32com.gen_py.%s.%s>" % (__doc__, self.__class__.__name__)
def __getattr__(self, attr):
d=self.__dict__["_dispobj_"]
if d is not None: return getattr(d, attr)
raise AttributeError(attr)
def __setattr__(self, attr, value):
if attr in self.__dict__: self.__dict__[attr] = value; return
try:
d=self.__dict__["_dispobj_"]
if d is not None:
d.__setattr__(attr, value)
return
except AttributeError:
pass
self.__dict__[attr] = value
# A very simple VARIANT class. Only to be used with poorly-implemented COM
# objects. If an object accepts an arg which is a simple "VARIANT", but still
# is very pickly about the actual variant type (eg, isn't happy with a VT_I4,
# which it would get from a Python integer), you can use this to force a
# particular VT.
class VARIANT(object):
def __init__(self, vt, value):
self.varianttype = vt
self._value = value
# 'value' is a property so when set by pythoncom it gets any magic wrapping
# which normally happens for result objects
def _get_value(self):
return self._value
def _set_value(self, newval):
self._value = _get_good_object_(newval)
def _del_value(self):
del self._value
value = property(_get_value, _set_value, _del_value)
def __repr__(self):
return "win32com.client.VARIANT(%r, %r)" % (self.varianttype, self._value)

View file

@ -0,0 +1,630 @@
"""Contains knowledge to build a COM object definition.
This module is used by both the @dynamic@ and @makepy@ modules to build
all knowledge of a COM object.
This module contains classes which contain the actual knowledge of the object.
This include parameter and return type information, the COM dispid and CLSID, etc.
Other modules may use this information to generate .py files, use the information
dynamically, or possibly even generate .html documentation for objects.
"""
#
# NOTES: DispatchItem and MapEntry used by dynamic.py.
# the rest is used by makepy.py
#
# OleItem, DispatchItem, MapEntry, BuildCallList() is used by makepy
import sys
import string
from keyword import iskeyword
import pythoncom
from pywintypes import TimeType
import winerror
import datetime
# It isn't really clear what the quoting rules are in a C/IDL string and
# literals like a quote char and backslashes makes life a little painful to
# always render the string perfectly - so just punt and fall-back to a repr()
def _makeDocString(s):
if sys.version_info < (3,):
s = s.encode("mbcs")
return repr(s)
error = "PythonCOM.Client.Build error"
class NotSupportedException(Exception): pass # Raised when we cant support a param type.
DropIndirection="DropIndirection"
NoTranslateTypes = [
pythoncom.VT_BOOL, pythoncom.VT_CLSID, pythoncom.VT_CY,
pythoncom.VT_DATE, pythoncom.VT_DECIMAL, pythoncom.VT_EMPTY,
pythoncom.VT_ERROR, pythoncom.VT_FILETIME, pythoncom.VT_HRESULT,
pythoncom.VT_I1, pythoncom.VT_I2, pythoncom.VT_I4,
pythoncom.VT_I8, pythoncom.VT_INT, pythoncom.VT_NULL,
pythoncom.VT_R4, pythoncom.VT_R8, pythoncom.VT_NULL,
pythoncom.VT_STREAM,
pythoncom.VT_UI1, pythoncom.VT_UI2, pythoncom.VT_UI4,
pythoncom.VT_UI8, pythoncom.VT_UINT, pythoncom.VT_VOID,
]
NoTranslateMap = {}
for v in NoTranslateTypes:
NoTranslateMap[v] = None
class MapEntry:
"Simple holder for named attibutes - items in a map."
def __init__(self, desc_or_id, names=None, doc=None, resultCLSID=pythoncom.IID_NULL, resultDoc = None, hidden=0):
if type(desc_or_id)==type(0):
self.dispid = desc_or_id
self.desc = None
else:
self.dispid = desc_or_id[0]
self.desc = desc_or_id
self.names = names
self.doc = doc
self.resultCLSID = resultCLSID
self.resultDocumentation = resultDoc
self.wasProperty = 0 # Have I been transformed into a function so I can pass args?
self.hidden = hidden
def GetResultCLSID(self):
rc = self.resultCLSID
if rc == pythoncom.IID_NULL: return None
return rc
# Return a string, suitable for output - either "'{...}'" or "None"
def GetResultCLSIDStr(self):
rc = self.GetResultCLSID()
if rc is None: return "None"
return repr(str(rc)) # Convert the IID object to a string, then to a string in a string.
def GetResultName(self):
if self.resultDocumentation is None:
return None
return self.resultDocumentation[0]
class OleItem:
typename = "OleItem"
def __init__(self, doc=None):
self.doc = doc
if self.doc:
self.python_name = MakePublicAttributeName(self.doc[0])
else:
self.python_name = None
self.bWritten = 0
self.bIsDispatch = 0
self.bIsSink = 0
self.clsid = None
self.co_class = None
class DispatchItem(OleItem):
typename = "DispatchItem"
def __init__(self, typeinfo=None, attr=None, doc=None, bForUser=1):
OleItem.__init__(self,doc)
self.propMap = {}
self.propMapGet = {}
self.propMapPut = {}
self.mapFuncs = {}
self.defaultDispatchName = None
self.hidden = 0
if typeinfo:
self.Build(typeinfo, attr, bForUser)
def _propMapPutCheck_(self,key,item):
ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
if ins>1: # if a Put property takes more than 1 arg:
if opts+1==ins or ins==item.desc[6]+1:
newKey = "Set" + key
deleteExisting = 0 # This one is still OK
else:
deleteExisting = 1 # No good to us
if key in self.mapFuncs or key in self.propMapGet:
newKey = "Set" + key
else:
newKey = key
item.wasProperty = 1
self.mapFuncs[newKey] = item
if deleteExisting:
del self.propMapPut[key]
def _propMapGetCheck_(self,key,item):
ins, outs, opts = self.CountInOutOptArgs(item.desc[2])
if ins > 0: # if a Get property takes _any_ in args:
if item.desc[6]==ins or ins==opts:
newKey = "Get" + key
deleteExisting = 0 # This one is still OK
else:
deleteExisting = 1 # No good to us
if key in self.mapFuncs:
newKey = "Get" + key
else:
newKey = key
item.wasProperty = 1
self.mapFuncs[newKey] = item
if deleteExisting:
del self.propMapGet[key]
def _AddFunc_(self,typeinfo,fdesc,bForUser):
id = fdesc.memid
funcflags = fdesc.wFuncFlags
try:
names = typeinfo.GetNames(id)
name=names[0]
except pythoncom.ole_error:
name = ""
names = None
doc = None
try:
if bForUser:
doc = typeinfo.GetDocumentation(id)
except pythoncom.ole_error:
pass
if id==0 and name:
self.defaultDispatchName = name
invkind = fdesc.invkind
# We need to translate any Alias', Enums, structs etc in result and args
typerepr, flag, defval = fdesc.rettype
# sys.stderr.write("%s result - %s -> " % (name, typerepr))
typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
# sys.stderr.write("%s\n" % (typerepr,))
fdesc.rettype = typerepr, flag, defval, resultCLSID
# Translate any Alias or Enums in argument list.
argList = []
for argDesc in fdesc.args:
typerepr, flag, defval = argDesc
# sys.stderr.write("%s arg - %s -> " % (name, typerepr))
arg_type, arg_clsid, arg_doc = _ResolveType(typerepr, typeinfo)
argDesc = arg_type, flag, defval, arg_clsid
# sys.stderr.write("%s\n" % (argDesc[0],))
argList.append(argDesc)
fdesc.args = tuple(argList)
hidden = (funcflags & pythoncom.FUNCFLAG_FHIDDEN) != 0
if invkind == pythoncom.INVOKE_PROPERTYGET:
map = self.propMapGet
# This is not the best solution, but I dont think there is
# one without specific "set" syntax.
# If there is a single PUT or PUTREF, it will function as a property.
# If there are both, then the PUT remains a property, and the PUTREF
# gets transformed into a function.
# (in vb, PUT=="obj=other_obj", PUTREF="set obj=other_obj
elif invkind in (pythoncom.INVOKE_PROPERTYPUT, pythoncom.INVOKE_PROPERTYPUTREF):
# Special case
existing = self.propMapPut.get(name, None)
if existing is not None:
if existing.desc[4]==pythoncom.INVOKE_PROPERTYPUT: # Keep this one
map = self.mapFuncs
name = "Set"+name
else: # Existing becomes a func.
existing.wasProperty = 1
self.mapFuncs["Set"+name]=existing
map = self.propMapPut # existing gets overwritten below.
else:
map = self.propMapPut # first time weve seen it.
elif invkind == pythoncom.INVOKE_FUNC:
map = self.mapFuncs
else:
map = None
if not map is None:
# if map.has_key(name):
# sys.stderr.write("Warning - overwriting existing method/attribute %s\n" % name)
map[name] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
# any methods that can't be reached via DISPATCH we return None
# for, so dynamic dispatch doesnt see it.
if fdesc.funckind != pythoncom.FUNC_DISPATCH:
return None
return (name,map)
return None
def _AddVar_(self,typeinfo,fdesc,bForUser):
### need pythoncom.VARFLAG_FRESTRICTED ...
### then check it
if fdesc.varkind == pythoncom.VAR_DISPATCH:
id = fdesc.memid
names = typeinfo.GetNames(id)
# Translate any Alias or Enums in result.
typerepr, flags, defval = fdesc.elemdescVar
typerepr, resultCLSID, resultDoc = _ResolveType(typerepr, typeinfo)
fdesc.elemdescVar = typerepr, flags, defval
doc = None
try:
if bForUser: doc = typeinfo.GetDocumentation(id)
except pythoncom.ole_error:
pass
# handle the enumerator specially
map = self.propMap
# Check if the element is hidden.
hidden = 0
if hasattr(fdesc,"wVarFlags"):
hidden = (fdesc.wVarFlags & 0x40) != 0 # VARFLAG_FHIDDEN
map[names[0]] = MapEntry(tuple(fdesc), names, doc, resultCLSID, resultDoc, hidden)
return (names[0],map)
else:
return None
def Build(self, typeinfo, attr, bForUser = 1):
self.clsid = attr[0]
self.bIsDispatch = (attr.wTypeFlags & pythoncom.TYPEFLAG_FDISPATCHABLE) != 0
if typeinfo is None: return
# Loop over all methods
for j in range(attr[6]):
fdesc = typeinfo.GetFuncDesc(j)
self._AddFunc_(typeinfo,fdesc,bForUser)
# Loop over all variables (ie, properties)
for j in range(attr[7]):
fdesc = typeinfo.GetVarDesc(j)
self._AddVar_(typeinfo,fdesc,bForUser)
# Now post-process the maps. For any "Get" or "Set" properties
# that have arguments, we must turn them into methods. If a method
# of the same name already exists, change the name.
for key, item in list(self.propMapGet.items()):
self._propMapGetCheck_(key,item)
for key, item in list(self.propMapPut.items()):
self._propMapPutCheck_(key,item)
def CountInOutOptArgs(self, argTuple):
"Return tuple counting in/outs/OPTS. Sum of result may not be len(argTuple), as some args may be in/out."
ins = out = opts = 0
for argCheck in argTuple:
inOut = argCheck[1]
if inOut==0:
ins = ins + 1
out = out + 1
else:
if inOut & pythoncom.PARAMFLAG_FIN:
ins = ins + 1
if inOut & pythoncom.PARAMFLAG_FOPT:
opts = opts + 1
if inOut & pythoncom.PARAMFLAG_FOUT:
out = out + 1
return ins, out, opts
def MakeFuncMethod(self, entry, name, bMakeClass = 1):
# If we have a type description, and not varargs...
if entry.desc is not None and (len(entry.desc) < 6 or entry.desc[6]!=-1):
return self.MakeDispatchFuncMethod(entry, name, bMakeClass)
else:
return self.MakeVarArgsFuncMethod(entry, name, bMakeClass)
def MakeDispatchFuncMethod(self, entry, name, bMakeClass = 1):
fdesc = entry.desc
doc = entry.doc
names = entry.names
ret = []
if bMakeClass:
linePrefix = "\t"
defNamedOptArg = "defaultNamedOptArg"
defNamedNotOptArg = "defaultNamedNotOptArg"
defUnnamedArg = "defaultUnnamedArg"
else:
linePrefix = ""
defNamedOptArg = "pythoncom.Missing"
defNamedNotOptArg = "pythoncom.Missing"
defUnnamedArg = "pythoncom.Missing"
defOutArg = "pythoncom.Missing"
id = fdesc[0]
s = linePrefix + 'def ' + name + '(self' + BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg, defOutArg) + '):'
ret.append(s)
if doc and doc[1]:
ret.append(linePrefix + '\t' + _makeDocString(doc[1]))
# print "fdesc is ", fdesc
resclsid = entry.GetResultCLSID()
if resclsid:
resclsid = "'%s'" % resclsid
else:
resclsid = 'None'
# Strip the default values from the arg desc
retDesc = fdesc[8][:2]
argsDesc = tuple([what[:2] for what in fdesc[2]])
# The runtime translation of the return types is expensive, so when we know the
# return type of the function, there is no need to check the type at runtime.
# To qualify, this function must return a "simple" type, and have no byref args.
# Check if we have byrefs or anything in the args which mean we still need a translate.
param_flags = [what[1] for what in fdesc[2]]
bad_params = [flag for flag in param_flags if flag & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FRETVAL)!=0]
s = None
if len(bad_params)==0 and len(retDesc)==2 and retDesc[1]==0:
rd = retDesc[0]
if rd in NoTranslateMap:
s = '%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, _BuildArgList(fdesc, names))
elif rd in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN]:
s = '%s\tret = self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)\n' % (linePrefix, id, fdesc[4], retDesc, repr(argsDesc), _BuildArgList(fdesc, names))
s = s + '%s\tif ret is not None:\n' % (linePrefix,)
if rd == pythoncom.VT_UNKNOWN:
s = s + "%s\t\t# See if this IUnknown is really an IDispatch\n" % (linePrefix,)
s = s + "%s\t\ttry:\n" % (linePrefix,)
s = s + "%s\t\t\tret = ret.QueryInterface(pythoncom.IID_IDispatch)\n" % (linePrefix,)
s = s + "%s\t\texcept pythoncom.error:\n" % (linePrefix,)
s = s + "%s\t\t\treturn ret\n" % (linePrefix,)
s = s + '%s\t\tret = Dispatch(ret, %s, %s)\n' % (linePrefix,repr(name), resclsid)
s = s + '%s\treturn ret' % (linePrefix)
elif rd == pythoncom.VT_BSTR:
s = "%s\t# Result is a Unicode object\n" % (linePrefix,)
s = s + '%s\treturn self._oleobj_.InvokeTypes(%d, LCID, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, repr(argsDesc), _BuildArgList(fdesc, names))
# else s remains None
if s is None:
s = '%s\treturn self._ApplyTypes_(%d, %s, %s, %s, %s, %s%s)' % (linePrefix, id, fdesc[4], retDesc, argsDesc, repr(name), resclsid, _BuildArgList(fdesc, names))
ret.append(s)
ret.append("")
return ret
def MakeVarArgsFuncMethod(self, entry, name, bMakeClass = 1):
fdesc = entry.desc
names = entry.names
doc = entry.doc
ret = []
argPrefix = "self"
if bMakeClass:
linePrefix = "\t"
else:
linePrefix = ""
ret.append(linePrefix + 'def ' + name + '(' + argPrefix + ', *args):')
if doc and doc[1]: ret.append(linePrefix + '\t' + _makeDocString(doc[1]))
if fdesc:
invoketype = fdesc[4]
else:
invoketype = pythoncom.DISPATCH_METHOD
s = linePrefix + '\treturn self._get_good_object_(self._oleobj_.Invoke(*(('
ret.append(s + str(entry.dispid) + ",0,%d,1)+args)),'%s')" % (invoketype, names[0]))
ret.append("")
return ret
# Note - "DispatchItem" poorly named - need a new intermediate class.
class VTableItem(DispatchItem):
def Build(self, typeinfo, attr, bForUser = 1):
DispatchItem.Build(self, typeinfo, attr, bForUser)
assert typeinfo is not None, "Cant build vtables without type info!"
meth_list = list(self.mapFuncs.values()) + list(self.propMapGet.values()) + list(self.propMapPut.values())
meth_list.sort(key=lambda m: m.desc[7])
# Now turn this list into the run-time representation
# (ready for immediate use or writing to gencache)
self.vtableFuncs = []
for entry in meth_list:
self.vtableFuncs.append( (entry.names, entry.dispid, entry.desc) )
# A Lazy dispatch item - builds an item on request using info from
# an ITypeComp. The dynamic module makes the called to build each item,
# and also holds the references to the typeinfo and typecomp.
class LazyDispatchItem(DispatchItem):
typename = "LazyDispatchItem"
def __init__(self, attr, doc):
self.clsid = attr[0]
DispatchItem.__init__(self, None, attr, doc, 0)
typeSubstMap = {
pythoncom.VT_INT: pythoncom.VT_I4,
pythoncom.VT_UINT: pythoncom.VT_UI4,
pythoncom.VT_HRESULT: pythoncom.VT_I4,
}
def _ResolveType(typerepr, itypeinfo):
# Resolve VT_USERDEFINED (often aliases or typed IDispatches)
if type(typerepr)==tuple:
indir_vt, subrepr = typerepr
if indir_vt == pythoncom.VT_PTR:
# If it is a VT_PTR to a VT_USERDEFINED that is an IDispatch/IUnknown,
# then it resolves to simply the object.
# Otherwise, it becomes a ByRef of the resolved type
# We need to drop an indirection level on pointer to user defined interfaces.
# eg, (VT_PTR, (VT_USERDEFINED, somehandle)) needs to become VT_DISPATCH
# only when "somehandle" is an object.
# but (VT_PTR, (VT_USERDEFINED, otherhandle)) doesnt get the indirection dropped.
was_user = type(subrepr)==tuple and subrepr[0]==pythoncom.VT_USERDEFINED
subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
if was_user and subrepr in [pythoncom.VT_DISPATCH, pythoncom.VT_UNKNOWN, pythoncom.VT_RECORD]:
# Drop the VT_PTR indirection
return subrepr, sub_clsid, sub_doc
# Change PTR indirection to byref
return subrepr | pythoncom.VT_BYREF, sub_clsid, sub_doc
if indir_vt == pythoncom.VT_SAFEARRAY:
# resolve the array element, and convert to VT_ARRAY
subrepr, sub_clsid, sub_doc = _ResolveType(subrepr, itypeinfo)
return pythoncom.VT_ARRAY | subrepr, sub_clsid, sub_doc
if indir_vt == pythoncom.VT_CARRAY: # runtime has no support for this yet.
# resolve the array element, and convert to VT_CARRAY
# sheesh - return _something_
return pythoncom.VT_CARRAY, None, None
if indir_vt == pythoncom.VT_USERDEFINED:
try:
resultTypeInfo = itypeinfo.GetRefTypeInfo(subrepr)
except pythoncom.com_error as details:
if details.hresult in [winerror.TYPE_E_CANTLOADLIBRARY, winerror.TYPE_E_LIBNOTREGISTERED]:
# an unregistered interface
return pythoncom.VT_UNKNOWN, None, None
raise
resultAttr = resultTypeInfo.GetTypeAttr()
typeKind = resultAttr.typekind
if typeKind == pythoncom.TKIND_ALIAS:
tdesc = resultAttr.tdescAlias
return _ResolveType(tdesc, resultTypeInfo)
elif typeKind in [pythoncom.TKIND_ENUM, pythoncom.TKIND_MODULE]:
# For now, assume Long
return pythoncom.VT_I4, None, None
elif typeKind == pythoncom.TKIND_DISPATCH:
clsid = resultTypeInfo.GetTypeAttr()[0]
retdoc = resultTypeInfo.GetDocumentation(-1)
return pythoncom.VT_DISPATCH, clsid, retdoc
elif typeKind in [pythoncom.TKIND_INTERFACE,
pythoncom.TKIND_COCLASS]:
# XXX - should probably get default interface for CO_CLASS???
clsid = resultTypeInfo.GetTypeAttr()[0]
retdoc = resultTypeInfo.GetDocumentation(-1)
return pythoncom.VT_UNKNOWN, clsid, retdoc
elif typeKind == pythoncom.TKIND_RECORD:
return pythoncom.VT_RECORD, None, None
raise NotSupportedException("Can not resolve alias or user-defined type")
return typeSubstMap.get(typerepr,typerepr), None, None
def _BuildArgList(fdesc, names):
"Builds list of args to the underlying Invoke method."
# Word has TypeInfo for Insert() method, but says "no args"
numArgs = max(fdesc[6], len(fdesc[2]))
names = list(names)
while None in names:
i = names.index(None)
names[i] = "arg%d" % (i,)
# We've seen 'source safe' libraries offer the name of 'ret' params in
# 'names' - although we can't reproduce this, it would be insane to offer
# more args than we have arg infos for - hence the upper limit on names...
names = list(map(MakePublicAttributeName, names[1:(numArgs + 1)]))
name_num = 0
while len(names) < numArgs:
names.append("arg%d" % (len(names),))
# As per BuildCallList(), avoid huge lines.
# Hack a "\n" at the end of every 5th name - "strides" would be handy
# here but don't exist in 2.2
for i in range(0, len(names), 5):
names[i] = names[i] + "\n\t\t\t"
return "," + ", ".join(names)
valid_identifier_chars = string.ascii_letters + string.digits + "_"
def demunge_leading_underscores(className):
i = 0
while className[i] == "_":
i += 1
assert i >= 2, "Should only be here with names starting with '__'"
return className[i-1:] + className[:i-1]
# Given a "public name" (eg, the name of a class, function, etc)
# make sure it is a legal (and reasonable!) Python name.
def MakePublicAttributeName(className, is_global = False):
# Given a class attribute that needs to be public, convert it to a
# reasonable name.
# Also need to be careful that the munging doesnt
# create duplicates - eg, just removing a leading "_" is likely to cause
# a clash.
# if is_global is True, then the name is a global variable that may
# overwrite a builtin - eg, "None"
if className[:2]=='__':
return demunge_leading_underscores(className)
elif className == 'None':
# assign to None is evil (and SyntaxError in 2.4, even though
# iskeyword says False there) - note that if it was a global
# it would get picked up below
className = 'NONE'
elif iskeyword(className):
# most keywords are lower case (except True, False etc in py3k)
ret = className.capitalize()
# but those which aren't get forced upper.
if ret == className:
ret = ret.upper()
return ret
elif is_global and hasattr(__builtins__, className):
# builtins may be mixed case. If capitalizing it doesn't change it,
# force to all uppercase (eg, "None", "True" become "NONE", "TRUE"
ret = className.capitalize()
if ret==className: # didn't change - force all uppercase.
ret = ret.upper()
return ret
# Strip non printable chars
return ''.join([char for char in className if char in valid_identifier_chars])
# Given a default value passed by a type library, return a string with
# an appropriate repr() for the type.
# Takes a raw ELEMDESC and returns a repr string, or None
# (NOTE: The string itself may be '"None"', which is valid, and different to None.
# XXX - To do: Dates are probably screwed, but can they come in?
def MakeDefaultArgRepr(defArgVal):
try:
inOut = defArgVal[1]
except IndexError:
# something strange - assume is in param.
inOut = pythoncom.PARAMFLAG_FIN
if inOut & pythoncom.PARAMFLAG_FHASDEFAULT:
# times need special handling...
val = defArgVal[2]
if isinstance(val, datetime.datetime):
# VARIANT <-> SYSTEMTIME conversions always lose any sub-second
# resolution, so just use a 'timetuple' here.
return repr(tuple(val.utctimetuple()))
if type(val) is TimeType:
# must be the 'old' pywintypes time object...
year=val.year; month=val.month; day=val.day; hour=val.hour; minute=val.minute; second=val.second; msec=val.msec
return "pywintypes.Time((%(year)d, %(month)d, %(day)d, %(hour)d, %(minute)d, %(second)d,0,0,0,%(msec)d))" % locals()
return repr(val)
return None
def BuildCallList(fdesc, names, defNamedOptArg, defNamedNotOptArg, defUnnamedArg, defOutArg, is_comment = False):
"Builds a Python declaration for a method."
# Names[0] is the func name - param names are from 1.
numArgs = len(fdesc[2])
numOptArgs = fdesc[6]
strval = ''
if numOptArgs==-1: # Special value that says "var args after here"
firstOptArg = numArgs
numArgs = numArgs - 1
else:
firstOptArg = numArgs - numOptArgs
for arg in range(numArgs):
try:
argName = names[arg+1]
namedArg = argName is not None
except IndexError:
namedArg = 0
if not namedArg: argName = "arg%d" % (arg)
thisdesc = fdesc[2][arg]
# See if the IDL specified a default value
defArgVal = MakeDefaultArgRepr(thisdesc)
if defArgVal is None:
# Out params always get their special default
if thisdesc[1] & (pythoncom.PARAMFLAG_FOUT | pythoncom.PARAMFLAG_FIN) == pythoncom.PARAMFLAG_FOUT:
defArgVal = defOutArg
else:
# Unnamed arg - always allow default values.
if namedArg:
# Is a named argument
if arg >= firstOptArg:
defArgVal = defNamedOptArg
else:
defArgVal = defNamedNotOptArg
else:
defArgVal = defUnnamedArg
argName = MakePublicAttributeName(argName)
# insanely long lines with an 'encoding' flag crashes python 2.4.0
# keep 5 args per line
# This may still fail if the arg names are insane, but that seems
# unlikely. See also _BuildArgList()
if (arg+1) % 5 == 0:
strval = strval + "\n"
if is_comment:
strval = strval + "#"
strval = strval + "\t\t\t"
strval = strval + ", " + argName
if defArgVal:
strval = strval + "=" + defArgVal
if numOptArgs==-1:
strval = strval + ", *" + names[-1]
return strval
if __name__=='__main__':
print("Use 'makepy.py' to generate Python code - this module is just a helper")

View file

@ -0,0 +1,540 @@
"""A utility for browsing COM objects.
Usage:
Command Prompt
Use the command *"python.exe catbrowse.py"*. This will display
display a fairly small, modal dialog.
Pythonwin
Use the "Run Script" menu item, and this will create the browser in an
MDI window. This window can be fully resized.
Details
This module allows browsing of registered Type Libraries, COM categories,
and running COM objects. The display is similar to the Pythonwin object
browser, and displays the objects in a hierarchical window.
Note that this module requires the win32ui (ie, Pythonwin) distribution to
work.
"""
import win32con
import win32api, win32ui
import sys
import pythoncom
from win32com.client import util
from pywin.tools import browser
class HLIRoot(browser.HLIPythonObject):
def __init__(self, title):
self.name = title
def GetSubList(self):
return [HLIHeadingCategory(), HLI_IEnumMoniker(pythoncom.GetRunningObjectTable().EnumRunning(), "Running Objects"), HLIHeadingRegisterdTypeLibs()]
def __cmp__(self, other):
return cmp(self.name, other.name)
class HLICOM(browser.HLIPythonObject):
def GetText(self):
return self.name
def CalculateIsExpandable(self):
return 1
class HLICLSID(HLICOM):
def __init__(self, myobject, name=None ):
if type(myobject)==type(''):
myobject = pythoncom.MakeIID(myobject)
if name is None:
try:
name = pythoncom.ProgIDFromCLSID(myobject)
except pythoncom.com_error:
name = str(myobject)
name = "IID: " + name
HLICOM.__init__(self, myobject, name)
def CalculateIsExpandable(self):
return 0
def GetSubList(self):
return []
class HLI_Interface(HLICOM):
pass
class HLI_Enum(HLI_Interface):
def GetBitmapColumn(self):
return 0 # Always a folder.
def CalculateIsExpandable(self):
if self.myobject is not None:
rc = len(self.myobject.Next(1))>0
self.myobject.Reset()
else:
rc = 0
return rc
pass
class HLI_IEnumMoniker(HLI_Enum):
def GetSubList(self):
ctx = pythoncom.CreateBindCtx()
ret = []
for mon in util.Enumerator(self.myobject):
ret.append(HLI_IMoniker(mon, mon.GetDisplayName(ctx, None)))
return ret
class HLI_IMoniker(HLI_Interface):
def GetSubList(self):
ret = []
ret.append(browser.MakeHLI(self.myobject.Hash(), "Hash Value"))
subenum = self.myobject.Enum(1)
ret.append(HLI_IEnumMoniker(subenum, "Sub Monikers"))
return ret
class HLIHeadingCategory(HLICOM):
"A tree heading for registered categories"
def GetText(self):
return "Registered Categories"
def GetSubList(self):
catinf=pythoncom.CoCreateInstance(pythoncom.CLSID_StdComponentCategoriesMgr,None,pythoncom.CLSCTX_INPROC,pythoncom.IID_ICatInformation)
enum=util.Enumerator(catinf.EnumCategories())
ret = []
try:
for catid, lcid, desc in enum:
ret.append(HLICategory((catid, lcid, desc)))
except pythoncom.com_error:
# Registered categories occasionally seem to give spurious errors.
pass # Use what we already have.
return ret
class HLICategory(HLICOM):
"An actual Registered Category"
def GetText(self):
desc = self.myobject[2]
if not desc: desc = "(unnamed category)"
return desc
def GetSubList(self):
win32ui.DoWaitCursor(1)
catid, lcid, desc = self.myobject
catinf=pythoncom.CoCreateInstance(pythoncom.CLSID_StdComponentCategoriesMgr,None,pythoncom.CLSCTX_INPROC,pythoncom.IID_ICatInformation)
ret = []
for clsid in util.Enumerator(catinf.EnumClassesOfCategories((catid,),())):
ret.append(HLICLSID(clsid))
win32ui.DoWaitCursor(0)
return ret
class HLIHelpFile(HLICOM):
def CalculateIsExpandable(self):
return 0
def GetText(self):
import os
fname, ctx = self.myobject
base = os.path.split(fname)[1]
return "Help reference in %s" %( base)
def TakeDefaultAction(self):
fname, ctx = self.myobject
if ctx:
cmd = win32con.HELP_CONTEXT
else:
cmd = win32con.HELP_FINDER
win32api.WinHelp(win32ui.GetMainFrame().GetSafeHwnd(), fname, cmd, ctx)
def GetBitmapColumn(self):
return 6
class HLIRegisteredTypeLibrary(HLICOM):
def GetSubList(self):
import os
clsidstr, versionStr = self.myobject
collected = []
helpPath = ""
key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "TypeLib\\%s\\%s" % (clsidstr, versionStr))
win32ui.DoWaitCursor(1)
try:
num = 0
while 1:
try:
subKey = win32api.RegEnumKey(key, num)
except win32api.error:
break
hSubKey = win32api.RegOpenKey(key, subKey)
try:
value, typ = win32api.RegQueryValueEx(hSubKey, None)
if typ == win32con.REG_EXPAND_SZ:
value = win32api.ExpandEnvironmentStrings(value)
except win32api.error:
value = ""
if subKey=="HELPDIR":
helpPath = value
elif subKey=="Flags":
flags = value
else:
try:
lcid = int(subKey)
lcidkey = win32api.RegOpenKey(key, subKey)
# Enumerate the platforms
lcidnum = 0
while 1:
try:
platform = win32api.RegEnumKey(lcidkey, lcidnum)
except win32api.error:
break
try:
hplatform = win32api.RegOpenKey(lcidkey, platform)
fname, typ = win32api.RegQueryValueEx(hplatform, None)
if typ == win32con.REG_EXPAND_SZ:
fname = win32api.ExpandEnvironmentStrings(fname)
except win32api.error:
fname = ""
collected.append((lcid, platform, fname))
lcidnum = lcidnum + 1
win32api.RegCloseKey(lcidkey)
except ValueError:
pass
num = num + 1
finally:
win32ui.DoWaitCursor(0)
win32api.RegCloseKey(key)
# Now, loop over my collected objects, adding a TypeLib and a HelpFile
ret = []
# if helpPath: ret.append(browser.MakeHLI(helpPath, "Help Path"))
ret.append(HLICLSID(clsidstr))
for lcid, platform, fname in collected:
extraDescs = []
if platform!="win32":
extraDescs.append(platform)
if lcid:
extraDescs.append("locale=%s"%lcid)
extraDesc = ""
if extraDescs: extraDesc = " (%s)" % ", ".join(extraDescs)
ret.append(HLITypeLib(fname, "Type Library" + extraDesc))
ret.sort()
return ret
class HLITypeLibEntry(HLICOM):
def GetText(self):
tlb, index = self.myobject
name, doc, ctx, helpFile = tlb.GetDocumentation(index)
try:
typedesc = HLITypeKinds[tlb.GetTypeInfoType(index)][1]
except KeyError:
typedesc = "Unknown!"
return name + " - " + typedesc
def GetSubList(self):
tlb, index = self.myobject
name, doc, ctx, helpFile = tlb.GetDocumentation(index)
ret = []
if doc: ret.append(browser.HLIDocString(doc, "Doc"))
if helpFile: ret.append(HLIHelpFile( (helpFile, ctx) ))
return ret
class HLICoClass(HLITypeLibEntry):
def GetSubList(self):
ret = HLITypeLibEntry.GetSubList(self)
tlb, index = self.myobject
typeinfo = tlb.GetTypeInfo(index)
attr = typeinfo.GetTypeAttr()
for j in range(attr[8]):
flags = typeinfo.GetImplTypeFlags(j)
refType = typeinfo.GetRefTypeInfo(typeinfo.GetRefTypeOfImplType(j))
refAttr = refType.GetTypeAttr()
ret.append(browser.MakeHLI(refAttr[0], "Name=%s, Flags = %d" % (refAttr[0], flags)))
return ret
class HLITypeLibMethod(HLITypeLibEntry):
def __init__(self, ob, name = None):
self.entry_type = "Method"
HLITypeLibEntry.__init__(self, ob, name)
def GetSubList(self):
ret = HLITypeLibEntry.GetSubList(self)
tlb, index = self.myobject
typeinfo = tlb.GetTypeInfo(index)
attr = typeinfo.GetTypeAttr()
for i in range(attr[7]):
ret.append(HLITypeLibProperty((typeinfo, i)))
for i in range(attr[6]):
ret.append(HLITypeLibFunction((typeinfo, i)))
return ret
class HLITypeLibEnum(HLITypeLibEntry):
def __init__(self, myitem):
typelib, index = myitem
typeinfo = typelib.GetTypeInfo(index)
self.id = typeinfo.GetVarDesc(index)[0]
name = typeinfo.GetNames(self.id)[0]
HLITypeLibEntry.__init__(self, myitem, name)
def GetText(self):
return self.name + " - Enum/Module"
def GetSubList(self):
ret = []
typelib, index = self.myobject
typeinfo = typelib.GetTypeInfo(index)
attr = typeinfo.GetTypeAttr()
for j in range(attr[7]):
vdesc = typeinfo.GetVarDesc(j)
name = typeinfo.GetNames(vdesc[0])[0]
ret.append(browser.MakeHLI(vdesc[1], name))
return ret
class HLITypeLibProperty(HLICOM):
def __init__(self, myitem):
typeinfo, index = myitem
self.id = typeinfo.GetVarDesc(index)[0]
name = typeinfo.GetNames(self.id)[0]
HLICOM.__init__(self, myitem, name)
def GetText(self):
return self.name + " - Property"
def GetSubList(self):
ret = []
typeinfo, index = self.myobject
names = typeinfo.GetNames(self.id)
if len(names)>1:
ret.append(browser.MakeHLI(names[1:], "Named Params"))
vd = typeinfo.GetVarDesc(index)
ret.append(browser.MakeHLI(self.id, "Dispatch ID"))
ret.append(browser.MakeHLI(vd[1], "Value"))
ret.append(browser.MakeHLI(vd[2], "Elem Desc"))
ret.append(browser.MakeHLI(vd[3], "Var Flags"))
ret.append(browser.MakeHLI(vd[4], "Var Kind"))
return ret
class HLITypeLibFunction(HLICOM):
funckinds = {pythoncom.FUNC_VIRTUAL : "Virtual",
pythoncom.FUNC_PUREVIRTUAL : "Pure Virtual",
pythoncom.FUNC_STATIC : "Static",
pythoncom.FUNC_DISPATCH : "Dispatch",
}
invokekinds = {pythoncom.INVOKE_FUNC: "Function",
pythoncom.INVOKE_PROPERTYGET : "Property Get",
pythoncom.INVOKE_PROPERTYPUT : "Property Put",
pythoncom.INVOKE_PROPERTYPUTREF : "Property Put by reference",
}
funcflags = [(pythoncom.FUNCFLAG_FRESTRICTED, "Restricted"),
(pythoncom.FUNCFLAG_FSOURCE, "Source"),
(pythoncom.FUNCFLAG_FBINDABLE, "Bindable"),
(pythoncom.FUNCFLAG_FREQUESTEDIT, "Request Edit"),
(pythoncom.FUNCFLAG_FDISPLAYBIND, "Display Bind"),
(pythoncom.FUNCFLAG_FDEFAULTBIND, "Default Bind"),
(pythoncom.FUNCFLAG_FHIDDEN, "Hidden"),
(pythoncom.FUNCFLAG_FUSESGETLASTERROR, "Uses GetLastError"),
]
vartypes = {pythoncom.VT_EMPTY: "Empty",
pythoncom.VT_NULL: "NULL",
pythoncom.VT_I2: "Integer 2",
pythoncom.VT_I4: "Integer 4",
pythoncom.VT_R4: "Real 4",
pythoncom.VT_R8: "Real 8",
pythoncom.VT_CY: "CY",
pythoncom.VT_DATE: "Date",
pythoncom.VT_BSTR: "String",
pythoncom.VT_DISPATCH: "IDispatch",
pythoncom.VT_ERROR: "Error",
pythoncom.VT_BOOL: "BOOL",
pythoncom.VT_VARIANT: "Variant",
pythoncom.VT_UNKNOWN: "IUnknown",
pythoncom.VT_DECIMAL: "Decimal",
pythoncom.VT_I1: "Integer 1",
pythoncom.VT_UI1: "Unsigned integer 1",
pythoncom.VT_UI2: "Unsigned integer 2",
pythoncom.VT_UI4: "Unsigned integer 4",
pythoncom.VT_I8: "Integer 8",
pythoncom.VT_UI8: "Unsigned integer 8",
pythoncom.VT_INT: "Integer",
pythoncom.VT_UINT: "Unsigned integer",
pythoncom.VT_VOID: "Void",
pythoncom.VT_HRESULT: "HRESULT",
pythoncom.VT_PTR: "Pointer",
pythoncom.VT_SAFEARRAY: "SafeArray",
pythoncom.VT_CARRAY: "C Array",
pythoncom.VT_USERDEFINED: "User Defined",
pythoncom.VT_LPSTR: "Pointer to string",
pythoncom.VT_LPWSTR: "Pointer to Wide String",
pythoncom.VT_FILETIME: "File time",
pythoncom.VT_BLOB: "Blob",
pythoncom.VT_STREAM: "IStream",
pythoncom.VT_STORAGE: "IStorage",
pythoncom.VT_STORED_OBJECT: "Stored object",
pythoncom.VT_STREAMED_OBJECT: "Streamed object",
pythoncom.VT_BLOB_OBJECT: "Blob object",
pythoncom.VT_CF: "CF",
pythoncom.VT_CLSID: "CLSID",
}
type_flags = [ (pythoncom.VT_VECTOR, "Vector"),
(pythoncom.VT_ARRAY, "Array"),
(pythoncom.VT_BYREF, "ByRef"),
(pythoncom.VT_RESERVED, "Reserved"),
]
def __init__(self, myitem):
typeinfo, index = myitem
self.id = typeinfo.GetFuncDesc(index)[0]
name = typeinfo.GetNames(self.id)[0]
HLICOM.__init__(self, myitem, name)
def GetText(self):
return self.name + " - Function"
def MakeReturnTypeName(self, typ):
justtyp = typ & pythoncom.VT_TYPEMASK
try:
typname = self.vartypes[justtyp]
except KeyError:
typname = "?Bad type?"
for (flag, desc) in self.type_flags:
if flag & typ:
typname = "%s(%s)" % (desc, typname)
return typname
def MakeReturnType(self, returnTypeDesc):
if type(returnTypeDesc)==type(()):
first = returnTypeDesc[0]
result = self.MakeReturnType(first)
if first != pythoncom.VT_USERDEFINED:
result = result + " " + self.MakeReturnType(returnTypeDesc[1])
return result
else:
return self.MakeReturnTypeName(returnTypeDesc)
def GetSubList(self):
ret = []
typeinfo, index = self.myobject
names = typeinfo.GetNames(self.id)
ret.append(browser.MakeHLI(self.id, "Dispatch ID"))
if len(names)>1:
ret.append(browser.MakeHLI(", ".join(names[1:]), "Named Params"))
fd = typeinfo.GetFuncDesc(index)
if fd[1]:
ret.append(browser.MakeHLI(fd[1], "Possible result values"))
if fd[8]:
typ, flags, default = fd[8]
val = self.MakeReturnType(typ)
if flags:
val = "%s (Flags=%d, default=%s)" % (val, flags, default)
ret.append(browser.MakeHLI(val, "Return Type"))
for argDesc in fd[2]:
typ, flags, default = argDesc
val = self.MakeReturnType(typ)
if flags:
val = "%s (Flags=%d)" % (val, flags)
if default is not None:
val = "%s (Default=%s)" % (val, default)
ret.append(browser.MakeHLI(val, "Argument"))
try:
fkind = self.funckinds[fd[3]]
except KeyError:
fkind = "Unknown"
ret.append(browser.MakeHLI(fkind, "Function Kind"))
try:
ikind = self.invokekinds[fd[4]]
except KeyError:
ikind = "Unknown"
ret.append(browser.MakeHLI(ikind, "Invoke Kind"))
# 5 = call conv
# 5 = offset vtbl
ret.append(browser.MakeHLI(fd[6], "Number Optional Params"))
flagDescs = []
for flag, desc in self.funcflags:
if flag & fd[9]:
flagDescs.append(desc)
if flagDescs:
ret.append(browser.MakeHLI(", ".join(flagDescs), "Function Flags"))
return ret
HLITypeKinds = {
pythoncom.TKIND_ENUM : (HLITypeLibEnum, 'Enumeration'),
pythoncom.TKIND_RECORD : (HLITypeLibEntry, 'Record'),
pythoncom.TKIND_MODULE : (HLITypeLibEnum, 'Module'),
pythoncom.TKIND_INTERFACE : (HLITypeLibMethod, 'Interface'),
pythoncom.TKIND_DISPATCH : (HLITypeLibMethod, 'Dispatch'),
pythoncom.TKIND_COCLASS : (HLICoClass, 'CoClass'),
pythoncom.TKIND_ALIAS : (HLITypeLibEntry, 'Alias'),
pythoncom.TKIND_UNION : (HLITypeLibEntry, 'Union')
}
class HLITypeLib(HLICOM):
def GetSubList(self):
ret = []
ret.append(browser.MakeHLI(self.myobject, "Filename"))
try:
tlb = pythoncom.LoadTypeLib(self.myobject)
except pythoncom.com_error:
return [browser.MakeHLI("%s can not be loaded" % self.myobject)]
for i in range(tlb.GetTypeInfoCount()):
try:
ret.append(HLITypeKinds[tlb.GetTypeInfoType(i)][0]( (tlb, i) ) )
except pythoncom.com_error:
ret.append(browser.MakeHLI("The type info can not be loaded!"))
ret.sort()
return ret
class HLIHeadingRegisterdTypeLibs(HLICOM):
"A tree heading for registered type libraries"
def GetText(self):
return "Registered Type Libraries"
def GetSubList(self):
# Explicit lookup in the registry.
ret = []
key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "TypeLib")
win32ui.DoWaitCursor(1)
try:
num = 0
while 1:
try:
keyName = win32api.RegEnumKey(key, num)
except win32api.error:
break
# Enumerate all version info
subKey = win32api.RegOpenKey(key, keyName)
name = None
try:
subNum = 0
bestVersion = 0.0
while 1:
try:
versionStr = win32api.RegEnumKey(subKey, subNum)
except win32api.error:
break
try:
versionFlt = float(versionStr)
except ValueError:
versionFlt = 0 # ????
if versionFlt > bestVersion:
bestVersion = versionFlt
name = win32api.RegQueryValue(subKey, versionStr)
subNum = subNum + 1
finally:
win32api.RegCloseKey(subKey)
if name is not None:
ret.append(HLIRegisteredTypeLibrary((keyName, versionStr), name))
num = num + 1
finally:
win32api.RegCloseKey(key)
win32ui.DoWaitCursor(0)
ret.sort()
return ret
def main():
from pywin.tools import hierlist
root = HLIRoot("COM Browser")
if "app" in sys.modules:
# do it in a window
browser.MakeTemplate()
browser.template.OpenObject(root)
else:
# list=hierlist.HierListWithItems( root, win32ui.IDB_BROWSER_HIER )
# dlg=hierlist.HierDialog("COM Browser",list)
dlg = browser.dynamic_browser(root)
dlg.DoModal()
if __name__=='__main__':
main()
ni = pythoncom._GetInterfaceCount()
ng = pythoncom._GetGatewayCount()
if ni or ng:
print("Warning - exiting with %d/%d objects alive" % (ni,ng))

View file

@ -0,0 +1,43 @@
"""Utilities for working with Connections"""
import win32com.server.util, pythoncom
class SimpleConnection:
"A simple, single connection object"
def __init__(self, coInstance = None, eventInstance = None, eventCLSID = None, debug = 0):
self.cp = None
self.cookie = None
self.debug = debug
if not coInstance is None:
self.Connect(coInstance , eventInstance, eventCLSID)
def __del__(self):
try:
self.Disconnect()
except pythoncom.error:
# Ignore disconnection as we are torn down.
pass
def _wrap(self, obj):
useDispatcher = None
if self.debug:
from win32com.server import dispatcher
useDispatcher = dispatcher.DefaultDebugDispatcher
return win32com.server.util.wrap(obj, useDispatcher=useDispatcher)
def Connect(self, coInstance, eventInstance, eventCLSID = None):
try:
oleobj = coInstance._oleobj_
except AttributeError:
oleobj = coInstance
cpc=oleobj.QueryInterface(pythoncom.IID_IConnectionPointContainer)
if eventCLSID is None: eventCLSID = eventInstance.CLSID
comEventInstance = self._wrap(eventInstance)
self.cp=cpc.FindConnectionPoint(eventCLSID)
self.cookie = self.cp.Advise(comEventInstance)
def Disconnect(self):
if not self.cp is None:
if self.cookie:
self.cp.Unadvise(self.cookie)
self.cookie = None
self.cp = None

View file

@ -0,0 +1,581 @@
"""Support for dynamic COM client support.
Introduction
Dynamic COM client support is the ability to use a COM server without
prior knowledge of the server. This can be used to talk to almost all
COM servers, including much of MS Office.
In general, you should not use this module directly - see below.
Example
>>> import win32com.client
>>> xl = win32com.client.Dispatch("Excel.Application")
# The line above invokes the functionality of this class.
# xl is now an object we can use to talk to Excel.
>>> xl.Visible = 1 # The Excel window becomes visible.
"""
import sys
import traceback
import types
import pythoncom
import winerror
from . import build
from pywintypes import IIDType
import win32com.client # Needed as code we eval() references it.
debugging=0 # General debugging
debugging_attr=0 # Debugging dynamic attribute lookups.
LCID = 0x0
# These errors generally mean the property or method exists,
# but can't be used in this context - eg, property instead of a method, etc.
# Used to determine if we have a real error or not.
ERRORS_BAD_CONTEXT = [
winerror.DISP_E_MEMBERNOTFOUND,
winerror.DISP_E_BADPARAMCOUNT,
winerror.DISP_E_PARAMNOTOPTIONAL,
winerror.DISP_E_TYPEMISMATCH,
winerror.E_INVALIDARG,
]
ALL_INVOKE_TYPES = [
pythoncom.INVOKE_PROPERTYGET,
pythoncom.INVOKE_PROPERTYPUT,
pythoncom.INVOKE_PROPERTYPUTREF,
pythoncom.INVOKE_FUNC
]
def debug_print(*args):
if debugging:
for arg in args:
print(arg, end=' ')
print()
def debug_attr_print(*args):
if debugging_attr:
for arg in args:
print(arg, end=' ')
print()
# A helper to create method objects on the fly
py3k = sys.version_info > (3,0)
if py3k:
def MakeMethod(func, inst, cls):
return types.MethodType(func, inst) # class not needed in py3k
else:
MakeMethod = types.MethodType # all args used in py2k.
# get the type objects for IDispatch and IUnknown
PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
PyIUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
if py3k:
_GoodDispatchTypes=(str, IIDType)
else:
_GoodDispatchTypes=(str, IIDType, str)
_defaultDispatchItem=build.DispatchItem
def _GetGoodDispatch(IDispatch, clsctx = pythoncom.CLSCTX_SERVER):
# quick return for most common case
if isinstance(IDispatch, PyIDispatchType):
return IDispatch
if isinstance(IDispatch, _GoodDispatchTypes):
try:
IDispatch = pythoncom.connect(IDispatch)
except pythoncom.ole_error:
IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
else:
# may already be a wrapped class.
IDispatch = getattr(IDispatch, "_oleobj_", IDispatch)
return IDispatch
def _GetGoodDispatchAndUserName(IDispatch, userName, clsctx):
# Get a dispatch object, and a 'user name' (ie, the name as
# displayed to the user in repr() etc.
if userName is None:
# Displayed name should be a plain string in py2k, and unicode in py3k
if isinstance(IDispatch, str):
userName = IDispatch
elif not py3k and isinstance(IDispatch, str):
# 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
userName = IDispatch.encode("ascii", "replace")
## ??? else userName remains None ???
elif not py3k and isinstance(userName, str):
# 2to3 converts the above 'unicode' to 'str', but this will never be executed in py3k
# As above - always a plain string in py2k
userName = userName.encode("ascii", "replace")
else:
userName = str(userName)
return (_GetGoodDispatch(IDispatch, clsctx), userName)
def _GetDescInvokeType(entry, invoke_type):
# determine the wFlags argument passed as input to IDispatch::Invoke
if not entry or not entry.desc: return invoke_type
varkind = entry.desc[4] # from VARDESC struct returned by ITypeComp::Bind
if varkind == pythoncom.VAR_DISPATCH and invoke_type == pythoncom.INVOKE_PROPERTYGET:
return pythoncom.INVOKE_FUNC | invoke_type # DISPATCH_METHOD & DISPATCH_PROPERTYGET can be combined in IDispatch::Invoke
else:
return varkind
def Dispatch(IDispatch, userName = None, createClass = None, typeinfo = None, UnicodeToString=None, clsctx = pythoncom.CLSCTX_SERVER):
assert UnicodeToString is None, "this is deprecated and will go away"
IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
if createClass is None:
createClass = CDispatch
lazydata = None
try:
if typeinfo is None:
typeinfo = IDispatch.GetTypeInfo()
if typeinfo is not None:
try:
#try for a typecomp
typecomp = typeinfo.GetTypeComp()
lazydata = typeinfo, typecomp
except pythoncom.com_error:
pass
except pythoncom.com_error:
typeinfo = None
olerepr = MakeOleRepr(IDispatch, typeinfo, lazydata)
return createClass(IDispatch, olerepr, userName, lazydata=lazydata)
def MakeOleRepr(IDispatch, typeinfo, typecomp):
olerepr = None
if typeinfo is not None:
try:
attr = typeinfo.GetTypeAttr()
# If the type info is a special DUAL interface, magically turn it into
# a DISPATCH typeinfo.
if attr[5] == pythoncom.TKIND_INTERFACE and attr[11] & pythoncom.TYPEFLAG_FDUAL:
# Get corresponding Disp interface;
# -1 is a special value which does this for us.
href = typeinfo.GetRefTypeOfImplType(-1);
typeinfo = typeinfo.GetRefTypeInfo(href)
attr = typeinfo.GetTypeAttr()
if typecomp is None:
olerepr = build.DispatchItem(typeinfo, attr, None, 0)
else:
olerepr = build.LazyDispatchItem(attr, None)
except pythoncom.ole_error:
pass
if olerepr is None: olerepr = build.DispatchItem()
return olerepr
def DumbDispatch(IDispatch, userName = None, createClass = None,UnicodeToString=None, clsctx=pythoncom.CLSCTX_SERVER):
"Dispatch with no type info"
assert UnicodeToString is None, "this is deprecated and will go away"
IDispatch, userName = _GetGoodDispatchAndUserName(IDispatch,userName,clsctx)
if createClass is None:
createClass = CDispatch
return createClass(IDispatch, build.DispatchItem(), userName)
class CDispatch:
def __init__(self, IDispatch, olerepr, userName=None, UnicodeToString=None, lazydata=None):
assert UnicodeToString is None, "this is deprecated and will go away"
if userName is None: userName = "<unknown>"
self.__dict__['_oleobj_'] = IDispatch
self.__dict__['_username_'] = userName
self.__dict__['_olerepr_'] = olerepr
self.__dict__['_mapCachedItems_'] = {}
self.__dict__['_builtMethods_'] = {}
self.__dict__['_enum_'] = None
self.__dict__['_unicode_to_string_'] = None
self.__dict__['_lazydata_'] = lazydata
def __call__(self, *args):
"Provide 'default dispatch' COM functionality - allow instance to be called"
if self._olerepr_.defaultDispatchName:
invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
else:
invkind, dispid = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, pythoncom.DISPID_VALUE
if invkind is not None:
allArgs = (dispid,LCID,invkind,1) + args
return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
raise TypeError("This dispatch object does not define a default method")
def __bool__(self):
return True # ie "if object:" should always be "true" - without this, __len__ is tried.
# _Possibly_ want to defer to __len__ if available, but Im not sure this is
# desirable???
def __repr__(self):
return "<COMObject %s>" % (self._username_)
def __str__(self):
# __str__ is used when the user does "print object", so we gracefully
# fall back to the __repr__ if the object has no default method.
try:
return str(self.__call__())
except pythoncom.com_error as details:
if details.hresult not in ERRORS_BAD_CONTEXT:
raise
return self.__repr__()
# Delegate comparison to the oleobjs, as they know how to do identity.
def __eq__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ == other
def __ne__(self, other):
other = getattr(other, "_oleobj_", other)
return self._oleobj_ != other
def __int__(self):
return int(self.__call__())
def __len__(self):
invkind, dispid = self._find_dispatch_type_("Count")
if invkind:
return self._oleobj_.Invoke(dispid, LCID, invkind, 1)
raise TypeError("This dispatch object does not define a Count method")
def _NewEnum(self):
try:
invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
except pythoncom.com_error:
return None # no enumerator for this object.
from . import util
return util.WrapEnum(enum, None)
def __getitem__(self, index): # syver modified
# Improved __getitem__ courtesy Syver Enstad
# Must check _NewEnum before Item, to ensure b/w compat.
if isinstance(index, int):
if self.__dict__['_enum_'] is None:
self.__dict__['_enum_'] = self._NewEnum()
if self.__dict__['_enum_'] is not None:
return self._get_good_object_(self._enum_.__getitem__(index))
# See if we have an "Item" method/property we can use (goes hand in hand with Count() above!)
invkind, dispid = self._find_dispatch_type_("Item")
if invkind is not None:
return self._get_good_object_(self._oleobj_.Invoke(dispid, LCID, invkind, 1, index))
raise TypeError("This object does not support enumeration")
def __setitem__(self, index, *args):
# XXX - todo - We should support calling Item() here too!
# print "__setitem__ with", index, args
if self._olerepr_.defaultDispatchName:
invkind, dispid = self._find_dispatch_type_(self._olerepr_.defaultDispatchName)
else:
invkind, dispid = pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF, pythoncom.DISPID_VALUE
if invkind is not None:
allArgs = (dispid,LCID,invkind,0,index) + args
return self._get_good_object_(self._oleobj_.Invoke(*allArgs),self._olerepr_.defaultDispatchName,None)
raise TypeError("This dispatch object does not define a default method")
def _find_dispatch_type_(self, methodName):
if methodName in self._olerepr_.mapFuncs:
item = self._olerepr_.mapFuncs[methodName]
return item.desc[4], item.dispid
if methodName in self._olerepr_.propMapGet:
item = self._olerepr_.propMapGet[methodName]
return item.desc[4], item.dispid
try:
dispid = self._oleobj_.GetIDsOfNames(0,methodName)
except: ### what error?
return None, None
return pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET, dispid
def _ApplyTypes_(self, dispid, wFlags, retType, argTypes, user, resultCLSID, *args):
result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
return self._get_good_object_(result, user, resultCLSID)
def _wrap_dispatch_(self, ob, userName = None, returnCLSID = None, UnicodeToString=None):
# Given a dispatch object, wrap it in a class
assert UnicodeToString is None, "this is deprecated and will go away"
return Dispatch(ob, userName)
def _get_good_single_object_(self,ob,userName = None, ReturnCLSID=None):
if isinstance(ob, PyIDispatchType):
# make a new instance of (probably this) class.
return self._wrap_dispatch_(ob, userName, ReturnCLSID)
if isinstance(ob, PyIUnknownType):
try:
ob = ob.QueryInterface(pythoncom.IID_IDispatch)
except pythoncom.com_error:
# It is an IUnknown, but not an IDispatch, so just let it through.
return ob
return self._wrap_dispatch_(ob, userName, ReturnCLSID)
return ob
def _get_good_object_(self,ob,userName = None, ReturnCLSID=None):
"""Given an object (usually the retval from a method), make it a good object to return.
Basically checks if it is a COM object, and wraps it up.
Also handles the fact that a retval may be a tuple of retvals"""
if ob is None: # Quick exit!
return None
elif isinstance(ob, tuple):
return tuple(map(lambda o, s=self, oun=userName, rc=ReturnCLSID: s._get_good_single_object_(o, oun, rc), ob))
else:
return self._get_good_single_object_(ob)
def _make_method_(self, name):
"Make a method object - Assumes in olerepr funcmap"
methodName = build.MakePublicAttributeName(name) # translate keywords etc.
methodCodeList = self._olerepr_.MakeFuncMethod(self._olerepr_.mapFuncs[name], methodName,0)
methodCode = "\n".join(methodCodeList)
try:
# print "Method code for %s is:\n" % self._username_, methodCode
# self._print_details_()
codeObject = compile(methodCode, "<COMObject %s>" % self._username_,"exec")
# Exec the code object
tempNameSpace = {}
# "Dispatch" in the exec'd code is win32com.client.Dispatch, not ours.
globNameSpace = globals().copy()
globNameSpace["Dispatch"] = win32com.client.Dispatch
exec(codeObject, globNameSpace, tempNameSpace) # self.__dict__, self.__dict__
name = methodName
# Save the function in map.
fn = self._builtMethods_[name] = tempNameSpace[name]
newMeth = MakeMethod(fn, self, self.__class__)
return newMeth
except:
debug_print("Error building OLE definition for code ", methodCode)
traceback.print_exc()
return None
def _Release_(self):
"""Cleanup object - like a close - to force cleanup when you dont
want to rely on Python's reference counting."""
for childCont in self._mapCachedItems_.values():
childCont._Release_()
self._mapCachedItems_ = {}
if self._oleobj_:
self._oleobj_.Release()
self.__dict__['_oleobj_'] = None
if self._olerepr_:
self.__dict__['_olerepr_'] = None
self._enum_ = None
def _proc_(self, name, *args):
"""Call the named method as a procedure, rather than function.
Mainly used by Word.Basic, which whinges about such things."""
try:
item = self._olerepr_.mapFuncs[name]
dispId = item.dispid
return self._get_good_object_(self._oleobj_.Invoke(*(dispId, LCID, item.desc[4], 0) + (args) ))
except KeyError:
raise AttributeError(name)
def _print_details_(self):
"Debug routine - dumps what it knows about an object."
print("AxDispatch container",self._username_)
try:
print("Methods:")
for method in self._olerepr_.mapFuncs.keys():
print("\t", method)
print("Props:")
for prop, entry in self._olerepr_.propMap.items():
print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
print("Get Props:")
for prop, entry in self._olerepr_.propMapGet.items():
print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
print("Put Props:")
for prop, entry in self._olerepr_.propMapPut.items():
print("\t%s = 0x%x - %s" % (prop, entry.dispid, repr(entry)))
except:
traceback.print_exc()
def __LazyMap__(self, attr):
try:
if self._LazyAddAttr_(attr):
debug_attr_print("%s.__LazyMap__(%s) added something" % (self._username_,attr))
return 1
except AttributeError:
return 0
# Using the typecomp, lazily create a new attribute definition.
def _LazyAddAttr_(self,attr):
if self._lazydata_ is None: return 0
res = 0
typeinfo, typecomp = self._lazydata_
olerepr = self._olerepr_
# We need to explicitly check each invoke type individually - simply
# specifying '0' will bind to "any member", which may not be the one
# we are actually after (ie, we may be after prop_get, but returned
# the info for the prop_put.)
for i in ALL_INVOKE_TYPES:
try:
x,t = typecomp.Bind(attr,i)
# Support 'Get' and 'Set' properties - see
# bug 1587023
if x==0 and attr[:3] in ('Set', 'Get'):
x,t = typecomp.Bind(attr[3:], i)
if x==1: #it's a FUNCDESC
r = olerepr._AddFunc_(typeinfo,t,0)
elif x==2: #it's a VARDESC
r = olerepr._AddVar_(typeinfo,t,0)
else: #not found or TYPEDESC/IMPLICITAPP
r=None
if not r is None:
key, map = r[0],r[1]
item = map[key]
if map==olerepr.propMapPut:
olerepr._propMapPutCheck_(key,item)
elif map==olerepr.propMapGet:
olerepr._propMapGetCheck_(key,item)
res = 1
except:
pass
return res
def _FlagAsMethod(self, *methodNames):
"""Flag these attribute names as being methods.
Some objects do not correctly differentiate methods and
properties, leading to problems when calling these methods.
Specifically, trying to say: ob.SomeFunc()
may yield an exception "None object is not callable"
In this case, an attempt to fetch the *property*has worked
and returned None, rather than indicating it is really a method.
Calling: ob._FlagAsMethod("SomeFunc")
should then allow this to work.
"""
for name in methodNames:
details = build.MapEntry(self.__AttrToID__(name), (name,))
self._olerepr_.mapFuncs[name] = details
def __AttrToID__(self,attr):
debug_attr_print("Calling GetIDsOfNames for property %s in Dispatch container %s" % (attr, self._username_))
return self._oleobj_.GetIDsOfNames(0,attr)
def __getattr__(self, attr):
if attr=='__iter__':
# We can't handle this as a normal method, as if the attribute
# exists, then it must return an iterable object.
try:
invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
enum = self._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,LCID,invkind,(13, 10),())
except pythoncom.com_error:
raise AttributeError("This object can not function as an iterator")
# We must return a callable object.
class Factory:
def __init__(self, ob):
self.ob = ob
def __call__(self):
import win32com.client.util
return win32com.client.util.Iterator(self.ob)
return Factory(enum)
if attr.startswith('_') and attr.endswith('_'): # Fast-track.
raise AttributeError(attr)
# If a known method, create new instance and return.
try:
return MakeMethod(self._builtMethods_[attr], self, self.__class__)
except KeyError:
pass
# XXX - Note that we current are case sensitive in the method.
#debug_attr_print("GetAttr called for %s on DispatchContainer %s" % (attr,self._username_))
# First check if it is in the method map. Note that an actual method
# must not yet exist, (otherwise we would not be here). This
# means we create the actual method object - which also means
# this code will never be asked for that method name again.
if attr in self._olerepr_.mapFuncs:
return self._make_method_(attr)
# Delegate to property maps/cached items
retEntry = None
if self._olerepr_ and self._oleobj_:
# first check general property map, then specific "put" map.
retEntry = self._olerepr_.propMap.get(attr)
if retEntry is None:
retEntry = self._olerepr_.propMapGet.get(attr)
# Not found so far - See what COM says.
if retEntry is None:
try:
if self.__LazyMap__(attr):
if attr in self._olerepr_.mapFuncs: return self._make_method_(attr)
retEntry = self._olerepr_.propMap.get(attr)
if retEntry is None:
retEntry = self._olerepr_.propMapGet.get(attr)
if retEntry is None:
retEntry = build.MapEntry(self.__AttrToID__(attr), (attr,))
except pythoncom.ole_error:
pass # No prop by that name - retEntry remains None.
if not retEntry is None: # see if in my cache
try:
ret = self._mapCachedItems_[retEntry.dispid]
debug_attr_print ("Cached items has attribute!", ret)
return ret
except (KeyError, AttributeError):
debug_attr_print("Attribute %s not in cache" % attr)
# If we are still here, and have a retEntry, get the OLE item
if not retEntry is None:
invoke_type = _GetDescInvokeType(retEntry, pythoncom.INVOKE_PROPERTYGET)
debug_attr_print("Getting property Id 0x%x from OLE object" % retEntry.dispid)
try:
ret = self._oleobj_.Invoke(retEntry.dispid,0,invoke_type,1)
except pythoncom.com_error as details:
if details.hresult in ERRORS_BAD_CONTEXT:
# May be a method.
self._olerepr_.mapFuncs[attr] = retEntry
return self._make_method_(attr)
raise
debug_attr_print("OLE returned ", ret)
return self._get_good_object_(ret)
# no where else to look.
raise AttributeError("%s.%s" % (self._username_, attr))
def __setattr__(self, attr, value):
if attr in self.__dict__: # Fast-track - if already in our dict, just make the assignment.
# XXX - should maybe check method map - if someone assigns to a method,
# it could mean something special (not sure what, tho!)
self.__dict__[attr] = value
return
# Allow property assignment.
debug_attr_print("SetAttr called for %s.%s=%s on DispatchContainer" % (self._username_, attr, repr(value)))
if self._olerepr_:
# Check the "general" property map.
if attr in self._olerepr_.propMap:
entry = self._olerepr_.propMap[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
# Check the specific "put" map.
if attr in self._olerepr_.propMapPut:
entry = self._olerepr_.propMapPut[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
# Try the OLE Object
if self._oleobj_:
if self.__LazyMap__(attr):
# Check the "general" property map.
if attr in self._olerepr_.propMap:
entry = self._olerepr_.propMap[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
# Check the specific "put" map.
if attr in self._olerepr_.propMapPut:
entry = self._olerepr_.propMapPut[attr]
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
return
try:
entry = build.MapEntry(self.__AttrToID__(attr),(attr,))
except pythoncom.com_error:
# No attribute of that name
entry = None
if entry is not None:
try:
invoke_type = _GetDescInvokeType(entry, pythoncom.INVOKE_PROPERTYPUT)
self._oleobj_.Invoke(entry.dispid, 0, invoke_type, 0, value)
self._olerepr_.propMap[attr] = entry
debug_attr_print("__setattr__ property %s (id=0x%x) in Dispatch container %s" % (attr, entry.dispid, self._username_))
return
except pythoncom.com_error:
pass
raise AttributeError("Property '%s.%s' can not be set." % (self._username_, attr))

View file

@ -0,0 +1,692 @@
"""Manages the cache of generated Python code.
Description
This file manages the cache of generated Python code. When run from the
command line, it also provides a number of options for managing that cache.
Implementation
Each typelib is generated into a filename of format "{guid}x{lcid}x{major}x{minor}.py"
An external persistant dictionary maps from all known IIDs in all known type libraries
to the type library itself.
Thus, whenever Python code knows the IID of an object, it can find the IID, LCID and version of
the type library which supports it. Given this information, it can find the Python module
with the support.
If necessary, this support can be generated on the fly.
Hacks, to do, etc
Currently just uses a pickled dictionary, but should used some sort of indexed file.
Maybe an OLE2 compound file, or a bsddb file?
"""
import pywintypes, os, sys
import pythoncom
import win32com, win32com.client
import glob
import traceback
from . import CLSIDToClass
import operator
try:
from imp import reload # exported by the imp module in py3k.
except:
pass # a builtin on py2k.
bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also makepy.py
# The global dictionary
clsidToTypelib = {}
# If we have a different version of the typelib generated, this
# maps the "requested version" to the "generated version".
versionRedirectMap = {}
# There is no reason we *must* be readonly in a .zip, but we are now,
# Rather than check for ".zip" or other tricks, PEP302 defines
# a "__loader__" attribute, so we use that.
# (Later, it may become necessary to check if the __loader__ can update files,
# as a .zip loader potentially could - but punt all that until a need arises)
is_readonly = is_zip = hasattr(win32com, "__loader__") and hasattr(win32com.__loader__, "archive")
# A dictionary of ITypeLibrary objects for demand generation explicitly handed to us
# Keyed by usual clsid, lcid, major, minor
demandGeneratedTypeLibraries = {}
import pickle as pickle
def __init__():
# Initialize the module. Called once explicitly at module import below.
try:
_LoadDicts()
except IOError:
Rebuild()
pickleVersion = 1
def _SaveDicts():
if is_readonly:
raise RuntimeError("Trying to write to a readonly gencache ('%s')!" \
% win32com.__gen_path__)
f = open(os.path.join(GetGeneratePath(), "dicts.dat"), "wb")
try:
p = pickle.Pickler(f)
p.dump(pickleVersion)
p.dump(clsidToTypelib)
finally:
f.close()
def _LoadDicts():
# Load the dictionary from a .zip file if that is where we live.
if is_zip:
import io as io
loader = win32com.__loader__
arc_path = loader.archive
dicts_path = os.path.join(win32com.__gen_path__, "dicts.dat")
if dicts_path.startswith(arc_path):
dicts_path = dicts_path[len(arc_path)+1:]
else:
# Hm. See below.
return
try:
data = loader.get_data(dicts_path)
except AttributeError:
# The __loader__ has no get_data method. See below.
return
except IOError:
# Our gencache is in a .zip file (and almost certainly readonly)
# but no dicts file. That actually needn't be fatal for a frozen
# application. Assuming they call "EnsureModule" with the same
# typelib IDs they have been frozen with, that EnsureModule will
# correctly re-build the dicts on the fly. However, objects that
# rely on the gencache but have not done an EnsureModule will
# fail (but their apps are likely to fail running from source
# with a clean gencache anyway, as then they would be getting
# Dynamic objects until the cache is built - so the best answer
# for these apps is to call EnsureModule, rather than freezing
# the dict)
return
f = io.BytesIO(data)
else:
# NOTE: IOError on file open must be caught by caller.
f = open(os.path.join(win32com.__gen_path__, "dicts.dat"), "rb")
try:
p = pickle.Unpickler(f)
version = p.load()
global clsidToTypelib
clsidToTypelib = p.load()
versionRedirectMap.clear()
finally:
f.close()
def GetGeneratedFileName(clsid, lcid, major, minor):
"""Given the clsid, lcid, major and minor for a type lib, return
the file name (no extension) providing this support.
"""
return str(clsid).upper()[1:-1] + "x%sx%sx%s" % (lcid, major, minor)
def SplitGeneratedFileName(fname):
"""Reverse of GetGeneratedFileName()
"""
return tuple(fname.split('x',4))
def GetGeneratePath():
"""Returns the name of the path to generate to.
Checks the directory is OK.
"""
assert not is_readonly, "Why do you want the genpath for a readonly store?"
try:
os.makedirs(win32com.__gen_path__)
#os.mkdir(win32com.__gen_path__)
except os.error:
pass
try:
fname = os.path.join(win32com.__gen_path__, "__init__.py")
os.stat(fname)
except os.error:
f = open(fname,"w")
f.write('# Generated file - this directory may be deleted to reset the COM cache...\n')
f.write('import win32com\n')
f.write('if __path__[:-1] != win32com.__gen_path__: __path__.append(win32com.__gen_path__)\n')
f.close()
return win32com.__gen_path__
#
# The helpers for win32com.client.Dispatch and OCX clients.
#
def GetClassForProgID(progid):
"""Get a Python class for a Program ID
Given a Program ID, return a Python class which wraps the COM object
Returns the Python class, or None if no module is available.
Params
progid -- A COM ProgramID or IID (eg, "Word.Application")
"""
clsid = pywintypes.IID(progid) # This auto-converts named to IDs.
return GetClassForCLSID(clsid)
def GetClassForCLSID(clsid):
"""Get a Python class for a CLSID
Given a CLSID, return a Python class which wraps the COM object
Returns the Python class, or None if no module is available.
Params
clsid -- A COM CLSID (or string repr of one)
"""
# first, take a short-cut - we may already have generated support ready-to-roll.
clsid = str(clsid)
if CLSIDToClass.HasClass(clsid):
return CLSIDToClass.GetClass(clsid)
mod = GetModuleForCLSID(clsid)
if mod is None:
return None
try:
return CLSIDToClass.GetClass(clsid)
except KeyError:
return None
def GetModuleForProgID(progid):
"""Get a Python module for a Program ID
Given a Program ID, return a Python module which contains the
class which wraps the COM object.
Returns the Python module, or None if no module is available.
Params
progid -- A COM ProgramID or IID (eg, "Word.Application")
"""
try:
iid = pywintypes.IID(progid)
except pywintypes.com_error:
return None
return GetModuleForCLSID(iid)
def GetModuleForCLSID(clsid):
"""Get a Python module for a CLSID
Given a CLSID, return a Python module which contains the
class which wraps the COM object.
Returns the Python module, or None if no module is available.
Params
progid -- A COM CLSID (ie, not the description)
"""
clsid_str = str(clsid)
try:
typelibCLSID, lcid, major, minor = clsidToTypelib[clsid_str]
except KeyError:
return None
try:
mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
except ImportError:
mod = None
if mod is not None:
sub_mod = mod.CLSIDToPackageMap.get(clsid_str)
if sub_mod is None:
sub_mod = mod.VTablesToPackageMap.get(clsid_str)
if sub_mod is not None:
sub_mod_name = mod.__name__ + "." + sub_mod
try:
__import__(sub_mod_name)
except ImportError:
info = typelibCLSID, lcid, major, minor
# Force the generation. If this typelibrary has explicitly been added,
# use it (it may not be registered, causing a lookup by clsid to fail)
if info in demandGeneratedTypeLibraries:
info = demandGeneratedTypeLibraries[info]
from . import makepy
makepy.GenerateChildFromTypeLibSpec(sub_mod, info)
# Generate does an import...
mod = sys.modules[sub_mod_name]
return mod
def GetModuleForTypelib(typelibCLSID, lcid, major, minor):
"""Get a Python module for a type library ID
Given the CLSID of a typelibrary, return an imported Python module,
else None
Params
typelibCLSID -- IID of the type library.
major -- Integer major version.
minor -- Integer minor version
lcid -- Integer LCID for the library.
"""
modName = GetGeneratedFileName(typelibCLSID, lcid, major, minor)
mod = _GetModule(modName)
# If the import worked, it doesn't mean we have actually added this
# module to our cache though - check that here.
if "_in_gencache_" not in mod.__dict__:
AddModuleToCache(typelibCLSID, lcid, major, minor)
assert "_in_gencache_" in mod.__dict__
return mod
def MakeModuleForTypelib(typelibCLSID, lcid, major, minor, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Generate support for a type library.
Given the IID, LCID and version information for a type library, generate
and import the necessary support files.
Returns the Python module. No exceptions are caught.
Params
typelibCLSID -- IID of the type library.
major -- Integer major version.
minor -- Integer minor version.
lcid -- Integer LCID for the library.
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
"""
from . import makepy
makepy.GenerateFromTypeLibSpec( (typelibCLSID, lcid, major, minor), progressInstance=progressInstance, bForDemand = bForDemand, bBuildHidden = bBuildHidden)
return GetModuleForTypelib(typelibCLSID, lcid, major, minor)
def MakeModuleForTypelibInterface(typelib_ob, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Generate support for a type library.
Given a PyITypeLib interface generate and import the necessary support files. This is useful
for getting makepy support for a typelibrary that is not registered - the caller can locate
and load the type library itself, rather than relying on COM to find it.
Returns the Python module.
Params
typelib_ob -- The type library itself
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
"""
from . import makepy
try:
makepy.GenerateFromTypeLibSpec( typelib_ob, progressInstance=progressInstance, bForDemand = bForDemandDefault, bBuildHidden = bBuildHidden)
except pywintypes.com_error:
return None
tla = typelib_ob.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
return GetModuleForTypelib(guid, lcid, major, minor)
def EnsureModuleForTypelibInterface(typelib_ob, progressInstance = None, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Check we have support for a type library, generating if not.
Given a PyITypeLib interface generate and import the necessary
support files if necessary. This is useful for getting makepy support
for a typelibrary that is not registered - the caller can locate and
load the type library itself, rather than relying on COM to find it.
Returns the Python module.
Params
typelib_ob -- The type library itself
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
"""
tla = typelib_ob.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
#If demand generated, save the typelib interface away for later use
if bForDemand:
demandGeneratedTypeLibraries[(str(guid), lcid, major, minor)] = typelib_ob
try:
return GetModuleForTypelib(guid, lcid, major, minor)
except ImportError:
pass
# Generate it.
return MakeModuleForTypelibInterface(typelib_ob, progressInstance, bForDemand, bBuildHidden)
def ForgetAboutTypelibInterface(typelib_ob):
"""Drop any references to a typelib previously added with EnsureModuleForTypelibInterface and forDemand"""
tla = typelib_ob.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
info = str(guid), lcid, major, minor
try:
del demandGeneratedTypeLibraries[info]
except KeyError:
# Not worth raising an exception - maybe they dont know we only remember for demand generated, etc.
print("ForgetAboutTypelibInterface:: Warning - type library with info %s is not being remembered!" % (info,))
# and drop any version redirects to it
for key, val in list(versionRedirectMap.items()):
if val==info:
del versionRedirectMap[key]
def EnsureModule(typelibCLSID, lcid, major, minor, progressInstance = None, bValidateFile=not is_readonly, bForDemand = bForDemandDefault, bBuildHidden = 1):
"""Ensure Python support is loaded for a type library, generating if necessary.
Given the IID, LCID and version information for a type library, check and if
necessary (re)generate, then import the necessary support files. If we regenerate the file, there
is no way to totally snuff out all instances of the old module in Python, and thus we will regenerate the file more than necessary,
unless makepy/genpy is modified accordingly.
Returns the Python module. No exceptions are caught during the generate process.
Params
typelibCLSID -- IID of the type library.
major -- Integer major version.
minor -- Integer minor version
lcid -- Integer LCID for the library.
progressInstance -- Instance to use as progress indicator, or None to
use the GUI progress bar.
bValidateFile -- Whether or not to perform cache validation or not
bForDemand -- Should a complete generation happen now, or on demand?
bBuildHidden -- Should hidden members/attributes etc be generated?
"""
bReloadNeeded = 0
try:
try:
module = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
except ImportError:
# If we get an ImportError
# We may still find a valid cache file under a different MinorVersion #
# (which windows will search out for us)
#print "Loading reg typelib", typelibCLSID, major, minor, lcid
module = None
try:
tlbAttr = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid).GetLibAttr()
# if the above line doesn't throw a pythoncom.com_error, check if
# it is actually a different lib than we requested, and if so, suck it in
if tlbAttr[1] != lcid or tlbAttr[4]!=minor:
#print "Trying 2nd minor #", tlbAttr[1], tlbAttr[3], tlbAttr[4]
try:
module = GetModuleForTypelib(typelibCLSID, tlbAttr[1], tlbAttr[3], tlbAttr[4])
except ImportError:
# We don't have a module, but we do have a better minor
# version - remember that.
minor = tlbAttr[4]
# else module remains None
except pythoncom.com_error:
# couldn't load any typelib - mod remains None
pass
if module is not None and bValidateFile:
assert not is_readonly, "Can't validate in a read-only gencache"
try:
typLibPath = pythoncom.QueryPathOfRegTypeLib(typelibCLSID, major, minor, lcid)
# windows seems to add an extra \0 (via the underlying BSTR)
# The mainwin toolkit does not add this erroneous \0
if typLibPath[-1]=='\0':
typLibPath=typLibPath[:-1]
suf = getattr(os.path, "supports_unicode_filenames", 0)
if not suf:
# can't pass unicode filenames directly - convert
try:
typLibPath=typLibPath.encode(sys.getfilesystemencoding())
except AttributeError: # no sys.getfilesystemencoding
typLibPath=str(typLibPath)
tlbAttributes = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid).GetLibAttr()
except pythoncom.com_error:
# We have a module, but no type lib - we should still
# run with what we have though - the typelib may not be
# deployed here.
bValidateFile = 0
if module is not None and bValidateFile:
assert not is_readonly, "Can't validate in a read-only gencache"
filePathPrefix = "%s\\%s" % (GetGeneratePath(), GetGeneratedFileName(typelibCLSID, lcid, major, minor))
filePath = filePathPrefix + ".py"
filePathPyc = filePathPrefix + ".py"
if __debug__:
filePathPyc = filePathPyc + "c"
else:
filePathPyc = filePathPyc + "o"
# Verify that type library is up to date.
# If we have a differing MinorVersion or genpy has bumped versions, update the file
from . import genpy
if module.MinorVersion != tlbAttributes[4] or genpy.makepy_version != module.makepy_version:
#print "Version skew: %d, %d" % (module.MinorVersion, tlbAttributes[4])
# try to erase the bad file from the cache
try:
os.unlink(filePath)
except os.error:
pass
try:
os.unlink(filePathPyc)
except os.error:
pass
if os.path.isdir(filePathPrefix):
import shutil
shutil.rmtree(filePathPrefix)
minor = tlbAttributes[4]
module = None
bReloadNeeded = 1
else:
minor = module.MinorVersion
filePathPrefix = "%s\\%s" % (GetGeneratePath(), GetGeneratedFileName(typelibCLSID, lcid, major, minor))
filePath = filePathPrefix + ".py"
filePathPyc = filePathPrefix + ".pyc"
#print "Trying py stat: ", filePath
fModTimeSet = 0
try:
pyModTime = os.stat(filePath)[8]
fModTimeSet = 1
except os.error as e:
# If .py file fails, try .pyc file
#print "Trying pyc stat", filePathPyc
try:
pyModTime = os.stat(filePathPyc)[8]
fModTimeSet = 1
except os.error as e:
pass
#print "Trying stat typelib", pyModTime
#print str(typLibPath)
typLibModTime = os.stat(typLibPath)[8]
if fModTimeSet and (typLibModTime > pyModTime):
bReloadNeeded = 1
module = None
except (ImportError, os.error):
module = None
if module is None:
# We need to build an item. If we are in a read-only cache, we
# can't/don't want to do this - so before giving up, check for
# a different minor version in our cache - according to COM, this is OK
if is_readonly:
key = str(typelibCLSID), lcid, major, minor
# If we have been asked before, get last result.
try:
return versionRedirectMap[key]
except KeyError:
pass
# Find other candidates.
items = []
for desc in GetGeneratedInfos():
if key[0]==desc[0] and key[1]==desc[1] and key[2]==desc[2]:
items.append(desc)
if items:
# Items are all identical, except for last tuple element
# We want the latest minor version we have - so just sort and grab last
items.sort()
new_minor = items[-1][3]
ret = GetModuleForTypelib(typelibCLSID, lcid, major, new_minor)
else:
ret = None
# remember and return
versionRedirectMap[key] = ret
return ret
#print "Rebuilding: ", major, minor
module = MakeModuleForTypelib(typelibCLSID, lcid, major, minor, progressInstance, bForDemand = bForDemand, bBuildHidden = bBuildHidden)
# If we replaced something, reload it
if bReloadNeeded:
module = reload(module)
AddModuleToCache(typelibCLSID, lcid, major, minor)
return module
def EnsureDispatch(prog_id, bForDemand = 1): # New fn, so we default the new demand feature to on!
"""Given a COM prog_id, return an object that is using makepy support, building if necessary"""
disp = win32com.client.Dispatch(prog_id)
if not disp.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
try:
ti = disp._oleobj_.GetTypeInfo()
disp_clsid = ti.GetTypeAttr()[0]
tlb, index = ti.GetContainingTypeLib()
tla = tlb.GetLibAttr()
mod = EnsureModule(tla[0], tla[1], tla[3], tla[4], bForDemand=bForDemand)
GetModuleForCLSID(disp_clsid)
# Get the class from the module.
from . import CLSIDToClass
disp_class = CLSIDToClass.GetClass(str(disp_clsid))
disp = disp_class(disp._oleobj_)
except pythoncom.com_error:
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
return disp
def AddModuleToCache(typelibclsid, lcid, major, minor, verbose = 1, bFlushNow = not is_readonly):
"""Add a newly generated file to the cache dictionary.
"""
fname = GetGeneratedFileName(typelibclsid, lcid, major, minor)
mod = _GetModule(fname)
# if mod._in_gencache_ is already true, then we are reloading this
# module - this doesn't mean anything special though!
mod._in_gencache_ = 1
dict = mod.CLSIDToClassMap
info = str(typelibclsid), lcid, major, minor
for clsid, cls in dict.items():
clsidToTypelib[clsid] = info
dict = mod.CLSIDToPackageMap
for clsid, name in dict.items():
clsidToTypelib[clsid] = info
dict = mod.VTablesToClassMap
for clsid, cls in dict.items():
clsidToTypelib[clsid] = info
dict = mod.VTablesToPackageMap
for clsid, cls in dict.items():
clsidToTypelib[clsid] = info
# If this lib was previously redirected, drop it
if info in versionRedirectMap:
del versionRedirectMap[info]
if bFlushNow:
_SaveDicts()
def GetGeneratedInfos():
zip_pos = win32com.__gen_path__.find(".zip\\")
if zip_pos >= 0:
import zipfile
zip_file = win32com.__gen_path__[:zip_pos+4]
zip_path = win32com.__gen_path__[zip_pos+5:].replace("\\", "/")
zf = zipfile.ZipFile(zip_file)
infos = {}
for n in zf.namelist():
if not n.startswith(zip_path):
continue
base = n[len(zip_path)+1:].split("/")[0]
try:
iid, lcid, major, minor = base.split("x")
lcid = int(lcid)
major = int(major)
minor = int(minor)
iid = pywintypes.IID("{" + iid + "}")
except ValueError:
continue
except pywintypes.com_error:
# invalid IID
continue
infos[(iid, lcid, major, minor)] = 1
zf.close()
return list(infos.keys())
else:
# on the file system
files = glob.glob(win32com.__gen_path__+ "\\*")
ret = []
for file in files:
if not os.path.isdir(file) and not os.path.splitext(file)[1]==".py":
continue
name = os.path.splitext(os.path.split(file)[1])[0]
try:
iid, lcid, major, minor = name.split("x")
iid = pywintypes.IID("{" + iid + "}")
lcid = int(lcid)
major = int(major)
minor = int(minor)
except ValueError:
continue
except pywintypes.com_error:
# invalid IID
continue
ret.append((iid, lcid, major, minor))
return ret
def _GetModule(fname):
"""Given the name of a module in the gen_py directory, import and return it.
"""
mod_name = "win32com.gen_py.%s" % fname
mod = __import__(mod_name)
return sys.modules[mod_name]
def Rebuild(verbose = 1):
"""Rebuild the cache indexes from the file system.
"""
clsidToTypelib.clear()
infos = GetGeneratedInfos()
if verbose and len(infos): # Dont bother reporting this when directory is empty!
print("Rebuilding cache of generated files for COM support...")
for info in infos:
iid, lcid, major, minor = info
if verbose:
print("Checking", GetGeneratedFileName(*info))
try:
AddModuleToCache(iid, lcid, major, minor, verbose, 0)
except:
print("Could not add module %s - %s: %s" % (info, sys.exc_info()[0],sys.exc_info()[1]))
if verbose and len(infos): # Dont bother reporting this when directory is empty!
print("Done.")
_SaveDicts()
def _Dump():
print("Cache is in directory", win32com.__gen_path__)
# Build a unique dir
d = {}
for clsid, (typelibCLSID, lcid, major, minor) in clsidToTypelib.items():
d[typelibCLSID, lcid, major, minor] = None
for typelibCLSID, lcid, major, minor in d.keys():
mod = GetModuleForTypelib(typelibCLSID, lcid, major, minor)
print("%s - %s" % (mod.__doc__, typelibCLSID))
# Boot up
__init__()
def usage():
usageString = """\
Usage: gencache [-q] [-d] [-r]
-q - Quiet
-d - Dump the cache (typelibrary description and filename).
-r - Rebuild the cache dictionary from the existing .py files
"""
print(usageString)
sys.exit(1)
if __name__=='__main__':
import getopt
try:
opts, args = getopt.getopt(sys.argv[1:], "qrd")
except getopt.error as message:
print(message)
usage()
# we only have options - complain about real args, or none at all!
if len(sys.argv)==1 or args:
print(usage())
verbose = 1
for opt, val in opts:
if opt=='-d': # Dump
_Dump()
if opt=='-r':
Rebuild(verbose)
if opt=='-q':
verbose = 0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,390 @@
# Originally written by Curt Hagenlocher, and various bits
# and pieces by Mark Hammond (and now Greg Stein has had
# a go too :-)
# Note that the main worker code has been moved to genpy.py
# As this is normally run from the command line, it reparses the code each time.
# Now this is nothing more than the command line handler and public interface.
# XXX - TO DO
# XXX - Greg and Mark have some ideas for a revamp - just no
# time - if you want to help, contact us for details.
# Main idea is to drop the classes exported and move to a more
# traditional data driven model.
"""Generate a .py file from an OLE TypeLibrary file.
This module is concerned only with the actual writing of
a .py file. It draws on the @build@ module, which builds
the knowledge of a COM interface.
"""
usageHelp = """ \
Usage:
makepy.py [-i] [-v|q] [-h] [-u] [-o output_file] [-d] [typelib, ...]
-i -- Show information for the specified typelib.
-v -- Verbose output.
-q -- Quiet output.
-h -- Do not generate hidden methods.
-u -- Python 1.5 and earlier: Do NOT convert all Unicode objects to
strings.
Python 1.6 and later: Convert all Unicode objects to strings.
-o -- Create output in a specified output file. If the path leading
to the file does not exist, any missing directories will be
created.
NOTE: -o cannot be used with -d. This will generate an error.
-d -- Generate the base code now and the class code on demand.
Recommended for large type libraries.
typelib -- A TLB, DLL, OCX or anything containing COM type information.
If a typelib is not specified, a window containing a textbox
will open from which you can select a registered type
library.
Examples:
makepy.py -d
Presents a list of registered type libraries from which you can make
a selection.
makepy.py -d "Microsoft Excel 8.0 Object Library"
Generate support for the type library with the specified description
(in this case, the MS Excel object model).
"""
import sys, os, pythoncom
from win32com.client import genpy, selecttlb, gencache
from win32com.client import Dispatch
bForDemandDefault = 0 # Default value of bForDemand - toggle this to change the world - see also gencache.py
error = "makepy.error"
def usage():
sys.stderr.write (usageHelp)
sys.exit(2)
def ShowInfo(spec):
if not spec:
tlbSpec = selecttlb.SelectTlb(excludeFlags=selecttlb.FLAG_HIDDEN)
if tlbSpec is None:
return
try:
tlb = pythoncom.LoadRegTypeLib(tlbSpec.clsid, tlbSpec.major, tlbSpec.minor, tlbSpec.lcid)
except pythoncom.com_error: # May be badly registered.
sys.stderr.write("Warning - could not load registered typelib '%s'\n" % (tlbSpec.clsid))
tlb = None
infos = [(tlb, tlbSpec)]
else:
infos = GetTypeLibsForSpec(spec)
for (tlb, tlbSpec) in infos:
desc = tlbSpec.desc
if desc is None:
if tlb is None:
desc = "<Could not load typelib %s>" % (tlbSpec.dll)
else:
desc = tlb.GetDocumentation(-1)[0]
print(desc)
print(" %s, lcid=%s, major=%s, minor=%s" % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor))
print(" >>> # Use these commands in Python code to auto generate .py support")
print(" >>> from win32com.client import gencache")
print(" >>> gencache.EnsureModule('%s', %s, %s, %s)" % (tlbSpec.clsid, tlbSpec.lcid, tlbSpec.major, tlbSpec.minor))
class SimpleProgress(genpy.GeneratorProgress):
"""A simple progress class prints its output to stderr
"""
def __init__(self, verboseLevel):
self.verboseLevel = verboseLevel
def Close(self):
pass
def Finished(self):
if self.verboseLevel>1:
sys.stderr.write("Generation complete..\n")
def SetDescription(self, desc, maxticks = None):
if self.verboseLevel:
sys.stderr.write(desc + "\n")
def Tick(self, desc = None):
pass
def VerboseProgress(self, desc, verboseLevel = 2):
if self.verboseLevel >= verboseLevel:
sys.stderr.write(desc + "\n")
def LogBeginGenerate(self, filename):
self.VerboseProgress("Generating to %s" % filename, 1)
def LogWarning(self, desc):
self.VerboseProgress("WARNING: " + desc, 1)
class GUIProgress(SimpleProgress):
def __init__(self, verboseLevel):
# Import some modules we need to we can trap failure now.
import win32ui, pywin
SimpleProgress.__init__(self, verboseLevel)
self.dialog = None
def Close(self):
if self.dialog is not None:
self.dialog.Close()
self.dialog = None
def Starting(self, tlb_desc):
SimpleProgress.Starting(self, tlb_desc)
if self.dialog is None:
from pywin.dialogs import status
self.dialog=status.ThreadedStatusProgressDialog(tlb_desc)
else:
self.dialog.SetTitle(tlb_desc)
def SetDescription(self, desc, maxticks = None):
self.dialog.SetText(desc)
if maxticks:
self.dialog.SetMaxTicks(maxticks)
def Tick(self, desc = None):
self.dialog.Tick()
if desc is not None:
self.dialog.SetText(desc)
def GetTypeLibsForSpec(arg):
"""Given an argument on the command line (either a file name, library
description, or ProgID of an object) return a list of actual typelibs
to use. """
typelibs = []
try:
try:
tlb = pythoncom.LoadTypeLib(arg)
spec = selecttlb.TypelibSpec(None, 0,0,0)
spec.FromTypelib(tlb, arg)
typelibs.append((tlb, spec))
except pythoncom.com_error:
# See if it is a description
tlbs = selecttlb.FindTlbsWithDescription(arg)
if len(tlbs)==0:
# Maybe it is the name of a COM object?
try:
ob = Dispatch(arg)
# and if so, it must support typelib info
tlb, index = ob._oleobj_.GetTypeInfo().GetContainingTypeLib()
spec = selecttlb.TypelibSpec(None, 0,0,0)
spec.FromTypelib(tlb)
tlbs.append(spec)
except pythoncom.com_error:
pass
if len(tlbs)==0:
print("Could not locate a type library matching '%s'" % (arg))
for spec in tlbs:
# Version numbers not always reliable if enumerated from registry.
# (as some libs use hex, other's dont. Both examples from MS, of course.)
if spec.dll is None:
tlb = pythoncom.LoadRegTypeLib(spec.clsid, spec.major, spec.minor, spec.lcid)
else:
tlb = pythoncom.LoadTypeLib(spec.dll)
# We have a typelib, but it may not be exactly what we specified
# (due to automatic version matching of COM). So we query what we really have!
attr = tlb.GetLibAttr()
spec.major = attr[3]
spec.minor = attr[4]
spec.lcid = attr[1]
typelibs.append((tlb, spec))
return typelibs
except pythoncom.com_error:
t,v,tb=sys.exc_info()
sys.stderr.write ("Unable to load type library from '%s' - %s\n" % (arg, v))
tb = None # Storing tb in a local is a cycle!
sys.exit(1)
def GenerateFromTypeLibSpec(typelibInfo, file = None, verboseLevel = None, progressInstance = None, bUnicodeToString=None, bForDemand = bForDemandDefault, bBuildHidden = 1):
assert bUnicodeToString is None, "this is deprecated and will go away"
if verboseLevel is None:
verboseLevel = 0 # By default, we use no gui and no verbose level!
if bForDemand and file is not None:
raise RuntimeError("You can only perform a demand-build when the output goes to the gen_py directory")
if isinstance(typelibInfo, tuple):
# Tuple
typelibCLSID, lcid, major, minor = typelibInfo
tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
spec.FromTypelib(tlb, str(typelibCLSID))
typelibs = [(tlb, spec)]
elif isinstance(typelibInfo, selecttlb.TypelibSpec):
if typelibInfo.dll is None:
# Version numbers not always reliable if enumerated from registry.
tlb = pythoncom.LoadRegTypeLib(typelibInfo.clsid, typelibInfo.major, typelibInfo.minor, typelibInfo.lcid)
else:
tlb = pythoncom.LoadTypeLib(typelibInfo.dll)
typelibs = [(tlb, typelibInfo)]
elif hasattr(typelibInfo, "GetLibAttr"):
# A real typelib object!
# Could also use isinstance(typelibInfo, PyITypeLib) instead, but PyITypeLib is not directly exposed by pythoncom.
# pythoncom.TypeIIDs[pythoncom.IID_ITypeLib] seems to work
tla = typelibInfo.GetLibAttr()
guid = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
spec = selecttlb.TypelibSpec(guid, lcid, major, minor)
typelibs = [(typelibInfo, spec)]
else:
typelibs = GetTypeLibsForSpec(typelibInfo)
if progressInstance is None:
progressInstance = SimpleProgress(verboseLevel)
progress = progressInstance
bToGenDir = (file is None)
for typelib, info in typelibs:
gen = genpy.Generator(typelib, info.dll, progress, bBuildHidden=bBuildHidden)
if file is None:
this_name = gencache.GetGeneratedFileName(info.clsid, info.lcid, info.major, info.minor)
full_name = os.path.join(gencache.GetGeneratePath(), this_name)
if bForDemand:
try: os.unlink(full_name + ".py")
except os.error: pass
try: os.unlink(full_name + ".pyc")
except os.error: pass
try: os.unlink(full_name + ".pyo")
except os.error: pass
if not os.path.isdir(full_name):
os.mkdir(full_name)
outputName = os.path.join(full_name, "__init__.py")
else:
outputName = full_name + ".py"
fileUse = gen.open_writer(outputName)
progress.LogBeginGenerate(outputName)
else:
fileUse = file
worked = False
try:
gen.generate(fileUse, bForDemand)
worked = True
finally:
if file is None:
gen.finish_writer(outputName, fileUse, worked)
if bToGenDir:
progress.SetDescription("Importing module")
gencache.AddModuleToCache(info.clsid, info.lcid, info.major, info.minor)
progress.Close()
def GenerateChildFromTypeLibSpec(child, typelibInfo, verboseLevel = None, progressInstance = None, bUnicodeToString=None):
assert bUnicodeToString is None, "this is deprecated and will go away"
if verboseLevel is None:
verboseLevel = 0 # By default, we use no gui, and no verbose level for the children.
if type(typelibInfo)==type(()):
typelibCLSID, lcid, major, minor = typelibInfo
tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
else:
tlb = typelibInfo
tla = typelibInfo.GetLibAttr()
typelibCLSID = tla[0]
lcid = tla[1]
major = tla[3]
minor = tla[4]
spec = selecttlb.TypelibSpec(typelibCLSID, lcid, major, minor)
spec.FromTypelib(tlb, str(typelibCLSID))
typelibs = [(tlb, spec)]
if progressInstance is None:
progressInstance = SimpleProgress(verboseLevel)
progress = progressInstance
for typelib, info in typelibs:
dir_name = gencache.GetGeneratedFileName(info.clsid, info.lcid, info.major, info.minor)
dir_path_name = os.path.join(gencache.GetGeneratePath(), dir_name)
progress.LogBeginGenerate(dir_path_name)
gen = genpy.Generator(typelib, info.dll, progress)
gen.generate_child(child, dir_path_name)
progress.SetDescription("Importing module")
__import__("win32com.gen_py." + dir_name + "." + child)
progress.Close()
def main():
import getopt
hiddenSpec = 1
outputName = None
verboseLevel = 1
doit = 1
bForDemand = bForDemandDefault
try:
opts, args = getopt.getopt(sys.argv[1:], 'vo:huiqd')
for o,v in opts:
if o=='-h':
hiddenSpec = 0
elif o=='-o':
outputName = v
elif o=='-v':
verboseLevel = verboseLevel + 1
elif o=='-q':
verboseLevel = verboseLevel - 1
elif o=='-i':
if len(args)==0:
ShowInfo(None)
else:
for arg in args:
ShowInfo(arg)
doit = 0
elif o=='-d':
bForDemand = not bForDemand
except (getopt.error, error) as msg:
sys.stderr.write (str(msg) + "\n")
usage()
if bForDemand and outputName is not None:
sys.stderr.write("Can not use -d and -o together\n")
usage()
if not doit:
return 0
if len(args)==0:
rc = selecttlb.SelectTlb()
if rc is None:
sys.exit(1)
args = [ rc ]
if outputName is not None:
path = os.path.dirname(outputName)
if path != '' and not os.path.exists(path):
os.makedirs(path)
if sys.version_info > (3,0):
f = open(outputName, "wt", encoding="mbcs")
else:
import codecs # not available in py3k.
f = codecs.open(outputName, "w", "mbcs")
else:
f = None
for arg in args:
GenerateFromTypeLibSpec(arg, f, verboseLevel = verboseLevel, bForDemand = bForDemand, bBuildHidden = hiddenSpec)
if f:
f.close()
if __name__=='__main__':
rc = main()
if rc:
sys.exit(rc)
sys.exit(0)

View file

@ -0,0 +1,160 @@
"""Utilities for selecting and enumerating the Type Libraries installed on the system
"""
import win32api, win32con, pythoncom
class TypelibSpec:
def __init__(self, clsid, lcid, major, minor, flags=0):
self.clsid = str(clsid)
self.lcid = int(lcid)
# We avoid assuming 'major' or 'minor' are integers - when
# read from the registry there is some confusion about if
# they are base 10 or base 16 (they *should* be base 16, but
# how they are written is beyond our control.)
self.major = major
self.minor = minor
self.dll = None
self.desc = None
self.ver_desc = None
self.flags = flags
# For the SelectList
def __getitem__(self, item):
if item==0:
return self.ver_desc
raise IndexError("Cant index me!")
def __lt__(self, other): # rich-cmp/py3k-friendly version
me = (self.ver_desc or "").lower(), (self.desc or "").lower(), self.major, self.minor
them = (other.ver_desc or "").lower(), (other.desc or "").lower(), other.major, other.minor
return me < them
def __eq__(self, other): # rich-cmp/py3k-friendly version
return ((self.ver_desc or "").lower() == (other.ver_desc or "").lower() and
(self.desc or "").lower() == (other.desc or "").lower() and
self.major == other.major and
self.minor == other.minor)
def Resolve(self):
if self.dll is None:
return 0
tlb = pythoncom.LoadTypeLib(self.dll)
self.FromTypelib(tlb, None)
return 1
def FromTypelib(self, typelib, dllName = None):
la = typelib.GetLibAttr()
self.clsid = str(la[0])
self.lcid = la[1]
self.major = la[3]
self.minor = la[4]
if dllName:
self.dll = dllName
def EnumKeys(root):
index = 0
ret = []
while 1:
try:
item = win32api.RegEnumKey(root, index)
except win32api.error:
break
try:
# Note this doesn't handle REG_EXPAND_SZ, but the implementation
# here doesn't need to - that is handled as the data is read.
val = win32api.RegQueryValue(root, item)
except win32api.error:
val = "" # code using this assumes a string.
ret.append((item, val))
index = index + 1
return ret
FLAG_RESTRICTED=1
FLAG_CONTROL=2
FLAG_HIDDEN=4
def EnumTlbs(excludeFlags = 0):
"""Return a list of TypelibSpec objects, one for each registered library.
"""
key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "Typelib")
iids = EnumKeys(key)
results = []
for iid, crap in iids:
try:
key2 = win32api.RegOpenKey(key, str(iid))
except win32api.error:
# A few good reasons for this, including "access denied".
continue
for version, tlbdesc in EnumKeys(key2):
major_minor = version.split('.', 1)
if len(major_minor) < 2:
major_minor.append('0')
# For some reason, this code used to assume the values were hex.
# This seems to not be true - particularly for CDO 1.21
# *sigh* - it appears there are no rules here at all, so when we need
# to know the info, we must load the tlb by filename and request it.
# The Resolve() method on the TypelibSpec does this.
# For this reason, keep the version numbers as strings - that
# way we can't be wrong! Let code that really needs an int to work
# out what to do. FWIW, http://support.microsoft.com/kb/816970 is
# pretty clear that they *should* be hex.
major = major_minor[0]
minor = major_minor[1]
key3 = win32api.RegOpenKey(key2, str(version))
try:
# The "FLAGS" are at this point
flags = int(win32api.RegQueryValue(key3, "FLAGS"))
except (win32api.error, ValueError):
flags = 0
if flags & excludeFlags==0:
for lcid, crap in EnumKeys(key3):
try:
lcid = int(lcid)
except ValueError: # not an LCID entry
continue
# Only care about "{lcid}\win32" key - jump straight there.
try:
key4 = win32api.RegOpenKey(key3, "%s\\win32" % (lcid,))
except win32api.error:
continue
try:
dll, typ = win32api.RegQueryValueEx(key4, None)
if typ==win32con.REG_EXPAND_SZ:
dll = win32api.ExpandEnvironmentStrings(dll)
except win32api.error:
dll = None
spec = TypelibSpec(iid, lcid, major, minor, flags)
spec.dll = dll
spec.desc = tlbdesc
spec.ver_desc = tlbdesc + " (" + version + ")"
results.append(spec)
return results
def FindTlbsWithDescription(desc):
"""Find all installed type libraries with the specified description
"""
ret = []
items = EnumTlbs()
for item in items:
if item.desc==desc:
ret.append(item)
return ret
def SelectTlb(title="Select Library", excludeFlags = 0):
"""Display a list of all the type libraries, and select one. Returns None if cancelled
"""
import pywin.dialogs.list
items = EnumTlbs(excludeFlags)
# fixup versions - we assume hex (see __init__ above)
for i in items:
i.major = int(i.major, 16)
i.minor = int(i.minor, 16)
items.sort()
rc = pywin.dialogs.list.SelectFromLists(title, items, ["Type Library"])
if rc is None:
return None
return items[rc]
# Test code.
if __name__=='__main__':
print(SelectTlb().__dict__)

View file

@ -0,0 +1,243 @@
import win32ui
import win32con
import win32api
import commctrl
import pythoncom
from pywin.mfc import dialog
class TLBrowserException(Exception):
"TypeLib browser internal error"
error = TLBrowserException
FRAMEDLG_STD = win32con.WS_CAPTION | win32con.WS_SYSMENU
SS_STD = win32con.WS_CHILD | win32con.WS_VISIBLE
BS_STD = SS_STD | win32con.WS_TABSTOP
ES_STD = BS_STD | win32con.WS_BORDER
LBS_STD = ES_STD | win32con.LBS_NOTIFY | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL
CBS_STD = ES_STD | win32con.CBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL
typekindmap = {
pythoncom.TKIND_ENUM : 'Enumeration',
pythoncom.TKIND_RECORD : 'Record',
pythoncom.TKIND_MODULE : 'Module',
pythoncom.TKIND_INTERFACE : 'Interface',
pythoncom.TKIND_DISPATCH : 'Dispatch',
pythoncom.TKIND_COCLASS : 'CoClass',
pythoncom.TKIND_ALIAS : 'Alias',
pythoncom.TKIND_UNION : 'Union'
}
TypeBrowseDialog_Parent=dialog.Dialog
class TypeBrowseDialog(TypeBrowseDialog_Parent):
"Browse a type library"
IDC_TYPELIST = 1000
IDC_MEMBERLIST = 1001
IDC_PARAMLIST = 1002
IDC_LISTVIEW = 1003
def __init__(self, typefile = None):
TypeBrowseDialog_Parent.__init__(self, self.GetTemplate())
try:
if typefile:
self.tlb = pythoncom.LoadTypeLib(typefile)
else:
self.tlb = None
except pythoncom.ole_error:
self.MessageBox("The file does not contain type information")
self.tlb = None
self.HookCommand(self.CmdTypeListbox, self.IDC_TYPELIST)
self.HookCommand(self.CmdMemberListbox, self.IDC_MEMBERLIST)
def OnAttachedObjectDeath(self):
self.tlb = None
self.typeinfo = None
self.attr = None
return TypeBrowseDialog_Parent.OnAttachedObjectDeath(self)
def _SetupMenu(self):
menu = win32ui.CreateMenu()
flags=win32con.MF_STRING|win32con.MF_ENABLED
menu.AppendMenu(flags, win32ui.ID_FILE_OPEN, "&Open...")
menu.AppendMenu(flags, win32con.IDCANCEL, "&Close")
mainMenu = win32ui.CreateMenu()
mainMenu.AppendMenu(flags|win32con.MF_POPUP, menu.GetHandle(), "&File")
self.SetMenu(mainMenu)
self.HookCommand(self.OnFileOpen,win32ui.ID_FILE_OPEN)
def OnFileOpen(self, id, code):
openFlags = win32con.OFN_OVERWRITEPROMPT | win32con.OFN_FILEMUSTEXIST
fspec = "Type Libraries (*.tlb, *.olb)|*.tlb;*.olb|OCX Files (*.ocx)|*.ocx|DLL's (*.dll)|*.dll|All Files (*.*)|*.*||"
dlg = win32ui.CreateFileDialog(1, None, None, openFlags, fspec)
if dlg.DoModal() == win32con.IDOK:
try:
self.tlb = pythoncom.LoadTypeLib(dlg.GetPathName())
except pythoncom.ole_error:
self.MessageBox("The file does not contain type information")
self.tlb = None
self._SetupTLB()
def OnInitDialog(self):
self._SetupMenu()
self.typelb = self.GetDlgItem(self.IDC_TYPELIST)
self.memberlb = self.GetDlgItem(self.IDC_MEMBERLIST)
self.paramlb = self.GetDlgItem(self.IDC_PARAMLIST)
self.listview = self.GetDlgItem(self.IDC_LISTVIEW)
# Setup the listview columns
itemDetails = (commctrl.LVCFMT_LEFT, 100, "Item", 0)
self.listview.InsertColumn(0, itemDetails)
itemDetails = (commctrl.LVCFMT_LEFT, 1024, "Details", 0)
self.listview.InsertColumn(1, itemDetails)
if self.tlb is None:
self.OnFileOpen(None,None)
else:
self._SetupTLB()
return TypeBrowseDialog_Parent.OnInitDialog(self)
def _SetupTLB(self):
self.typelb.ResetContent()
self.memberlb.ResetContent()
self.paramlb.ResetContent()
self.typeinfo = None
self.attr = None
if self.tlb is None: return
n = self.tlb.GetTypeInfoCount()
for i in range(n):
self.typelb.AddString(self.tlb.GetDocumentation(i)[0])
def _SetListviewTextItems(self, items):
self.listview.DeleteAllItems()
index = -1
for item in items:
index = self.listview.InsertItem(index+1,item[0])
data = item[1]
if data is None: data = ""
self.listview.SetItemText(index, 1, data)
def SetupAllInfoTypes(self):
infos = self._GetMainInfoTypes() + self._GetMethodInfoTypes()
self._SetListviewTextItems(infos)
def _GetMainInfoTypes(self):
pos = self.typelb.GetCurSel()
if pos<0: return []
docinfo = self.tlb.GetDocumentation(pos)
infos = [('GUID', str(self.attr[0]))]
infos.append(('Help File', docinfo[3]))
infos.append(('Help Context', str(docinfo[2])))
try:
infos.append(('Type Kind', typekindmap[self.tlb.GetTypeInfoType(pos)]))
except:
pass
info = self.tlb.GetTypeInfo(pos)
attr = info.GetTypeAttr()
infos.append(('Attributes', str(attr)))
for j in range(attr[8]):
flags = info.GetImplTypeFlags(j)
refInfo = info.GetRefTypeInfo(info.GetRefTypeOfImplType(j))
doc = refInfo.GetDocumentation(-1)
attr = refInfo.GetTypeAttr()
typeKind = attr[5]
typeFlags = attr[11]
desc = doc[0]
desc = desc + ", Flags=0x%x, typeKind=0x%x, typeFlags=0x%x" % (flags, typeKind, typeFlags)
if flags & pythoncom.IMPLTYPEFLAG_FSOURCE:
desc = desc + "(Source)"
infos.append( ('Implements', desc))
return infos
def _GetMethodInfoTypes(self):
pos = self.memberlb.GetCurSel()
if pos<0: return []
realPos, isMethod = self._GetRealMemberPos(pos)
ret = []
if isMethod:
funcDesc = self.typeinfo.GetFuncDesc(realPos)
id = funcDesc[0]
ret.append(("Func Desc", str(funcDesc)))
else:
id = self.typeinfo.GetVarDesc(realPos)[0]
docinfo = self.typeinfo.GetDocumentation(id)
ret.append(('Help String', docinfo[1]))
ret.append(('Help Context', str(docinfo[2])))
return ret
def CmdTypeListbox(self, id, code):
if code == win32con.LBN_SELCHANGE:
pos = self.typelb.GetCurSel()
if pos >= 0:
self.memberlb.ResetContent()
self.typeinfo = self.tlb.GetTypeInfo(pos)
self.attr = self.typeinfo.GetTypeAttr()
for i in range(self.attr[7]):
id = self.typeinfo.GetVarDesc(i)[0]
self.memberlb.AddString(self.typeinfo.GetNames(id)[0])
for i in range(self.attr[6]):
id = self.typeinfo.GetFuncDesc(i)[0]
self.memberlb.AddString(self.typeinfo.GetNames(id)[0])
self.SetupAllInfoTypes()
return 1
def _GetRealMemberPos(self, pos):
pos = self.memberlb.GetCurSel()
if pos >= self.attr[7]:
return pos - self.attr[7], 1
elif pos >= 0:
return pos, 0
else:
raise error("The position is not valid")
def CmdMemberListbox(self, id, code):
if code == win32con.LBN_SELCHANGE:
self.paramlb.ResetContent()
pos = self.memberlb.GetCurSel()
realPos, isMethod = self._GetRealMemberPos(pos)
if isMethod:
id = self.typeinfo.GetFuncDesc(realPos)[0]
names = self.typeinfo.GetNames(id)
for i in range(len(names)):
if i > 0:
self.paramlb.AddString(names[i])
self.SetupAllInfoTypes()
return 1
def GetTemplate(self):
"Return the template used to create this dialog"
w = 272 # Dialog width
h = 192 # Dialog height
style = FRAMEDLG_STD | win32con.WS_VISIBLE | win32con.DS_SETFONT | win32con.WS_MINIMIZEBOX
template = [['Type Library Browser', (0, 0, w, h), style, None, (8, 'Helv')], ]
template.append([130, "&Type", -1, (10, 10, 62, 9), SS_STD | win32con.SS_LEFT])
template.append([131, None, self.IDC_TYPELIST, (10, 20, 80, 80), LBS_STD])
template.append([130, "&Members", -1, (100, 10, 62, 9), SS_STD | win32con.SS_LEFT])
template.append([131, None, self.IDC_MEMBERLIST, (100, 20, 80, 80), LBS_STD])
template.append([130, "&Parameters", -1, (190, 10, 62, 9), SS_STD | win32con.SS_LEFT])
template.append([131, None, self.IDC_PARAMLIST, (190, 20, 75, 80), LBS_STD])
lvStyle = SS_STD | commctrl.LVS_REPORT | commctrl.LVS_AUTOARRANGE | commctrl.LVS_ALIGNLEFT | win32con.WS_BORDER | win32con.WS_TABSTOP
template.append(["SysListView32", "", self.IDC_LISTVIEW, (10, 110, 255, 65), lvStyle])
return template
if __name__=='__main__':
import sys
fname = None
try:
fname = sys.argv[1]
except:
pass
dlg = TypeBrowseDialog(fname)
try:
win32api.GetConsoleTitle()
dlg.DoModal()
except:
dlg.CreateWindow(win32ui.GetMainFrame())

View file

@ -0,0 +1,84 @@
"""General client side utilities.
This module contains utility functions, used primarily by advanced COM
programmers, or other COM modules.
"""
import pythoncom
from win32com.client import Dispatch, _get_good_object_
PyIDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
def WrapEnum(ob, resultCLSID = None):
"""Wrap an object in a VARIANT enumerator.
All VT_DISPATCHs returned by the enumerator are converted to wrapper objects
(which may be either a class instance, or a dynamic.Dispatch type object).
"""
if type(ob) != pythoncom.TypeIIDs[pythoncom.IID_IEnumVARIANT]:
ob = ob.QueryInterface(pythoncom.IID_IEnumVARIANT)
return EnumVARIANT(ob, resultCLSID)
class Enumerator:
"""A class that provides indexed access into an Enumerator
By wrapping a PyIEnum* object in this class, you can perform
natural looping and indexing into the Enumerator.
Looping is very efficient, but it should be noted that although random
access is supported, the underlying object is still an enumerator, so
this will force many reset-and-seek operations to find the requested index.
"""
def __init__(self, enum):
self._oleobj_ = enum # a PyIEnumVARIANT
self.index = -1
def __getitem__(self, index):
return self.__GetIndex(index)
def __call__(self, index):
return self.__GetIndex(index)
def __GetIndex(self, index):
if type(index)!=type(0): raise TypeError("Only integer indexes are supported for enumerators")
# NOTE
# In this context, self.index is users purely as a flag to say
# "am I still in sequence". The user may call Next() or Reset() if they
# so choose, in which case self.index will not be correct (although we
# still want to stay in sequence)
if index != self.index + 1:
# Index requested out of sequence.
self._oleobj_.Reset()
if index: self._oleobj_.Skip(index) # if asked for item 1, must skip 1, Python always zero based.
self.index = index
result = self._oleobj_.Next(1)
if len(result):
return self._make_retval_(result[0])
raise IndexError("list index out of range")
def Next(self, count=1):
ret = self._oleobj_.Next(count)
realRets = []
for r in ret:
realRets.append(self._make_retval_(r))
return tuple(realRets) # Convert back to tuple.
def Reset(self):
return self._oleobj_.Reset()
def Clone(self):
return self.__class__( self._oleobj_.Clone(), self.resultCLSID)
def _make_retval_(self, result):
return result
class EnumVARIANT(Enumerator):
def __init__(self, enum, resultCLSID = None):
self.resultCLSID = resultCLSID
Enumerator.__init__(self, enum)
def _make_retval_(self, result):
return _get_good_object_(result, resultCLSID = self.resultCLSID)
class Iterator:
def __init__(self, enum, resultCLSID = None):
self.resultCLSID = resultCLSID
self._iter_ = iter(enum.QueryInterface(pythoncom.IID_IEnumVARIANT))
def __iter__(self):
return self
def __next__(self):
return _get_good_object_(next(self._iter_), resultCLSID = self.resultCLSID)

View file

@ -0,0 +1,83 @@
# Implements _both_ a connectable client, and a connectable server.
#
# Note that we cheat just a little - the Server in this demo is not created
# via Normal COM - this means we can avoid registering the server.
# However, the server _is_ accessed as a COM object - just the creation
# is cheated on - so this is still working as a fully-fledged server.
import pythoncom
import win32com.server.util
import win32com.server.connect
from win32com.server.exception import Exception
from pywin32_testutil import str2bytes
# This is the IID of the Events interface both Client and Server support.
IID_IConnectDemoEvents = pythoncom.MakeIID("{A4988850-49C3-11d0-AE5D-52342E000000}")
# The server which implements
# Create a connectable class, that has a single public method
# 'DoIt', which echos to a single sink 'DoneIt'
class ConnectableServer(win32com.server.connect.ConnectableServer):
_public_methods_ = ["DoIt"] + win32com.server.connect.ConnectableServer._public_methods_
_connect_interfaces_ = [IID_IConnectDemoEvents]
# The single public method that the client can call on us
# (ie, as a normal COM server, this exposes just this single method.
def DoIt(self,arg):
# Simply broadcast a notification.
self._BroadcastNotify(self.NotifyDoneIt, (arg,))
def NotifyDoneIt(self, interface, arg):
interface.Invoke(1000, 0, pythoncom.DISPATCH_METHOD, 1, arg)
# Here is the client side of the connection world.
# Define a COM object which implements the methods defined by the
# IConnectDemoEvents interface.
class ConnectableClient:
# This is another cheat - I _know_ the server defines the "DoneIt" event
# as DISPID==1000 - I also know from the implementation details of COM
# that the first method in _public_methods_ gets 1000.
# Normally some explicit DISPID->Method mapping is required.
_public_methods_ = ["OnDoneIt"]
def __init__(self):
self.last_event_arg = None
# A client must implement QI, and respond to a query for the Event interface.
# In addition, it must provide a COM object (which server.util.wrap) does.
def _query_interface_(self, iid):
import win32com.server.util
# Note that this seems like a necessary hack. I am responding to IID_IConnectDemoEvents
# but only creating an IDispatch gateway object.
if iid==IID_IConnectDemoEvents: return win32com.server.util.wrap(self)
# And here is our event method which gets called.
def OnDoneIt(self, arg):
self.last_event_arg = arg
def CheckEvent(server, client, val, verbose):
client.last_event_arg = None
server.DoIt(val)
if client.last_event_arg != val:
raise RuntimeError("Sent %r, but got back %r" % (val, client.last_event_arg))
if verbose:
print("Sent and received %r" % val)
# A simple test script for all this.
# In the real world, it is likely that the code controlling the server
# will be in the same class as that getting the notifications.
def test(verbose=0):
import win32com.client.dynamic, win32com.client.connect
import win32com.server.policy
server = win32com.client.dynamic.Dispatch(win32com.server.util.wrap(ConnectableServer()))
connection = win32com.client.connect.SimpleConnection()
client = ConnectableClient()
connection.Connect(server, client, IID_IConnectDemoEvents)
CheckEvent(server, client, "Hello", verbose)
CheckEvent(server, client, str2bytes("Here is a null>\x00<"), verbose)
CheckEvent(server, client, "Here is a null>\x00<", verbose)
val = "test-\xe0\xf2" # 2 extended characters.
CheckEvent(server, client, val, verbose)
if verbose:
print("Everything seemed to work!")
# Aggressive memory leak checking (ie, do nothing!) :-) All should cleanup OK???
if __name__=='__main__':
test(1)

View file

@ -0,0 +1,68 @@
import pythoncom
import win32con
formats = """CF_TEXT CF_BITMAP CF_METAFILEPICT CF_SYLK CF_DIF CF_TIFF
CF_OEMTEXT CF_DIB CF_PALETTE CF_PENDATA CF_RIFF CF_WAVE
CF_UNICODETEXT CF_ENHMETAFILE CF_HDROP CF_LOCALE CF_MAX
CF_OWNERDISPLAY CF_DSPTEXT CF_DSPBITMAP CF_DSPMETAFILEPICT
CF_DSPENHMETAFILE""".split()
format_name_map = {}
for f in formats:
val = getattr(win32con, f)
format_name_map[val]=f
tymeds = [attr for attr in pythoncom.__dict__.keys() if attr.startswith("TYMED_")]
def DumpClipboard():
do = pythoncom.OleGetClipboard()
print("Dumping all clipboard formats...")
for fe in do.EnumFormatEtc():
fmt, td, aspect, index, tymed = fe
tymeds_this = [getattr(pythoncom, t) for t in tymeds if tymed & getattr(pythoncom, t)]
print("Clipboard format", format_name_map.get(fmt,str(fmt)))
for t_this in tymeds_this:
# As we are enumerating there should be no need to call
# QueryGetData, but we do anyway!
fetc_query = fmt, td, aspect, index, t_this
try:
do.QueryGetData(fetc_query)
except pythoncom.com_error:
print("Eeek - QGD indicated failure for tymed", t_this)
# now actually get it.
try:
medium = do.GetData(fetc_query)
except pythoncom.com_error as exc:
print("Failed to get the clipboard data:", exc)
continue
if medium.tymed==pythoncom.TYMED_GDI:
data = "GDI handle %d" % medium.data
elif medium.tymed==pythoncom.TYMED_MFPICT:
data = "METAFILE handle %d" % medium.data
elif medium.tymed==pythoncom.TYMED_ENHMF:
data = "ENHMETAFILE handle %d" % medium.data
elif medium.tymed==pythoncom.TYMED_HGLOBAL:
data = "%d bytes via HGLOBAL" % len(medium.data)
elif medium.tymed==pythoncom.TYMED_FILE:
data = "filename '%s'" % data
elif medium.tymed==pythoncom.TYMED_ISTREAM:
stream = medium.data
stream.Seek(0,0)
bytes = 0
while 1:
chunk = stream.Read(4096)
if not chunk:
break
bytes += len(chunk)
data = "%d bytes via IStream" % bytes
elif medium.tymed==pythoncom.TYMED_ISTORAGE:
data = "a IStorage"
else:
data = "*** unknown tymed!"
print(" -> got", data)
do = None
if __name__=='__main__':
DumpClipboard()
if pythoncom._GetInterfaceCount()+pythoncom._GetGatewayCount():
print("XXX - Leaving with %d/%d COM objects alive" % \
(pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))

View file

@ -0,0 +1,94 @@
# A sample originally provided by Richard Bell, and modified by Mark Hammond.
# This sample demonstrates how to use COM events in an aparment-threaded
# world. In this world, COM itself ensures that all calls to and events
# from an object happen on the same thread that created the object, even
# if they originated from different threads. For this cross-thread
# marshalling to work, this main thread *must* run a "message-loop" (ie,
# a loop fetching and dispatching Windows messages). Without such message
# processing, dead-locks can occur.
# See also eventsFreeThreaded.py for how to do this in a free-threaded
# world where these marshalling considerations do not exist.
# NOTE: This example uses Internet Explorer, but it should not be considerd
# a "best-practices" for writing against IE events, but for working with
# events in general. For example:
# * The first OnDocumentComplete event is not a reliable indicator that the
# URL has completed loading
# * As we are demonstrating the most efficient way of handling events, when
# running this sample you will see an IE Windows briefly appear, but
# vanish without ever being repainted.
import sys
import os
import win32com.client
import win32api
import win32event
# sys.coinit_flags not set, so pythoncom initializes apartment-threaded.
import pythoncom
import time
class ExplorerEvents:
def __init__(self):
self.event = win32event.CreateEvent(None, 0, 0, None)
def OnDocumentComplete(self,
pDisp=pythoncom.Empty,
URL=pythoncom.Empty):
thread = win32api.GetCurrentThreadId()
print("OnDocumentComplete event processed on thread %d"%thread)
# Set the event our main thread is waiting on.
win32event.SetEvent(self.event)
def OnQuit(self):
thread = win32api.GetCurrentThreadId()
print("OnQuit event processed on thread %d"%thread)
win32event.SetEvent(self.event)
def WaitWhileProcessingMessages(event, timeout = 2):
start = time.clock()
while True:
# Wake 4 times a second - we can't just specify the
# full timeout here, as then it would reset for every
# message we process.
rc = win32event.MsgWaitForMultipleObjects( (event,), 0,
250,
win32event.QS_ALLEVENTS)
if rc == win32event.WAIT_OBJECT_0:
# event signalled - stop now!
return True
if (time.clock() - start) > timeout:
# Timeout expired.
return False
# must be a message.
pythoncom.PumpWaitingMessages()
def TestExplorerEvents():
iexplore = win32com.client.DispatchWithEvents(
"InternetExplorer.Application", ExplorerEvents)
thread = win32api.GetCurrentThreadId()
print('TestExplorerEvents created IE object on thread %d'%thread)
iexplore.Visible = 1
try:
iexplore.Navigate(win32api.GetFullPathName('..\\readme.htm'))
except pythoncom.com_error as details:
print("Warning - could not open the test HTML file", details)
# Wait for the event to be signalled while pumping messages.
if not WaitWhileProcessingMessages(iexplore.event):
print("Document load event FAILED to fire!!!")
iexplore.Quit()
#
# Give IE a chance to shutdown, else it can get upset on fast machines.
# Note, Quit generates events. Although this test does NOT catch them
# it is NECESSARY to pump messages here instead of a sleep so that the Quit
# happens properly!
if not WaitWhileProcessingMessages(iexplore.event):
print("OnQuit event FAILED to fire!!!")
iexplore = None
if __name__=='__main__':
TestExplorerEvents()

View file

@ -0,0 +1,88 @@
# A sample originally provided by Richard Bell, and modified by Mark Hammond.
# This sample demonstrates how to use COM events in a free-threaded world.
# In this world, there is no need to marshall calls across threads, so
# no message loops are needed at all. This means regular cross-thread
# sychronization can be used. In this sample we just wait on win32 event
# objects.
# See also ieEventsApartmentThreaded.py for how to do this in an
# aparment-threaded world, where thread-marshalling complicates things.
# NOTE: This example uses Internet Explorer, but it should not be considerd
# a "best-practices" for writing against IE events, but for working with
# events in general. For example:
# * The first OnDocumentComplete event is not a reliable indicator that the
# URL has completed loading
# * As we are demonstrating the most efficient way of handling events, when
# running this sample you will see an IE Windows briefly appear, but
# vanish without ever being repainted.
import sys
sys.coinit_flags=0 # specify free threading
import os
import win32api
import win32event
import win32com.client
import pythoncom
import time
# The print statements indicate that COM has actually started another thread
# and will deliver the events to that thread (ie, the events do not actually
# fire on our main thread.
class ExplorerEvents:
def __init__(self):
# We reuse this event for all events.
self.event = win32event.CreateEvent(None, 0, 0, None)
def OnDocumentComplete(self,
pDisp=pythoncom.Empty,
URL=pythoncom.Empty):
#
# Caution: Since the main thread and events thread(s) are different
# it may be necessary to serialize access to shared data. Because
# this is a simple test case, that is not required here. Your
# situation may be different. Caveat programmer.
#
thread = win32api.GetCurrentThreadId()
print("OnDocumentComplete event processed on thread %d"%thread)
# Set the event our main thread is waiting on.
win32event.SetEvent(self.event)
def OnQuit(self):
thread = win32api.GetCurrentThreadId()
print("OnQuit event processed on thread %d"%thread)
win32event.SetEvent(self.event)
def TestExplorerEvents():
iexplore = win32com.client.DispatchWithEvents(
"InternetExplorer.Application", ExplorerEvents)
thread = win32api.GetCurrentThreadId()
print('TestExplorerEvents created IE object on thread %d'%thread)
iexplore.Visible = 1
try:
iexplore.Navigate(win32api.GetFullPathName('..\\readme.htm'))
except pythoncom.com_error as details:
print("Warning - could not open the test HTML file", details)
# In this free-threaded example, we can simply wait until an event has
# been set - we will give it 2 seconds before giving up.
rc = win32event.WaitForSingleObject(iexplore.event, 2000)
if rc != win32event.WAIT_OBJECT_0:
print("Document load event FAILED to fire!!!")
iexplore.Quit()
# Now we can do the same thing to wait for exit!
# Although Quit generates events, in this free-threaded world we
# do *not* need to run any message pumps.
rc = win32event.WaitForSingleObject(iexplore.event, 2000)
if rc != win32event.WAIT_OBJECT_0:
print("OnQuit event FAILED to fire!!!")
iexplore = None
print("Finished the IE event sample!")
if __name__=='__main__':
TestExplorerEvents()

View file

@ -0,0 +1,139 @@
# A demo plugin for Microsoft Excel
#
# This addin simply adds a new button to the main Excel toolbar,
# and displays a message box when clicked. Thus, it demonstrates
# how to plug in to Excel itself, and hook Excel events.
#
#
# To register the addin, simply execute:
# excelAddin.py
# This will install the COM server, and write the necessary
# AddIn key to Excel
#
# To unregister completely:
# excelAddin.py --unregister
#
# To debug, execute:
# excelAddin.py --debug
#
# Then open Pythonwin, and select "Tools->Trace Collector Debugging Tool"
# Restart excel, and you should see some output generated.
#
# NOTE: If the AddIn fails with an error, Excel will re-register
# the addin to not automatically load next time Excel starts. To
# correct this, simply re-register the addin (see above)
#
# Author <ekoome@yahoo.com> Eric Koome
# Copyright (c) 2003 Wavecom Inc. All rights reserved
#
# Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions
#are met:
#
#1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL ERIC KOOME OR
# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
from win32com import universal
from win32com.server.exception import COMException
from win32com.client import gencache, DispatchWithEvents
import winerror
import pythoncom
from win32com.client import constants, Dispatch
import sys
# Support for COM objects we use.
gencache.EnsureModule('{00020813-0000-0000-C000-000000000046}', 0, 1, 3, bForDemand=True) # Excel 9
gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1, bForDemand=True) # Office 9
# The TLB defiining the interfaces we implement
universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, ["_IDTExtensibility2"])
class ButtonEvent:
def OnClick(self, button, cancel):
import win32ui # Possible, but not necessary, to use a Pythonwin GUI
import win32con
win32ui.MessageBox("Hello from Python", "Python Test",win32con.MB_OKCANCEL)
return cancel
class ExcelAddin:
_com_interfaces_ = ['_IDTExtensibility2']
_public_methods_ = []
_reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
_reg_clsid_ = "{C5482ECA-F559-45A0-B078-B2036E6F011A}"
_reg_progid_ = "Python.Test.ExcelAddin"
_reg_policy_spec_ = "win32com.server.policy.EventHandlerPolicy"
def __init__(self):
self.appHostApp = None
def OnConnection(self, application, connectMode, addin, custom):
print("OnConnection", application, connectMode, addin, custom)
try:
self.appHostApp = application
cbcMyBar = self.appHostApp.CommandBars.Add(Name="PythonBar", Position=constants.msoBarTop, MenuBar=constants.msoBarTypeNormal, Temporary=True)
btnMyButton = cbcMyBar.Controls.Add(Type=constants.msoControlButton, Parameter="Greetings")
btnMyButton=self.toolbarButton = DispatchWithEvents(btnMyButton, ButtonEvent)
btnMyButton.Style = constants.msoButtonCaption
btnMyButton.BeginGroup = True
btnMyButton.Caption = "&Python"
btnMyButton.TooltipText = "Python rules the World"
btnMyButton.Width = "34"
cbcMyBar.Visible = True
except pythoncom.com_error as xxx_todo_changeme:
(hr, msg, exc, arg) = xxx_todo_changeme.args
print("The Excel call failed with code %d: %s" % (hr, msg))
if exc is None:
print("There is no extended error information")
else:
wcode, source, text, helpFile, helpId, scode = exc
print("The source of the error is", source)
print("The error message is", text)
print("More info can be found in %s (id=%d)" % (helpFile, helpId))
def OnDisconnection(self, mode, custom):
print("OnDisconnection")
self.appHostApp.CommandBars("PythonBar").Delete
self.appHostApp=None
def OnAddInsUpdate(self, custom):
print("OnAddInsUpdate", custom)
def OnStartupComplete(self, custom):
print("OnStartupComplete", custom)
def OnBeginShutdown(self, custom):
print("OnBeginShutdown", custom)
def RegisterAddin(klass):
import winreg
key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Excel\\Addins")
subkey = winreg.CreateKey(key, klass._reg_progid_)
winreg.SetValueEx(subkey, "CommandLineSafe", 0, winreg.REG_DWORD, 0)
winreg.SetValueEx(subkey, "LoadBehavior", 0, winreg.REG_DWORD, 3)
winreg.SetValueEx(subkey, "Description", 0, winreg.REG_SZ, "Excel Addin")
winreg.SetValueEx(subkey, "FriendlyName", 0, winreg.REG_SZ, "A Simple Excel Addin")
def UnregisterAddin(klass):
import winreg
try:
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Excel\\Addins\\" + klass._reg_progid_)
except WindowsError:
pass
if __name__ == '__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(ExcelAddin)
if "--unregister" in sys.argv:
UnregisterAddin(ExcelAddin)
else:
RegisterAddin(ExcelAddin)

View file

@ -0,0 +1,409 @@
"""Excel IRTDServer implementation.
This module is a functional example of how to implement the IRTDServer interface
in python, using the pywin32 extensions. Further details, about this interface
and it can be found at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnexcl2k2/html/odc_xlrtdfaq.asp
"""
# Copyright (c) 2003-2004 by Chris Nilsson <chris@slort.org>
#
# By obtaining, using, and/or copying this software and/or its
# associated documentation, you agree that you have read, understood,
# and will comply with the following terms and conditions:
#
# Permission to use, copy, modify, and distribute this software and
# its associated documentation for any purpose and without fee is
# hereby granted, provided that the above copyright notice appears in
# all copies, and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# Christopher Nilsson (the author) not be used in advertising or publicity
# pertaining to distribution of the software without specific, written
# prior permission.
#
# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
# ABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
# OF THIS SOFTWARE.
import pythoncom
import win32com.client
from win32com import universal
from win32com.client import gencache
from win32com.server.exception import COMException
import threading
import datetime # For the example classes...
# Typelib info for version 10 - aka Excel XP.
# This is the minimum version of excel that we can work with as this is when
# Microsoft introduced these interfaces.
EXCEL_TLB_GUID = '{00020813-0000-0000-C000-000000000046}'
EXCEL_TLB_LCID = 0
EXCEL_TLB_MAJOR = 1
EXCEL_TLB_MINOR = 4
# Import the excel typelib to make sure we've got early-binding going on.
# The "ByRef" parameters we use later won't work without this.
gencache.EnsureModule(EXCEL_TLB_GUID, EXCEL_TLB_LCID, \
EXCEL_TLB_MAJOR, EXCEL_TLB_MINOR)
# Tell pywin to import these extra interfaces.
# --
# QUESTION: Why? The interfaces seem to descend from IDispatch, so
# I'd have thought, for example, calling callback.UpdateNotify() (on the
# IRTDUpdateEvent callback excel gives us) would work without molestation.
# But the callback needs to be cast to a "real" IRTDUpdateEvent type. Hmm...
# This is where my small knowledge of the pywin framework / COM gets hazy.
# --
# Again, we feed in the Excel typelib as the source of these interfaces.
universal.RegisterInterfaces(EXCEL_TLB_GUID,
EXCEL_TLB_LCID, EXCEL_TLB_MAJOR, EXCEL_TLB_MINOR,
['IRtdServer','IRTDUpdateEvent'])
class ExcelRTDServer(object):
"""Base RTDServer class.
Provides most of the features needed to implement the IRtdServer interface.
Manages topic adding, removal, and packing up the values for excel.
Shouldn't be instanciated directly.
Instead, descendant classes should override the CreateTopic() method.
Topic objects only need to provide a GetValue() function to play nice here.
The values given need to be atomic (eg. string, int, float... etc).
Also note: nothing has been done within this class to ensure that we get
time to check our topics for updates. I've left that up to the subclass
since the ways, and needs, of refreshing your topics will vary greatly. For
example, the sample implementation uses a timer thread to wake itself up.
Whichever way you choose to do it, your class needs to be able to wake up
occaisionally, since excel will never call your class without being asked to
first.
Excel will communicate with our object in this order:
1. Excel instanciates our object and calls ServerStart, providing us with
an IRTDUpdateEvent callback object.
2. Excel calls ConnectData when it wants to subscribe to a new "topic".
3. When we have new data to provide, we call the UpdateNotify method of the
callback object we were given.
4. Excel calls our RefreshData method, and receives a 2d SafeArray (row-major)
containing the Topic ids in the 1st dim, and the topic values in the
2nd dim.
5. When not needed anymore, Excel will call our DisconnectData to
unsubscribe from a topic.
6. When there are no more topics left, Excel will call our ServerTerminate
method to kill us.
Throughout, at undetermined periods, Excel will call our Heartbeat
method to see if we're still alive. It must return a non-zero value, or
we'll be killed.
NOTE: By default, excel will at most call RefreshData once every 2 seconds.
This is a setting that needs to be changed excel-side. To change this,
you can set the throttle interval like this in the excel VBA object model:
Application.RTD.ThrottleInterval = 1000 ' milliseconds
"""
_com_interfaces_ = ['IRtdServer']
_public_methods_ = ['ConnectData','DisconnectData','Heartbeat',
'RefreshData','ServerStart','ServerTerminate']
_reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
#_reg_clsid_ = "# subclass must provide this class attribute"
#_reg_desc_ = "# subclass should provide this description"
#_reg_progid_ = "# subclass must provide this class attribute"
ALIVE = 1
NOT_ALIVE = 0
def __init__(self):
"""Constructor"""
super(ExcelRTDServer, self).__init__()
self.IsAlive = self.ALIVE
self.__callback = None
self.topics = {}
def SignalExcel(self):
"""Use the callback we were given to tell excel new data is available."""
if self.__callback is None:
raise COMException(desc="Callback excel provided is Null")
self.__callback.UpdateNotify()
def ConnectData(self, TopicID, Strings, GetNewValues):
"""Creates a new topic out of the Strings excel gives us."""
try:
self.topics[TopicID] = self.CreateTopic(Strings)
except Exception as why:
raise COMException(desc=str(why))
GetNewValues = True
result = self.topics[TopicID]
if result is None:
result = "# %s: Waiting for update" % self.__class__.__name__
else:
result = result.GetValue()
# fire out internal event...
self.OnConnectData(TopicID)
# GetNewValues as per interface is ByRef, so we need to pass it back too.
return result, GetNewValues
def DisconnectData(self, TopicID):
"""Deletes the given topic."""
self.OnDisconnectData(TopicID)
if TopicID in self.topics:
self.topics[TopicID] = None
del self.topics[TopicID]
def Heartbeat(self):
"""Called by excel to see if we're still here."""
return self.IsAlive
def RefreshData(self, TopicCount):
"""Packs up the topic values. Called by excel when it's ready for an update.
Needs to:
* Return the current number of topics, via the "ByRef" TopicCount
* Return a 2d SafeArray of the topic data.
- 1st dim: topic numbers
- 2nd dim: topic values
We could do some caching, instead of repacking everytime...
But this works for demonstration purposes."""
TopicCount = len(self.topics)
self.OnRefreshData()
# Grow the lists, so we don't need a heap of calls to append()
results = [[None] * TopicCount, [None] * TopicCount]
# Excel expects a 2-dimensional array. The first dim contains the
# topic numbers, and the second contains the values for the topics.
# In true VBA style (yuck), we need to pack the array in row-major format,
# which looks like:
# ( (topic_num1, topic_num2, ..., topic_numN), \
# (topic_val1, topic_val2, ..., topic_valN) )
for idx, topicdata in enumerate(self.topics.items()):
topicNum, topic = topicdata
results[0][idx] = topicNum
results[1][idx] = topic.GetValue()
# TopicCount is meant to be passed to us ByRef, so return it as well, as per
# the way pywin32 handles ByRef arguments.
return tuple(results), TopicCount
def ServerStart(self, CallbackObject):
"""Excel has just created us... We take its callback for later, and set up shop."""
self.IsAlive = self.ALIVE
if CallbackObject is None:
raise COMException(desc='Excel did not provide a callback')
# Need to "cast" the raw PyIDispatch object to the IRTDUpdateEvent interface
IRTDUpdateEventKlass = win32com.client.CLSIDToClass.GetClass('{A43788C1-D91B-11D3-8F39-00C04F3651B8}')
self.__callback = IRTDUpdateEventKlass(CallbackObject)
self.OnServerStart()
return self.IsAlive
def ServerTerminate(self):
"""Called when excel no longer wants us."""
self.IsAlive = self.NOT_ALIVE # On next heartbeat, excel will free us
self.OnServerTerminate()
def CreateTopic(self, TopicStrings=None):
"""Topic factory method. Subclass must override.
Topic objects need to provide:
* GetValue() method which returns an atomic value.
Will raise NotImplemented if not overridden.
"""
raise NotImplemented('Subclass must implement')
# Overridable class events...
def OnConnectData(self, TopicID):
"""Called when a new topic has been created, at excel's request."""
pass
def OnDisconnectData(self, TopicID):
"""Called when a topic is about to be deleted, at excel's request."""
pass
def OnRefreshData(self):
"""Called when excel has requested all current topic data."""
pass
def OnServerStart(self):
"""Called when excel has instanciated us."""
pass
def OnServerTerminate(self):
"""Called when excel is about to destroy us."""
pass
class RTDTopic(object):
"""Base RTD Topic.
Only method required by our RTDServer implementation is GetValue().
The others are more for convenience."""
def __init__(self, TopicStrings):
super(RTDTopic, self).__init__()
self.TopicStrings = TopicStrings
self.__currentValue = None
self.__dirty = False
def Update(self, sender):
"""Called by the RTD Server.
Gives us a chance to check if our topic data needs to be
changed (eg. check a file, quiz a database, etc)."""
raise NotImplemented('subclass must implement')
def Reset(self):
"""Call when this topic isn't considered "dirty" anymore."""
self.__dirty = False
def GetValue(self):
return self.__currentValue
def SetValue(self, value):
self.__dirty = True
self.__currentValue = value
def HasChanged(self):
return self.__dirty
# -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
######################################
# Example classes
######################################
class TimeServer(ExcelRTDServer):
"""Example Time RTD server.
Sends time updates back to excel.
example of use, in an excel sheet:
=RTD("Python.RTD.TimeServer","","seconds","5")
This will cause a timestamp string to fill the cell, and update its value
every 5 seconds (or as close as possible depending on how busy excel is).
The empty string parameter denotes the com server is running on the local
machine. Otherwise, put in the hostname to look on. For more info
on this, lookup the Excel help for its "RTD" worksheet function.
Obviously, you'd want to wrap this kind of thing in a friendlier VBA
function.
Also, remember that the RTD function accepts a maximum of 28 arguments!
If you want to pass more, you may need to concatenate arguments into one
string, and have your topic parse them appropriately.
"""
# win32com.server setup attributes...
# Never copy the _reg_clsid_ value in your own classes!
_reg_clsid_ = '{EA7F2CF1-11A2-45E4-B2D5-68E240DB8CB1}'
_reg_progid_ = 'Python.RTD.TimeServer'
_reg_desc_ = "Python class implementing Excel IRTDServer -- feeds time"
# other class attributes...
INTERVAL = 0.5 # secs. Threaded timer will wake us up at this interval.
def __init__(self):
super(TimeServer, self).__init__()
# Simply timer thread to ensure we get to update our topics, and
# tell excel about any changes. This is a pretty basic and dirty way to
# do this. Ideally, there should be some sort of waitable (eg. either win32
# event, socket data event...) and be kicked off by that event triggering.
# As soon as we set up shop here, we _must_ return control back to excel.
# (ie. we can't block and do our own thing...)
self.ticker = threading.Timer(self.INTERVAL, self.Update)
def OnServerStart(self):
self.ticker.start()
def OnServerTerminate(self):
if not self.ticker.finished.isSet():
self.ticker.cancel() # Cancel our wake-up thread. Excel has killed us.
def Update(self):
# Get our wake-up thread ready...
self.ticker = threading.Timer(self.INTERVAL, self.Update)
try:
# Check if any of our topics have new info to pass on
if len(self.topics):
refresh = False
for topic in self.topics.values():
topic.Update(self)
if topic.HasChanged():
refresh = True
topic.Reset()
if refresh:
self.SignalExcel()
finally:
self.ticker.start() # Make sure we get to run again
def CreateTopic(self, TopicStrings=None):
"""Topic factory. Builds a TimeTopic object out of the given TopicStrings."""
return TimeTopic(TopicStrings)
class TimeTopic(RTDTopic):
"""Example topic for example RTD server.
Will accept some simple commands to alter how long to delay value updates.
Commands:
* seconds, delay_in_seconds
* minutes, delay_in_minutes
* hours, delay_in_hours
"""
def __init__(self, TopicStrings):
super(TimeTopic, self).__init__(TopicStrings)
try:
self.cmd, self.delay = self.TopicStrings
except Exception as E:
# We could simply return a "# ERROR" type string as the
# topic value, but explosions like this should be able to get handled by
# the VBA-side "On Error" stuff.
raise ValueError("Invalid topic strings: %s" % str(TopicStrings))
#self.cmd = str(self.cmd)
self.delay = float(self.delay)
# setup our initial value
self.checkpoint = self.timestamp()
self.SetValue(str(self.checkpoint))
def timestamp(self):
return datetime.datetime.now()
def Update(self, sender):
now = self.timestamp()
delta = now - self.checkpoint
refresh = False
if self.cmd == "seconds":
if delta.seconds >= self.delay:
refresh = True
elif self.cmd == "minutes":
if delta.minutes >= self.delay:
refresh = True
elif self.cmd == "hours":
if delta.hours >= self.delay:
refresh = True
else:
self.SetValue("#Unknown command: " + self.cmd)
if refresh:
self.SetValue(str(now))
self.checkpoint = now
if __name__ == "__main__":
import win32com.server.register
# Register/Unregister TimeServer example
# eg. at the command line: excelrtd.py --register
# Then type in an excel cell something like:
# =RTD("Python.RTD.TimeServer","","seconds","5")
win32com.server.register.UseCommandLine(TimeServer)

View file

@ -0,0 +1,188 @@
# -*- coding: latin-1 -*-
# PyWin32 Internet Explorer Button
#
# written by Leonard Ritter (paniq@gmx.net)
# and Robert Förtsch (info@robert-foertsch.com)
"""
This sample implements a simple IE Button COM server
with access to the IWebBrowser2 interface.
To demonstrate:
* Execute this script to register the server.
* Open Pythonwin's Tools -> Trace Collector Debugging Tool, so you can
see the output of 'print' statements in this demo.
* Open a new IE instance. The toolbar should have a new "scissors" icon,
with tooltip text "IE Button" - this is our new button - click it.
* Switch back to the Pythonwin window - you should see:
IOleCommandTarget::Exec called.
This is the button being clicked. Extending this to do something more
useful is left as an exercise.
Contribtions to this sample to make it a little "friendlier" welcome!
"""
# imports section
import sys, os
from win32com import universal
from win32com.client import gencache, DispatchWithEvents, Dispatch
from win32com.client import constants, getevents
import win32com.server.register
import win32com
import pythoncom
import win32api
# This demo uses 'print' - use win32traceutil to see it if we have no
# console.
try:
win32api.GetConsoleTitle()
except win32api.error:
import win32traceutil
from win32com.axcontrol import axcontrol
import array, struct
# ensure we know the ms internet controls typelib so we have access to IWebBrowser2 later on
win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
#
IObjectWithSite_methods = ['SetSite','GetSite']
IOleCommandTarget_methods = ['Exec','QueryStatus']
_iebutton_methods_ = IOleCommandTarget_methods + IObjectWithSite_methods
_iebutton_com_interfaces_ = [
axcontrol.IID_IOleCommandTarget,
axcontrol.IID_IObjectWithSite, # IObjectWithSite
]
class Stub:
"""
this class serves as a method stub,
outputting debug info whenever the object
is being called.
"""
def __init__(self,name):
self.name = name
def __call__(self,*args):
print('STUB: ',self.name,args)
class IEButton:
"""
The actual COM server class
"""
_com_interfaces_ = _iebutton_com_interfaces_
_public_methods_ = _iebutton_methods_
_reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
_button_text_ = 'IE Button'
_tool_tip_ = 'An example implementation for an IE Button.'
_icon_ = ''
_hot_icon_ = ''
def __init__( self ):
# put stubs for non-implemented methods
for method in self._public_methods_:
if not hasattr(self,method):
print('providing default stub for %s' % method)
setattr(self,method,Stub(method))
def QueryStatus (self, pguidCmdGroup, prgCmds, cmdtextf):
# 'cmdtextf' is the 'cmdtextf' element from the OLECMDTEXT structure,
# or None if a NULL pointer was passed.
result = []
for id, flags in prgCmds:
flags |= axcontrol.OLECMDF_SUPPORTED | axcontrol.OLECMDF_ENABLED
result.append((id, flags))
if cmdtextf is None:
cmdtext = None # must return None if nothing requested.
# IE never seems to want any text - this code is here for
# demo purposes only
elif cmdtextf == axcontrol.OLECMDTEXTF_NAME:
cmdtext = "IEButton Name"
else:
cmdtext = "IEButton State"
return result, cmdtext
def Exec(self, pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn):
print(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn)
print("IOleCommandTarget::Exec called.")
#self.webbrowser.ShowBrowserBar(GUID_IETOOLBAR, not is_ietoolbar_visible())
def SetSite(self,unknown):
if unknown:
# first get a command target
cmdtarget = unknown.QueryInterface(axcontrol.IID_IOleCommandTarget)
# then travel over to a service provider
serviceprovider = cmdtarget.QueryInterface(pythoncom.IID_IServiceProvider)
# finally ask for the internet explorer application, returned as a dispatch object
self.webbrowser = win32com.client.Dispatch(serviceprovider.QueryService('{0002DF05-0000-0000-C000-000000000046}',pythoncom.IID_IDispatch))
else:
# lose all references
self.webbrowser = None
def GetClassID(self):
return self._reg_clsid_
def register(classobj):
import winreg
subKeyCLSID = "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\%38s" % classobj._reg_clsid_
try:
hKey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, subKeyCLSID )
subKey = winreg.SetValueEx( hKey, "ButtonText", 0, winreg.REG_SZ, classobj._button_text_ )
winreg.SetValueEx( hKey, "ClsidExtension", 0, winreg.REG_SZ, classobj._reg_clsid_ ) # reg value for calling COM object
winreg.SetValueEx( hKey, "CLSID", 0, winreg.REG_SZ, "{1FBA04EE-3024-11D2-8F1F-0000F87ABD16}" ) # CLSID for button that sends command to COM object
winreg.SetValueEx( hKey, "Default Visible", 0, winreg.REG_SZ, "Yes" )
winreg.SetValueEx( hKey, "ToolTip", 0, winreg.REG_SZ, classobj._tool_tip_ )
winreg.SetValueEx( hKey, "Icon", 0, winreg.REG_SZ, classobj._icon_)
winreg.SetValueEx( hKey, "HotIcon", 0, winreg.REG_SZ, classobj._hot_icon_)
except WindowsError:
print("Couldn't set standard toolbar reg keys.")
else:
print("Set standard toolbar reg keys.")
def unregister(classobj):
import winreg
subKeyCLSID = "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\%38s" % classobj._reg_clsid_
try:
hKey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, subKeyCLSID )
subKey = winreg.DeleteValue( hKey, "ButtonText" )
winreg.DeleteValue( hKey, "ClsidExtension" ) # for calling COM object
winreg.DeleteValue( hKey, "CLSID" )
winreg.DeleteValue( hKey, "Default Visible" )
winreg.DeleteValue( hKey, "ToolTip" )
winreg.DeleteValue( hKey, "Icon" )
winreg.DeleteValue( hKey, "HotIcon" )
winreg.DeleteKey( winreg.HKEY_LOCAL_MACHINE, subKeyCLSID )
except WindowsError:
print("Couldn't delete Standard toolbar regkey.")
else:
print("Deleted Standard toolbar regkey.")
#
# test implementation
#
class PyWin32InternetExplorerButton(IEButton):
_reg_clsid_ = "{104B66A9-9E68-49D1-A3F5-94754BE9E0E6}"
_reg_progid_ = "PyWin32.IEButton"
_reg_desc_ = 'Test Button'
_button_text_ = 'IE Button'
_tool_tip_ = 'An example implementation for an IE Button.'
_icon_ = ''
_hot_icon_ = _icon_
def DllRegisterServer():
register(PyWin32InternetExplorerButton)
def DllUnregisterServer():
unregister(PyWin32InternetExplorerButton)
if __name__ == '__main__':
win32com.server.register.UseCommandLine(PyWin32InternetExplorerButton,
finalize_register = DllRegisterServer,
finalize_unregister = DllUnregisterServer)

View file

@ -0,0 +1,320 @@
# -*- coding: latin-1 -*-
# PyWin32 Internet Explorer Toolbar
#
# written by Leonard Ritter (paniq@gmx.net)
# and Robert Förtsch (info@robert-foertsch.com)
"""
This sample implements a simple IE Toolbar COM server
supporting Windows XP styles and access to
the IWebBrowser2 interface.
It also demonstrates how to hijack the parent window
to catch WM_COMMAND messages.
"""
# imports section
import sys, os
from win32com import universal
from win32com.client import gencache, DispatchWithEvents, Dispatch
from win32com.client import constants, getevents
import win32com
import pythoncom
import winreg
from win32com.shell import shell
from win32com.shell.shellcon import *
from win32com.axcontrol import axcontrol
try:
# try to get styles (winxp)
import winxpgui as win32gui
except:
# import default module (win2k and lower)
import win32gui
import win32ui
import win32con
import commctrl
import array, struct
# ensure we know the ms internet controls typelib so we have access to IWebBrowser2 later on
win32com.client.gencache.EnsureModule('{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}',0,1,1)
#
IDeskBand_methods = ['GetBandInfo']
IDockingWindow_methods = ['ShowDW','CloseDW','ResizeBorderDW']
IOleWindow_methods = ['GetWindow','ContextSensitiveHelp']
IInputObject_methods = ['UIActivateIO','HasFocusIO','TranslateAcceleratorIO']
IObjectWithSite_methods = ['SetSite','GetSite']
IPersistStream_methods = ['GetClassID','IsDirty','Load','Save','GetSizeMax']
_ietoolbar_methods_ = IDeskBand_methods + IDockingWindow_methods + \
IOleWindow_methods + IInputObject_methods + \
IObjectWithSite_methods + IPersistStream_methods
_ietoolbar_com_interfaces_ = [
shell.IID_IDeskBand, # IDeskBand
axcontrol.IID_IObjectWithSite, # IObjectWithSite
pythoncom.IID_IPersistStream,
axcontrol.IID_IOleCommandTarget,
]
class WIN32STRUCT:
def __init__(self, **kw):
full_fmt = ""
for name, fmt, default in self._struct_items_:
self.__dict__[name] = None
if fmt == "z":
full_fmt += "pi"
else:
full_fmt += fmt
for name, val in kw.items():
self.__dict__[name] = val
def __setattr__(self, attr, val):
if not attr.startswith("_") and attr not in self.__dict__:
raise AttributeError(attr)
self.__dict__[attr] = val
def toparam(self):
self._buffs = []
full_fmt = ""
vals = []
for name, fmt, default in self._struct_items_:
val = self.__dict__[name]
if fmt == "z":
fmt = "Pi"
if val is None:
vals.append(0)
vals.append(0)
else:
str_buf = array.array("c", val+'\0')
vals.append(str_buf.buffer_info()[0])
vals.append(len(val))
self._buffs.append(str_buf) # keep alive during the call.
else:
if val is None:
val = default
vals.append(val)
full_fmt += fmt
return struct.pack(*(full_fmt,) + tuple(vals))
class TBBUTTON(WIN32STRUCT):
_struct_items_ = [
("iBitmap", "i", 0),
("idCommand", "i", 0),
("fsState", "B", 0),
("fsStyle", "B", 0),
("bReserved", "H", 0),
("dwData", "I", 0),
("iString", "z", None),
]
class Stub:
"""
this class serves as a method stub,
outputting debug info whenever the object
is being called.
"""
def __init__(self,name):
self.name = name
def __call__(self,*args):
print('STUB: ',self.name,args)
class IEToolbarCtrl:
"""
a tiny wrapper for our winapi-based
toolbar control implementation.
"""
def __init__(self,hwndparent):
styles = win32con.WS_CHILD \
| win32con.WS_VISIBLE \
| win32con.WS_CLIPSIBLINGS \
| win32con.WS_CLIPCHILDREN \
| commctrl.TBSTYLE_LIST \
| commctrl.TBSTYLE_FLAT \
| commctrl.TBSTYLE_TRANSPARENT \
| commctrl.CCS_TOP \
| commctrl.CCS_NODIVIDER \
| commctrl.CCS_NORESIZE \
| commctrl.CCS_NOPARENTALIGN
self.hwnd = win32gui.CreateWindow('ToolbarWindow32', None, styles,
0, 0, 100, 100,
hwndparent, 0, win32gui.dllhandle,
None)
win32gui.SendMessage(self.hwnd, commctrl.TB_BUTTONSTRUCTSIZE, 20, 0)
def ShowWindow(self,mode):
win32gui.ShowWindow(self.hwnd,mode)
def AddButtons(self,*buttons):
tbbuttons = ''
for button in buttons:
tbbuttons += button.toparam()
return win32gui.SendMessage(self.hwnd, commctrl.TB_ADDBUTTONS,
len(buttons), tbbuttons)
def GetSafeHwnd(self):
return self.hwnd
class IEToolbar:
"""
The actual COM server class
"""
_com_interfaces_ = _ietoolbar_com_interfaces_
_public_methods_ = _ietoolbar_methods_
_reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
# if you copy and modify this example, be sure to change the clsid below
_reg_clsid_ = "{F21202A2-959A-4149-B1C3-68B9013F3335}"
_reg_progid_ = "PyWin32.IEToolbar"
_reg_desc_ = 'PyWin32 IE Toolbar'
def __init__( self ):
# put stubs for non-implemented methods
for method in self._public_methods_:
if not hasattr(self,method):
print('providing default stub for %s' % method)
setattr(self,method,Stub(method))
def GetWindow(self):
return self.toolbar.GetSafeHwnd()
def Load(self, stream):
# called when the toolbar is loaded
pass
def Save(self, pStream, fClearDirty):
# called when the toolbar shall save its information
pass
def CloseDW(self, dwReserved):
del self.toolbar
def ShowDW(self, bShow):
if bShow:
self.toolbar.ShowWindow(win32con.SW_SHOW)
else:
self.toolbar.ShowWindow(win32con.SW_HIDE)
def on_first_button(self):
print("first!")
self.webbrowser.Navigate2('http://starship.python.net/crew/mhammond/')
def on_second_button(self):
print("second!")
def on_third_button(self):
print("third!")
def toolbar_command_handler(self,args):
hwnd,message,wparam,lparam,time,point = args
if lparam == self.toolbar.GetSafeHwnd():
self._command_map[wparam]()
def SetSite(self,unknown):
if unknown:
# retrieve the parent window interface for this site
olewindow = unknown.QueryInterface(pythoncom.IID_IOleWindow)
# ask the window for its handle
hwndparent = olewindow.GetWindow()
# first get a command target
cmdtarget = unknown.QueryInterface(axcontrol.IID_IOleCommandTarget)
# then travel over to a service provider
serviceprovider = cmdtarget.QueryInterface(pythoncom.IID_IServiceProvider)
# finally ask for the internet explorer application, returned as a dispatch object
self.webbrowser = win32com.client.Dispatch(serviceprovider.QueryService('{0002DF05-0000-0000-C000-000000000046}',pythoncom.IID_IDispatch))
# now create and set up the toolbar
self.toolbar = IEToolbarCtrl(hwndparent)
buttons = [
('Visit PyWin32 Homepage',self.on_first_button),
('Another Button', self.on_second_button),
('Yet Another Button', self.on_third_button),
]
self._command_map = {}
# wrap our parent window so we can hook message handlers
window = win32ui.CreateWindowFromHandle(hwndparent)
# add the buttons
for i in range(len(buttons)):
button = TBBUTTON()
name,func = buttons[i]
id = 0x4444+i
button.iBitmap = -2
button.idCommand = id
button.fsState = commctrl.TBSTATE_ENABLED
button.fsStyle = commctrl.TBSTYLE_BUTTON
button.iString = name
self._command_map[0x4444+i] = func
self.toolbar.AddButtons(button)
window.HookMessage(self.toolbar_command_handler,win32con.WM_COMMAND)
else:
# lose all references
self.webbrowser = None
def GetClassID(self):
return self._reg_clsid_
def GetBandInfo(self, dwBandId, dwViewMode, dwMask):
ptMinSize = (0,24)
ptMaxSize = (2000,24)
ptIntegral = (0,0)
ptActual = (2000,24)
wszTitle = 'PyWin32 IE Toolbar'
dwModeFlags = DBIMF_VARIABLEHEIGHT
crBkgnd = 0
return (ptMinSize,ptMaxSize,ptIntegral,ptActual,wszTitle,dwModeFlags,crBkgnd)
# used for HKLM install
def DllInstall( bInstall, cmdLine ):
comclass = IEToolbar
# register plugin
def DllRegisterServer():
comclass = IEToolbar
# register toolbar with IE
try:
print("Trying to register Toolbar.\n")
hkey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Internet Explorer\\Toolbar" )
subKey = winreg.SetValueEx( hkey, comclass._reg_clsid_, 0, winreg.REG_BINARY, "\0" )
except WindowsError:
print("Couldn't set registry value.\nhkey: %d\tCLSID: %s\n" % ( hkey, comclass._reg_clsid_ ))
else:
print("Set registry value.\nhkey: %d\tCLSID: %s\n" % ( hkey, comclass._reg_clsid_ ))
# TODO: implement reg settings for standard toolbar button
# unregister plugin
def DllUnregisterServer():
comclass = IEToolbar
# unregister toolbar from internet explorer
try:
print("Trying to unregister Toolbar.\n")
hkey = winreg.CreateKey( winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Internet Explorer\\Toolbar" )
winreg.DeleteValue( hkey, comclass._reg_clsid_ )
except WindowsError:
print("Couldn't delete registry value.\nhkey: %d\tCLSID: %s\n" % ( hkey, comclass._reg_clsid_ ))
else:
print("Deleting reg key succeeded.\n")
# entry point
if __name__ == '__main__':
import win32com.server.register
win32com.server.register.UseCommandLine( IEToolbar )
# parse actual command line option
if "--unregister" in sys.argv:
DllUnregisterServer()
else:
DllRegisterServer()
else:
# import trace utility for remote debugging
import win32traceutil

View file

@ -0,0 +1,114 @@
# A demo plugin for Microsoft Outlook (NOT Outlook Express)
#
# This addin simply adds a new button to the main Outlook toolbar,
# and displays a message box when clicked. Thus, it demonstrates
# how to plug in to Outlook itself, and hook outlook events.
#
# Additionally, each time a new message arrives in the Inbox, a message
# is printed with the subject of the message.
#
# To register the addin, simply execute:
# outlookAddin.py
# This will install the COM server, and write the necessary
# AddIn key to Outlook
#
# To unregister completely:
# outlookAddin.py --unregister
#
# To debug, execute:
# outlookAddin.py --debug
#
# Then open Pythonwin, and select "Tools->Trace Collector Debugging Tool"
# Restart Outlook, and you should see some output generated.
#
# NOTE: If the AddIn fails with an error, Outlook will re-register
# the addin to not automatically load next time Outlook starts. To
# correct this, simply re-register the addin (see above)
from win32com import universal
from win32com.server.exception import COMException
from win32com.client import gencache, DispatchWithEvents
import winerror
import pythoncom
from win32com.client import constants
import sys
# Support for COM objects we use.
gencache.EnsureModule('{00062FFF-0000-0000-C000-000000000046}', 0, 9, 0, bForDemand=True) # Outlook 9
gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1, bForDemand=True) # Office 9
# The TLB defining the interfaces we implement
universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, ["_IDTExtensibility2"])
class ButtonEvent:
def OnClick(self, button, cancel):
import win32ui # Possible, but not necessary, to use a Pythonwin GUI
win32ui.MessageBox("Hello from Python")
return cancel
class FolderEvent:
def OnItemAdd(self, item):
try:
print("An item was added to the inbox with subject:", item.Subject)
except AttributeError:
print("An item was added to the inbox, but it has no subject! - ", repr(item))
class OutlookAddin:
_com_interfaces_ = ['_IDTExtensibility2']
_public_methods_ = []
_reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER
_reg_clsid_ = "{0F47D9F3-598B-4d24-B7E3-92AC15ED27E2}"
_reg_progid_ = "Python.Test.OutlookAddin"
_reg_policy_spec_ = "win32com.server.policy.EventHandlerPolicy"
def OnConnection(self, application, connectMode, addin, custom):
print("OnConnection", application, connectMode, addin, custom)
# ActiveExplorer may be none when started without a UI (eg, WinCE synchronisation)
activeExplorer = application.ActiveExplorer()
if activeExplorer is not None:
bars = activeExplorer.CommandBars
toolbar = bars.Item("Standard")
item = toolbar.Controls.Add(Type=constants.msoControlButton, Temporary=True)
# Hook events for the item
item = self.toolbarButton = DispatchWithEvents(item, ButtonEvent)
item.Caption="Python"
item.TooltipText = "Click for Python"
item.Enabled = True
# And now, for the sake of demonstration, setup a hook for all new messages
inbox = application.Session.GetDefaultFolder(constants.olFolderInbox)
self.inboxItems = DispatchWithEvents(inbox.Items, FolderEvent)
def OnDisconnection(self, mode, custom):
print("OnDisconnection")
def OnAddInsUpdate(self, custom):
print("OnAddInsUpdate", custom)
def OnStartupComplete(self, custom):
print("OnStartupComplete", custom)
def OnBeginShutdown(self, custom):
print("OnBeginShutdown", custom)
def RegisterAddin(klass):
import winreg
key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Outlook\\Addins")
subkey = winreg.CreateKey(key, klass._reg_progid_)
winreg.SetValueEx(subkey, "CommandLineSafe", 0, winreg.REG_DWORD, 0)
winreg.SetValueEx(subkey, "LoadBehavior", 0, winreg.REG_DWORD, 3)
winreg.SetValueEx(subkey, "Description", 0, winreg.REG_SZ, klass._reg_progid_)
winreg.SetValueEx(subkey, "FriendlyName", 0, winreg.REG_SZ, klass._reg_progid_)
def UnregisterAddin(klass):
import winreg
try:
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Office\\Outlook\\Addins\\" + klass._reg_progid_)
except WindowsError:
pass
if __name__ == '__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(OutlookAddin)
if "--unregister" in sys.argv:
UnregisterAddin(OutlookAddin)
else:
RegisterAddin(OutlookAddin)

View file

@ -0,0 +1,75 @@
import pythoncom
from win32com.server import util
from win32com.server import exception
VT_EMPTY = pythoncom.VT_EMPTY
class Bag:
_public_methods_ = [ 'Read', 'Write' ]
_com_interfaces_ = [ pythoncom.IID_IPropertyBag ]
def __init__(self):
self.data = { }
def Read(self, propName, varType, errorLog):
print("read: name=", propName, "type=", varType)
if propName not in self.data:
if errorLog:
hr = 0x80070057
exc = pythoncom.com_error(0, "Bag.Read", "no such item", None, 0, hr)
errorLog.AddError(propName, exc)
raise exception.Exception(scode=hr)
return self.data[propName]
def Write(self, propName, value):
print("write: name=", propName, "value=", value)
self.data[propName] = value
class Target:
_public_methods_ = [ 'GetClassID', 'InitNew', 'Load', 'Save' ]
_com_interfaces_ = [ pythoncom.IID_IPersist,
pythoncom.IID_IPersistPropertyBag ]
def GetClassID(self):
raise exception.Exception(scode=0x80004005) # E_FAIL
def InitNew(self):
pass
def Load(self, bag, log):
print(bag.Read('prop1', VT_EMPTY, log))
print(bag.Read('prop2', VT_EMPTY, log))
try:
print(bag.Read('prop3', VT_EMPTY, log))
except exception.Exception:
pass
def Save(self, bag, clearDirty, saveAllProps):
bag.Write('prop1', 'prop1.hello')
bag.Write('prop2', 'prop2.there')
class Log:
_public_methods_ = [ 'AddError' ]
_com_interfaces_ = [ pythoncom.IID_IErrorLog ]
def AddError(self, propName, excepInfo):
print("error: propName=", propName, "error=", excepInfo)
def test():
bag = Bag()
target = Target()
log = Log()
target.Save(bag, 1, 1)
target.Load(bag, log)
comBag = util.wrap(bag, pythoncom.IID_IPropertyBag)
comTarget = util.wrap(target, pythoncom.IID_IPersistPropertyBag)
comLog = util.wrap(log, pythoncom.IID_IErrorLog)
comTarget.Save(comBag, 1, 1)
comTarget.Load(comBag, comLog)
if __name__ == '__main__':
test()

View file

@ -0,0 +1,760 @@
/* PythonCOM.h
Main header for Python COM support.
This file is involved mainly with client side COM support for
Python.
Most COM work put together by Greg Stein and Mark Hammond, with a
few others starting to come out of the closet.
--------------------------------------------------------------------
Thread State Rules
------------------
These rules apply to PythonCOM in general, and not just to
the client side.
The rules are quite simple, but it is critical they be followed.
In general, errors here will be picked up quite quickly, as Python
will raise a Fatal Error. However, the Release() issue in particular
may keep a number of problems well hidden.
Interfaces:
-----------
Before making ANY call out to COM, you MUST release the Python lock.
This is true to ANY call whatsoever, including the COM call in question,
but also any calls to "->Release();"
This is normally achieved with the calls
PY_INTERFACE_PRECALL and PY_INTERFACE_POSTCALL, which release
and acquire the Python lock.
Gateways:
---------
Before doing anything related to Python, gateways MUST acquire the
Python lock, and must release it before returning.
This is normally achieved with PY_GATEWAY_METHOD at the top of a
gateway method. This macro resolves to a class, which automatically does
the right thing.
Release:
--------
As mentioned above for Interfaces, EVERY call to Release() must be done
with the Python lock released. This is expanded here.
This is very important, but an error may not be noticed. The problem will
only be seen when the Release() is on a Python object and the Release() is the
final one for the object. In this case, the Python object will attempt to
acquire the Python lock before destroying itself, and Python will raise a
fatal error.
In many many cases, you will not notice this error, but someday, someone will
implement the other side in Python, and suddenly FatalErrors will start
appearing. Make sure you get this right.
Eg, this code is correct:
PY_INTERFACE_PRECALL;
pSomeObj->SomeFunction(pSomeOtherObject);
pSomeOtherObject->Release();
PY_INTERFACE_POSTCALL;
However, this code is WRONG, but will RARELY FAIL.
PY_INTERFACE_PRECALL;
pSomeObj->SomeFunction(pSomeOtherObject);
PY_INTERFACE_POSTCALL;
pSomeOtherObject->Release();
--------------------------------------------------------------------
*/
#ifndef __PYTHONCOM_H__
#define __PYTHONCOM_H__
// #define _DEBUG_LIFETIMES // Trace COM object lifetimes.
#ifdef FREEZE_PYTHONCOM
/* The pythoncom module is being included in a frozen .EXE/.DLL */
#define PYCOM_EXPORT
#else
#ifdef BUILD_PYTHONCOM
/* We are building pythoncomxx.dll */
#define PYCOM_EXPORT __declspec(dllexport)
#else
/* This module uses pythoncomxx.dll */
#define PYCOM_EXPORT __declspec(dllimport)
#ifndef _DEBUG
#pragma comment(lib, "pythoncom.lib")
#else
#pragma comment(lib, "pythoncom_d.lib")
#endif
#endif
#endif
#ifdef MS_WINCE
// List of interfaces not supported by CE.
#define NO_PYCOM_IDISPATCHEX
#define NO_PYCOM_IPROVIDECLASSINFO
#define NO_PYCOM_IENUMGUID
#define NO_PYCOM_IENUMCATEGORYINFO
#define NO_PYCOM_ICATINFORMATION
#define NO_PYCOM_ICATREGISTER
#define NO_PYCOM_ISERVICEPROVIDER
#define NO_PYCOM_IPROPERTYSTORAGE
#define NO_PYCOM_IPROPERTYSETSTORAGE
#define NO_PYCOM_ENUMSTATPROPSTG
#include "ocidl.h"
#include "oleauto.h"
#endif // MS_WINCE
#ifdef __MINGW32__
// Special Mingw32 considerations.
#define NO_PYCOM_ENUMSTATPROPSTG
#define __try try
#define __except catch
#include <olectl.h>
#endif // __MINGW32__
#include <PyWinTypes.h> // Standard Win32 Types
#ifndef NO_PYCOM_IDISPATCHEX
#include <dispex.h> // New header for IDispatchEx interface.
#endif // NO_PYCOM_IDISPATCHEX
#if defined(MAINWIN)
// Mainwin seems to have 1/2 the VT_RECORD infrastructure in place
#if !defined(VT_RECORD)
#define VT_RECORD 36
#define V_RECORDINFO(X) ((X)->brecVal.pRecInfo)
#define V_RECORD(X) ((X)->brecVal.pvRecord)
#else
#pragma message( \
"MAINWIN appears to have grown correct VT_RECORD " \
"support. Please update PythonCOM.h accordingly")
#endif // VT_RECORD
#endif // MAINWIN
class PyIUnknown;
// To make life interesting/complicated, I use C++ classes for
// all Python objects. The main advantage is that I can derive
// a PyIDispatch object from a PyIUnknown, etc. This provides a
// clean C++ interface, and "automatically" provides all base
// Python methods to "derived" Python types.
//
// Main disadvantage is that any extension DLLs will need to include
// these headers, and link with this .lib
//
// Base class for (most of) the type objects.
class PYCOM_EXPORT PyComTypeObject : public PyTypeObject {
public:
PyComTypeObject(const char *name, PyComTypeObject *pBaseType, int typeSize, struct PyMethodDef *methodList,
PyIUnknown *(*thector)(IUnknown *));
~PyComTypeObject();
// is the given object an interface type object? (e.g. PyIUnknown)
static BOOL is_interface_type(PyObject *ob);
public:
PyIUnknown *(*ctor)(IUnknown *);
};
// A type used for interfaces that can automatically provide enumerators
// (ie, they themselves aren't enumerable, but do have a suitable default
// method that returns a PyIEnum object
class PYCOM_EXPORT PyComEnumProviderTypeObject : public PyComTypeObject {
public:
PyComEnumProviderTypeObject(const char *name, PyComTypeObject *pBaseType, int typeSize,
struct PyMethodDef *methodList, PyIUnknown *(*thector)(IUnknown *),
const char *enum_method_name);
static PyObject *iter(PyObject *self);
const char *enum_method_name;
};
// A type used for PyIEnum interfaces
class PYCOM_EXPORT PyComEnumTypeObject : public PyComTypeObject {
public:
static PyObject *iter(PyObject *self);
static PyObject *iternext(PyObject *self);
PyComEnumTypeObject(const char *name, PyComTypeObject *pBaseType, int typeSize, struct PyMethodDef *methodList,
PyIUnknown *(*thector)(IUnknown *));
};
// Very very base class - not COM specific - Should exist in the
// Python core somewhere, IMO.
class PYCOM_EXPORT PyIBase : public PyObject {
public:
// virtuals for Python support
virtual PyObject *getattr(char *name);
virtual int setattr(char *name, PyObject *v);
virtual PyObject *repr();
virtual int compare(PyObject *other)
{
if (this == other)
return 0;
if (this < other)
return -1;
return 1;
}
// These iter are a little special, in that returning NULL means
// use the implementation in the type
virtual PyObject *iter() { return NULL; }
virtual PyObject *iternext() { return NULL; }
protected:
PyIBase();
virtual ~PyIBase();
public:
static BOOL is_object(PyObject *, PyComTypeObject *which);
BOOL is_object(PyComTypeObject *which);
static void dealloc(PyObject *ob);
static PyObject *repr(PyObject *ob);
static PyObject *getattro(PyObject *self, PyObject *name);
static int setattro(PyObject *op, PyObject *obname, PyObject *v);
static int cmp(PyObject *ob1, PyObject *ob2);
static PyObject *richcmp(PyObject *ob1, PyObject *ob2, int op);
};
/* Special Type objects */
extern PYCOM_EXPORT PyTypeObject PyOleEmptyType; // equivalent to VT_EMPTY
extern PYCOM_EXPORT PyTypeObject PyOleMissingType; // special Python handling.
extern PYCOM_EXPORT PyTypeObject PyOleArgNotFoundType; // special VT_ERROR value
extern PYCOM_EXPORT PyTypeObject PyOleNothingType; // special VT_ERROR value
// ALL of these set an appropriate Python error on bad return.
// Given a Python object that is a registered COM type, return a given
// interface pointer on its underlying object, with a new reference added.
PYCOM_EXPORT BOOL PyCom_InterfaceFromPyObject(PyObject *ob, REFIID iid, LPVOID *ppv, BOOL bNoneOK = TRUE);
// As above, but allows instance with "_oleobj_" attribute.
PYCOM_EXPORT BOOL PyCom_InterfaceFromPyInstanceOrObject(PyObject *ob, REFIID iid, LPVOID *ppv, BOOL bNoneOK = TRUE);
// Release an arbitary COM pointer.
// NOTE: the PRECALL/POSTCALL stuff is probably not strictly necessary
// since the PyGILSTATE stuff has been in place (and even then, it only
// mattered when it was the last Release() on a Python implemented object)
#define PYCOM_RELEASE(pUnk) \
{ \
if (pUnk) { \
PY_INTERFACE_PRECALL; \
(pUnk)->Release(); \
PY_INTERFACE_POSTCALL; \
} \
}
// Given an IUnknown and an Interface ID, create and return an object
// of the appropriate type. eg IID_Unknown->PyIUnknown,
// IID_IDispatch->PyIDispatch, etc.
// Uses a map that external extension DLLs can populate with their IID/type.
// Under the principal of least surprise, this will return Py_None is punk is NULL.
// Otherwise, a valid PyI*, but with NULL m_obj (and therefore totally useless)
// object would be created.
// BOOL bAddRef indicates if a COM reference count should be added to the IUnknown.
// This depends purely on the context in which it is called. If the IUnknown is obtained
// from a function that creates a new ref (eg, CoCreateInstance()) then you should use
// FALSE. If you receive the pointer as (eg) a param to a gateway function, then
// you normally need to pass TRUE, as this is truly a new reference.
// *** ALWAYS take the time to get this right. ***
PYCOM_EXPORT PyObject *PyCom_PyObjectFromIUnknown(IUnknown *punk, REFIID riid, BOOL bAddRef = FALSE);
// VARIANT <-> PyObject conversion utilities.
PYCOM_EXPORT BOOL PyCom_VariantFromPyObject(PyObject *obj, VARIANT *var);
PYCOM_EXPORT PyObject *PyCom_PyObjectFromVariant(const VARIANT *var);
// PROPVARIANT
PYCOM_EXPORT PyObject *PyObject_FromPROPVARIANT(PROPVARIANT *pVar);
PYCOM_EXPORT PyObject *PyObject_FromPROPVARIANTs(PROPVARIANT *pVars, ULONG cVars);
PYCOM_EXPORT BOOL PyObject_AsPROPVARIANT(PyObject *ob, PROPVARIANT *pVar);
// Other conversion helpers...
PYCOM_EXPORT PyObject *PyCom_PyObjectFromSTATSTG(STATSTG *pStat);
PYCOM_EXPORT BOOL PyCom_PyObjectAsSTATSTG(PyObject *ob, STATSTG *pStat, DWORD flags = 0);
PYCOM_EXPORT BOOL PyCom_SAFEARRAYFromPyObject(PyObject *obj, SAFEARRAY **ppSA, VARENUM vt = VT_VARIANT);
PYCOM_EXPORT PyObject *PyCom_PyObjectFromSAFEARRAY(SAFEARRAY *psa, VARENUM vt = VT_VARIANT);
#ifndef NO_PYCOM_STGOPTIONS
PYCOM_EXPORT BOOL PyCom_PyObjectAsSTGOPTIONS(PyObject *obstgoptions, STGOPTIONS **ppstgoptions);
#endif
PYCOM_EXPORT PyObject *PyCom_PyObjectFromSTATPROPSETSTG(STATPROPSETSTG *pStat);
PYCOM_EXPORT BOOL PyCom_PyObjectAsSTATPROPSETSTG(PyObject *, STATPROPSETSTG *);
// Currency support.
PYCOM_EXPORT PyObject *PyObject_FromCurrency(CURRENCY &cy);
PYCOM_EXPORT BOOL PyObject_AsCurrency(PyObject *ob, CURRENCY *pcy);
// OLEMENUGROUPWIDTHS are used by axcontrol, shell, etc
PYCOM_EXPORT BOOL PyObject_AsOLEMENUGROUPWIDTHS(PyObject *oblpMenuWidths, OLEMENUGROUPWIDTHS *pWidths);
PYCOM_EXPORT PyObject *PyObject_FromOLEMENUGROUPWIDTHS(const OLEMENUGROUPWIDTHS *pWidths);
/* Functions for Initializing COM, and also letting the core know about it!
*/
PYCOM_EXPORT HRESULT PyCom_CoInitializeEx(LPVOID reserved, DWORD dwInit);
PYCOM_EXPORT HRESULT PyCom_CoInitialize(LPVOID reserved);
PYCOM_EXPORT void PyCom_CoUninitialize();
///////////////////////////////////////////////////////////////////
// Error related functions
// Client related functions - generally called by interfaces before
// they return NULL back to Python to indicate the error.
// All these functions return NULL so interfaces can generally
// just "return PyCom_BuildPyException(hr, punk, IID_IWhatever)"
// Uses the HRESULT, and IErrorInfo interfaces if available to
// create and set a pythoncom.com_error.
PYCOM_EXPORT PyObject *PyCom_BuildPyException(HRESULT hr, IUnknown *pUnk = NULL, REFIID iid = IID_NULL);
// Uses the HRESULT and an EXCEPINFO structure to create and
// set a pythoncom.com_error.
PYCOM_EXPORT PyObject *PyCom_BuildPyExceptionFromEXCEPINFO(HRESULT hr, EXCEPINFO *pexcepInfo, UINT nArgErr = (UINT)-1);
// Sets a pythoncom.internal_error - no one should ever see these!
PYCOM_EXPORT PyObject *PyCom_BuildInternalPyException(char *msg);
// Log an error to a Python logger object if one can be found, or
// to stderr if no log available.
// If logProvider is not NULL, we will call a "_GetLogger_()" method on it.
// If logProvider is NULL, we attempt to fetch "win32com.logger".
// If they do not exist, return None, or raise an error fetching them
// (or even writing to them once fetched), the message still goes to stderr.
// NOTE: By default, win32com does *not* provide a logger, so default is that
// all errors are written to stdout.
// This will *not* write a record if a COM Server error is current.
PYCOM_EXPORT void PyCom_LoggerNonServerException(PyObject *logProvider, const char *fmt, ...);
// Write an error record, including exception. This will write an error
// record even if a COM server error is current.
PYCOM_EXPORT void PyCom_LoggerException(PyObject *logProvider, const char *fmt, ...);
// Write a warning record - in general this does *not* mean a call failed, but
// still is something in the programmers control that they should change.
// XXX - if an exception is pending when this is called, the traceback will
// also be written. This is undesirable and will be changed should this
// start being a problem.
PYCOM_EXPORT void PyCom_LoggerWarning(PyObject *logProvider, const char *fmt, ...);
// Server related error functions
// These are supplied so that any Python errors we detect can be
// converted into COM error information. The HRESULT returned should
// be returned by the COM function, and these functions also set the
// IErrorInfo interfaces, so the caller can extract more detailed
// information about the Python exception.
// Set a COM exception, logging the exception if not an explicitly raised 'server' exception
PYCOM_EXPORT HRESULT PyCom_SetAndLogCOMErrorFromPyException(const char *methodName, REFIID riid /* = IID_NULL */);
PYCOM_EXPORT HRESULT PyCom_SetAndLogCOMErrorFromPyExceptionEx(PyObject *provider, const char *methodName,
REFIID riid /* = IID_NULL */);
// Used in gateways to SetErrorInfo() with a simple HRESULT, then return it.
// The description is generally only useful for debugging purposes,
// and if you are debugging via a server that supports IErrorInfo (like Python :-)
// NOTE: this function is usuable from outside the Python context
PYCOM_EXPORT HRESULT PyCom_SetCOMErrorFromSimple(HRESULT hr, REFIID riid = IID_NULL, const char *description = NULL);
// Used in gateways to SetErrorInfo() the current Python exception, and
// (assuming not a server error explicitly raised) also logs an error
// to stdout/win32com.logger.
// NOTE: this function assumes GIL held
PYCOM_EXPORT HRESULT PyCom_SetCOMErrorFromPyException(REFIID riid = IID_NULL);
// A couple of EXCEPINFO helpers - could be private to IDispatch
// if it wasnt for the AXScript support (and ITypeInfo if we get around to that :-)
// These functions do not set any error states to either Python or
// COM - they simply convert to/from PyObjects and EXCEPINFOs
// Use the current Python exception to fill an EXCEPINFO structure.
PYCOM_EXPORT void PyCom_ExcepInfoFromPyException(EXCEPINFO *pExcepInfo);
// Fill in an EXCEPINFO structure from a Python instance or tuple object.
// (ie, similar to the above, except the Python exception object is specified,
// rather than using the "current"
PYCOM_EXPORT BOOL PyCom_ExcepInfoFromPyObject(PyObject *obExcepInfo, EXCEPINFO *pexcepInfo, HRESULT *phresult = NULL);
// Create a Python object holding the exception information. The exception
// information is *not* freed by this function. Python exceptions are
// raised and NULL is returned if an error occurs.
PYCOM_EXPORT PyObject *PyCom_PyObjectFromExcepInfo(const EXCEPINFO *pexcepInfo);
///////////////////////////////////////////////////////////////////
//
// External C++ helpers - these helpers are for other DLLs which
// may need similar functionality, but dont want to duplicate all
// This helper is for an application that has an IDispatch, and COM arguments
// and wants to call a Python function. It is assumed the caller can map the IDispatch
// to a Python object, so the Python handler is passed.
// Args:
// handler : A Python callable object.
// dispparms : the COM arguments.
// pVarResult : The variant for the return value of the Python call.
// pexcepinfo : Exception info the helper may fill out.
// puArgErr : Argument error the helper may fill out on exception
// addnArgs : Any additional arguments to the Python function. May be NULL.
// If addnArgs is NULL, then it is assumed the Python call should be native -
// ie, the COM args are packed as normal Python args to the call.
// If addnArgs is NOT NULL, it is assumed the Python function itself is
// a helper. This Python function will be called with 2 arguments - both
// tuples - first one is the COM args, second is the addn args.
PYCOM_EXPORT BOOL PyCom_MakeOlePythonCall(PyObject *handler, DISPPARAMS FAR *params, VARIANT FAR *pVarResult,
EXCEPINFO FAR *pexcepinfo, UINT FAR *puArgErr, PyObject *addnlArgs);
/////////////////////////////////////////////////////////////////////////////
// Various special purpose singletons
class PYCOM_EXPORT PyOleEmpty : public PyObject {
public:
PyOleEmpty();
};
class PYCOM_EXPORT PyOleMissing : public PyObject {
public:
PyOleMissing();
};
class PYCOM_EXPORT PyOleArgNotFound : public PyObject {
public:
PyOleArgNotFound();
};
class PYCOM_EXPORT PyOleNothing : public PyObject {
public:
PyOleNothing();
};
// We need to dynamically create C++ Python objects
// These helpers allow each type object to create it.
#define MAKE_PYCOM_CTOR(classname) \
static PyIUnknown *classname::PyObConstruct(IUnknown *pInitObj) { return new classname(pInitObj); }
#define MAKE_PYCOM_CTOR_ERRORINFO(classname, iid) \
static PyIUnknown *classname::PyObConstruct(IUnknown *pInitObj) { return new classname(pInitObj); } \
static PyObject *SetPythonCOMError(PyObject *self, HRESULT hr) \
{ \
return PyCom_BuildPyException(hr, GetI(self), iid); \
}
#define GET_PYCOM_CTOR(classname) classname::PyObConstruct
// Macros that interfaces should use. PY_INTERFACE_METHOD at the top of the method
// The other 2 wrap directly around the underlying method call.
#define PY_INTERFACE_METHOD
// Identical to Py_BEGIN_ALLOW_THREADS except no { !!!
#define PY_INTERFACE_PRECALL PyThreadState *_save = PyEval_SaveThread();
#define PY_INTERFACE_POSTCALL PyEval_RestoreThread(_save);
/////////////////////////////////////////////////////////////////////////////
// class PyIUnknown
class PYCOM_EXPORT PyIUnknown : public PyIBase {
public:
MAKE_PYCOM_CTOR(PyIUnknown);
virtual PyObject *repr();
virtual int compare(PyObject *other);
static IUnknown *GetI(PyObject *self);
IUnknown *m_obj;
static char *szErrMsgObjectReleased;
static void SafeRelease(PyIUnknown *ob);
static PyComTypeObject type;
// The Python methods
static PyObject *QueryInterface(PyObject *self, PyObject *args);
static PyObject *SafeRelease(PyObject *self, PyObject *args);
protected:
PyIUnknown(IUnknown *punk);
~PyIUnknown();
};
/////////////////////////////////////////////////////////////////////////////
// class PyIDispatch
class PYCOM_EXPORT PyIDispatch : public PyIUnknown {
public:
MAKE_PYCOM_CTOR(PyIDispatch);
static IDispatch *GetI(PyObject *self);
static PyComTypeObject type;
// The Python methods
static PyObject *Invoke(PyObject *self, PyObject *args);
static PyObject *InvokeTypes(PyObject *self, PyObject *args);
static PyObject *GetIDsOfNames(PyObject *self, PyObject *args);
static PyObject *GetTypeInfo(PyObject *self, PyObject *args);
static PyObject *GetTypeInfoCount(PyObject *self, PyObject *args);
protected:
PyIDispatch(IUnknown *pdisp);
~PyIDispatch();
};
#ifndef NO_PYCOM_IDISPATCHEX
/////////////////////////////////////////////////////////////////////////////
// class PyIDispatchEx
class PYCOM_EXPORT PyIDispatchEx : public PyIDispatch {
public:
MAKE_PYCOM_CTOR_ERRORINFO(PyIDispatchEx, IID_IDispatchEx);
static IDispatchEx *GetI(PyObject *self);
static PyComTypeObject type;
// The Python methods
static PyObject *GetDispID(PyObject *self, PyObject *args);
static PyObject *InvokeEx(PyObject *self, PyObject *args);
static PyObject *DeleteMemberByName(PyObject *self, PyObject *args);
static PyObject *DeleteMemberByDispID(PyObject *self, PyObject *args);
static PyObject *GetMemberProperties(PyObject *self, PyObject *args);
static PyObject *GetMemberName(PyObject *self, PyObject *args);
static PyObject *GetNextDispID(PyObject *self, PyObject *args);
protected:
PyIDispatchEx(IUnknown *pdisp);
~PyIDispatchEx();
};
#endif // NO_PYCOM_IDISPATCHEX
/////////////////////////////////////////////////////////////////////////////
// class PyIClassFactory
class PYCOM_EXPORT PyIClassFactory : public PyIUnknown {
public:
MAKE_PYCOM_CTOR(PyIClassFactory);
static IClassFactory *GetI(PyObject *self);
static PyComTypeObject type;
// The Python methods
static PyObject *CreateInstance(PyObject *self, PyObject *args);
static PyObject *LockServer(PyObject *self, PyObject *args);
protected:
PyIClassFactory(IUnknown *pdisp);
~PyIClassFactory();
};
#ifndef NO_PYCOM_IPROVIDECLASSINFO
/////////////////////////////////////////////////////////////////////////////
// class PyIProvideTypeInfo
class PYCOM_EXPORT PyIProvideClassInfo : public PyIUnknown {
public:
MAKE_PYCOM_CTOR(PyIProvideClassInfo);
static IProvideClassInfo *GetI(PyObject *self);
static PyComTypeObject type;
// The Python methods
static PyObject *GetClassInfo(PyObject *self, PyObject *args);
protected:
PyIProvideClassInfo(IUnknown *pdisp);
~PyIProvideClassInfo();
};
class PYCOM_EXPORT PyIProvideClassInfo2 : public PyIProvideClassInfo {
public:
MAKE_PYCOM_CTOR(PyIProvideClassInfo2);
static IProvideClassInfo2 *GetI(PyObject *self);
static PyComTypeObject type;
// The Python methods
static PyObject *GetGUID(PyObject *self, PyObject *args);
protected:
PyIProvideClassInfo2(IUnknown *pdisp);
~PyIProvideClassInfo2();
};
#endif // NO_PYCOM_IPROVIDECLASSINFO
/////////////////////////////////////////////////////////////////////////////
// class PyITypeInfo
class PYCOM_EXPORT PyITypeInfo : public PyIUnknown {
public:
MAKE_PYCOM_CTOR(PyITypeInfo);
static PyComTypeObject type;
static ITypeInfo *GetI(PyObject *self);
PyObject *GetContainingTypeLib();
PyObject *GetDocumentation(MEMBERID);
PyObject *GetRefTypeInfo(HREFTYPE href);
PyObject *GetRefTypeOfImplType(int index);
PyObject *GetFuncDesc(int pos);
PyObject *GetIDsOfNames(OLECHAR FAR *FAR *, int);
PyObject *GetNames(MEMBERID);
PyObject *GetTypeAttr();
PyObject *GetVarDesc(int pos);
PyObject *GetImplTypeFlags(int index);
PyObject *GetTypeComp();
protected:
PyITypeInfo(IUnknown *);
~PyITypeInfo();
};
/////////////////////////////////////////////////////////////////////////////
// class PyITypeComp
class PYCOM_EXPORT PyITypeComp : public PyIUnknown {
public:
MAKE_PYCOM_CTOR(PyITypeComp);
static PyComTypeObject type;
static ITypeComp *GetI(PyObject *self);
PyObject *Bind(OLECHAR *szName, unsigned short wflags);
PyObject *BindType(OLECHAR *szName);
protected:
PyITypeComp(IUnknown *);
~PyITypeComp();
};
/////////////////////////////////////////////////////////////////////////////
// class CPyTypeLib
class PYCOM_EXPORT PyITypeLib : public PyIUnknown {
public:
MAKE_PYCOM_CTOR(PyITypeLib);
static PyComTypeObject type;
static ITypeLib *GetI(PyObject *self);
PyObject *GetLibAttr();
PyObject *GetDocumentation(int pos);
PyObject *GetTypeInfo(int pos);
PyObject *GetTypeInfoCount();
PyObject *GetTypeInfoOfGuid(REFGUID guid);
PyObject *GetTypeInfoType(int pos);
PyObject *GetTypeComp();
protected:
PyITypeLib(IUnknown *);
~PyITypeLib();
};
/////////////////////////////////////////////////////////////////////////////
// class PyIConnectionPoint
class PYCOM_EXPORT PyIConnectionPoint : public PyIUnknown {
public:
MAKE_PYCOM_CTOR_ERRORINFO(PyIConnectionPoint, IID_IConnectionPoint);
static PyComTypeObject type;
static IConnectionPoint *GetI(PyObject *self);
static PyObject *GetConnectionInterface(PyObject *self, PyObject *args);
static PyObject *GetConnectionPointContainer(PyObject *self, PyObject *args);
static PyObject *Advise(PyObject *self, PyObject *args);
static PyObject *Unadvise(PyObject *self, PyObject *args);
static PyObject *EnumConnections(PyObject *self, PyObject *args);
protected:
PyIConnectionPoint(IUnknown *);
~PyIConnectionPoint();
};
class PYCOM_EXPORT PyIConnectionPointContainer : public PyIUnknown {
public:
MAKE_PYCOM_CTOR_ERRORINFO(PyIConnectionPointContainer, IID_IConnectionPointContainer);
static PyComTypeObject type;
static IConnectionPointContainer *GetI(PyObject *self);
static PyObject *EnumConnectionPoints(PyObject *self, PyObject *args);
static PyObject *FindConnectionPoint(PyObject *self, PyObject *args);
protected:
PyIConnectionPointContainer(IUnknown *);
~PyIConnectionPointContainer();
};
/////////////////////////////////////////////////////////////////////////////
// class PythonOleArgHelper
//
// A PythonOleArgHelper is used primarily to help out Python helpers
// which need to convert from a Python object when the specific OLE
// type is known - eg, when a TypeInfo is available.
//
// The type of conversion determines who owns what buffers etc. I wish BYREF didnt exist :-)
typedef enum {
// We dont know what sort of conversion it is yet.
POAH_CONVERT_UNKNOWN,
// A PyObject is given, we convert to a VARIANT, make the COM call, then BYREFs back to a PyObject
// ie, this is typically a "normal" COM call, where Python initiates the call
POAH_CONVERT_FROM_PYOBJECT,
// A VARIANT is given, we convert to a PyObject, make the Python call, then BYREFs back to a VARIANT.
// ie, this is typically handling a COM event, where COM itself initiates the call.
POAH_CONVERT_FROM_VARIANT,
} POAH_CONVERT_DIRECTION;
class PYCOM_EXPORT PythonOleArgHelper {
public:
PythonOleArgHelper();
~PythonOleArgHelper();
BOOL ParseTypeInformation(PyObject *reqdObjectTuple);
// Using this call with reqdObject != NULL will check the existing
// VT_ of the variant. If not VT_EMPTY, then the result will be coerced to
// that type. This contrasts with PyCom_PyObjectToVariant which just
// uses the Python type to determine the variant type.
BOOL MakeObjToVariant(PyObject *obj, VARIANT *var, PyObject *reqdObjectTuple = NULL);
PyObject *MakeVariantToObj(VARIANT *var);
VARTYPE m_reqdType;
BOOL m_bParsedTypeInfo;
BOOL m_bIsOut;
POAH_CONVERT_DIRECTION m_convertDirection;
PyObject *m_pyVariant; // if non-null, a win32com.client.VARIANT
union {
void *m_pValueHolder;
short m_sBuf;
long m_lBuf;
LONGLONG m_llBuf;
VARIANT_BOOL m_boolBuf;
double m_dBuf;
float m_fBuf;
IDispatch *m_dispBuf;
IUnknown *m_unkBuf;
SAFEARRAY *m_arrayBuf;
VARIANT *m_varBuf;
DATE m_dateBuf;
CY m_cyBuf;
};
};
/////////////////////////////////////////////////////////////////////////////
// global functions and variables
PYCOM_EXPORT BOOL MakePythonArgumentTuples(PyObject **pArgs, PythonOleArgHelper **ppHelpers, PyObject **pNamedArgs,
PythonOleArgHelper **ppNamedHelpers, DISPPARAMS FAR *params);
// Convert a Python object to a BSTR - allow embedded NULLs, None, etc.
PYCOM_EXPORT BOOL PyCom_BstrFromPyObject(PyObject *stringObject, BSTR *pResult, BOOL bNoneOK = FALSE);
// MakeBstrToObj - convert a BSTR into a Python string.
//
// ONLY USE THIS FOR TRUE BSTR's - Use the fn below for OLECHAR *'s.
// NOTE - does not use standard macros, so NULLs get through!
PYCOM_EXPORT PyObject *MakeBstrToObj(const BSTR bstr);
// Size info is available (eg, a fn returns a string and also fills in a size variable)
PYCOM_EXPORT PyObject *MakeOLECHARToObj(const OLECHAR *str, int numChars);
// No size info avail.
PYCOM_EXPORT PyObject *MakeOLECHARToObj(const OLECHAR *str);
PYCOM_EXPORT void PyCom_LogF(const char *fmt, ...);
// Generic conversion from python sequence to VT_VECTOR array
// Resulting array must be freed with CoTaskMemFree
template <typename arraytype>
BOOL SeqToVector(PyObject *ob, arraytype **pA, ULONG *pcount, BOOL (*converter)(PyObject *, arraytype *))
{
TmpPyObject seq = PyWinSequence_Tuple(ob, pcount);
if (seq == NULL)
return FALSE;
*pA = (arraytype *)CoTaskMemAlloc(*pcount * sizeof(arraytype));
if (*pA == NULL) {
PyErr_NoMemory();
return FALSE;
}
for (ULONG i = 0; i < *pcount; i++) {
PyObject *item = PyTuple_GET_ITEM((PyObject *)seq, i);
if (!(*converter)(item, &(*pA)[i]))
return FALSE;
}
return TRUE;
}
#endif // __PYTHONCOM_H__

View file

@ -0,0 +1,84 @@
// Support for PythonCOM and its extensions to register the interfaces,
// gateways and IIDs it supports.
//
// The module can simply declare an array of type PyCom_InterfaceSupportInfo, then
// use the macros to populate it.
//
// See Register.cpp and AXScript.cpp for examples on its use.
#ifndef __PYTHONCOMREGISTER_H__
#define __PYTHONCOMREGISTER_H__
#include "PythonCOMServer.h" // Need defns in this file...
typedef struct {
const GUID *pGUID; // The supported IID - required
const char *interfaceName; // Name of the interface - required
const char *iidName; // Name of the IID that goes into the dict. - required
PyTypeObject *pTypeOb; // the type object for client PyI* side - NULL for server only support.
pfnPyGatewayConstructor ctor; // Gateway (PyG*) interface constructor - NULL for client only support
} PyCom_InterfaceSupportInfo;
#define PYCOM_INTERFACE_IID_ONLY(ifc) \
{ \
&IID_I##ifc, "I" #ifc, "IID_I" #ifc, NULL, NULL \
}
#define PYCOM_INTERFACE_CLSID_ONLY(ifc) \
{ \
&CLSID_##ifc, "CLSID_" #ifc, "CLSID_" #ifc, NULL, NULL \
}
#define PYCOM_INTERFACE_CATID_ONLY(ifc) \
{ \
&CATID_##ifc, "CATID_" #ifc, "CATID_" #ifc, NULL, NULL \
}
#define PYCOM_INTERFACE_CLIENT_ONLY(ifc) \
{ \
&IID_I##ifc, "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, NULL \
}
#define PYCOM_INTERFACE_SERVER_ONLY(ifc) \
{ \
&IID_I##ifc, "I" #ifc, "IID_I" #ifc, NULL, GET_PYGATEWAY_CTOR(PyG##ifc) \
}
#define PYCOM_INTERFACE_FULL(ifc) \
{ \
&IID_I##ifc, "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, GET_PYGATEWAY_CTOR(PyG##ifc) \
}
// Versions that use __uuidof() to get the IID, which seems to avoid the need
// to link with a lib holding the IIDs. Note that almost all extensions
// build with __uuidof() being the default; the build failed at 'shell' - so
// we could consider making this the default and making the 'explicit' version
// above the special case.
#define PYCOM_INTERFACE_IID_ONLY_UUIDOF(ifc) \
{ \
&__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, NULL, NULL \
}
#define PYCOM_INTERFACE_CLIENT_ONLY_UUIDOF(ifc) \
{ \
&__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, NULL \
}
#define PYCOM_INTERFACE_SERVER_ONLY_UUIDOF(ifc) \
{ \
&__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, NULL, GET_PYGATEWAY_CTOR(PyG##ifc) \
}
#define PYCOM_INTERFACE_FULL_UUIDOF(ifc) \
{ \
&__uuidof(I##ifc), "I" #ifc, "IID_I" #ifc, &PyI##ifc::type, GET_PYGATEWAY_CTOR(PyG##ifc) \
}
// Prototypes for the register functions
// Register a PythonCOM extension module
PYCOM_EXPORT int PyCom_RegisterExtensionSupport(PyObject *dict, const PyCom_InterfaceSupportInfo *pInterfaces,
int numEntries);
// THESE SHOULD NO LONGER BE USED. Instead, use the functions above passing an
// array of PyCom_InterfaceSupportInfo objects.
PYCOM_EXPORT int PyCom_RegisterClientType(PyTypeObject *typeOb, const GUID *guid);
HRESULT PYCOM_EXPORT PyCom_RegisterGatewayObject(REFIID iid, pfnPyGatewayConstructor ctor, const char *interfaceName);
PYCOM_EXPORT int PyCom_IsGatewayRegistered(REFIID iid);
#endif /* __PYTHONCOMREGISTER_H__ */

View file

@ -0,0 +1,176 @@
#ifndef __PYTHONCOMSERVER_H__
#define __PYTHONCOMSERVER_H__
// PythonCOMServer.h :Server side COM support
#include <Python.h>
#define DLLAcquireGlobalLock PyWin_AcquireGlobalLock
#define DLLReleaseGlobalLock PyWin_ReleaseGlobalLock
void PYCOM_EXPORT PyCom_DLLAddRef(void);
void PYCOM_EXPORT PyCom_DLLReleaseRef(void);
// Use this macro at the start of all gateway methods.
#define PY_GATEWAY_METHOD CEnterLeavePython _celp
class PyGatewayBase;
// Gateway constructors.
// Each gateway must be able to be created from a "gateway constructor". This
// is simply a function that takes a Python instance as as argument, and returns
// a gateway object of the correct type. The MAKE_PYGATEWAY_CTOR is a helper that
// will embed such a constructor in the class - however, this is not necessary -
// _any_ function of the correct signature can be used.
typedef HRESULT (*pfnPyGatewayConstructor)(PyObject *PythonInstance, PyGatewayBase *, void **ppResult, REFIID iid);
HRESULT PyCom_MakeRegisteredGatewayObject(REFIID iid, PyObject *instance, PyGatewayBase *base, void **ppv);
// A version of the above which support classes being derived from
// other than IUnknown
#define PYGATEWAY_MAKE_SUPPORT2(classname, IInterface, theIID, gatewaybaseclass) \
public: \
static HRESULT classname::PyGatewayConstruct(PyObject *pPyInstance, PyGatewayBase *unkBase, void **ppResult, \
REFIID iid) \
{ \
if (ppResult == NULL) \
return E_INVALIDARG; \
classname *newob = new classname(pPyInstance); \
newob->m_pBaseObject = unkBase; \
if (unkBase) \
unkBase->AddRef(); \
*ppResult = newob->ThisAsIID(iid); \
return *ppResult ? S_OK : E_OUTOFMEMORY; \
} \
\
protected: \
virtual IID GetIID(void) { return theIID; } \
virtual void *ThisAsIID(IID iid) \
{ \
if (this == NULL) \
return NULL; \
if (iid == theIID) \
return (IInterface *)this; \
else \
return gatewaybaseclass::ThisAsIID(iid); \
} \
STDMETHOD_(ULONG, AddRef)(void) { return gatewaybaseclass::AddRef(); } \
STDMETHOD_(ULONG, Release)(void) { return gatewaybaseclass::Release(); } \
STDMETHOD(QueryInterface)(REFIID iid, void **obj) { return gatewaybaseclass::QueryInterface(iid, obj); };
// This is the "old" version to use, or use it if you derive
// directly from PyGatewayBase
#define PYGATEWAY_MAKE_SUPPORT(classname, IInterface, theIID) \
PYGATEWAY_MAKE_SUPPORT2(classname, IInterface, theIID, PyGatewayBase)
#define GET_PYGATEWAY_CTOR(classname) classname::PyGatewayConstruct
#ifdef _MSC_VER
// Disable an OK warning...
#pragma warning(disable : 4275)
// warning C4275: non dll-interface struct 'IDispatch' used as base for dll-interface class 'PyGatewayBase'
#endif // _MSC_VER
// Helper interface for fetching a Python object from a gateway
extern const GUID IID_IInternalUnwrapPythonObject;
interface IInternalUnwrapPythonObject : public IUnknown
{
public:
STDMETHOD(Unwrap)(PyObject * *ppPyObject) = 0;
};
/////////////////////////////////////////////////////////////////////////////
// PyGatewayBase
//
// Base class for all gateways.
//
class PYCOM_EXPORT PyGatewayBase :
#ifndef NO_PYCOM_IDISPATCHEX
public IDispatchEx, // IDispatch comes along for the ride!
#else
public IDispatch, // No IDispatchEx - must explicitely use IDispatch
#endif
public ISupportErrorInfo,
public IInternalUnwrapPythonObject {
protected:
PyGatewayBase(PyObject *instance);
virtual ~PyGatewayBase();
// Invoke the Python method (via the policy object)
STDMETHOD(InvokeViaPolicy)(const char *szMethodName, PyObject **ppResult = NULL, const char *szFormat = NULL, ...);
public:
// IUnknown
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
STDMETHOD(QueryInterface)(REFIID iid, void **obj);
// IDispatch
STDMETHOD(GetTypeInfoCount)(UINT FAR *pctInfo);
STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo FAR *FAR *pptInfo);
STDMETHOD(GetIDsOfNames)(REFIID refiid, OLECHAR FAR *FAR *rgszNames, UINT cNames, LCID lcid, DISPID FAR *rgdispid);
STDMETHOD(Invoke)
(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *params, VARIANT FAR *pVarResult,
EXCEPINFO FAR *pexcepinfo, UINT FAR *puArgErr);
// IDispatchEx
#ifndef NO_PYCOM_IDISPATCHEX
STDMETHOD(GetDispID)(BSTR bstrName, DWORD grfdex, DISPID *pid);
STDMETHOD(InvokeEx)
(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller);
STDMETHOD(DeleteMemberByName)(BSTR bstr, DWORD grfdex);
STDMETHOD(DeleteMemberByDispID)(DISPID id);
STDMETHOD(GetMemberProperties)(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex);
STDMETHOD(GetMemberName)(DISPID id, BSTR *pbstrName);
STDMETHOD(GetNextDispID)(DWORD grfdex, DISPID id, DISPID *pid);
STDMETHOD(GetNameSpaceParent)(IUnknown **ppunk);
#endif // NO_PYCOM_IDISPATCHEX
// ISupportErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
// IInternalUnwrapPythonObject
STDMETHOD(Unwrap)(PyObject **ppPyObject);
// Basically just PYGATEWAY_MAKE_SUPPORT(PyGatewayBase, IDispatch, IID_IDispatch);
// but with special handling as its the base class.
static HRESULT PyGatewayBase::PyGatewayConstruct(PyObject *pPyInstance, PyGatewayBase *gatewayBase, void **ppResult,
REFIID iid)
{
if (ppResult == NULL)
return E_INVALIDARG;
PyGatewayBase *obNew = new PyGatewayBase(pPyInstance);
obNew->m_pBaseObject = gatewayBase;
if (gatewayBase)
gatewayBase->AddRef();
*ppResult = (IDispatch *)obNew;
return *ppResult ? S_OK : E_OUTOFMEMORY;
}
// Currently this is used only for ISupportErrorInfo,
// so hopefully this will never be called in this base class.
// (however, this is not a rule, so we wont assert or anything!)
virtual IID GetIID(void) { return IID_IUnknown; }
virtual void *ThisAsIID(IID iid);
// End of PYGATEWAY_MAKE_SUPPORT
PyObject *m_pPyObject;
PyGatewayBase *m_pBaseObject;
private:
LONG m_cRef;
};
#ifdef _MSC_VER
#pragma warning(default : 4275)
#endif // _MSC_VER
// B/W compat hack for gateways.
#define PyCom_HandlePythonFailureToCOM() \
PyCom_SetAndLogCOMErrorFromPyExceptionEx(this->m_pPyObject, "<unknown>", GetIID())
// F/W compat hack for gateways! Must be careful about updating
// PyGatewayBase vtable, so a slightly older pythoncomXX.dll will work
// with slightly later extensions. So use a #define.
#define MAKE_PYCOM_GATEWAY_FAILURE_CODE(method_name) \
PyCom_SetAndLogCOMErrorFromPyExceptionEx(this->m_pPyObject, method_name, GetIID())
#endif /* __PYTHONCOMSERVER_H__ */

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1 @@
# indicates a python package.

View file

@ -0,0 +1,451 @@
"""Utility functions for writing out gateway C++ files
This module will generate a C++/Python binding for a specific COM
interface.
At this stage, no command line interface exists. You must start Python,
import this module, change to the directory where the generated code should
be written, and run the public function.
This module is capable of generating both 'Interfaces' (ie, Python
client side support for the interface) and 'Gateways' (ie, Python
server side support for the interface). Many COM interfaces are useful
both as Client and Server. Other interfaces, however, really only make
sense to implement one side or the other. For example, it would be pointless
for Python to implement Server side for 'IRunningObjectTable', unless we were
implementing core COM for an operating system in Python (hey - now there's an idea!)
Most COM interface code is totally boiler-plate - it consists of
converting arguments, dispatching the call to Python, and processing
any result values.
This module automates the generation of such code. It has the ability to
parse a .H file generated by the MIDL tool (ie, almost all COM .h files)
and build almost totally complete C++ code.
The module understands some of the well known data types, and how to
convert them. There are only a couple of places where hand-editing is
necessary, as detailed below:
unsupported types -- If a type is not known, the generator will
pretty much ignore it, but write a comment to the generated code. You
may want to add custom support for this type. In some cases, C++ compile errors
will result. These are intentional - generating code to remove these errors would
imply a false sense of security that the generator has done the right thing.
other return policies -- By default, Python never sees the return SCODE from
a COM function. The interface usually returns None if OK, else a COM exception
if "FAILED(scode)" is TRUE. You may need to change this if:
* EXCEPINFO is passed to the COM function. This is not detected and handled
* For some reason Python should always see the result SCODE, even if it
did fail or succeed. For example, some functions return a BOOLEAN result
in the SCODE, meaning Python should always see it.
* FAILED(scode) for the interface still has valid data to return (by default,
the code generated does not process the return values, and raise an exception
to Python/COM
"""
import re
from . import makegwparse
def make_framework_support(header_file_name, interface_name, bMakeInterface = 1, bMakeGateway = 1):
"""Generate C++ code for a Python Interface and Gateway
header_file_name -- The full path to the .H file which defines the interface.
interface_name -- The name of the interface to search for, and to generate.
bMakeInterface = 1 -- Should interface (ie, client) support be generated.
bMakeGatewayInterface = 1 -- Should gateway (ie, server) support be generated.
This method will write a .cpp and .h file into the current directory,
(using the name of the interface to build the file name.
"""
fin=open(header_file_name)
try:
interface = makegwparse.parse_interface_info(interface_name, fin)
finally:
fin.close()
if bMakeInterface and bMakeGateway:
desc = "Interface and Gateway"
elif bMakeInterface and not bMakeGateway:
desc = "Interface"
else:
desc = "Gateway"
if interface.name[:5]=="IEnum": # IEnum - use my really simple template-based one
import win32com.makegw.makegwenum
ifc_cpp_writer = win32com.makegw.makegwenum._write_enumifc_cpp
gw_cpp_writer = win32com.makegw.makegwenum._write_enumgw_cpp
else: # Use my harder working ones.
ifc_cpp_writer = _write_ifc_cpp
gw_cpp_writer = _write_gw_cpp
fout=open("Py%s.cpp" % interface.name, "w")
try:
fout.write(\
'''\
// This file implements the %s %s for Python.
// Generated by makegw.py
#include "shell_pch.h"
''' % (interface.name, desc))
# if bMakeGateway:
# fout.write('#include "PythonCOMServer.h"\n')
# if interface.base not in ["IUnknown", "IDispatch"]:
# fout.write('#include "Py%s.h"\n' % interface.base)
fout.write('#include "Py%s.h"\n\n// @doc - This file contains autoduck documentation\n' % interface.name)
if bMakeInterface: ifc_cpp_writer(fout, interface)
if bMakeGateway: gw_cpp_writer(fout, interface)
finally:
fout.close()
fout=open("Py%s.h" % interface.name, "w")
try:
fout.write(\
'''\
// This file declares the %s %s for Python.
// Generated by makegw.py
''' % (interface.name, desc))
if bMakeInterface: _write_ifc_h(fout, interface)
if bMakeGateway: _write_gw_h(fout, interface)
finally:
fout.close()
###########################################################################
#
# INTERNAL FUNCTIONS
#
#
def _write_ifc_h(f, interface):
f.write(\
'''\
// ---------------------------------------------------
//
// Interface Declaration
class Py%s : public Py%s
{
public:
MAKE_PYCOM_CTOR(Py%s);
static %s *GetI(PyObject *self);
static PyComTypeObject type;
// The Python methods
''' % (interface.name, interface.base, interface.name, interface.name))
for method in interface.methods:
f.write('\tstatic PyObject *%s(PyObject *self, PyObject *args);\n' % method.name)
f.write(\
'''\
protected:
Py%s(IUnknown *pdisp);
~Py%s();
};
''' % (interface.name, interface.name))
def _write_ifc_cpp(f, interface):
name = interface.name
f.write(\
'''\
// ---------------------------------------------------
//
// Interface Implementation
Py%(name)s::Py%(name)s(IUnknown *pdisp):
Py%(base)s(pdisp)
{
ob_type = &type;
}
Py%(name)s::~Py%(name)s()
{
}
/* static */ %(name)s *Py%(name)s::GetI(PyObject *self)
{
return (%(name)s *)Py%(base)s::GetI(self);
}
''' % (interface.__dict__))
ptr = re.sub('[a-z]', '', interface.name)
strdict = {'interfacename':interface.name, 'ptr': ptr}
for method in interface.methods:
strdict['method'] = method.name
f.write(\
'''\
// @pymethod |Py%(interfacename)s|%(method)s|Description of %(method)s.
PyObject *Py%(interfacename)s::%(method)s(PyObject *self, PyObject *args)
{
%(interfacename)s *p%(ptr)s = GetI(self);
if ( p%(ptr)s == NULL )
return NULL;
''' % strdict)
argsParseTuple = argsCOM = formatChars = codePost = \
codePobjects = codeCobjects = cleanup = cleanup_gil = ""
needConversion = 0
# if method.name=="Stat": import win32dbg;win32dbg.brk()
for arg in method.args:
try:
argCvt = makegwparse.make_arg_converter(arg)
if arg.HasAttribute("in"):
val = argCvt.GetFormatChar()
if val:
f.write ('\t' + argCvt.GetAutoduckString() + "\n")
formatChars = formatChars + val
argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg()
codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter()
codePost = codePost + argCvt.GetParsePostCode()
needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
cleanup = cleanup + argCvt.GetInterfaceArgCleanup()
cleanup_gil = cleanup_gil + argCvt.GetInterfaceArgCleanupGIL()
comArgName, comArgDeclString = argCvt.GetInterfaceCppObjectInfo()
if comArgDeclString: # If we should declare a variable
codeCobjects = codeCobjects + "\t%s;\n" % (comArgDeclString)
argsCOM = argsCOM + ", " + comArgName
except makegwparse.error_not_supported as why:
f.write('// *** The input argument %s of type "%s" was not processed ***\n// Please check the conversion function is appropriate and exists!\n' % (arg.name, arg.raw_type))
f.write('\t%s %s;\n\tPyObject *ob%s;\n' % (arg.type, arg.name, arg.name))
f.write('\t// @pyparm <o Py%s>|%s||Description for %s\n' % (arg.type, arg.name, arg.name))
codePost = codePost + '\tif (bPythonIsHappy && !PyObject_As%s( ob%s, &%s )) bPythonIsHappy = FALSE;\n' % (arg.type, arg.name, arg.name)
formatChars = formatChars + "O"
argsParseTuple = argsParseTuple + ", &ob%s" % (arg.name)
argsCOM = argsCOM + ", " + arg.name
cleanup = cleanup + "\tPyObject_Free%s(%s);\n" % (arg.type, arg.name)
if needConversion: f.write("\tUSES_CONVERSION;\n")
f.write(codePobjects);
f.write(codeCobjects);
f.write('\tif ( !PyArg_ParseTuple(args, "%s:%s"%s) )\n\t\treturn NULL;\n' % (formatChars, method.name, argsParseTuple))
if codePost:
f.write('\tBOOL bPythonIsHappy = TRUE;\n')
f.write(codePost);
f.write('\tif (!bPythonIsHappy) return NULL;\n')
strdict['argsCOM'] = argsCOM[1:]
strdict['cleanup'] = cleanup
strdict['cleanup_gil'] = cleanup_gil
f.write(\
''' HRESULT hr;
PY_INTERFACE_PRECALL;
hr = p%(ptr)s->%(method)s(%(argsCOM)s );
%(cleanup)s
PY_INTERFACE_POSTCALL;
%(cleanup_gil)s
if ( FAILED(hr) )
return PyCom_BuildPyException(hr, p%(ptr)s, IID_%(interfacename)s );
''' % strdict)
codePre = codePost = formatChars = codeVarsPass = codeDecl = ""
for arg in method.args:
if not arg.HasAttribute("out"):
continue
try:
argCvt = makegwparse.make_arg_converter(arg)
formatChar = argCvt.GetFormatChar()
if formatChar:
formatChars = formatChars + formatChar
codePre = codePre + argCvt.GetBuildForInterfacePreCode()
codePost = codePost + argCvt.GetBuildForInterfacePostCode()
codeVarsPass = codeVarsPass + ", " + argCvt.GetBuildValueArg()
codeDecl = codeDecl + argCvt.DeclareParseArgTupleInputConverter()
except makegwparse.error_not_supported as why:
f.write('// *** The output argument %s of type "%s" was not processed ***\n// %s\n' % (arg.name, arg.raw_type, why))
continue
if formatChars:
f.write('%s\n%s\tPyObject *pyretval = Py_BuildValue("%s"%s);\n%s\treturn pyretval;' % (codeDecl, codePre, formatChars, codeVarsPass, codePost))
else:
f.write('\tPy_INCREF(Py_None);\n\treturn Py_None;\n')
f.write('\n}\n\n')
f.write ('// @object Py%s|Description of the interface\n' % (name))
f.write('static struct PyMethodDef Py%s_methods[] =\n{\n' % name)
for method in interface.methods:
f.write('\t{ "%s", Py%s::%s, 1 }, // @pymeth %s|Description of %s\n' % (method.name, interface.name, method.name, method.name, method.name))
interfacebase = interface.base
f.write('''\
{ NULL }
};
PyComTypeObject Py%(name)s::type("Py%(name)s",
&Py%(interfacebase)s::type,
sizeof(Py%(name)s),
Py%(name)s_methods,
GET_PYCOM_CTOR(Py%(name)s));
''' % locals())
def _write_gw_h(f, interface):
if interface.name[0] == "I":
gname = 'PyG' + interface.name[1:]
else:
gname = 'PyG' + interface.name
name = interface.name
if interface.base == "IUnknown" or interface.base == "IDispatch":
base_name = "PyGatewayBase"
else:
if interface.base[0] == "I":
base_name = 'PyG' + interface.base[1:]
else:
base_name = 'PyG' + interface.base
f.write(\
'''\
// ---------------------------------------------------
//
// Gateway Declaration
class %s : public %s, public %s
{
protected:
%s(PyObject *instance) : %s(instance) { ; }
PYGATEWAY_MAKE_SUPPORT2(%s, %s, IID_%s, %s)
''' % (gname, base_name, name, gname, base_name, gname, name, name, base_name))
if interface.base != "IUnknown":
f.write("\t// %s\n\t// *** Manually add %s method decls here\n\n" % (interface.base, interface.base))
else:
f.write('\n\n')
f.write("\t// %s\n" % name)
for method in interface.methods:
f.write('\tSTDMETHOD(%s)(\n' % method.name)
if method.args:
for arg in method.args[:-1]:
f.write("\t\t%s,\n" % (arg.GetRawDeclaration()))
arg = method.args[-1]
f.write("\t\t%s);\n\n" % (arg.GetRawDeclaration()))
else:
f.write('\t\tvoid);\n\n')
f.write('};\n')
f.close()
def _write_gw_cpp(f, interface):
if interface.name[0] == "I":
gname = 'PyG' + interface.name[1:]
else:
gname = 'PyG' + interface.name
name = interface.name
if interface.base == "IUnknown" or interface.base == "IDispatch":
base_name = "PyGatewayBase"
else:
if interface.base[0] == "I":
base_name = 'PyG' + interface.base[1:]
else:
base_name = 'PyG' + interface.base
f.write('''\
// ---------------------------------------------------
//
// Gateway Implementation
''' % {'name':name, 'gname':gname, 'base_name':base_name})
for method in interface.methods:
f.write(\
'''\
STDMETHODIMP %s::%s(
''' % (gname, method.name))
if method.args:
for arg in method.args[:-1]:
inoutstr = ']['.join(arg.inout)
f.write("\t\t/* [%s] */ %s,\n" % (inoutstr, arg.GetRawDeclaration()))
arg = method.args[-1]
inoutstr = ']['.join(arg.inout)
f.write("\t\t/* [%s] */ %s)\n" % (inoutstr, arg.GetRawDeclaration()))
else:
f.write('\t\tvoid)\n')
f.write("{\n\tPY_GATEWAY_METHOD;\n")
cout = 0
codePre = codePost = codeVars = ""
argStr = ""
needConversion = 0
formatChars = ""
if method.args:
for arg in method.args:
if arg.HasAttribute("out"):
cout = cout + 1
if arg.indirectionLevel ==2 :
f.write("\tif (%s==NULL) return E_POINTER;\n" % arg.name)
if arg.HasAttribute("in"):
try:
argCvt = makegwparse.make_arg_converter(arg)
argCvt.SetGatewayMode()
formatchar = argCvt.GetFormatChar();
needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
if formatchar:
formatChars = formatChars + formatchar
codeVars = codeVars + argCvt.DeclareParseArgTupleInputConverter()
argStr = argStr + ", " + argCvt.GetBuildValueArg()
codePre = codePre + argCvt.GetBuildForGatewayPreCode()
codePost = codePost + argCvt.GetBuildForGatewayPostCode()
except makegwparse.error_not_supported as why:
f.write('// *** The input argument %s of type "%s" was not processed ***\n// - Please ensure this conversion function exists, and is appropriate\n// - %s\n' % (arg.name, arg.raw_type, why))
f.write('\tPyObject *ob%s = PyObject_From%s(%s);\n' % (arg.name, arg.type, arg.name))
f.write('\tif (ob%s==NULL) return MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n' % (arg.name, method.name))
codePost = codePost + "\tPy_DECREF(ob%s);\n" % arg.name
formatChars = formatChars + "O"
argStr = argStr + ", ob%s" % (arg.name)
if needConversion: f.write('\tUSES_CONVERSION;\n')
f.write(codeVars)
f.write(codePre)
if cout:
f.write("\tPyObject *result;\n")
resStr = "&result"
else:
resStr = "NULL"
if formatChars:
fullArgStr = '%s, "%s"%s' % (resStr, formatChars, argStr)
else:
fullArgStr = resStr
f.write('\tHRESULT hr=InvokeViaPolicy("%s", %s);\n' % (method.name, fullArgStr))
f.write(codePost)
if cout:
f.write("\tif (FAILED(hr)) return hr;\n")
f.write("\t// Process the Python results, and convert back to the real params\n")
# process the output arguments.
formatChars = codePobjects = codePost = argsParseTuple = ""
needConversion = 0
for arg in method.args:
if not arg.HasAttribute("out"):
continue
try:
argCvt = makegwparse.make_arg_converter(arg)
argCvt.SetGatewayMode()
val = argCvt.GetFormatChar()
if val:
formatChars = formatChars + val
argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg()
codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter()
codePost = codePost + argCvt.GetParsePostCode()
needConversion = needConversion or argCvt.NeedUSES_CONVERSION()
except makegwparse.error_not_supported as why:
f.write('// *** The output argument %s of type "%s" was not processed ***\n// %s\n' % (arg.name, arg.raw_type, why))
if formatChars: # If I have any to actually process.
if len(formatChars)==1:
parseFn = "PyArg_Parse"
else:
parseFn = "PyArg_ParseTuple"
if codePobjects: f.write(codePobjects)
f.write('\tif (!%s(result, "%s" %s))\n\t\treturn MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n' % (parseFn, formatChars, argsParseTuple, method.name))
if codePost:
f.write('\tBOOL bPythonIsHappy = TRUE;\n')
f.write(codePost)
f.write('\tif (!bPythonIsHappy) hr = MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n' % method.name)
f.write('\tPy_DECREF(result);\n');
f.write('\treturn hr;\n}\n\n')
def test():
# make_framework_support("d:\\msdev\\include\\objidl.h", "ILockBytes")
make_framework_support("d:\\msdev\\include\\objidl.h", "IStorage")
# make_framework_support("d:\\msdev\\include\\objidl.h", "IEnumSTATSTG")

View file

@ -0,0 +1,317 @@
"""Utility file for generating PyIEnum support.
This is almost a 'template' file. It simplay contains almost full
C++ source code for PyIEnum* support, and the Python code simply
substitutes the appropriate interface name.
This module is notmally not used directly - the @makegw@ module
automatically calls this.
"""
#
# INTERNAL FUNCTIONS
#
#
import string
def is_interface_enum(enumtype):
return not (enumtype[0] in string.uppercase and enumtype[2] in string.uppercase)
def _write_enumifc_cpp(f, interface):
enumtype = interface.name[5:]
if is_interface_enum(enumtype):
# Assume an interface.
enum_interface = "I" + enumtype[:-1]
converter = "PyObject *ob = PyCom_PyObjectFromIUnknown(rgVar[i], IID_%(enum_interface)s, FALSE);" % locals()
arraydeclare = "%(enum_interface)s **rgVar = new %(enum_interface)s *[celt];" % locals()
else:
# Enum of a simple structure
converter = "PyObject *ob = PyCom_PyObjectFrom%(enumtype)s(&rgVar[i]);" % locals()
arraydeclare = "%(enumtype)s *rgVar = new %(enumtype)s[celt];" % locals()
f.write(\
'''
// ---------------------------------------------------
//
// Interface Implementation
PyIEnum%(enumtype)s::PyIEnum%(enumtype)s(IUnknown *pdisp):
PyIUnknown(pdisp)
{
ob_type = &type;
}
PyIEnum%(enumtype)s::~PyIEnum%(enumtype)s()
{
}
/* static */ IEnum%(enumtype)s *PyIEnum%(enumtype)s::GetI(PyObject *self)
{
return (IEnum%(enumtype)s *)PyIUnknown::GetI(self);
}
// @pymethod object|PyIEnum%(enumtype)s|Next|Retrieves a specified number of items in the enumeration sequence.
PyObject *PyIEnum%(enumtype)s::Next(PyObject *self, PyObject *args)
{
long celt = 1;
// @pyparm int|num|1|Number of items to retrieve.
if ( !PyArg_ParseTuple(args, "|l:Next", &celt) )
return NULL;
IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
if ( pIE%(enumtype)s == NULL )
return NULL;
%(arraydeclare)s
if ( rgVar == NULL ) {
PyErr_SetString(PyExc_MemoryError, "allocating result %(enumtype)ss");
return NULL;
}
int i;
/* for ( i = celt; i--; )
// *** possibly init each structure element???
*/
ULONG celtFetched = 0;
PY_INTERFACE_PRECALL;
HRESULT hr = pIE%(enumtype)s->Next(celt, rgVar, &celtFetched);
PY_INTERFACE_POSTCALL;
if ( HRESULT_CODE(hr) != ERROR_NO_MORE_ITEMS && FAILED(hr) )
{
delete [] rgVar;
return PyCom_BuildPyException(hr,pIE%(enumtype)s, IID_IE%(enumtype)s);
}
PyObject *result = PyTuple_New(celtFetched);
if ( result != NULL )
{
for ( i = celtFetched; i--; )
{
%(converter)s
if ( ob == NULL )
{
Py_DECREF(result);
result = NULL;
break;
}
PyTuple_SET_ITEM(result, i, ob);
}
}
/* for ( i = celtFetched; i--; )
// *** possibly cleanup each structure element???
*/
delete [] rgVar;
return result;
}
// @pymethod |PyIEnum%(enumtype)s|Skip|Skips over the next specified elementes.
PyObject *PyIEnum%(enumtype)s::Skip(PyObject *self, PyObject *args)
{
long celt;
if ( !PyArg_ParseTuple(args, "l:Skip", &celt) )
return NULL;
IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
if ( pIE%(enumtype)s == NULL )
return NULL;
PY_INTERFACE_PRECALL;
HRESULT hr = pIE%(enumtype)s->Skip(celt);
PY_INTERFACE_POSTCALL;
if ( FAILED(hr) )
return PyCom_BuildPyException(hr, pIE%(enumtype)s, IID_IE%(enumtype)s);
Py_INCREF(Py_None);
return Py_None;
}
// @pymethod |PyIEnum%(enumtype)s|Reset|Resets the enumeration sequence to the beginning.
PyObject *PyIEnum%(enumtype)s::Reset(PyObject *self, PyObject *args)
{
if ( !PyArg_ParseTuple(args, ":Reset") )
return NULL;
IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
if ( pIE%(enumtype)s == NULL )
return NULL;
PY_INTERFACE_PRECALL;
HRESULT hr = pIE%(enumtype)s->Reset();
PY_INTERFACE_POSTCALL;
if ( FAILED(hr) )
return PyCom_BuildPyException(hr, pIE%(enumtype)s, IID_IE%(enumtype)s);
Py_INCREF(Py_None);
return Py_None;
}
// @pymethod <o PyIEnum%(enumtype)s>|PyIEnum%(enumtype)s|Clone|Creates another enumerator that contains the same enumeration state as the current one
PyObject *PyIEnum%(enumtype)s::Clone(PyObject *self, PyObject *args)
{
if ( !PyArg_ParseTuple(args, ":Clone") )
return NULL;
IEnum%(enumtype)s *pIE%(enumtype)s = GetI(self);
if ( pIE%(enumtype)s == NULL )
return NULL;
IEnum%(enumtype)s *pClone;
PY_INTERFACE_PRECALL;
HRESULT hr = pIE%(enumtype)s->Clone(&pClone);
PY_INTERFACE_POSTCALL;
if ( FAILED(hr) )
return PyCom_BuildPyException(hr, pIE%(enumtype)s, IID_IE%(enumtype)s);
return PyCom_PyObjectFromIUnknown(pClone, IID_IEnum%(enumtype)s, FALSE);
}
// @object PyIEnum%(enumtype)s|A Python interface to IEnum%(enumtype)s
static struct PyMethodDef PyIEnum%(enumtype)s_methods[] =
{
{ "Next", PyIEnum%(enumtype)s::Next, 1 }, // @pymeth Next|Retrieves a specified number of items in the enumeration sequence.
{ "Skip", PyIEnum%(enumtype)s::Skip, 1 }, // @pymeth Skip|Skips over the next specified elementes.
{ "Reset", PyIEnum%(enumtype)s::Reset, 1 }, // @pymeth Reset|Resets the enumeration sequence to the beginning.
{ "Clone", PyIEnum%(enumtype)s::Clone, 1 }, // @pymeth Clone|Creates another enumerator that contains the same enumeration state as the current one.
{ NULL }
};
PyComEnumTypeObject PyIEnum%(enumtype)s::type("PyIEnum%(enumtype)s",
&PyIUnknown::type,
sizeof(PyIEnum%(enumtype)s),
PyIEnum%(enumtype)s_methods,
GET_PYCOM_CTOR(PyIEnum%(enumtype)s));
''' % locals() )
def _write_enumgw_cpp(f, interface):
enumtype = interface.name[5:]
if is_interface_enum(enumtype):
# Assume an interface.
enum_interface = "I" + enumtype[:-1]
converter = "if ( !PyCom_InterfaceFromPyObject(ob, IID_%(enum_interface)s, (void **)&rgVar[i], FALSE) )" % locals()
argdeclare="%(enum_interface)s __RPC_FAR * __RPC_FAR *rgVar" % locals()
else:
argdeclare="%(enumtype)s __RPC_FAR *rgVar" % locals()
converter="if ( !PyCom_PyObjectAs%(enumtype)s(ob, &rgVar[i]) )" % locals()
f.write(
'''
// ---------------------------------------------------
//
// Gateway Implementation
// Std delegation
STDMETHODIMP_(ULONG) PyGEnum%(enumtype)s::AddRef(void) {return PyGatewayBase::AddRef();}
STDMETHODIMP_(ULONG) PyGEnum%(enumtype)s::Release(void) {return PyGatewayBase::Release();}
STDMETHODIMP PyGEnum%(enumtype)s::QueryInterface(REFIID iid, void ** obj) {return PyGatewayBase::QueryInterface(iid, obj);}
STDMETHODIMP PyGEnum%(enumtype)s::GetTypeInfoCount(UINT FAR* pctInfo) {return PyGatewayBase::GetTypeInfoCount(pctInfo);}
STDMETHODIMP PyGEnum%(enumtype)s::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptInfo) {return PyGatewayBase::GetTypeInfo(itinfo, lcid, pptInfo);}
STDMETHODIMP PyGEnum%(enumtype)s::GetIDsOfNames(REFIID refiid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid) {return PyGatewayBase::GetIDsOfNames( refiid, rgszNames, cNames, lcid, rgdispid);}
STDMETHODIMP PyGEnum%(enumtype)s::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* params, VARIANT FAR* pVarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) {return PyGatewayBase::Invoke( dispid, riid, lcid, wFlags, params, pVarResult, pexcepinfo, puArgErr);}
STDMETHODIMP PyGEnum%(enumtype)s::Next(
/* [in] */ ULONG celt,
/* [length_is][size_is][out] */ %(argdeclare)s,
/* [out] */ ULONG __RPC_FAR *pCeltFetched)
{
PY_GATEWAY_METHOD;
PyObject *result;
HRESULT hr = InvokeViaPolicy("Next", &result, "i", celt);
if ( FAILED(hr) )
return hr;
if ( !PySequence_Check(result) )
goto error;
int len;
len = PyObject_Length(result);
if ( len == -1 )
goto error;
if ( len > (int)celt)
len = celt;
if ( pCeltFetched )
*pCeltFetched = len;
int i;
for ( i = 0; i < len; ++i )
{
PyObject *ob = PySequence_GetItem(result, i);
if ( ob == NULL )
goto error;
%(converter)s
{
Py_DECREF(result);
return PyCom_SetCOMErrorFromPyException(IID_IEnum%(enumtype)s);
}
}
Py_DECREF(result);
return len < (int)celt ? S_FALSE : S_OK;
error:
PyErr_Clear(); // just in case
Py_DECREF(result);
return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum%(enumtype)s, "Next() did not return a sequence of objects");
}
STDMETHODIMP PyGEnum%(enumtype)s::Skip(
/* [in] */ ULONG celt)
{
PY_GATEWAY_METHOD;
return InvokeViaPolicy("Skip", NULL, "i", celt);
}
STDMETHODIMP PyGEnum%(enumtype)s::Reset(void)
{
PY_GATEWAY_METHOD;
return InvokeViaPolicy("Reset");
}
STDMETHODIMP PyGEnum%(enumtype)s::Clone(
/* [out] */ IEnum%(enumtype)s __RPC_FAR *__RPC_FAR *ppEnum)
{
PY_GATEWAY_METHOD;
PyObject * result;
HRESULT hr = InvokeViaPolicy("Clone", &result);
if ( FAILED(hr) )
return hr;
/*
** Make sure we have the right kind of object: we should have some kind
** of IUnknown subclass wrapped into a PyIUnknown instance.
*/
if ( !PyIBase::is_object(result, &PyIUnknown::type) )
{
/* the wrong kind of object was returned to us */
Py_DECREF(result);
return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum%(enumtype)s);
}
/*
** Get the IUnknown out of the thing. note that the Python ob maintains
** a reference, so we don't have to explicitly AddRef() here.
*/
IUnknown *punk = ((PyIUnknown *)result)->m_obj;
if ( !punk )
{
/* damn. the object was released. */
Py_DECREF(result);
return PyCom_SetCOMErrorFromSimple(E_FAIL, IID_IEnum%(enumtype)s);
}
/*
** Get the interface we want. note it is returned with a refcount.
** This QI is actually going to instantiate a PyGEnum%(enumtype)s.
*/
hr = punk->QueryInterface(IID_IEnum%(enumtype)s, (LPVOID *)ppEnum);
/* done with the result; this DECREF is also for <punk> */
Py_DECREF(result);
return PyCom_SetCOMErrorFromSimple(hr, IID_IEnum%(enumtype)s, "Python could not convert the result from Next() into the required COM interface");
}
''' % locals())

View file

@ -0,0 +1,783 @@
"""Utilities for makegw - Parse a header file to build an interface
This module contains the core code for parsing a header file describing a
COM interface, and building it into an "Interface" structure.
Each Interface has methods, and each method has arguments.
Each argument knows how to use Py_BuildValue or Py_ParseTuple to
exchange itself with Python.
See the @win32com.makegw@ module for information in building a COM
interface
"""
import re
import traceback
class error_not_found(Exception):
def __init__(self, msg="The requested item could not be found"):
super(error_not_found, self).__init__(msg)
class error_not_supported(Exception):
def __init__(self, msg="The required functionality is not supported"):
super(error_not_supported, self).__init__(msg)
VERBOSE=0
DEBUG=0
## NOTE : For interfaces as params to work correctly, you must
## make sure any PythonCOM extensions which expose the interface are loaded
## before generating.
class ArgFormatter:
"""An instance for a specific type of argument. Knows how to convert itself"""
def __init__(self, arg, builtinIndirection, declaredIndirection = 0):
#print 'init:', arg.name, builtinIndirection, declaredIndirection, arg.indirectionLevel
self.arg = arg
self.builtinIndirection = builtinIndirection
self.declaredIndirection = declaredIndirection
self.gatewayMode = 0
def _IndirectPrefix(self, indirectionFrom, indirectionTo):
"""Given the indirection level I was declared at (0=Normal, 1=*, 2=**)
return a string prefix so I can pass to a function with the
required indirection (where the default is the indirection of the method's param.
eg, assuming my arg has indirection level of 2, if this function was passed 1
it would return "&", so that a variable declared with indirection of 1
can be prefixed with this to turn it into the indirection level required of 2
"""
dif = indirectionFrom - indirectionTo
if dif==0:
return ""
elif dif==-1:
return "&"
elif dif==1:
return "*"
else:
return "?? (%d)" % (dif,)
raise error_not_supported("Can't indirect this far - please fix me :-)")
def GetIndirectedArgName(self, indirectFrom, indirectionTo):
#print 'get:',self.arg.name, indirectFrom,self._GetDeclaredIndirection() + self.builtinIndirection, indirectionTo, self.arg.indirectionLevel
if indirectFrom is None:
### ACK! this does not account for [in][out] variables.
### when this method is called, we need to know which
indirectFrom = self._GetDeclaredIndirection() + self.builtinIndirection
return self._IndirectPrefix(indirectFrom, indirectionTo) + self.arg.name
def GetBuildValueArg(self):
"Get the argument to be passes to Py_BuildValue"
return self.arg.name
def GetParseTupleArg(self):
"Get the argument to be passed to PyArg_ParseTuple"
if self.gatewayMode:
# use whatever they were declared with
return self.GetIndirectedArgName(None, 1)
# local declarations have just their builtin indirection
return self.GetIndirectedArgName(self.builtinIndirection, 1)
def GetInterfaceCppObjectInfo(self):
"""Provide information about the C++ object used.
Simple variables (such as integers) can declare their type (eg an integer)
and use it as the target of both PyArg_ParseTuple and the COM function itself.
More complex types require a PyObject * declared as the target of PyArg_ParseTuple,
then some conversion routine to the C++ object which is actually passed to COM.
This method provides the name, and optionally the type of that C++ variable.
If the type if provided, the caller will likely generate a variable declaration.
The name must always be returned.
Result is a tuple of (variableName, [DeclareType|None|""])
"""
# the first return element is the variable to be passed as
# an argument to an interface method. the variable was
# declared with only its builtin indirection level. when
# we pass it, we'll need to pass in whatever amount of
# indirection was applied (plus the builtin amount)
# the second return element is the variable declaration; it
# should simply be builtin indirection
return self.GetIndirectedArgName(self.builtinIndirection, self.arg.indirectionLevel + self.builtinIndirection), \
"%s %s" % (self.GetUnconstType(), self.arg.name)
def GetInterfaceArgCleanup(self):
"Return cleanup code for C++ args passed to the interface method."
if DEBUG:
return "/* GetInterfaceArgCleanup output goes here: %s */\n" % self.arg.name
else:
return ""
def GetInterfaceArgCleanupGIL(self):
"""Return cleanup code for C++ args passed to the interface
method that must be executed with the GIL held"""
if DEBUG:
return "/* GetInterfaceArgCleanup (GIL held) output goes here: %s */\n" % self.arg.name
else:
return ""
def GetUnconstType(self):
return self.arg.unc_type
def SetGatewayMode(self):
self.gatewayMode = 1
def _GetDeclaredIndirection(self):
return self.arg.indirectionLevel
print('declared:', self.arg.name, self.gatewayMode)
if self.gatewayMode:
return self.arg.indirectionLevel
else:
return self.declaredIndirection
def DeclareParseArgTupleInputConverter(self):
"Declare the variable used as the PyArg_ParseTuple param for a gateway"
# Only declare it??
#if self.arg.indirectionLevel==0:
# return "\t%s %s;\n" % (self.arg.type, self.arg.name)
#else:
if DEBUG:
return "/* Declare ParseArgTupleInputConverter goes here: %s */\n" % self.arg.name
else:
return ""
def GetParsePostCode(self):
"Get a string of C++ code to be executed after (ie, to finalise) the PyArg_ParseTuple conversion"
if DEBUG:
return "/* GetParsePostCode code goes here: %s */\n" % self.arg.name
else:
return ""
def GetBuildForInterfacePreCode(self):
"Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Interfaces"
if DEBUG:
return "/* GetBuildForInterfacePreCode goes here: %s */\n" % self.arg.name
else:
return ""
def GetBuildForGatewayPreCode(self):
"Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Gateways"
s = self.GetBuildForInterfacePreCode() # Usually the same
if DEBUG:
if s[:4] == "/* G":
s = "/* GetBuildForGatewayPreCode goes here: %s */\n" % self.arg.name
return s
def GetBuildForInterfacePostCode(self):
"Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Interfaces"
if DEBUG:
return "/* GetBuildForInterfacePostCode goes here: %s */\n" % self.arg.name
return ""
def GetBuildForGatewayPostCode(self):
"Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Gateways"
s = self.GetBuildForInterfacePostCode() # Usually the same
if DEBUG:
if s[:4] == "/* G":
s = "/* GetBuildForGatewayPostCode goes here: %s */\n" % self.arg.name
return s
def GetAutoduckString(self):
return '// @pyparm %s|%s||Description for %s' % (self._GetPythonTypeDesc(), self.arg.name, self.arg.name)
def _GetPythonTypeDesc(self):
"Returns a string with the description of the type. Used for doco purposes"
return None
def NeedUSES_CONVERSION(self):
"Determines if this arg forces a USES_CONVERSION macro"
return 0
# Special formatter for floats since they're smaller than Python floats.
class ArgFormatterFloat(ArgFormatter):
def GetFormatChar(self):
return "f"
def DeclareParseArgTupleInputConverter(self):
# Declare a double variable
return "\tdouble dbl%s;\n" % self.arg.name
def GetParseTupleArg(self):
return "&dbl" + self.arg.name
def _GetPythonTypeDesc(self):
return "float"
def GetBuildValueArg(self):
return "&dbl" + self.arg.name
def GetBuildForInterfacePreCode(self):
return "\tdbl" + self.arg.name + " = " + self.arg.name + ";\n"
def GetBuildForGatewayPreCode(self):
return "\tdbl%s = " % self.arg.name + self._IndirectPrefix( \
self._GetDeclaredIndirection(),
0) + self.arg.name + ";\n"
def GetParsePostCode(self):
s = "\t"
if self.gatewayMode:
s = s + self._IndirectPrefix(
self._GetDeclaredIndirection(),
0)
s = s + self.arg.name
s = s + " = (float)dbl%s;\n" % self.arg.name
return s
# Special formatter for Shorts because they're
# a different size than Python ints!
class ArgFormatterShort(ArgFormatter):
def GetFormatChar(self):
return "i"
def DeclareParseArgTupleInputConverter(self):
# Declare a double variable
return "\tINT i%s;\n" % self.arg.name
def GetParseTupleArg(self):
return "&i" + self.arg.name
def _GetPythonTypeDesc(self):
return "int"
def GetBuildValueArg(self):
return "&i" + self.arg.name
def GetBuildForInterfacePreCode(self):
return "\ti" + self.arg.name + " = " + self.arg.name + ";\n"
def GetBuildForGatewayPreCode(self):
return "\ti%s = " % self.arg.name + self._IndirectPrefix( \
self._GetDeclaredIndirection(),
0) + self.arg.name + ";\n"
def GetParsePostCode(self):
s = "\t"
if self.gatewayMode:
s = s + self._IndirectPrefix(
self._GetDeclaredIndirection(),
0)
s = s + self.arg.name
s = s + " = i%s;\n" % self.arg.name
return s
# for types which are 64bits on AMD64 - eg, HWND
class ArgFormatterLONG_PTR(ArgFormatter):
def GetFormatChar(self):
return "O"
def DeclareParseArgTupleInputConverter(self):
# Declare a PyObject variable
return "\tPyObject *ob%s;\n" % self.arg.name
def GetParseTupleArg(self):
return "&ob"+self.arg.name
def _GetPythonTypeDesc(self):
return "int/long"
def GetBuildValueArg(self):
return "ob" + self.arg.name
def GetBuildForInterfacePostCode(self):
return "\tPy_XDECREF(ob%s);\n" % self.arg.name
def DeclareParseArgTupleInputConverter(self):
# Declare a PyObject variable
return "\tPyObject *ob%s;\n" % self.arg.name
def GetParsePostCode(self):
return "\tif (bPythonIsHappy && !PyWinLong_AsULONG_PTR(ob%s, (ULONG_PTR *)%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
def GetBuildForInterfacePreCode(self):
notdirected = self.GetIndirectedArgName(None, 1)
return "\tob%s = PyWinObject_FromULONG_PTR(%s);\n" % \
(self.arg.name, notdirected)
def GetBuildForGatewayPostCode(self):
return "\tPy_XDECREF(ob%s);\n" % self.arg.name
class ArgFormatterPythonCOM(ArgFormatter):
"""An arg formatter for types exposed in the PythonCOM module"""
def GetFormatChar(self):
return "O"
#def GetInterfaceCppObjectInfo(self):
# return ArgFormatter.GetInterfaceCppObjectInfo(self)[0], \
# "%s %s%s" % (self.arg.unc_type, "*" * self._GetDeclaredIndirection(), self.arg.name)
def DeclareParseArgTupleInputConverter(self):
# Declare a PyObject variable
return "\tPyObject *ob%s;\n" % self.arg.name
def GetParseTupleArg(self):
return "&ob"+self.arg.name
def _GetPythonTypeDesc(self):
return "<o Py%s>" % self.arg.type
def GetBuildValueArg(self):
return "ob" + self.arg.name
def GetBuildForInterfacePostCode(self):
return "\tPy_XDECREF(ob%s);\n" % self.arg.name
def DeclareParseArgTupleInputConverter(self):
# Declare a PyObject variable
return "\tPyObject *ob%s;\n" % self.arg.name
class ArgFormatterBSTR(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "<o unicode>"
def GetParsePostCode(self):
return "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
def GetBuildForInterfacePreCode(self):
notdirected = self.GetIndirectedArgName(None, 1)
return "\tob%s = MakeBstrToObj(%s);\n" % \
(self.arg.name, notdirected)
def GetBuildForInterfacePostCode(self):
return "\tSysFreeString(%s);\n" % (self.arg.name,) + \
ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
def GetBuildForGatewayPostCode(self):
return "\tPy_XDECREF(ob%s);\n" % self.arg.name
class ArgFormatterOLECHAR(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "<o unicode>"
def GetUnconstType(self):
if self.arg.type[:3]=="LPC":
return self.arg.type[:2] + self.arg.type[3:]
else:
return self.arg.unc_type
def GetParsePostCode(self):
return "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
def GetInterfaceArgCleanup(self):
return "\tSysFreeString(%s);\n" % self.GetIndirectedArgName(None, 1)
def GetBuildForInterfacePreCode(self):
# the variable was declared with just its builtin indirection
notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1)
return "\tob%s = MakeOLECHARToObj(%s);\n" % \
(self.arg.name, notdirected)
def GetBuildForInterfacePostCode(self):
# memory returned into an OLECHAR should be freed
return "\tCoTaskMemFree(%s);\n" % (self.arg.name,) + \
ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
def GetBuildForGatewayPostCode(self):
return "\tPy_XDECREF(ob%s);\n" % self.arg.name
class ArgFormatterTCHAR(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "string/<o unicode>"
def GetUnconstType(self):
if self.arg.type[:3]=="LPC":
return self.arg.type[:2] + self.arg.type[3:]
else:
return self.arg.unc_type
def GetParsePostCode(self):
return "\tif (bPythonIsHappy && !PyWinObject_AsTCHAR(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2))
def GetInterfaceArgCleanup(self):
return "\tPyWinObject_FreeTCHAR(%s);\n" % self.GetIndirectedArgName(None, 1)
def GetBuildForInterfacePreCode(self):
# the variable was declared with just its builtin indirection
notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1)
return "\tob%s = PyWinObject_FromTCHAR(%s);\n" % \
(self.arg.name, notdirected)
def GetBuildForInterfacePostCode(self):
return "// ??? - TCHAR post code\n"
def GetBuildForGatewayPostCode(self):
return "\tPy_XDECREF(ob%s);\n" % self.arg.name
class ArgFormatterIID(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "<o PyIID>"
def GetParsePostCode(self):
return "\tif (!PyWinObject_AsIID(ob%s, &%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.arg.name)
def GetBuildForInterfacePreCode(self):
# notdirected = self.GetIndirectedArgName(self.arg.indirectionLevel, 0)
notdirected = self.GetIndirectedArgName(None, 0)
return "\tob%s = PyWinObject_FromIID(%s);\n" % (self.arg.name, notdirected)
def GetInterfaceCppObjectInfo(self):
return self.arg.name, "IID %s" % (self.arg.name)
class ArgFormatterTime(ArgFormatterPythonCOM):
def __init__(self, arg, builtinIndirection, declaredIndirection = 0):
# we don't want to declare LPSYSTEMTIME / LPFILETIME objects
if arg.indirectionLevel == 0 and arg.unc_type[:2] == "LP":
arg.unc_type = arg.unc_type[2:]
# reduce the builtin and increment the declaration
arg.indirectionLevel = arg.indirectionLevel + 1
builtinIndirection = 0
ArgFormatterPythonCOM.__init__(self, arg, builtinIndirection, declaredIndirection)
def _GetPythonTypeDesc(self):
return "<o PyTime>"
def GetParsePostCode(self):
# variable was declared with only the builtinIndirection
### NOTE: this is an [in] ... so use only builtin
return '\tif (!PyTime_Check(ob%s)) {\n\t\tPyErr_SetString(PyExc_TypeError, "The argument must be a PyTime object");\n\t\tbPythonIsHappy = FALSE;\n\t}\n\tif (!((PyTime *)ob%s)->GetTime(%s)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.arg.name, self.GetIndirectedArgName(self.builtinIndirection, 1))
def GetBuildForInterfacePreCode(self):
### use just the builtinIndirection again...
notdirected = self.GetIndirectedArgName(self.builtinIndirection,0)
return "\tob%s = new PyTime(%s);\n" % (self.arg.name, notdirected)
def GetBuildForInterfacePostCode(self):
### hack to determine if we need to free stuff
ret = ''
if self.builtinIndirection + self.arg.indirectionLevel > 1:
# memory returned into an OLECHAR should be freed
ret = "\tCoTaskMemFree(%s);\n" % self.arg.name
return ret + ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self)
class ArgFormatterSTATSTG(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "<o STATSTG>"
def GetParsePostCode(self):
return '\tif (!PyCom_PyObjectAsSTATSTG(ob%s, %s, 0/*flags*/)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
def GetBuildForInterfacePreCode(self):
notdirected = self.GetIndirectedArgName(None, 1)
return "\tob%s = PyCom_PyObjectFromSTATSTG(%s);\n\t// STATSTG doco says our responsibility to free\n\tif ((%s).pwcsName) CoTaskMemFree((%s).pwcsName);\n" % (self.arg.name, self.GetIndirectedArgName(None, 1),notdirected,notdirected)
class ArgFormatterGeneric(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "<o %s>" % self.arg.type
def GetParsePostCode(self):
return '\tif (!PyObject_As%s(ob%s, &%s) bPythonIsHappy = FALSE;\n' % (self.arg.type, self.arg.name, self.GetIndirectedArgName(None, 1))
def GetInterfaceArgCleanup(self):
return '\tPyObject_Free%s(%s);\n' % (self.arg.type, self.arg.name)
def GetBuildForInterfacePreCode(self):
notdirected = self.GetIndirectedArgName(None, 1)
return "\tob%s = PyObject_From%s(%s);\n" % (self.arg.name, self.arg.type, self.GetIndirectedArgName(None, 1))
class ArgFormatterIDLIST(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "<o PyIDL>"
def GetParsePostCode(self):
return '\tif (bPythonIsHappy && !PyObject_AsPIDL(ob%s, &%s)) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
def GetInterfaceArgCleanup(self):
return '\tPyObject_FreePIDL(%s);\n' % (self.arg.name,)
def GetBuildForInterfacePreCode(self):
notdirected = self.GetIndirectedArgName(None, 1)
return "\tob%s = PyObject_FromPIDL(%s);\n" % (self.arg.name, self.GetIndirectedArgName(None, 1))
class ArgFormatterHANDLE(ArgFormatterPythonCOM):
def _GetPythonTypeDesc(self):
return "<o PyHANDLE>"
def GetParsePostCode(self):
return '\tif (!PyWinObject_AsHANDLE(ob%s, &%s, FALSE) bPythonIsHappy = FALSE;\n' % (self.arg.name, self.GetIndirectedArgName(None, 1))
def GetBuildForInterfacePreCode(self):
notdirected = self.GetIndirectedArgName(None, 1)
return "\tob%s = PyWinObject_FromHANDLE(%s);\n" % (self.arg.name, self.GetIndirectedArgName(None, 0))
class ArgFormatterLARGE_INTEGER(ArgFormatterPythonCOM):
def GetKeyName(self):
return "LARGE_INTEGER"
def _GetPythonTypeDesc(self):
return "<o %s>" % self.GetKeyName()
def GetParsePostCode(self):
return '\tif (!PyWinObject_As%s(ob%s, %s)) bPythonIsHappy = FALSE;\n' % (self.GetKeyName(), self.arg.name, self.GetIndirectedArgName(None, 1))
def GetBuildForInterfacePreCode(self):
notdirected = self.GetIndirectedArgName(None, 0)
return "\tob%s = PyWinObject_From%s(%s);\n" % (self.arg.name, self.GetKeyName(), notdirected)
class ArgFormatterULARGE_INTEGER(ArgFormatterLARGE_INTEGER):
def GetKeyName(self):
return "ULARGE_INTEGER"
class ArgFormatterInterface(ArgFormatterPythonCOM):
def GetInterfaceCppObjectInfo(self):
return self.GetIndirectedArgName(1, self.arg.indirectionLevel), \
"%s * %s" % (self.GetUnconstType(), self.arg.name)
def GetParsePostCode(self):
# This gets called for out params in gateway mode
if self.gatewayMode:
sArg = self.GetIndirectedArgName(None, 2)
else:
# vs. in params for interface mode.
sArg = self.GetIndirectedArgName(1, 2)
return "\tif (bPythonIsHappy && !PyCom_InterfaceFromPyInstanceOrObject(ob%s, IID_%s, (void **)%s, TRUE /* bNoneOK */))\n\t\t bPythonIsHappy = FALSE;\n" % (self.arg.name, self.arg.type, sArg)
def GetBuildForInterfacePreCode(self):
return "\tob%s = PyCom_PyObjectFromIUnknown(%s, IID_%s, FALSE);\n" % (self.arg.name, self.arg.name, self.arg.type)
def GetBuildForGatewayPreCode(self):
sPrefix = self._IndirectPrefix(self._GetDeclaredIndirection(), 1)
return "\tob%s = PyCom_PyObjectFromIUnknown(%s%s, IID_%s, TRUE);\n" % (self.arg.name, sPrefix, self.arg.name, self.arg.type)
def GetInterfaceArgCleanup(self):
return "\tif (%s) %s->Release();\n" % (self.arg.name, self.arg.name)
class ArgFormatterVARIANT(ArgFormatterPythonCOM):
def GetParsePostCode(self):
return "\tif ( !PyCom_VariantFromPyObject(ob%s, %s) )\n\t\tbPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 1))
def GetBuildForGatewayPreCode(self):
notdirected = self.GetIndirectedArgName(None, 1)
return "\tob%s = PyCom_PyObjectFromVariant(%s);\n" % (self.arg.name, notdirected)
def GetBuildForGatewayPostCode(self):
return "\tPy_XDECREF(ob%s);\n" % self.arg.name
# Key : , Python Type Description, ParseTuple format char
ConvertSimpleTypes = {"BOOL":("BOOL", "int", "i"),
"UINT":("UINT", "int", "i"),
"BYTE": ("BYTE", "int", "i"),
"INT": ("INT", "int", "i"),
"DWORD": ("DWORD", "int", "l"),
"HRESULT":("HRESULT", "int", "l"),
"ULONG": ("ULONG", "int", "l"),
"LONG": ("LONG", "int", "l"),
"int": ("int", "int", "i"),
"long": ("long", "int", "l"),
"DISPID": ("DISPID", "long", "l"),
"APPBREAKFLAGS": ("int", "int", "i"),
"BREAKRESUMEACTION": ("int", "int", "i"),
"ERRORRESUMEACTION": ("int", "int", "i"),
"BREAKREASON": ("int", "int", "i"),
"BREAKPOINT_STATE": ("int", "int", "i"),
"BREAKRESUME_ACTION": ("int", "int", "i"),
"SOURCE_TEXT_ATTR": ("int", "int", "i"),
"TEXT_DOC_ATTR": ("int", "int", "i"),
"QUERYOPTION": ("int", "int", "i"),
"PARSEACTION": ("int", "int", "i"),
}
class ArgFormatterSimple(ArgFormatter):
"""An arg formatter for simple integer etc types"""
def GetFormatChar(self):
return ConvertSimpleTypes[self.arg.type][2]
def _GetPythonTypeDesc(self):
return ConvertSimpleTypes[self.arg.type][1]
AllConverters = {"const OLECHAR": (ArgFormatterOLECHAR, 0, 1),
"WCHAR": (ArgFormatterOLECHAR, 0, 1),
"OLECHAR": (ArgFormatterOLECHAR, 0, 1),
"LPCOLESTR": (ArgFormatterOLECHAR, 1, 1),
"LPOLESTR": (ArgFormatterOLECHAR, 1, 1),
"LPCWSTR": (ArgFormatterOLECHAR, 1, 1),
"LPWSTR": (ArgFormatterOLECHAR, 1, 1),
"LPCSTR": (ArgFormatterOLECHAR, 1, 1),
"LPTSTR": (ArgFormatterTCHAR, 1, 1),
"LPCTSTR": (ArgFormatterTCHAR, 1, 1),
"HANDLE": (ArgFormatterHANDLE, 0),
"BSTR": (ArgFormatterBSTR, 1, 0),
"const IID": (ArgFormatterIID, 0),
"CLSID": (ArgFormatterIID, 0),
"IID": (ArgFormatterIID, 0),
"GUID": (ArgFormatterIID, 0),
"const GUID": (ArgFormatterIID, 0),
"const IID": (ArgFormatterIID, 0),
"REFCLSID": (ArgFormatterIID, 0),
"REFIID": (ArgFormatterIID, 0),
"REFGUID": (ArgFormatterIID, 0),
"const FILETIME": (ArgFormatterTime, 0),
"const SYSTEMTIME":(ArgFormatterTime, 0),
"const LPSYSTEMTIME":(ArgFormatterTime, 1, 1),
"LPSYSTEMTIME": (ArgFormatterTime, 1, 1),
"FILETIME": (ArgFormatterTime, 0),
"SYSTEMTIME": (ArgFormatterTime, 0),
"STATSTG": (ArgFormatterSTATSTG, 0),
"LARGE_INTEGER": (ArgFormatterLARGE_INTEGER, 0),
"ULARGE_INTEGER": (ArgFormatterULARGE_INTEGER, 0),
"VARIANT": (ArgFormatterVARIANT, 0),
"float": (ArgFormatterFloat, 0),
"single": (ArgFormatterFloat, 0),
"short": (ArgFormatterShort, 0),
"WORD": (ArgFormatterShort, 0),
"VARIANT_BOOL": (ArgFormatterShort, 0),
"HWND": (ArgFormatterLONG_PTR, 1),
"HMENU": (ArgFormatterLONG_PTR, 1),
"HOLEMENU": (ArgFormatterLONG_PTR, 1),
"HICON": (ArgFormatterLONG_PTR, 1),
"HDC": (ArgFormatterLONG_PTR, 1),
"LPARAM": (ArgFormatterLONG_PTR, 1),
"WPARAM": (ArgFormatterLONG_PTR, 1),
"LRESULT": (ArgFormatterLONG_PTR, 1),
"UINT": (ArgFormatterShort, 0),
"SVSIF": (ArgFormatterShort, 0),
"Control": (ArgFormatterInterface, 0, 1),
"DataObject": (ArgFormatterInterface, 0, 1),
"_PropertyBag": (ArgFormatterInterface, 0, 1),
"AsyncProp": (ArgFormatterInterface, 0, 1),
"DataSource": (ArgFormatterInterface, 0, 1),
"DataFormat": (ArgFormatterInterface, 0, 1),
"void **": (ArgFormatterInterface, 2, 2),
"ITEMIDLIST": (ArgFormatterIDLIST, 0, 0),
"LPITEMIDLIST": (ArgFormatterIDLIST, 0, 1),
"LPCITEMIDLIST": (ArgFormatterIDLIST, 0, 1),
"const ITEMIDLIST": (ArgFormatterIDLIST, 0, 1),
}
# Auto-add all the simple types
for key in ConvertSimpleTypes.keys():
AllConverters[key] = ArgFormatterSimple, 0
def make_arg_converter(arg):
try:
clz = AllConverters[arg.type][0]
bin = AllConverters[arg.type][1]
decl = 0
if len(AllConverters[arg.type])>2:
decl = AllConverters[arg.type][2]
return clz(arg,bin, decl)
except KeyError:
if arg.type[0]=="I":
return ArgFormatterInterface(arg, 0, 1)
raise error_not_supported("The type '%s' (%s) is unknown." % (arg.type, arg.name))
#############################################################
#
# The instances that represent the args, methods and interface
class Argument:
"""A representation of an argument to a COM method
This class contains information about a specific argument to a method.
In addition, methods exist so that an argument knows how to convert itself
to/from Python arguments.
"""
# in,out type name [ ]
# -------------- -------- ------------ ------
regex = re.compile(r'/\* \[([^\]]*.*?)] \*/[ \t](.*[* ]+)(\w+)(\[ *])?[\),]')
def __init__(self, good_interface_names):
self.good_interface_names = good_interface_names
self.inout = self.name = self.type = None
self.const = 0
self.arrayDecl = 0
def BuildFromFile(self, file):
"""Parse and build my data from a file
Reads the next line in the file, and matches it as an argument
description. If not a valid argument line, an error_not_found exception
is raised.
"""
line = file.readline()
mo = self.regex.search(line)
if not mo:
raise error_not_found
self.name = mo.group(3)
self.inout = mo.group(1).split('][')
typ = mo.group(2).strip()
self.raw_type = typ
self.indirectionLevel = 0
if mo.group(4): # Has "[ ]" decl
self.arrayDecl = 1
try:
pos = typ.rindex("__RPC_FAR")
self.indirectionLevel = self.indirectionLevel + 1
typ = typ[:pos].strip()
except ValueError:
pass
typ = typ.replace("__RPC_FAR", "")
while 1:
try:
pos = typ.rindex("*")
self.indirectionLevel = self.indirectionLevel + 1
typ = typ[:pos].strip()
except ValueError:
break
self.type = typ
if self.type[:6]=="const ":
self.unc_type = self.type[6:]
else:
self.unc_type = self.type
if VERBOSE:
print(" Arg %s of type %s%s (%s)" % (self.name, self.type, "*" * self.indirectionLevel, self.inout))
def HasAttribute(self, typ):
"""Determines if the argument has the specific attribute.
Argument attributes are specified in the header file, such as
"[in][out][retval]" etc. You can pass a specific string (eg "out")
to find if this attribute was specified for the argument
"""
return typ in self.inout
def GetRawDeclaration(self):
ret = "%s %s" % (self.raw_type, self.name)
if self.arrayDecl:
ret = ret + "[]"
return ret
class Method:
"""A representation of a C++ method on a COM interface
This class contains information about a specific method, as well as
a list of all @Argument@s
"""
# options ret type callconv name
# ----------------- -------- -------- --------
regex = re.compile(r'virtual (/\*.*?\*/ )?(.*?) (.*?) (.*?)\(\w?')
def __init__(self, good_interface_names):
self.good_interface_names = good_interface_names
self.name = self.result = self.callconv = None
self.args = []
def BuildFromFile(self, file):
"""Parse and build my data from a file
Reads the next line in the file, and matches it as a method
description. If not a valid method line, an error_not_found exception
is raised.
"""
line = file.readline()
mo = self.regex.search(line)
if not mo:
raise error_not_found
self.name = mo.group(4)
self.result = mo.group(2)
if self.result != "HRESULT":
if self.result=="DWORD": # DWORD is for old old stuff?
print("Warning: Old style interface detected - compilation errors likely!")
else:
print("Method %s - Only HRESULT return types are supported." % self.name)
# raise error_not_supported, if VERBOSE:
print(" Method %s %s(" % (self.result, self.name))
while 1:
arg = Argument(self.good_interface_names)
try:
arg.BuildFromFile(file)
self.args.append(arg)
except error_not_found:
break
class Interface:
"""A representation of a C++ COM Interface
This class contains information about a specific interface, as well as
a list of all @Method@s
"""
# name base
# -------- --------
regex = re.compile("(interface|) ([^ ]*) : public (.*)$")
def __init__(self, mo):
self.methods = []
self.name = mo.group(2)
self.base = mo.group(3)
if VERBOSE:
print("Interface %s : public %s" % (self.name, self.base))
def BuildMethods(self, file):
"""Build all sub-methods for this interface"""
# skip the next 2 lines.
file.readline();file.readline();
while 1:
try:
method = Method([self.name])
method.BuildFromFile(file)
self.methods.append(method)
except error_not_found:
break
def find_interface(interfaceName, file):
"""Find and return an interface in a file
Given an interface name and file, search for the specified interface.
Upon return, the interface itself has been built,
but not the methods.
"""
interface = None
line = file.readline()
while line:
mo = Interface.regex.search(line)
if mo:
name = mo.group(2)
print(name)
AllConverters[name] = (ArgFormatterInterface, 0, 1)
if name==interfaceName:
interface = Interface(mo)
interface.BuildMethods(file)
line = file.readline()
if interface:
return interface
raise error_not_found
def parse_interface_info(interfaceName, file):
"""Find, parse and return an interface in a file
Given an interface name and file, search for the specified interface.
Upon return, the interface itself is fully built,
"""
try:
return find_interface(interfaceName, file)
except re.error:
traceback.print_exc()
print("The interface could not be built, as the regular expression failed!")
def test():
f=open("d:\\msdev\\include\\objidl.h")
try:
parse_interface_info("IPersistStream", f)
finally:
f.close()
def test_regex(r,text):
res=r.search(text,0)
if res==-1:
print("** Not found")
else:
print("%d\n%s\n%s\n%s\n%s" % (res, r.group(1), r.group(2), r.group(3), r.group(4)))

View file

@ -0,0 +1,68 @@
"""Constants used by COM Controls
Hand created version of OLECTL.H constants.
"""
import winerror
FACILITY_CONTROL = 0xa
def MAKE_SCODE(sev, fac, code):
return int((int(-sev)<<31) | ((fac)<<16) | ((code)))
def STD_CTL_SCODE(n):
return MAKE_SCODE(winerror.SEVERITY_ERROR, FACILITY_CONTROL, n)
CTL_E_ILLEGALFUNCTIONCALL = STD_CTL_SCODE(5)
CTL_E_OVERFLOW = STD_CTL_SCODE(6)
CTL_E_OUTOFMEMORY = STD_CTL_SCODE(7)
CTL_E_DIVISIONBYZERO = STD_CTL_SCODE(11)
CTL_E_OUTOFSTRINGSPACE = STD_CTL_SCODE(14)
CTL_E_OUTOFSTACKSPACE = STD_CTL_SCODE(28)
CTL_E_BADFILENAMEORNUMBER = STD_CTL_SCODE(52)
CTL_E_FILENOTFOUND = STD_CTL_SCODE(53)
CTL_E_BADFILEMODE = STD_CTL_SCODE(54)
CTL_E_FILEALREADYOPEN = STD_CTL_SCODE(55)
CTL_E_DEVICEIOERROR = STD_CTL_SCODE(57)
CTL_E_FILEALREADYEXISTS = STD_CTL_SCODE(58)
CTL_E_BADRECORDLENGTH = STD_CTL_SCODE(59)
CTL_E_DISKFULL = STD_CTL_SCODE(61)
CTL_E_BADRECORDNUMBER = STD_CTL_SCODE(63)
CTL_E_BADFILENAME = STD_CTL_SCODE(64)
CTL_E_TOOMANYFILES = STD_CTL_SCODE(67)
CTL_E_DEVICEUNAVAILABLE = STD_CTL_SCODE(68)
CTL_E_PERMISSIONDENIED = STD_CTL_SCODE(70)
CTL_E_DISKNOTREADY = STD_CTL_SCODE(71)
CTL_E_PATHFILEACCESSERROR = STD_CTL_SCODE(75)
CTL_E_PATHNOTFOUND = STD_CTL_SCODE(76)
CTL_E_INVALIDPATTERNSTRING = STD_CTL_SCODE(93)
CTL_E_INVALIDUSEOFNULL = STD_CTL_SCODE(94)
CTL_E_INVALIDFILEFORMAT = STD_CTL_SCODE(321)
CTL_E_INVALIDPROPERTYVALUE = STD_CTL_SCODE(380)
CTL_E_INVALIDPROPERTYARRAYINDEX = STD_CTL_SCODE(381)
CTL_E_SETNOTSUPPORTEDATRUNTIME = STD_CTL_SCODE(382)
CTL_E_SETNOTSUPPORTED = STD_CTL_SCODE(383)
CTL_E_NEEDPROPERTYARRAYINDEX = STD_CTL_SCODE(385)
CTL_E_SETNOTPERMITTED = STD_CTL_SCODE(387)
CTL_E_GETNOTSUPPORTEDATRUNTIME = STD_CTL_SCODE(393)
CTL_E_GETNOTSUPPORTED = STD_CTL_SCODE(394)
CTL_E_PROPERTYNOTFOUND = STD_CTL_SCODE(422)
CTL_E_INVALIDCLIPBOARDFORMAT = STD_CTL_SCODE(460)
CTL_E_INVALIDPICTURE = STD_CTL_SCODE(481)
CTL_E_PRINTERERROR = STD_CTL_SCODE(482)
CTL_E_CANTSAVEFILETOTEMP = STD_CTL_SCODE(735)
CTL_E_SEARCHTEXTNOTFOUND = STD_CTL_SCODE(744)
CTL_E_REPLACEMENTSTOOLONG = STD_CTL_SCODE(746)
CONNECT_E_FIRST = MAKE_SCODE(winerror.SEVERITY_ERROR, winerror.FACILITY_ITF, 0x0200)
CONNECT_E_LAST = MAKE_SCODE(winerror.SEVERITY_ERROR, winerror.FACILITY_ITF, 0x020F)
CONNECT_S_FIRST = MAKE_SCODE(winerror.SEVERITY_SUCCESS, winerror.FACILITY_ITF, 0x0200)
CONNECT_S_LAST = MAKE_SCODE(winerror.SEVERITY_SUCCESS, winerror.FACILITY_ITF, 0x020F)
CONNECT_E_NOCONNECTION = CONNECT_E_FIRST+0
CONNECT_E_ADVISELIMIT = CONNECT_E_FIRST+1
CONNECT_E_CANNOTCONNECT = CONNECT_E_FIRST+2
CONNECT_E_OVERRIDDEN = CONNECT_E_FIRST+3
CLASS_E_NOTLICENSED = winerror.CLASSFACTORY_E_FIRST+2

View file

@ -0,0 +1,87 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>win32com Readme</title>
</head>
<body>
<p><img width="551" height="99" id="_x0000_i1025"
src="html%5Cimage%5Cpycom_blowing.gif"
alt="Python and COM - Blowing the others away"> </p>
<h1>Python COM Extensions Readme </h1>
<p>This is the readme for win32com. Please check out the <a
href="html/docindex.html">win32com documentation index</a></p>
<p>The <a href="test/.">win32com/test directory</a> contains some interesting
scripts (and a new <a href="test/readme.txt">readme.txt</a>). Although these
are used for testing, they do show a variety of COM techniques.</p>
<h3>VARIANT objects</h3>
<p>win32com.client now has explicit VARIANT objects which can be used in
situations where you need more control over the argument types passed when
calling COM methods. See the <a href="html/variant.html">documentation on
this object</a>
<a name="currency"><h3>Important Currency changes</h3></a>
<p>
In all builds prior to 204, a COM currency value was returned as a tuple of
integers. Working with 2 integers to represent a currency object was a poor
choice, but the alternative was never clear. Now Python ships with the
<a href="http://www.python.org/dev/doc/devel/lib/module-decimal.html">decimal</a>
module, the alternative has arrived!
</p>
<p>
Up until build 212, code could set <code>pythoncom.__future_currency__ = True</code>
to force use of the decimal module, with a warning issued otherwise. In
builds 213 and later, the decimal module is unconditionally used when
pythoncon returns you a currency value.
</p>
<h3>Recent Changes</h3>
<h4>Lots of internal changes on the road to py3k</h4>
<h4>win32com.axcontrol and win2con.internet</h4>
Many more interfaces for hosting AX controls and the interfaces
used by Internet Explorer.
<h4>win32com.shell</h4>
The shell interfaces have undergone a number of enhancements and changes.
A couple of methods have changed signature between the first build with shell support (200) and later builds.
SHGetFileInfo was broken in its result handling, so had to be changed - this
is the only function used by the samples that changed, but others not used by the samples also have changed.
These shell interfaces are now generally stable.
<h4>New win32com.taskscheduler module</h4>
Roger Upole has contributed an interface to the Windows task scheduler. This is actually very neat, and it allows
Python to edit the task list as shown by Windows Control Panel. Property page suppport may even appear later,
now that the win32 library has the new win32rcparser module.
<h4>ActiveX Scripting </h4>
<p>Python only supports "trusted" execution hosts - thus, it will no longer work
as an engine inside IE (Python itself no longer has a restricted execution environment).
Python continues to work fine as an Active Scripting Engine in all other
applications, including Windows Scripting Host, and ASP.
<p>There is also support for Python as an ActiveX Scripting Host.</p>
<p>Active Debugging seems to be fully functional.</p>
<h4>Older stuff</h4>
<ul>
</li>
<li>Unexpected exceptions in Python COM objects will generally now dump
the exception and traceback to stdout. &nbsp;This is useful for debugging
and testing - it means that in some cases there will be no need to register
an object with <span style="font-style: italic;">--debug</span> to see these
tracebacks. &nbsp;Note that COM objects used by server processes (such as
ASP) generally have no valid stdout, so will still need to use <span
style="font-style: italic;">--debug</span> as usual.<br>
</li>
<li>universal gateway support has been improved - we can now work as an
Outlook Addin<br>
</li>
</body>
</html>

View file

@ -0,0 +1 @@
# Empty __init__ file to designate a sub-package.

View file

@ -0,0 +1,65 @@
"""Utilities for Server Side connections.
A collection of helpers for server side connection points.
"""
import pythoncom
from .exception import Exception
import winerror
from win32com import olectl
import win32com.server.util
# Methods implemented by the interfaces.
IConnectionPointContainer_methods = ["EnumConnectionPoints","FindConnectionPoint"]
IConnectionPoint_methods = ["EnumConnections","Unadvise","Advise","GetConnectionPointContainer","GetConnectionInterface"]
class ConnectableServer:
_public_methods_ = IConnectionPointContainer_methods + IConnectionPoint_methods
_com_interfaces_ = [pythoncom.IID_IConnectionPoint, pythoncom.IID_IConnectionPointContainer]
# Clients must set _connect_interfaces_ = [...]
def __init__(self):
self.cookieNo = 0
self.connections = {}
# IConnectionPoint interfaces
def EnumConnections(self):
raise Exception(winerror.E_NOTIMPL)
def GetConnectionInterface(self):
raise Exception(winerror.E_NOTIMPL)
def GetConnectionPointContainer(self):
return win32com.server.util.wrap(self)
def Advise(self, pUnk):
# Creates a connection to the client. Simply allocate a new cookie,
# find the clients interface, and store it in a dictionary.
try:
interface = pUnk.QueryInterface(self._connect_interfaces_[0],pythoncom.IID_IDispatch)
except pythoncom.com_error:
raise Exception(scode=olectl.CONNECT_E_NOCONNECTION)
self.cookieNo = self.cookieNo + 1
self.connections[self.cookieNo] = interface
return self.cookieNo
def Unadvise(self, cookie):
# Destroy a connection - simply delete interface from the map.
try:
del self.connections[cookie]
except KeyError:
raise Exception(scode=winerror.E_UNEXPECTED)
# IConnectionPointContainer interfaces
def EnumConnectionPoints(self):
raise Exception(winerror.E_NOTIMPL)
def FindConnectionPoint(self, iid):
# Find a connection we support. Only support the single event interface.
if iid in self._connect_interfaces_:
return win32com.server.util.wrap(self)
def _BroadcastNotify(self, broadcaster, extraArgs):
# Broadcasts a notification to all connections.
# Ignores clients that fail.
for interface in self.connections.values():
try:
broadcaster(*(interface,)+extraArgs)
except pythoncom.com_error as details:
self._OnNotifyFail(interface, details)
def _OnNotifyFail(self, interface, details):
print("Ignoring COM error to connection - %s" % (repr(details)))

View file

@ -0,0 +1,270 @@
"""Dispatcher
Please see policy.py for a discussion on dispatchers and policies
"""
import pythoncom, traceback, win32api
from sys import exc_info
#
from win32com.server.exception import IsCOMServerException
from win32com.util import IIDToInterfaceName
import win32com
class DispatcherBase:
""" The base class for all Dispatchers.
This dispatcher supports wrapping all operations in exception handlers,
and all the necessary delegation to the policy.
This base class supports the printing of "unexpected" exceptions. Note, however,
that exactly where the output of print goes may not be useful! A derived class may
provide additional semantics for this.
"""
def __init__(self, policyClass, object):
self.policy = policyClass(object)
# The logger we should dump to. If None, we should send to the
# default location (typically 'print')
self.logger = getattr(win32com, "logger", None)
# Note the "return self._HandleException_()" is purely to stop pychecker
# complaining - _HandleException_ will itself raise an exception for the
# pythoncom framework, so the result will never be seen.
def _CreateInstance_(self, clsid, reqIID):
try:
self.policy._CreateInstance_(clsid, reqIID)
return pythoncom.WrapObject(self, reqIID)
except:
return self._HandleException_()
def _QueryInterface_(self, iid):
try:
return self.policy._QueryInterface_(iid)
except:
return self._HandleException_()
def _Invoke_(self, dispid, lcid, wFlags, args):
try:
return self.policy._Invoke_(dispid, lcid, wFlags, args)
except:
return self._HandleException_()
def _GetIDsOfNames_(self, names, lcid):
try:
return self.policy._GetIDsOfNames_(names, lcid)
except:
return self._HandleException_()
def _GetTypeInfo_(self, index, lcid):
try:
return self.policy._GetTypeInfo_(index, lcid)
except:
return self._HandleException_()
def _GetTypeInfoCount_(self):
try:
return self.policy._GetTypeInfoCount_()
except:
return self._HandleException_()
def _GetDispID_(self, name, fdex):
try:
return self.policy._GetDispID_(name, fdex)
except:
return self._HandleException_()
def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
try:
return self.policy._InvokeEx_(dispid, lcid, wFlags, args, kwargs, serviceProvider)
except:
return self._HandleException_()
def _DeleteMemberByName_(self, name, fdex):
try:
return self.policy._DeleteMemberByName_(name, fdex)
except:
return self._HandleException_()
def _DeleteMemberByDispID_(self, id):
try:
return self.policy._DeleteMemberByDispID_(id)
except:
return self._HandleException_()
def _GetMemberProperties_(self, id, fdex):
try:
return self.policy._GetMemberProperties_(id, fdex)
except:
return self._HandleException_()
def _GetMemberName_(self, dispid):
try:
return self.policy._GetMemberName_(dispid)
except:
return self._HandleException_()
def _GetNextDispID_(self, fdex, flags):
try:
return self.policy._GetNextDispID_(fdex, flags)
except:
return self._HandleException_()
def _GetNameSpaceParent_(self):
try:
return self.policy._GetNameSpaceParent_()
except:
return self._HandleException_()
def _HandleException_(self):
"""Called whenever an exception is raised.
Default behaviour is to print the exception.
"""
# If not a COM exception, print it for the developer.
if not IsCOMServerException():
if self.logger is not None:
self.logger.exception("pythoncom server error")
else:
traceback.print_exc()
# But still raise it for the framework.
raise
def _trace_(self, *args):
if self.logger is not None:
record = " ".join(map(str, args))
self.logger.debug(record)
else:
for arg in args[:-1]:
print(arg, end=' ')
print(args[-1])
class DispatcherTrace(DispatcherBase):
"""A dispatcher, which causes a 'print' line for each COM function called.
"""
def _QueryInterface_(self, iid):
rc = DispatcherBase._QueryInterface_(self, iid)
if not rc:
self._trace_("in %s._QueryInterface_ with unsupported IID %s (%s)" % (repr(self.policy._obj_), IIDToInterfaceName(iid),iid))
return rc
def _GetIDsOfNames_(self, names, lcid):
self._trace_("in _GetIDsOfNames_ with '%s' and '%d'\n" % (names, lcid))
return DispatcherBase._GetIDsOfNames_(self, names, lcid)
def _GetTypeInfo_(self, index, lcid):
self._trace_("in _GetTypeInfo_ with index=%d, lcid=%d\n" % (index, lcid))
return DispatcherBase._GetTypeInfo_(self, index, lcid)
def _GetTypeInfoCount_(self):
self._trace_("in _GetTypeInfoCount_\n")
return DispatcherBase._GetTypeInfoCount_(self)
def _Invoke_(self, dispid, lcid, wFlags, args):
self._trace_("in _Invoke_ with", dispid, lcid, wFlags, args)
return DispatcherBase._Invoke_(self, dispid, lcid, wFlags, args)
def _GetDispID_(self, name, fdex):
self._trace_("in _GetDispID_ with", name, fdex)
return DispatcherBase._GetDispID_(self, name, fdex)
def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
self._trace_("in %r._InvokeEx_-%s%r [%x,%s,%r]" % (self.policy._obj_, dispid, args, wFlags, lcid, serviceProvider))
return DispatcherBase._InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider)
def _DeleteMemberByName_(self, name, fdex):
self._trace_("in _DeleteMemberByName_ with", name, fdex)
return DispatcherBase._DeleteMemberByName_(self, name, fdex)
def _DeleteMemberByDispID_(self, id):
self._trace_("in _DeleteMemberByDispID_ with", id)
return DispatcherBase._DeleteMemberByDispID_(self, id)
def _GetMemberProperties_(self, id, fdex):
self._trace_("in _GetMemberProperties_ with", id, fdex)
return DispatcherBase._GetMemberProperties_(self, id, fdex)
def _GetMemberName_(self, dispid):
self._trace_("in _GetMemberName_ with", dispid)
return DispatcherBase._GetMemberName_(self, dispid)
def _GetNextDispID_(self, fdex, flags):
self._trace_("in _GetNextDispID_ with", fdex, flags)
return DispatcherBase._GetNextDispID_(self, fdex, flags)
def _GetNameSpaceParent_(self):
self._trace_("in _GetNameSpaceParent_")
return DispatcherBase._GetNameSpaceParent_(self)
class DispatcherWin32trace(DispatcherTrace):
"""A tracing dispatcher that sends its output to the win32trace remote collector.
"""
def __init__(self, policyClass, object):
DispatcherTrace.__init__(self, policyClass, object)
if self.logger is None:
# If we have no logger, setup our output.
import win32traceutil # Sets up everything.
self._trace_("Object with win32trace dispatcher created (object=%s)" % repr(object))
class DispatcherOutputDebugString(DispatcherTrace):
"""A tracing dispatcher that sends its output to win32api.OutputDebugString
"""
def _trace_(self, *args):
for arg in args[:-1]:
win32api.OutputDebugString(str(arg)+" ")
win32api.OutputDebugString(str(args[-1])+"\n")
class DispatcherWin32dbg(DispatcherBase):
"""A source-level debugger dispatcher
A dispatcher which invokes the debugger as an object is instantiated, or
when an unexpected exception occurs.
Requires Pythonwin.
"""
def __init__(self, policyClass, ob):
# No one uses this, and it just causes py2exe to drag all of
# pythonwin in.
#import pywin.debugger
pywin.debugger.brk()
print("The DispatcherWin32dbg dispatcher is deprecated!")
print("Please let me know if this is a problem.")
print("Uncomment the relevant lines in dispatcher.py to re-enable")
# DEBUGGER Note - You can either:
# * Hit Run and wait for a (non Exception class) exception to occur!
# * Set a breakpoint and hit run.
# * Step into the object creation (a few steps away!)
DispatcherBase.__init__(self, policyClass, ob)
def _HandleException_(self):
""" Invoke the debugger post mortem capability """
# Save details away.
typ, val, tb = exc_info()
#import pywin.debugger, pywin.debugger.dbgcon
debug = 0
try:
raise typ(val)
except Exception: # AARG - What is this Exception???
# Use some inside knowledge to borrow a Debugger option which dictates if we
# stop at "expected" exceptions.
debug = pywin.debugger.GetDebugger().get_option(pywin.debugger.dbgcon.OPT_STOP_EXCEPTIONS)
except:
debug = 1
if debug:
try:
pywin.debugger.post_mortem(tb, typ, val) # The original exception
except:
traceback.print_exc()
# But still raise it.
del tb
raise
try:
import win32trace
DefaultDebugDispatcher = DispatcherWin32trace
except ImportError: # no win32trace module - just use a print based one.
DefaultDebugDispatcher = DispatcherTrace

View file

@ -0,0 +1,91 @@
"""Exception Handling
Exceptions
To better support COM exceptions, the framework allows for an instance to be
raised. This instance may have a certain number of known attributes, which are
translated into COM exception details.
This means, for example, that Python could raise a COM exception that includes details
on a Help file and location, and a description for the user.
This module provides a class which provides the necessary attributes.
"""
import sys, pythoncom
# Note that we derive from com_error, which derives from exceptions.Exception
# Also note that we dont support "self.args", as we dont support tuple-unpacking
class COMException(pythoncom.com_error):
"""An Exception object that is understood by the framework.
If the framework is presented with an exception of type class,
it looks for certain known attributes on this class to provide rich
error information to the caller.
It should be noted that the framework supports providing this error
information via COM Exceptions, or via the ISupportErrorInfo interface.
By using this class, you automatically provide rich error information to the
server.
"""
def __init__(self, description = None, scode = None,
source = None, helpfile = None, helpContext = None,
desc = None, hresult = None):
"""Initialize an exception
**Params**
description -- A string description for the exception.
scode -- An integer scode to be returned to the server, if necessary.
The pythoncom framework defaults this to be DISP_E_EXCEPTION if not specified otherwise.
source -- A string which identifies the source of the error.
helpfile -- A string which points to a help file which contains details on the error.
helpContext -- An integer context in the help file.
desc -- A short-cut for description.
hresult -- A short-cut for scode.
"""
# convert a WIN32 error into an HRESULT
scode = scode or hresult
if scode and scode != 1: # We dont want S_FALSE mapped!
if scode >= -32768 and scode < 32768:
# this is HRESULT_FROM_WIN32()
scode = -2147024896 | (scode & 0x0000FFFF)
self.scode = scode
self.description = description or desc
if scode==1 and not self.description:
self.description = "S_FALSE"
elif scode and not self.description:
self.description = pythoncom.GetScodeString(scode)
self.source = source
self.helpfile = helpfile
self.helpcontext = helpContext
# todo - fill in the exception value
pythoncom.com_error.__init__(self, scode, self.description, None, -1)
def __repr__(self):
return "<COM Exception - scode=%s, desc=%s>" % (self.scode, self.description)
# Old name for the COMException class.
# Do NOT use the name Exception, as it is now a built-in
# COMException is the new, official name.
Exception = COMException
def IsCOMException(t = None):
if t is None:
t = sys.exc_info()[0]
try:
return issubclass(t, pythoncom.com_error)
except TypeError: # 1.5 in -X mode?
return t is pythoncon.com_error
def IsCOMServerException(t = None):
if t is None:
t = sys.exc_info()[0]
try:
return issubclass(t, COMException)
except TypeError: # String exception
return 0

View file

@ -0,0 +1,22 @@
# Class factory utilities.
import pythoncom
def RegisterClassFactories(clsids, flags = None, clsctx = None):
"""Given a list of CLSID, create and register class factories.
Returns a list, which should be passed to RevokeClassFactories
"""
if flags is None: flags = pythoncom.REGCLS_MULTIPLEUSE|pythoncom.REGCLS_SUSPENDED
if clsctx is None: clsctx = pythoncom.CLSCTX_LOCAL_SERVER
ret = []
for clsid in clsids:
# Some server append '-Embedding' etc
if clsid[0] not in ['-', '/']:
factory = pythoncom.MakePyFactory(clsid)
regId = pythoncom.CoRegisterClassObject(clsid, factory, clsctx, flags)
ret.append((factory, regId))
return ret
def RevokeClassFactories(infos):
for factory, revokeId in infos:
pythoncom.CoRevokeClassObject(revokeId)

View file

@ -0,0 +1,49 @@
# LocalServer .EXE support for Python.
#
# This is designed to be used as a _script_ file by pythonw.exe
#
# In some cases, you could also use Python.exe, which will create
# a console window useful for debugging.
#
# NOTE: When NOT running in any sort of debugging mode,
# 'print' statements may fail, as sys.stdout is not valid!!!
#
# Usage:
# wpython.exe LocalServer.py clsid [, clsid]
import sys
sys.coinit_flags = 2
import pythoncom
import win32api
from win32com.server import factory
usage = """\
Invalid command line arguments
This program provides LocalServer COM support
for Python COM objects.
It is typically run automatically by COM, passing as arguments
The ProgID or CLSID of the Python Server(s) to be hosted
"""
def serve(clsids):
infos = factory.RegisterClassFactories(clsids)
pythoncom.EnableQuitMessage(win32api.GetCurrentThreadId())
pythoncom.CoResumeClassObjects()
pythoncom.PumpMessages()
factory.RevokeClassFactories( infos )
pythoncom.CoUninitialize()
def main():
if len(sys.argv)==1:
win32api.MessageBox(0, usage, "Python COM Server")
sys.exit(1)
serve(sys.argv[1:])
if __name__=='__main__':
main()

Some files were not shown because too many files have changed in this diff Show more