install pywin32-220
104
Lib/site-packages/win32com/HTML/GeneratedSupport.html
Normal 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. </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. </P>
|
||||
<P> </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> </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> </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> </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>
|
||||
90
Lib/site-packages/win32com/HTML/PythonCOM.html
Normal 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->QueryInterface(IID_IConnectionPoint, (void **)&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>>>> disp=someobj.QueryInterface(win32com.IID_Dispatch) </P>
|
||||
</CODE><P>will return a PyIDispatch object, whereas </P>
|
||||
<CODE><P>>>> unk=someobj.QueryInterface(SomeUnknownIID) # returns PyIUnknown<BR>
|
||||
>>> disp=unk.QueryInterface(win32com.IID_Dispatch) <BR>
|
||||
>>> 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>>>> 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>
|
||||
>>> 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>
|
||||
82
Lib/site-packages/win32com/HTML/QuickStartClientCom.html
Normal 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><COMObject Excel.Application></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>>>> import win32com.client</P>
|
||||
<P>>>> w=win32com.client.Dispatch("Word.Application")</P>
|
||||
<P>>>> w.Visible=1</P>
|
||||
<P>>>> w</P>
|
||||
<P><win32com.gen_py.Microsoft Word 8.0 Object Library._Application></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>>>> w.WindowState = win32com.client.constants.wdWindowStateMinimize</P>
|
||||
</CODE><P>and Word will Minimize.</P></BODY>
|
||||
</HTML>
|
||||
195
Lib/site-packages/win32com/HTML/QuickStartServerCom.html
Normal 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 don’t 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>>>> import pythoncom<BR>
|
||||
>>> 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>
|
||||
	# ni only for 1.4!<BR>
|
||||
	import ni, win32com.server.register <BR>
|
||||
	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>
|
||||
22
Lib/site-packages/win32com/HTML/docindex.html
Normal 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 don’t 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>
|
||||
BIN
Lib/site-packages/win32com/HTML/image/BTN_HomePage.gif
Normal file
|
After Width: | Height: | Size: 211 B |
BIN
Lib/site-packages/win32com/HTML/image/BTN_ManualTop.gif
Normal file
|
After Width: | Height: | Size: 215 B |
BIN
Lib/site-packages/win32com/HTML/image/BTN_NextPage.gif
Normal file
|
After Width: | Height: | Size: 218 B |
BIN
Lib/site-packages/win32com/HTML/image/BTN_PrevPage.gif
Normal file
|
After Width: | Height: | Size: 216 B |
BIN
Lib/site-packages/win32com/HTML/image/blank.gif
Normal file
|
After Width: | Height: | Size: 864 B |
BIN
Lib/site-packages/win32com/HTML/image/pycom_blowing.gif
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
Lib/site-packages/win32com/HTML/image/pythoncom.gif
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
Lib/site-packages/win32com/HTML/image/www_icon.gif
Normal file
|
After Width: | Height: | Size: 275 B |
31
Lib/site-packages/win32com/HTML/index.html
Normal 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>
|
||||
18
Lib/site-packages/win32com/HTML/misc.html
Normal 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 don’t 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>
|
||||
37
Lib/site-packages/win32com/HTML/package.html
Normal 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> </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->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>
|
||||
162
Lib/site-packages/win32com/HTML/variant.html
Normal 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>
|
||||
30
Lib/site-packages/win32com/License.txt
Normal 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.
|
||||
120
Lib/site-packages/win32com/__init__.py
Normal 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
|
||||
53
Lib/site-packages/win32com/client/CLSIDToClass.py
Normal 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
|
||||
539
Lib/site-packages/win32com/client/__init__.py
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
# 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):
|
||||
"""'Cast' a COM object to another interface"""
|
||||
# todo - should support target being an IID
|
||||
if 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)
|
||||
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)
|
||||
630
Lib/site-packages/win32com/client/build.py
Normal 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")
|
||||
540
Lib/site-packages/win32com/client/combrowse.py
Normal 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) diestribution 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))
|
||||
43
Lib/site-packages/win32com/client/connect.py
Normal 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
|
||||
581
Lib/site-packages/win32com/client/dynamic.py
Normal 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 invoke_type
|
||||
|
||||
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))
|
||||
698
Lib/site-packages/win32com/client/gencache.py
Normal file
|
|
@ -0,0 +1,698 @@
|
|||
"""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.StringIO(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, bGUIProgress = 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.
|
||||
"""
|
||||
if bGUIProgress is not None:
|
||||
print("The 'bGuiProgress' param to 'MakeModuleForTypelib' is obsolete.")
|
||||
|
||||
from . import makepy
|
||||
try:
|
||||
makepy.GenerateFromTypeLibSpec( (typelibCLSID, lcid, major, minor), progressInstance=progressInstance, bForDemand = bForDemand, bBuildHidden = bBuildHidden)
|
||||
except pywintypes.com_error:
|
||||
return None
|
||||
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
|
||||
1074
Lib/site-packages/win32com/client/genpy.py
Normal file
390
Lib/site-packages/win32com/client/makepy.py
Normal 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 is not '' 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)
|
||||
160
Lib/site-packages/win32com/client/selecttlb.py
Normal 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__)
|
||||
243
Lib/site-packages/win32com/client/tlbrowse.py
Normal 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())
|
||||
84
Lib/site-packages/win32com/client/util.py
Normal 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)
|
||||
0
Lib/site-packages/win32com/demos/__init__.py
Normal file
83
Lib/site-packages/win32com/demos/connect.py
Normal 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)
|
||||
68
Lib/site-packages/win32com/demos/dump_clipboard.py
Normal 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()))
|
||||
94
Lib/site-packages/win32com/demos/eventsApartmentThreaded.py
Normal 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()
|
||||
88
Lib/site-packages/win32com/demos/eventsFreeThreaded.py
Normal 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()
|
||||
139
Lib/site-packages/win32com/demos/excelAddin.py
Normal 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)
|
||||
409
Lib/site-packages/win32com/demos/excelRTDServer.py
Normal 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)
|
||||
188
Lib/site-packages/win32com/demos/iebutton.py
Normal 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)
|
||||
320
Lib/site-packages/win32com/demos/ietoolbar.py
Normal 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
|
||||
114
Lib/site-packages/win32com/demos/outlookAddin.py
Normal 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 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
|
||||
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)
|
||||
75
Lib/site-packages/win32com/demos/trybag.py
Normal 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()
|
||||
787
Lib/site-packages/win32com/include/PythonCOM.h
Normal file
|
|
@ -0,0 +1,787 @@
|
|||
/* 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_IDISPATCHEX
|
||||
#define NO_PYCOM_IPROVIDECLASSINFO
|
||||
#define NO_PYCOM_ISERVICEPROVIDER
|
||||
#define NO_PYCOM_ENUMSTATPROPSTG
|
||||
#define NO_PYCOM_IPROPERTYSTORAGE
|
||||
#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__
|
||||
55
Lib/site-packages/win32com/include/PythonCOMRegister.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// 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__ */
|
||||
162
Lib/site-packages/win32com/include/PythonCOMServer.h
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
#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__ */
|
||||
BIN
Lib/site-packages/win32com/libs/axscript.lib
Normal file
BIN
Lib/site-packages/win32com/libs/pythoncom.lib
Normal file
1
Lib/site-packages/win32com/makegw/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# indicates a python package.
|
||||
451
Lib/site-packages/win32com/makegw/makegw.py
Normal 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")
|
||||
317
Lib/site-packages/win32com/makegw/makegwenum.py
Normal 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())
|
||||
783
Lib/site-packages/win32com/makegw/makegwparse.py
Normal 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)))
|
||||
68
Lib/site-packages/win32com/olectl.py
Normal 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
|
||||
|
||||
87
Lib/site-packages/win32com/readme.htm
Normal 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. 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. 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>
|
||||
1
Lib/site-packages/win32com/server/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Empty __init__ file to designate a sub-package.
|
||||
65
Lib/site-packages/win32com/server/connect.py
Normal 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)))
|
||||
|
||||
|
||||
270
Lib/site-packages/win32com/server/dispatcher.py
Normal 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
|
||||
91
Lib/site-packages/win32com/server/exception.py
Normal 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
|
||||
22
Lib/site-packages/win32com/server/factory.py
Normal 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)
|
||||
49
Lib/site-packages/win32com/server/localserver.py
Normal 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()
|
||||
750
Lib/site-packages/win32com/server/policy.py
Normal file
|
|
@ -0,0 +1,750 @@
|
|||
"""Policies
|
||||
|
||||
Note that Dispatchers are now implemented in "dispatcher.py", but
|
||||
are still documented here.
|
||||
|
||||
Policies
|
||||
|
||||
A policy is an object which manages the interaction between a public
|
||||
Python object, and COM . In simple terms, the policy object is the
|
||||
object which is actually called by COM, and it invokes the requested
|
||||
method, fetches/sets the requested property, etc. See the
|
||||
@win32com.server.policy.CreateInstance@ method for a description of
|
||||
how a policy is specified or created.
|
||||
|
||||
Exactly how a policy determines which underlying object method/property
|
||||
is obtained is up to the policy. A few policies are provided, but you
|
||||
can build your own. See each policy class for a description of how it
|
||||
implements its policy.
|
||||
|
||||
There is a policy that allows the object to specify exactly which
|
||||
methods and properties will be exposed. There is also a policy that
|
||||
will dynamically expose all Python methods and properties - even those
|
||||
added after the object has been instantiated.
|
||||
|
||||
Dispatchers
|
||||
|
||||
A Dispatcher is a level in front of a Policy. A dispatcher is the
|
||||
thing which actually receives the COM calls, and passes them to the
|
||||
policy object (which in turn somehow does something with the wrapped
|
||||
object).
|
||||
|
||||
It is important to note that a policy does not need to have a dispatcher.
|
||||
A dispatcher has the same interface as a policy, and simply steps in its
|
||||
place, delegating to the real policy. The primary use for a Dispatcher
|
||||
is to support debugging when necessary, but without imposing overheads
|
||||
when not (ie, by not using a dispatcher at all).
|
||||
|
||||
There are a few dispatchers provided - "tracing" dispatchers which simply
|
||||
prints calls and args (including a variation which uses
|
||||
win32api.OutputDebugString), and a "debugger" dispatcher, which can
|
||||
invoke the debugger when necessary.
|
||||
|
||||
Error Handling
|
||||
|
||||
It is important to realise that the caller of these interfaces may
|
||||
not be Python. Therefore, general Python exceptions and tracebacks aren't
|
||||
much use.
|
||||
|
||||
In general, there is an Exception class that should be raised, to allow
|
||||
the framework to extract rich COM type error information.
|
||||
|
||||
The general rule is that the **only** exception returned from Python COM
|
||||
Server code should be an Exception instance. Any other Python exception
|
||||
should be considered an implementation bug in the server (if not, it
|
||||
should be handled, and an appropriate Exception instance raised). Any
|
||||
other exception is considered "unexpected", and a dispatcher may take
|
||||
special action (see Dispatchers above)
|
||||
|
||||
Occasionally, the implementation will raise the policy.error error.
|
||||
This usually means there is a problem in the implementation that the
|
||||
Python programmer should fix.
|
||||
|
||||
For example, if policy is asked to wrap an object which it can not
|
||||
support (because, eg, it does not provide _public_methods_ or _dynamic_)
|
||||
then policy.error will be raised, indicating it is a Python programmers
|
||||
problem, rather than a COM error.
|
||||
|
||||
"""
|
||||
__author__ = "Greg Stein and Mark Hammond"
|
||||
|
||||
import win32api
|
||||
import winerror
|
||||
import sys
|
||||
import types
|
||||
import pywintypes
|
||||
import win32con, pythoncom
|
||||
|
||||
#Import a few important constants to speed lookups.
|
||||
from pythoncom import \
|
||||
DISPATCH_METHOD, DISPATCH_PROPERTYGET, DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF, \
|
||||
DISPID_UNKNOWN, DISPID_VALUE, DISPID_PROPERTYPUT, DISPID_NEWENUM, \
|
||||
DISPID_EVALUATE, DISPID_CONSTRUCTOR, DISPID_DESTRUCTOR, DISPID_COLLECT,DISPID_STARTENUM
|
||||
|
||||
S_OK = 0
|
||||
|
||||
# Few more globals to speed things.
|
||||
IDispatchType = pythoncom.TypeIIDs[pythoncom.IID_IDispatch]
|
||||
IUnknownType = pythoncom.TypeIIDs[pythoncom.IID_IUnknown]
|
||||
|
||||
from .exception import COMException
|
||||
error = __name__ + " error"
|
||||
|
||||
regSpec = 'CLSID\\%s\\PythonCOM'
|
||||
regPolicy = 'CLSID\\%s\\PythonCOMPolicy'
|
||||
regDispatcher = 'CLSID\\%s\\PythonCOMDispatcher'
|
||||
regAddnPath = 'CLSID\\%s\\PythonCOMPath'
|
||||
|
||||
def CreateInstance(clsid, reqIID):
|
||||
"""Create a new instance of the specified IID
|
||||
|
||||
The COM framework **always** calls this function to create a new
|
||||
instance for the specified CLSID. This function looks up the
|
||||
registry for the name of a policy, creates the policy, and asks the
|
||||
policy to create the specified object by calling the _CreateInstance_ method.
|
||||
|
||||
Exactly how the policy creates the instance is up to the policy. See the
|
||||
specific policy documentation for more details.
|
||||
"""
|
||||
# First see is sys.path should have something on it.
|
||||
try:
|
||||
addnPaths = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
|
||||
regAddnPath % clsid).split(';')
|
||||
for newPath in addnPaths:
|
||||
if newPath not in sys.path:
|
||||
sys.path.insert(0, newPath)
|
||||
except win32api.error:
|
||||
pass
|
||||
try:
|
||||
policy = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
|
||||
regPolicy % clsid)
|
||||
policy = resolve_func(policy)
|
||||
except win32api.error:
|
||||
policy = DefaultPolicy
|
||||
|
||||
try:
|
||||
dispatcher = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
|
||||
regDispatcher % clsid)
|
||||
if dispatcher: dispatcher = resolve_func(dispatcher)
|
||||
except win32api.error:
|
||||
dispatcher = None
|
||||
|
||||
if dispatcher:
|
||||
retObj = dispatcher(policy, None)
|
||||
else:
|
||||
retObj = policy(None)
|
||||
return retObj._CreateInstance_(clsid, reqIID)
|
||||
|
||||
class BasicWrapPolicy:
|
||||
"""The base class of policies.
|
||||
|
||||
Normally not used directly (use a child class, instead)
|
||||
|
||||
This policy assumes we are wrapping another object
|
||||
as the COM server. This supports the delegation of the core COM entry points
|
||||
to either the wrapped object, or to a child class.
|
||||
|
||||
This policy supports the following special attributes on the wrapped object
|
||||
|
||||
_query_interface_ -- A handler which can respond to the COM 'QueryInterface' call.
|
||||
_com_interfaces_ -- An optional list of IIDs which the interface will assume are
|
||||
valid for the object.
|
||||
_invoke_ -- A handler which can respond to the COM 'Invoke' call. If this attribute
|
||||
is not provided, then the default policy implementation is used. If this attribute
|
||||
does exist, it is responsible for providing all required functionality - ie, the
|
||||
policy _invoke_ method is not invoked at all (and nor are you able to call it!)
|
||||
_getidsofnames_ -- A handler which can respond to the COM 'GetIDsOfNames' call. If this attribute
|
||||
is not provided, then the default policy implementation is used. If this attribute
|
||||
does exist, it is responsible for providing all required functionality - ie, the
|
||||
policy _getidsofnames_ method is not invoked at all (and nor are you able to call it!)
|
||||
|
||||
IDispatchEx functionality:
|
||||
|
||||
_invokeex_ -- Very similar to _invoke_, except slightly different arguments are used.
|
||||
And the result is just the _real_ result (rather than the (hresult, argErr, realResult)
|
||||
tuple that _invoke_ uses.
|
||||
This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)
|
||||
_getdispid_ -- Very similar to _getidsofnames_, except slightly different arguments are used,
|
||||
and only 1 property at a time can be fetched (which is all we support in getidsofnames anyway!)
|
||||
This is the new, prefered handler (the default _invoke_ handler simply called _invokeex_)
|
||||
_getnextdispid_- uses self._name_to_dispid_ to enumerate the DISPIDs
|
||||
"""
|
||||
def __init__(self, object):
|
||||
"""Initialise the policy object
|
||||
|
||||
Params:
|
||||
|
||||
object -- The object to wrap. May be None *iff* @BasicWrapPolicy._CreateInstance_@ will be
|
||||
called immediately after this to setup a brand new object
|
||||
"""
|
||||
if object is not None:
|
||||
self._wrap_(object)
|
||||
|
||||
def _CreateInstance_(self, clsid, reqIID):
|
||||
"""Creates a new instance of a **wrapped** object
|
||||
|
||||
This method looks up a "@win32com.server.policy.regSpec@" % clsid entry
|
||||
in the registry (using @DefaultPolicy@)
|
||||
"""
|
||||
try:
|
||||
classSpec = win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
|
||||
regSpec % clsid)
|
||||
except win32api.error:
|
||||
raise error("The object is not correctly registered - %s key can not be read" % (regSpec % clsid))
|
||||
myob = call_func(classSpec)
|
||||
self._wrap_(myob)
|
||||
try:
|
||||
return pythoncom.WrapObject(self, reqIID)
|
||||
except pythoncom.com_error as xxx_todo_changeme:
|
||||
(hr, desc, exc, arg) = xxx_todo_changeme.args
|
||||
from win32com.util import IIDToInterfaceName
|
||||
desc = "The object '%r' was created, but does not support the " \
|
||||
"interface '%s'(%s): %s" \
|
||||
% (myob, IIDToInterfaceName(reqIID), reqIID, desc)
|
||||
raise pythoncom.com_error(hr, desc, exc, arg)
|
||||
|
||||
|
||||
def _wrap_(self, object):
|
||||
"""Wraps up the specified object.
|
||||
|
||||
This function keeps a reference to the passed
|
||||
object, and may interogate it to determine how to respond to COM requests, etc.
|
||||
"""
|
||||
# We "clobber" certain of our own methods with ones
|
||||
# provided by the wrapped object, iff they exist.
|
||||
self._name_to_dispid_ = { }
|
||||
ob = self._obj_ = object
|
||||
if hasattr(ob, '_query_interface_'):
|
||||
self._query_interface_ = ob._query_interface_
|
||||
|
||||
if hasattr(ob, '_invoke_'):
|
||||
self._invoke_ = ob._invoke_
|
||||
|
||||
if hasattr(ob, '_invokeex_'):
|
||||
self._invokeex_ = ob._invokeex_
|
||||
|
||||
if hasattr(ob, '_getidsofnames_'):
|
||||
self._getidsofnames_ = ob._getidsofnames_
|
||||
|
||||
if hasattr(ob, '_getdispid_'):
|
||||
self._getdispid_ = ob._getdispid_
|
||||
|
||||
# Allow for override of certain special attributes.
|
||||
if hasattr(ob, '_com_interfaces_'):
|
||||
self._com_interfaces_ = []
|
||||
# Allow interfaces to be specified by name.
|
||||
for i in ob._com_interfaces_:
|
||||
if type(i) != pywintypes.IIDType:
|
||||
# Prolly a string!
|
||||
if i[0] != "{":
|
||||
i = pythoncom.InterfaceNames[i]
|
||||
else:
|
||||
i = pythoncom.MakeIID(i)
|
||||
self._com_interfaces_.append(i)
|
||||
else:
|
||||
self._com_interfaces_ = [ ]
|
||||
|
||||
# "QueryInterface" handling.
|
||||
def _QueryInterface_(self, iid):
|
||||
"""The main COM entry-point for QueryInterface.
|
||||
|
||||
This checks the _com_interfaces_ attribute and if the interface is not specified
|
||||
there, it calls the derived helper _query_interface_
|
||||
"""
|
||||
if iid in self._com_interfaces_:
|
||||
return 1
|
||||
return self._query_interface_(iid)
|
||||
|
||||
def _query_interface_(self, iid):
|
||||
"""Called if the object does not provide the requested interface in _com_interfaces,
|
||||
and does not provide a _query_interface_ handler.
|
||||
|
||||
Returns a result to the COM framework indicating the interface is not supported.
|
||||
"""
|
||||
return 0
|
||||
|
||||
# "Invoke" handling.
|
||||
def _Invoke_(self, dispid, lcid, wFlags, args):
|
||||
"""The main COM entry-point for Invoke.
|
||||
|
||||
This calls the _invoke_ helper.
|
||||
"""
|
||||
#Translate a possible string dispid to real dispid.
|
||||
if type(dispid) == type(""):
|
||||
try:
|
||||
dispid = self._name_to_dispid_[dispid.lower()]
|
||||
except KeyError:
|
||||
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
|
||||
return self._invoke_(dispid, lcid, wFlags, args)
|
||||
|
||||
def _invoke_(self, dispid, lcid, wFlags, args):
|
||||
# Delegates to the _invokeex_ implementation. This allows
|
||||
# a custom policy to define _invokeex_, and automatically get _invoke_ too.
|
||||
return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
|
||||
|
||||
# "GetIDsOfNames" handling.
|
||||
def _GetIDsOfNames_(self, names, lcid):
|
||||
"""The main COM entry-point for GetIDsOfNames.
|
||||
|
||||
This checks the validity of the arguments, and calls the _getidsofnames_ helper.
|
||||
"""
|
||||
if len(names) > 1:
|
||||
raise COMException(scode = winerror.DISP_E_INVALID, desc="Cannot support member argument names")
|
||||
return self._getidsofnames_(names, lcid)
|
||||
|
||||
def _getidsofnames_(self, names, lcid):
|
||||
### note: lcid is being ignored...
|
||||
return (self._getdispid_(names[0], 0), )
|
||||
|
||||
# IDispatchEx support for policies. Most of the IDispathEx functionality
|
||||
# by default will raise E_NOTIMPL. Thus it is not necessary for derived
|
||||
# policies to explicitely implement all this functionality just to not implement it!
|
||||
|
||||
def _GetDispID_(self, name, fdex):
|
||||
return self._getdispid_(name, fdex)
|
||||
|
||||
def _getdispid_(self, name, fdex):
|
||||
try:
|
||||
### TODO - look at the fdex flags!!!
|
||||
return self._name_to_dispid_[name.lower()]
|
||||
except KeyError:
|
||||
raise COMException(scode = winerror.DISP_E_UNKNOWNNAME)
|
||||
|
||||
# "InvokeEx" handling.
|
||||
def _InvokeEx_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
|
||||
"""The main COM entry-point for InvokeEx.
|
||||
|
||||
This calls the _invokeex_ helper.
|
||||
"""
|
||||
#Translate a possible string dispid to real dispid.
|
||||
if type(dispid) == type(""):
|
||||
try:
|
||||
dispid = self._name_to_dispid_[dispid.lower()]
|
||||
except KeyError:
|
||||
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
|
||||
return self._invokeex_(dispid, lcid, wFlags, args, kwargs, serviceProvider)
|
||||
|
||||
def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
|
||||
"""A stub for _invokeex_ - should never be called.
|
||||
|
||||
Simply raises an exception.
|
||||
"""
|
||||
# Base classes should override this method (and not call the base)
|
||||
raise error("This class does not provide _invokeex_ semantics")
|
||||
|
||||
def _DeleteMemberByName_(self, name, fdex):
|
||||
return self._deletememberbyname_(name, fdex)
|
||||
def _deletememberbyname_(self, name, fdex):
|
||||
raise COMException(scode = winerror.E_NOTIMPL)
|
||||
|
||||
def _DeleteMemberByDispID_(self, id):
|
||||
return self._deletememberbydispid(id)
|
||||
def _deletememberbydispid_(self, id):
|
||||
raise COMException(scode = winerror.E_NOTIMPL)
|
||||
|
||||
def _GetMemberProperties_(self, id, fdex):
|
||||
return self._getmemberproperties_(id, fdex)
|
||||
def _getmemberproperties_(self, id, fdex):
|
||||
raise COMException(scode = winerror.E_NOTIMPL)
|
||||
|
||||
def _GetMemberName_(self, dispid):
|
||||
return self._getmembername_(dispid)
|
||||
def _getmembername_(self, dispid):
|
||||
raise COMException(scode = winerror.E_NOTIMPL)
|
||||
|
||||
def _GetNextDispID_(self, fdex, dispid):
|
||||
return self._getnextdispid_(fdex, dispid)
|
||||
def _getnextdispid_(self, fdex, dispid):
|
||||
ids = list(self._name_to_dispid_.values())
|
||||
ids.sort()
|
||||
if DISPID_STARTENUM in ids: ids.remove(DISPID_STARTENUM)
|
||||
if dispid==DISPID_STARTENUM:
|
||||
return ids[0]
|
||||
else:
|
||||
try:
|
||||
return ids[ids.index(dispid)+1]
|
||||
except ValueError: # dispid not in list?
|
||||
raise COMException(scode = winerror.E_UNEXPECTED)
|
||||
except IndexError: # No more items
|
||||
raise COMException(scode = winerror.S_FALSE)
|
||||
|
||||
def _GetNameSpaceParent_(self):
|
||||
return self._getnamespaceparent()
|
||||
def _getnamespaceparent_(self):
|
||||
raise COMException(scode = winerror.E_NOTIMPL)
|
||||
|
||||
|
||||
class MappedWrapPolicy(BasicWrapPolicy):
|
||||
"""Wraps an object using maps to do its magic
|
||||
|
||||
This policy wraps up a Python object, using a number of maps
|
||||
which translate from a Dispatch ID and flags, into an object to call/getattr, etc.
|
||||
|
||||
It is the responsibility of derived classes to determine exactly how the
|
||||
maps are filled (ie, the derived classes determine the map filling policy.
|
||||
|
||||
This policy supports the following special attributes on the wrapped object
|
||||
|
||||
_dispid_to_func_/_dispid_to_get_/_dispid_to_put_ -- These are dictionaries
|
||||
(keyed by integer dispid, values are string attribute names) which the COM
|
||||
implementation uses when it is processing COM requests. Note that the implementation
|
||||
uses this dictionary for its own purposes - not a copy - which means the contents of
|
||||
these dictionaries will change as the object is used.
|
||||
|
||||
"""
|
||||
def _wrap_(self, object):
|
||||
BasicWrapPolicy._wrap_(self, object)
|
||||
ob = self._obj_
|
||||
if hasattr(ob, '_dispid_to_func_'):
|
||||
self._dispid_to_func_ = ob._dispid_to_func_
|
||||
else:
|
||||
self._dispid_to_func_ = { }
|
||||
if hasattr(ob, '_dispid_to_get_'):
|
||||
self._dispid_to_get_ = ob._dispid_to_get_
|
||||
else:
|
||||
self._dispid_to_get_ = { }
|
||||
if hasattr(ob, '_dispid_to_put_'):
|
||||
self._dispid_to_put_ = ob._dispid_to_put_
|
||||
else:
|
||||
self._dispid_to_put_ = { }
|
||||
|
||||
def _getmembername_(self, dispid):
|
||||
if dispid in self._dispid_to_func_:
|
||||
return self._dispid_to_func_[dispid]
|
||||
elif dispid in self._dispid_to_get_:
|
||||
return self._dispid_to_get_[dispid]
|
||||
elif dispid in self._dispid_to_put_:
|
||||
return self._dispid_to_put_[dispid]
|
||||
else:
|
||||
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND)
|
||||
|
||||
class DesignatedWrapPolicy(MappedWrapPolicy):
|
||||
"""A policy which uses a mapping to link functions and dispid
|
||||
|
||||
A MappedWrappedPolicy which allows the wrapped object to specify, via certain
|
||||
special named attributes, exactly which methods and properties are exposed.
|
||||
|
||||
All a wrapped object need do is provide the special attributes, and the policy
|
||||
will handle everything else.
|
||||
|
||||
Attributes:
|
||||
|
||||
_public_methods_ -- Required, unless a typelib GUID is given -- A list
|
||||
of strings, which must be the names of methods the object
|
||||
provides. These methods will be exposed and callable
|
||||
from other COM hosts.
|
||||
_public_attrs_ A list of strings, which must be the names of attributes on the object.
|
||||
These attributes will be exposed and readable and possibly writeable from other COM hosts.
|
||||
_readonly_attrs_ -- A list of strings, which must also appear in _public_attrs. These
|
||||
attributes will be readable, but not writable, by other COM hosts.
|
||||
_value_ -- A method that will be called if the COM host requests the "default" method
|
||||
(ie, calls Invoke with dispid==DISPID_VALUE)
|
||||
_NewEnum -- A method that will be called if the COM host requests an enumerator on the
|
||||
object (ie, calls Invoke with dispid==DISPID_NEWENUM.)
|
||||
It is the responsibility of the method to ensure the returned
|
||||
object conforms to the required Enum interface.
|
||||
|
||||
_typelib_guid_ -- The GUID of the typelibrary with interface definitions we use.
|
||||
_typelib_version_ -- A tuple of (major, minor) with a default of 1,1
|
||||
_typelib_lcid_ -- The LCID of the typelib, default = LOCALE_USER_DEFAULT
|
||||
|
||||
_Evaluate -- Dunno what this means, except the host has called Invoke with dispid==DISPID_EVALUATE!
|
||||
See the COM documentation for details.
|
||||
"""
|
||||
def _wrap_(self, ob):
|
||||
# If we have nominated universal interfaces to support, load them now
|
||||
tlb_guid = getattr(ob, '_typelib_guid_', None)
|
||||
if tlb_guid is not None:
|
||||
tlb_major, tlb_minor = getattr(ob, '_typelib_version_', (1,0))
|
||||
tlb_lcid = getattr(ob, '_typelib_lcid_', 0)
|
||||
from win32com import universal
|
||||
# XXX - what if the user wants to implement interfaces from multiple
|
||||
# typelibs?
|
||||
# Filter out all 'normal' IIDs (ie, IID objects and strings starting with {
|
||||
interfaces = [i for i in getattr(ob, '_com_interfaces_', [])
|
||||
if type(i) != pywintypes.IIDType and not i.startswith("{")]
|
||||
universal_data = universal.RegisterInterfaces(tlb_guid, tlb_lcid,
|
||||
tlb_major, tlb_minor, interfaces)
|
||||
else:
|
||||
universal_data = []
|
||||
MappedWrapPolicy._wrap_(self, ob)
|
||||
if not hasattr(ob, '_public_methods_') and not hasattr(ob, "_typelib_guid_"):
|
||||
raise error("Object does not support DesignatedWrapPolicy, as it does not have either _public_methods_ or _typelib_guid_ attributes.")
|
||||
|
||||
# Copy existing _dispid_to_func_ entries to _name_to_dispid_
|
||||
for dispid, name in self._dispid_to_func_.items():
|
||||
self._name_to_dispid_[name.lower()]=dispid
|
||||
for dispid, name in self._dispid_to_get_.items():
|
||||
self._name_to_dispid_[name.lower()]=dispid
|
||||
for dispid, name in self._dispid_to_put_.items():
|
||||
self._name_to_dispid_[name.lower()]=dispid
|
||||
|
||||
# Patch up the universal stuff.
|
||||
for dispid, invkind, name in universal_data:
|
||||
self._name_to_dispid_[name.lower()]=dispid
|
||||
if invkind == DISPATCH_METHOD:
|
||||
self._dispid_to_func_[dispid] = name
|
||||
elif invkind in (DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF):
|
||||
self._dispid_to_put_[dispid] = name
|
||||
elif invkind == DISPATCH_PROPERTYGET:
|
||||
self._dispid_to_get_[dispid] = name
|
||||
else:
|
||||
raise ValueError("unexpected invkind: %d (%s)" % (invkind,name))
|
||||
|
||||
# look for reserved methods
|
||||
if hasattr(ob, '_value_'):
|
||||
self._dispid_to_get_[DISPID_VALUE] = '_value_'
|
||||
self._dispid_to_put_[DISPID_PROPERTYPUT] = '_value_'
|
||||
if hasattr(ob, '_NewEnum'):
|
||||
self._name_to_dispid_['_newenum'] = DISPID_NEWENUM
|
||||
self._dispid_to_func_[DISPID_NEWENUM] = '_NewEnum'
|
||||
if hasattr(ob, '_Evaluate'):
|
||||
self._name_to_dispid_['_evaluate'] = DISPID_EVALUATE
|
||||
self._dispid_to_func_[DISPID_EVALUATE] = '_Evaluate'
|
||||
|
||||
next_dispid = self._allocnextdispid(999)
|
||||
# note: funcs have precedence over attrs (install attrs first)
|
||||
if hasattr(ob, '_public_attrs_'):
|
||||
if hasattr(ob, '_readonly_attrs_'):
|
||||
readonly = ob._readonly_attrs_
|
||||
else:
|
||||
readonly = [ ]
|
||||
for name in ob._public_attrs_:
|
||||
dispid = self._name_to_dispid_.get(name.lower())
|
||||
if dispid is None:
|
||||
dispid = next_dispid
|
||||
self._name_to_dispid_[name.lower()] = dispid
|
||||
next_dispid = self._allocnextdispid(next_dispid)
|
||||
self._dispid_to_get_[dispid] = name
|
||||
if name not in readonly:
|
||||
self._dispid_to_put_[dispid] = name
|
||||
for name in getattr(ob, "_public_methods_", []):
|
||||
dispid = self._name_to_dispid_.get(name.lower())
|
||||
if dispid is None:
|
||||
dispid = next_dispid
|
||||
self._name_to_dispid_[name.lower()] = dispid
|
||||
next_dispid = self._allocnextdispid(next_dispid)
|
||||
self._dispid_to_func_[dispid] = name
|
||||
self._typeinfos_ = None # load these on demand.
|
||||
|
||||
def _build_typeinfos_(self):
|
||||
# Can only ever be one for now.
|
||||
tlb_guid = getattr(self._obj_, '_typelib_guid_', None)
|
||||
if tlb_guid is None:
|
||||
return []
|
||||
tlb_major, tlb_minor = getattr(self._obj_, '_typelib_version_', (1,0))
|
||||
tlb = pythoncom.LoadRegTypeLib(tlb_guid, tlb_major, tlb_minor)
|
||||
typecomp = tlb.GetTypeComp()
|
||||
# Not 100% sure what semantics we should use for the default interface.
|
||||
# Look for the first name in _com_interfaces_ that exists in the typelib.
|
||||
for iname in self._obj_._com_interfaces_:
|
||||
try:
|
||||
type_info, type_comp = typecomp.BindType(iname)
|
||||
if type_info is not None:
|
||||
return [type_info]
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
return []
|
||||
|
||||
def _GetTypeInfoCount_(self):
|
||||
if self._typeinfos_ is None:
|
||||
self._typeinfos_ = self._build_typeinfos_()
|
||||
return len(self._typeinfos_)
|
||||
|
||||
def _GetTypeInfo_(self, index, lcid):
|
||||
if self._typeinfos_ is None:
|
||||
self._typeinfos_ = self._build_typeinfos_()
|
||||
if index < 0 or index >= len(self._typeinfos_):
|
||||
raise COMException(scode=winerror.DISP_E_BADINDEX)
|
||||
return 0, self._typeinfos_[index]
|
||||
|
||||
def _allocnextdispid(self, last_dispid):
|
||||
while 1:
|
||||
last_dispid = last_dispid + 1
|
||||
if last_dispid not in self._dispid_to_func_ and \
|
||||
last_dispid not in self._dispid_to_get_ and \
|
||||
last_dispid not in self._dispid_to_put_:
|
||||
return last_dispid
|
||||
|
||||
def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):
|
||||
### note: lcid is being ignored...
|
||||
|
||||
if wFlags & DISPATCH_METHOD:
|
||||
try:
|
||||
funcname = self._dispid_to_func_[dispid]
|
||||
except KeyError:
|
||||
if not wFlags & DISPATCH_PROPERTYGET:
|
||||
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
|
||||
else:
|
||||
try:
|
||||
func = getattr(self._obj_, funcname)
|
||||
except AttributeError:
|
||||
# May have a dispid, but that doesnt mean we have the function!
|
||||
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)
|
||||
# Should check callable here
|
||||
try:
|
||||
return func(*args)
|
||||
except TypeError as v:
|
||||
# Particularly nasty is "wrong number of args" type error
|
||||
# This helps you see what 'func' and 'args' actually is
|
||||
if str(v).find("arguments")>=0:
|
||||
print("** TypeError %s calling function %r(%r)" % (v, func, args))
|
||||
raise
|
||||
|
||||
if wFlags & DISPATCH_PROPERTYGET:
|
||||
try:
|
||||
name = self._dispid_to_get_[dispid]
|
||||
except KeyError:
|
||||
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
|
||||
retob = getattr(self._obj_, name)
|
||||
if type(retob)==types.MethodType: # a method as a property - call it.
|
||||
retob = retob(*args)
|
||||
return retob
|
||||
|
||||
if wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF): ### correct?
|
||||
try:
|
||||
name = self._dispid_to_put_[dispid]
|
||||
except KeyError:
|
||||
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # read-only
|
||||
# If we have a method of that name (ie, a property get function), and
|
||||
# we have an equiv. property set function, use that instead.
|
||||
if type(getattr(self._obj_, name, None)) == types.MethodType and \
|
||||
type(getattr(self._obj_, "Set" + name, None)) == types.MethodType:
|
||||
fn = getattr(self._obj_, "Set" + name)
|
||||
fn( *args )
|
||||
else:
|
||||
# just set the attribute
|
||||
setattr(self._obj_, name, args[0])
|
||||
return
|
||||
|
||||
raise COMException(scode=winerror.E_INVALIDARG, desc="invalid wFlags")
|
||||
|
||||
class EventHandlerPolicy(DesignatedWrapPolicy):
|
||||
"""The default policy used by event handlers in the win32com.client package.
|
||||
|
||||
In addition to the base policy, this provides argument conversion semantics for
|
||||
params
|
||||
* dispatch params are converted to dispatch objects.
|
||||
* Unicode objects are converted to strings (1.5.2 and earlier)
|
||||
|
||||
NOTE: Later, we may allow the object to override this process??
|
||||
"""
|
||||
def _transform_args_(self, args, kwArgs, dispid, lcid, wFlags, serviceProvider):
|
||||
ret = []
|
||||
for arg in args:
|
||||
arg_type = type(arg)
|
||||
if arg_type == IDispatchType:
|
||||
import win32com.client
|
||||
arg = win32com.client.Dispatch(arg)
|
||||
elif arg_type == IUnknownType:
|
||||
try:
|
||||
import win32com.client
|
||||
arg = win32com.client.Dispatch(arg.QueryInterface(pythoncom.IID_IDispatch))
|
||||
except pythoncom.error:
|
||||
pass # Keep it as IUnknown
|
||||
ret.append(arg)
|
||||
return tuple(ret), kwArgs
|
||||
def _invokeex_(self, dispid, lcid, wFlags, args, kwArgs, serviceProvider):
|
||||
# transform the args.
|
||||
args, kwArgs = self._transform_args_(args, kwArgs, dispid, lcid, wFlags, serviceProvider)
|
||||
return DesignatedWrapPolicy._invokeex_( self, dispid, lcid, wFlags, args, kwArgs, serviceProvider)
|
||||
|
||||
class DynamicPolicy(BasicWrapPolicy):
|
||||
"""A policy which dynamically (ie, at run-time) determines public interfaces.
|
||||
|
||||
A dynamic policy is used to dynamically dispatch methods and properties to the
|
||||
wrapped object. The list of objects and properties does not need to be known in
|
||||
advance, and methods or properties added to the wrapped object after construction
|
||||
are also handled.
|
||||
|
||||
The wrapped object must provide the following attributes:
|
||||
|
||||
_dynamic_ -- A method that will be called whenever an invoke on the object
|
||||
is called. The method is called with the name of the underlying method/property
|
||||
(ie, the mapping of dispid to/from name has been resolved.) This name property
|
||||
may also be '_value_' to indicate the default, and '_NewEnum' to indicate a new
|
||||
enumerator is requested.
|
||||
|
||||
"""
|
||||
def _wrap_(self, object):
|
||||
BasicWrapPolicy._wrap_(self, object)
|
||||
if not hasattr(self._obj_, '_dynamic_'):
|
||||
raise error("Object does not support Dynamic COM Policy")
|
||||
self._next_dynamic_ = self._min_dynamic_ = 1000
|
||||
self._dyn_dispid_to_name_ = {DISPID_VALUE:'_value_', DISPID_NEWENUM:'_NewEnum' }
|
||||
|
||||
def _getdispid_(self, name, fdex):
|
||||
# TODO - Look at fdex flags.
|
||||
lname = name.lower()
|
||||
try:
|
||||
return self._name_to_dispid_[lname]
|
||||
except KeyError:
|
||||
dispid = self._next_dynamic_ = self._next_dynamic_ + 1
|
||||
self._name_to_dispid_[lname] = dispid
|
||||
self._dyn_dispid_to_name_[dispid] = name # Keep case in this map...
|
||||
return dispid
|
||||
|
||||
def _invoke_(self, dispid, lcid, wFlags, args):
|
||||
return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
|
||||
|
||||
def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
|
||||
### note: lcid is being ignored...
|
||||
### note: kwargs is being ignored...
|
||||
### note: serviceProvider is being ignored...
|
||||
### there might be assigned DISPID values to properties, too...
|
||||
try:
|
||||
name = self._dyn_dispid_to_name_[dispid]
|
||||
except KeyError:
|
||||
raise COMException(scode = winerror.DISP_E_MEMBERNOTFOUND, desc="Member not found")
|
||||
return self._obj_._dynamic_(name, lcid, wFlags, args)
|
||||
|
||||
|
||||
DefaultPolicy = DesignatedWrapPolicy
|
||||
|
||||
def resolve_func(spec):
|
||||
"""Resolve a function by name
|
||||
|
||||
Given a function specified by 'module.function', return a callable object
|
||||
(ie, the function itself)
|
||||
"""
|
||||
try:
|
||||
idx = spec.rindex(".")
|
||||
mname = spec[:idx]
|
||||
fname = spec[idx+1:]
|
||||
# Dont attempt to optimize by looking in sys.modules,
|
||||
# as another thread may also be performing the import - this
|
||||
# way we take advantage of the built-in import lock.
|
||||
module = _import_module(mname)
|
||||
return getattr(module, fname)
|
||||
except ValueError: # No "." in name - assume in this module
|
||||
return globals()[spec]
|
||||
|
||||
def call_func(spec, *args):
|
||||
"""Call a function specified by name.
|
||||
|
||||
Call a function specified by 'module.function' and return the result.
|
||||
"""
|
||||
|
||||
return resolve_func(spec)(*args)
|
||||
|
||||
def _import_module(mname):
|
||||
"""Import a module just like the 'import' statement.
|
||||
|
||||
Having this function is much nicer for importing arbitrary modules than
|
||||
using the 'exec' keyword. It is more efficient and obvious to the reader.
|
||||
"""
|
||||
__import__(mname)
|
||||
# Eeek - result of _import_ is "win32com" - not "win32com.a.b.c"
|
||||
# Get the full module from sys.modules
|
||||
return sys.modules[mname]
|
||||
|
||||
#######
|
||||
#
|
||||
# Temporary hacks until all old code moves.
|
||||
#
|
||||
# These have been moved to a new source file, but some code may
|
||||
# still reference them here. These will end up being removed.
|
||||
try:
|
||||
from .dispatcher import DispatcherTrace, DispatcherWin32trace
|
||||
except ImportError: # Quite likely a frozen executable that doesnt need dispatchers
|
||||
pass
|
||||
617
Lib/site-packages/win32com/server/register.py
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
"""Utilities for registering objects.
|
||||
|
||||
This module contains utility functions to register Python objects as
|
||||
valid COM Servers. The RegisterServer function provides all information
|
||||
necessary to allow the COM framework to respond to a request for a COM object,
|
||||
construct the necessary Python object, and dispatch COM events.
|
||||
|
||||
"""
|
||||
import sys
|
||||
import win32api
|
||||
import win32con
|
||||
import pythoncom
|
||||
import winerror
|
||||
import os
|
||||
|
||||
CATID_PythonCOMServer = "{B3EF80D0-68E2-11D0-A689-00C04FD658FF}"
|
||||
|
||||
def _set_subkeys(keyName, valueDict, base=win32con.HKEY_CLASSES_ROOT):
|
||||
hkey = win32api.RegCreateKey(base, keyName)
|
||||
try:
|
||||
for key, value in valueDict.items():
|
||||
win32api.RegSetValueEx(hkey, key, None, win32con.REG_SZ, value)
|
||||
finally:
|
||||
win32api.RegCloseKey(hkey)
|
||||
|
||||
def _set_string(path, value, base=win32con.HKEY_CLASSES_ROOT):
|
||||
"Set a string value in the registry."
|
||||
|
||||
win32api.RegSetValue(base,
|
||||
path,
|
||||
win32con.REG_SZ,
|
||||
value)
|
||||
|
||||
def _get_string(path, base=win32con.HKEY_CLASSES_ROOT):
|
||||
"Get a string value from the registry."
|
||||
|
||||
try:
|
||||
return win32api.RegQueryValue(base, path)
|
||||
except win32api.error:
|
||||
return None
|
||||
|
||||
def _remove_key(path, base=win32con.HKEY_CLASSES_ROOT):
|
||||
"Remove a string from the registry."
|
||||
|
||||
try:
|
||||
win32api.RegDeleteKey(base, path)
|
||||
except win32api.error as xxx_todo_changeme1:
|
||||
(code, fn, msg) = xxx_todo_changeme1.args
|
||||
if code != winerror.ERROR_FILE_NOT_FOUND:
|
||||
raise win32api.error(code, fn, msg)
|
||||
|
||||
def recurse_delete_key(path, base=win32con.HKEY_CLASSES_ROOT):
|
||||
"""Recursively delete registry keys.
|
||||
|
||||
This is needed since you can't blast a key when subkeys exist.
|
||||
"""
|
||||
try:
|
||||
h = win32api.RegOpenKey(base, path)
|
||||
except win32api.error as xxx_todo_changeme2:
|
||||
(code, fn, msg) = xxx_todo_changeme2.args
|
||||
if code != winerror.ERROR_FILE_NOT_FOUND:
|
||||
raise win32api.error(code, fn, msg)
|
||||
else:
|
||||
# parent key found and opened successfully. do some work, making sure
|
||||
# to always close the thing (error or no).
|
||||
try:
|
||||
# remove all of the subkeys
|
||||
while 1:
|
||||
try:
|
||||
subkeyname = win32api.RegEnumKey(h, 0)
|
||||
except win32api.error as xxx_todo_changeme:
|
||||
(code, fn, msg) = xxx_todo_changeme.args
|
||||
if code != winerror.ERROR_NO_MORE_ITEMS:
|
||||
raise win32api.error(code, fn, msg)
|
||||
break
|
||||
recurse_delete_key(path + '\\' + subkeyname, base)
|
||||
|
||||
# remove the parent key
|
||||
_remove_key(path, base)
|
||||
finally:
|
||||
win32api.RegCloseKey(h)
|
||||
|
||||
def _cat_registrar():
|
||||
return pythoncom.CoCreateInstance(
|
||||
pythoncom.CLSID_StdComponentCategoriesMgr,
|
||||
None,
|
||||
pythoncom.CLSCTX_INPROC_SERVER,
|
||||
pythoncom.IID_ICatRegister
|
||||
)
|
||||
|
||||
def _find_localserver_exe(mustfind):
|
||||
if not sys.platform.startswith("win32"):
|
||||
return sys.executable
|
||||
if pythoncom.__file__.find("_d") < 0:
|
||||
exeBaseName = "pythonw.exe"
|
||||
else:
|
||||
exeBaseName = "pythonw_d.exe"
|
||||
# First see if in the same directory as this .EXE
|
||||
exeName = os.path.join( os.path.split(sys.executable)[0], exeBaseName )
|
||||
if not os.path.exists(exeName):
|
||||
# See if in our sys.prefix directory
|
||||
exeName = os.path.join( sys.prefix, exeBaseName )
|
||||
if not os.path.exists(exeName):
|
||||
# See if in our sys.prefix/pcbuild directory (for developers)
|
||||
if "64 bit" in sys.version:
|
||||
exeName = os.path.join( sys.prefix, "PCbuild", "amd64", exeBaseName )
|
||||
else:
|
||||
exeName = os.path.join( sys.prefix, "PCbuild", exeBaseName )
|
||||
if not os.path.exists(exeName):
|
||||
# See if the registry has some info.
|
||||
try:
|
||||
key = "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % sys.winver
|
||||
path = win32api.RegQueryValue( win32con.HKEY_LOCAL_MACHINE, key )
|
||||
exeName = os.path.join( path, exeBaseName )
|
||||
except (AttributeError,win32api.error):
|
||||
pass
|
||||
if not os.path.exists(exeName):
|
||||
if mustfind:
|
||||
raise RuntimeError("Can not locate the program '%s'" % exeBaseName)
|
||||
return None
|
||||
return exeName
|
||||
|
||||
def _find_localserver_module():
|
||||
import win32com.server
|
||||
path = win32com.server.__path__[0]
|
||||
baseName = "localserver"
|
||||
pyfile = os.path.join(path, baseName + ".py")
|
||||
try:
|
||||
os.stat(pyfile)
|
||||
except os.error:
|
||||
# See if we have a compiled extension
|
||||
if __debug__:
|
||||
ext = ".pyc"
|
||||
else:
|
||||
ext = ".pyo"
|
||||
pyfile = os.path.join(path, baseName + ext)
|
||||
try:
|
||||
os.stat(pyfile)
|
||||
except os.error:
|
||||
raise RuntimeError("Can not locate the Python module 'win32com.server.%s'" % baseName)
|
||||
return pyfile
|
||||
|
||||
def RegisterServer(clsid,
|
||||
pythonInstString=None,
|
||||
desc=None,
|
||||
progID=None, verProgID=None,
|
||||
defIcon=None,
|
||||
threadingModel="both",
|
||||
policy=None,
|
||||
catids=[], other={},
|
||||
addPyComCat=None,
|
||||
dispatcher = None,
|
||||
clsctx = None,
|
||||
addnPath = None,
|
||||
):
|
||||
"""Registers a Python object as a COM Server. This enters almost all necessary
|
||||
information in the system registry, allowing COM to use the object.
|
||||
|
||||
clsid -- The (unique) CLSID of the server.
|
||||
pythonInstString -- A string holding the instance name that will be created
|
||||
whenever COM requests a new object.
|
||||
desc -- The description of the COM object.
|
||||
progID -- The user name of this object (eg, Word.Document)
|
||||
verProgId -- The user name of this version's implementation (eg Word.6.Document)
|
||||
defIcon -- The default icon for the object.
|
||||
threadingModel -- The threading model this object supports.
|
||||
policy -- The policy to use when creating this object.
|
||||
catids -- A list of category ID's this object belongs in.
|
||||
other -- A dictionary of extra items to be registered.
|
||||
addPyComCat -- A flag indicating if the object should be added to the list
|
||||
of Python servers installed on the machine. If None (the default)
|
||||
then it will be registered when running from python source, but
|
||||
not registered if running in a frozen environment.
|
||||
dispatcher -- The dispatcher to use when creating this object.
|
||||
clsctx -- One of the CLSCTX_* constants.
|
||||
addnPath -- An additional path the COM framework will add to sys.path
|
||||
before attempting to create the object.
|
||||
"""
|
||||
|
||||
|
||||
### backwards-compat check
|
||||
### Certain policies do not require a "class name", just the policy itself.
|
||||
if not pythonInstString and not policy:
|
||||
raise TypeError('You must specify either the Python Class or Python Policy which implement the COM object.')
|
||||
|
||||
keyNameRoot = "CLSID\\%s" % str(clsid)
|
||||
_set_string(keyNameRoot, desc)
|
||||
|
||||
# Also register as an "Application" so DCOM etc all see us.
|
||||
_set_string("AppID\\%s" % clsid, progID)
|
||||
# Depending on contexts requested, register the specified server type.
|
||||
# Set default clsctx.
|
||||
if not clsctx:
|
||||
clsctx = pythoncom.CLSCTX_INPROC_SERVER | pythoncom.CLSCTX_LOCAL_SERVER
|
||||
# And if we are frozen, ignore the ones that don't make sense in this
|
||||
# context.
|
||||
if pythoncom.frozen:
|
||||
assert sys.frozen, "pythoncom is frozen, but sys.frozen is not set - don't know the context!"
|
||||
if sys.frozen == "dll":
|
||||
clsctx = clsctx & pythoncom.CLSCTX_INPROC_SERVER
|
||||
else:
|
||||
clsctx = clsctx & pythoncom.CLSCTX_LOCAL_SERVER
|
||||
# Now setup based on the clsctx left over.
|
||||
if clsctx & pythoncom.CLSCTX_INPROC_SERVER:
|
||||
# get the module to use for registration.
|
||||
# nod to Gordon's installer - if sys.frozen and sys.frozendllhandle
|
||||
# exist, then we are being registered via a DLL - use this DLL as the
|
||||
# file name.
|
||||
if pythoncom.frozen:
|
||||
if hasattr(sys, "frozendllhandle"):
|
||||
dllName = win32api.GetModuleFileName(sys.frozendllhandle)
|
||||
else:
|
||||
raise RuntimeError("We appear to have a frozen DLL, but I don't know the DLL to use")
|
||||
else:
|
||||
# Normal case - running from .py file, so register pythoncom's DLL.
|
||||
# Although now we prefer a 'loader' DLL if it exists to avoid some
|
||||
# manifest issues (the 'loader' DLL has a manifest, but pythoncom does not)
|
||||
pythoncom_dir = os.path.dirname(pythoncom.__file__)
|
||||
if pythoncom.__file__.find("_d") < 0:
|
||||
suffix = ""
|
||||
else:
|
||||
suffix = "_d"
|
||||
loadername = "pythoncomloader%d%d%s.dll" % (sys.version_info[0], sys.version_info[1], suffix)
|
||||
if os.path.isfile(os.path.join(pythoncom_dir, loadername)):
|
||||
dllName = loadername
|
||||
else:
|
||||
# just use pythoncom.
|
||||
dllName = os.path.basename(pythoncom.__file__)
|
||||
|
||||
_set_subkeys(keyNameRoot + "\\InprocServer32",
|
||||
{ None : dllName,
|
||||
"ThreadingModel" : threadingModel,
|
||||
})
|
||||
else: # Remove any old InProcServer32 registrations
|
||||
_remove_key(keyNameRoot + "\\InprocServer32")
|
||||
|
||||
if clsctx & pythoncom.CLSCTX_LOCAL_SERVER:
|
||||
if pythoncom.frozen:
|
||||
# If we are frozen, we write "{exe} /Automate", just
|
||||
# like "normal" .EXEs do
|
||||
exeName = win32api.GetShortPathName(sys.executable)
|
||||
command = '%s /Automate' % (exeName,)
|
||||
else:
|
||||
# Running from .py sources - we need to write
|
||||
# 'python.exe win32com\server\localserver.py {clsid}"
|
||||
exeName = _find_localserver_exe(1)
|
||||
exeName = win32api.GetShortPathName(exeName)
|
||||
pyfile = _find_localserver_module()
|
||||
command = '%s "%s" %s' % (exeName, pyfile, str(clsid))
|
||||
_set_string(keyNameRoot + '\\LocalServer32', command)
|
||||
else: # Remove any old LocalServer32 registrations
|
||||
_remove_key(keyNameRoot + "\\LocalServer32")
|
||||
|
||||
if pythonInstString:
|
||||
_set_string(keyNameRoot + '\\PythonCOM', pythonInstString)
|
||||
else:
|
||||
_remove_key(keyNameRoot + '\\PythonCOM')
|
||||
if policy:
|
||||
_set_string(keyNameRoot + '\\PythonCOMPolicy', policy)
|
||||
else:
|
||||
_remove_key(keyNameRoot + '\\PythonCOMPolicy')
|
||||
|
||||
if dispatcher:
|
||||
_set_string(keyNameRoot + '\\PythonCOMDispatcher', dispatcher)
|
||||
else:
|
||||
_remove_key(keyNameRoot + '\\PythonCOMDispatcher')
|
||||
|
||||
if defIcon:
|
||||
_set_string(keyNameRoot + '\\DefaultIcon', defIcon)
|
||||
else:
|
||||
_remove_key(keyNameRoot + '\\DefaultIcon')
|
||||
|
||||
if addnPath:
|
||||
_set_string(keyNameRoot + "\\PythonCOMPath", addnPath)
|
||||
else:
|
||||
_remove_key(keyNameRoot + "\\PythonCOMPath")
|
||||
|
||||
if addPyComCat is None:
|
||||
addPyComCat = pythoncom.frozen == 0
|
||||
if addPyComCat:
|
||||
catids = catids + [ CATID_PythonCOMServer ]
|
||||
|
||||
# Set up the implemented categories
|
||||
if catids:
|
||||
regCat = _cat_registrar()
|
||||
regCat.RegisterClassImplCategories(clsid, catids)
|
||||
|
||||
# set up any other reg values they might have
|
||||
if other:
|
||||
for key, value in other.items():
|
||||
_set_string(keyNameRoot + '\\' + key, value)
|
||||
|
||||
if progID:
|
||||
# set the progID as the most specific that was given to us
|
||||
if verProgID:
|
||||
_set_string(keyNameRoot + '\\ProgID', verProgID)
|
||||
else:
|
||||
_set_string(keyNameRoot + '\\ProgID', progID)
|
||||
|
||||
# Set up the root entries - version independent.
|
||||
if desc:
|
||||
_set_string(progID, desc)
|
||||
_set_string(progID + '\\CLSID', str(clsid))
|
||||
|
||||
# Set up the root entries - version dependent.
|
||||
if verProgID:
|
||||
# point from independent to the current version
|
||||
_set_string(progID + '\\CurVer', verProgID)
|
||||
|
||||
# point to the version-independent one
|
||||
_set_string(keyNameRoot + '\\VersionIndependentProgID', progID)
|
||||
|
||||
# set up the versioned progID
|
||||
if desc:
|
||||
_set_string(verProgID, desc)
|
||||
_set_string(verProgID + '\\CLSID', str(clsid))
|
||||
|
||||
def GetUnregisterServerKeys(clsid, progID=None, verProgID=None, customKeys = None):
|
||||
"""Given a server, return a list of of ("key", root), which are keys recursively
|
||||
and uncondtionally deleted at unregister or uninstall time.
|
||||
"""
|
||||
# remove the main CLSID registration
|
||||
ret = [("CLSID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT)]
|
||||
# remove the versioned ProgID registration
|
||||
if verProgID:
|
||||
ret.append((verProgID, win32con.HKEY_CLASSES_ROOT))
|
||||
# blow away the independent ProgID. we can't leave it since we just
|
||||
# torched the class.
|
||||
### could potentially check the CLSID... ?
|
||||
if progID:
|
||||
ret.append((progID, win32con.HKEY_CLASSES_ROOT))
|
||||
# The DCOM config tool may write settings to the AppID key for our CLSID
|
||||
ret.append( ("AppID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT) )
|
||||
# Any custom keys?
|
||||
if customKeys:
|
||||
ret = ret + customKeys
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def UnregisterServer(clsid, progID=None, verProgID=None, customKeys = None):
|
||||
"""Unregisters a Python COM server."""
|
||||
|
||||
for args in GetUnregisterServerKeys(clsid, progID, verProgID, customKeys ):
|
||||
recurse_delete_key(*args)
|
||||
|
||||
### it might be nice at some point to "roll back" the independent ProgID
|
||||
### to an earlier version if one exists, and just blowing away the
|
||||
### specified version of the ProgID (and its corresponding CLSID)
|
||||
### another time, though...
|
||||
|
||||
### NOTE: ATL simply blows away the above three keys without the
|
||||
### potential checks that I describe. Assuming that defines the
|
||||
### "standard" then we have no additional changes necessary.
|
||||
|
||||
def GetRegisteredServerOption(clsid, optionName):
|
||||
"""Given a CLSID for a server and option name, return the option value
|
||||
"""
|
||||
keyNameRoot = "CLSID\\%s\\%s" % (str(clsid), str(optionName))
|
||||
return _get_string(keyNameRoot)
|
||||
|
||||
|
||||
def _get(ob, attr, default=None):
|
||||
try:
|
||||
return getattr(ob, attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
# look down sub-classes
|
||||
try:
|
||||
bases = ob.__bases__
|
||||
except AttributeError:
|
||||
# ob is not a class - no probs.
|
||||
return default
|
||||
for base in bases:
|
||||
val = _get(base, attr, None)
|
||||
if val is not None:
|
||||
return val
|
||||
return default
|
||||
|
||||
def RegisterClasses(*classes, **flags):
|
||||
quiet = 'quiet' in flags and flags['quiet']
|
||||
debugging = 'debug' in flags and flags['debug']
|
||||
for cls in classes:
|
||||
clsid = cls._reg_clsid_
|
||||
progID = _get(cls, '_reg_progid_')
|
||||
desc = _get(cls, '_reg_desc_', progID)
|
||||
spec = _get(cls, '_reg_class_spec_')
|
||||
verProgID = _get(cls, '_reg_verprogid_')
|
||||
defIcon = _get(cls, '_reg_icon_')
|
||||
threadingModel = _get(cls, '_reg_threading_', 'both')
|
||||
catids = _get(cls, '_reg_catids_', [])
|
||||
options = _get(cls, '_reg_options_', {})
|
||||
policySpec = _get(cls, '_reg_policy_spec_')
|
||||
clsctx = _get(cls, '_reg_clsctx_')
|
||||
tlb_filename = _get(cls, '_reg_typelib_filename_')
|
||||
# default to being a COM category only when not frozen.
|
||||
addPyComCat = not _get(cls, '_reg_disable_pycomcat_', pythoncom.frozen!=0)
|
||||
addnPath = None
|
||||
if debugging:
|
||||
# If the class has a debugging dispatcher specified, use it, otherwise
|
||||
# use our default dispatcher.
|
||||
dispatcherSpec = _get(cls, '_reg_debug_dispatcher_spec_')
|
||||
if dispatcherSpec is None:
|
||||
dispatcherSpec = "win32com.server.dispatcher.DefaultDebugDispatcher"
|
||||
# And remember the debugging flag as servers may wish to use it at runtime.
|
||||
debuggingDesc = "(for debugging)"
|
||||
options['Debugging'] = "1"
|
||||
else:
|
||||
dispatcherSpec = _get(cls, '_reg_dispatcher_spec_')
|
||||
debuggingDesc = ""
|
||||
options['Debugging'] = "0"
|
||||
|
||||
if spec is None:
|
||||
moduleName = cls.__module__
|
||||
if moduleName == '__main__':
|
||||
# Use argv[0] to determine the module name.
|
||||
try:
|
||||
# Use the win32api to find the case-sensitive name
|
||||
moduleName = os.path.splitext(win32api.FindFiles(sys.argv[0])[0][8])[0]
|
||||
except (IndexError, win32api.error):
|
||||
# Can't find the script file - the user must explicitely set the _reg_... attribute.
|
||||
raise TypeError("Can't locate the script hosting the COM object - please set _reg_class_spec_ in your object")
|
||||
|
||||
spec = moduleName + "." + cls.__name__
|
||||
# Frozen apps don't need their directory on sys.path
|
||||
if not pythoncom.frozen:
|
||||
scriptDir = os.path.split(sys.argv[0])[0]
|
||||
if not scriptDir: scriptDir = "."
|
||||
addnPath = win32api.GetFullPathName(scriptDir)
|
||||
|
||||
RegisterServer(clsid, spec, desc, progID, verProgID, defIcon,
|
||||
threadingModel, policySpec, catids, options,
|
||||
addPyComCat, dispatcherSpec, clsctx, addnPath)
|
||||
if not quiet:
|
||||
print('Registered:', progID or spec, debuggingDesc)
|
||||
# Register the typelibrary
|
||||
if tlb_filename:
|
||||
tlb_filename = os.path.abspath(tlb_filename)
|
||||
typelib = pythoncom.LoadTypeLib(tlb_filename)
|
||||
pythoncom.RegisterTypeLib(typelib, tlb_filename)
|
||||
if not quiet:
|
||||
print('Registered type library:', tlb_filename)
|
||||
extra = flags.get('finalize_register')
|
||||
if extra:
|
||||
extra()
|
||||
|
||||
def UnregisterClasses(*classes, **flags):
|
||||
quiet = 'quiet' in flags and flags['quiet']
|
||||
for cls in classes:
|
||||
clsid = cls._reg_clsid_
|
||||
progID = _get(cls, '_reg_progid_')
|
||||
verProgID = _get(cls, '_reg_verprogid_')
|
||||
customKeys = _get(cls, '_reg_remove_keys_')
|
||||
unregister_typelib = _get(cls, '_reg_typelib_filename_') is not None
|
||||
|
||||
UnregisterServer(clsid, progID, verProgID, customKeys)
|
||||
if not quiet:
|
||||
print('Unregistered:', progID or str(clsid))
|
||||
if unregister_typelib:
|
||||
tlb_guid = _get(cls, "_typelib_guid_")
|
||||
if tlb_guid is None:
|
||||
# I guess I could load the typelib, but they need the GUID anyway.
|
||||
print("Have typelib filename, but no GUID - can't unregister")
|
||||
else:
|
||||
major, minor = _get(cls, "_typelib_version_", (1,0))
|
||||
lcid = _get(cls, "_typelib_lcid_", 0)
|
||||
try:
|
||||
pythoncom.UnRegisterTypeLib(tlb_guid, major, minor, lcid)
|
||||
if not quiet:
|
||||
print('Unregistered type library')
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
|
||||
extra = flags.get('finalize_unregister')
|
||||
if extra:
|
||||
extra()
|
||||
#
|
||||
# Unregister info is for installers or external uninstallers.
|
||||
# The WISE installer, for example firstly registers the COM server,
|
||||
# then queries for the Unregister info, appending it to its
|
||||
# install log. Uninstalling the package will the uninstall the server
|
||||
def UnregisterInfoClasses(*classes, **flags):
|
||||
ret = []
|
||||
for cls in classes:
|
||||
clsid = cls._reg_clsid_
|
||||
progID = _get(cls, '_reg_progid_')
|
||||
verProgID = _get(cls, '_reg_verprogid_')
|
||||
customKeys = _get(cls, '_reg_remove_keys_')
|
||||
|
||||
ret = ret + GetUnregisterServerKeys(clsid, progID, verProgID, customKeys)
|
||||
return ret
|
||||
|
||||
# Attempt to 're-execute' our current process with elevation.
|
||||
def ReExecuteElevated(flags):
|
||||
from win32com.shell.shell import ShellExecuteEx
|
||||
from win32com.shell import shellcon
|
||||
import win32process, win32event
|
||||
import winxpgui # we've already checked we are running XP above
|
||||
import tempfile
|
||||
|
||||
if not flags['quiet']:
|
||||
print("Requesting elevation and retrying...")
|
||||
new_params = " ".join(['"' + a + '"' for a in sys.argv])
|
||||
# If we aren't already in unattended mode, we want our sub-process to
|
||||
# be.
|
||||
if not flags['unattended']:
|
||||
new_params += " --unattended"
|
||||
# specifying the parent means the dialog is centered over our window,
|
||||
# which is a good usability clue.
|
||||
# hwnd is unlikely on the command-line, but flags may come from elsewhere
|
||||
hwnd = flags.get('hwnd', None)
|
||||
if hwnd is None:
|
||||
try:
|
||||
hwnd = winxpgui.GetConsoleWindow()
|
||||
except winxpgui.error:
|
||||
hwnd = 0
|
||||
# Redirect output so we give the user some clue what went wrong. This
|
||||
# also means we need to use COMSPEC. However, the "current directory"
|
||||
# appears to end up ignored - so we execute things via a temp batch file.
|
||||
tempbase = tempfile.mktemp("pycomserverreg")
|
||||
outfile = tempbase + ".out"
|
||||
batfile = tempbase + ".bat"
|
||||
|
||||
# If registering from pythonwin, need to run python console instead since
|
||||
# pythonwin will just open script for editting
|
||||
current_exe = os.path.split(sys.executable)[1].lower()
|
||||
exe_to_run = None
|
||||
if current_exe == 'pythonwin.exe':
|
||||
exe_to_run = os.path.join(sys.prefix, 'python.exe')
|
||||
elif current_exe == 'pythonwin_d.exe':
|
||||
exe_to_run = os.path.join(sys.prefix, 'python_d.exe')
|
||||
if not exe_to_run or not os.path.exists(exe_to_run):
|
||||
exe_to_run = sys.executable
|
||||
|
||||
try:
|
||||
batf = open(batfile, "w")
|
||||
try:
|
||||
cwd = os.getcwd()
|
||||
print("@echo off", file=batf)
|
||||
# nothing is 'inherited' by the elevated process, including the
|
||||
# environment. I wonder if we need to set more?
|
||||
print("set PYTHONPATH=%s" % os.environ.get('PYTHONPATH', ''), file=batf)
|
||||
# may be on a different drive - select that before attempting to CD.
|
||||
print(os.path.splitdrive(cwd)[0], file=batf)
|
||||
print('cd "%s"' % os.getcwd(), file=batf)
|
||||
print('%s %s > "%s" 2>&1' % (win32api.GetShortPathName(exe_to_run), new_params, outfile), file=batf)
|
||||
finally:
|
||||
batf.close()
|
||||
executable = os.environ.get('COMSPEC', 'cmd.exe')
|
||||
rc = ShellExecuteEx(hwnd=hwnd,
|
||||
fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
|
||||
lpVerb="runas",
|
||||
lpFile=executable,
|
||||
lpParameters='/C "%s"' % batfile,
|
||||
nShow=win32con.SW_SHOW)
|
||||
hproc = rc['hProcess']
|
||||
win32event.WaitForSingleObject(hproc, win32event.INFINITE)
|
||||
exit_code = win32process.GetExitCodeProcess(hproc)
|
||||
outf = open(outfile)
|
||||
try:
|
||||
output = outf.read()
|
||||
finally:
|
||||
outf.close()
|
||||
|
||||
if exit_code:
|
||||
# Even if quiet you get to see this message.
|
||||
print("Error: registration failed (exit code %s)." % exit_code)
|
||||
# if we are quiet then the output if likely to already be nearly
|
||||
# empty, so always print it.
|
||||
print(output, end=' ')
|
||||
finally:
|
||||
for f in (outfile, batfile):
|
||||
try:
|
||||
os.unlink(f)
|
||||
except os.error as exc:
|
||||
print("Failed to remove tempfile '%s': %s" % (f, exc))
|
||||
|
||||
def UseCommandLine(*classes, **flags):
|
||||
unregisterInfo = '--unregister_info' in sys.argv
|
||||
unregister = '--unregister' in sys.argv
|
||||
flags['quiet'] = flags.get('quiet',0) or '--quiet' in sys.argv
|
||||
flags['debug'] = flags.get('debug',0) or '--debug' in sys.argv
|
||||
flags['unattended'] = flags.get('unattended',0) or '--unattended' in sys.argv
|
||||
if unregisterInfo:
|
||||
return UnregisterInfoClasses(*classes, **flags)
|
||||
try:
|
||||
if unregister:
|
||||
UnregisterClasses(*classes, **flags)
|
||||
else:
|
||||
RegisterClasses(*classes, **flags)
|
||||
except win32api.error as exc:
|
||||
# If we are on xp+ and have "access denied", retry using
|
||||
# ShellExecuteEx with 'runas' verb to force elevation (vista) and/or
|
||||
# admin login dialog (vista/xp)
|
||||
if flags['unattended'] or exc.winerror != winerror.ERROR_ACCESS_DENIED \
|
||||
or sys.getwindowsversion()[0] < 5:
|
||||
raise
|
||||
ReExecuteElevated(flags)
|
||||
|
||||
def RegisterPyComCategory():
|
||||
""" Register the Python COM Server component category.
|
||||
"""
|
||||
regCat = _cat_registrar()
|
||||
regCat.RegisterCategories( [ (CATID_PythonCOMServer,
|
||||
0x0409,
|
||||
"Python COM Server") ] )
|
||||
|
||||
if not pythoncom.frozen:
|
||||
try:
|
||||
win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
|
||||
'Component Categories\\%s' % CATID_PythonCOMServer)
|
||||
except win32api.error:
|
||||
try:
|
||||
RegisterPyComCategory()
|
||||
except pythoncom.error: # Error with the COM category manager - oh well.
|
||||
pass
|
||||
|
||||
220
Lib/site-packages/win32com/server/util.py
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
""" General Server side utilities
|
||||
"""
|
||||
import pythoncom
|
||||
from . import policy
|
||||
import winerror
|
||||
from .exception import COMException
|
||||
|
||||
def wrap(ob, iid=None, usePolicy=None, useDispatcher=None):
|
||||
"""Wraps an object in a PyGDispatch gateway.
|
||||
|
||||
Returns a client side PyI{iid} interface.
|
||||
|
||||
Interface and gateway support must exist for the specified IID, as
|
||||
the QueryInterface() method is used.
|
||||
|
||||
"""
|
||||
if usePolicy is None:
|
||||
usePolicy = policy.DefaultPolicy
|
||||
if useDispatcher == 1: # True will also work here.
|
||||
import win32com.server.dispatcher
|
||||
useDispatcher = win32com.server.dispatcher.DefaultDebugDispatcher
|
||||
if useDispatcher is None or useDispatcher==0:
|
||||
ob = usePolicy(ob)
|
||||
else:
|
||||
ob = useDispatcher(usePolicy, ob)
|
||||
|
||||
# get a PyIDispatch, which interfaces to PyGDispatch
|
||||
ob = pythoncom.WrapObject(ob)
|
||||
if iid is not None:
|
||||
ob = ob.QueryInterface(iid) # Ask the PyIDispatch if it supports it?
|
||||
return ob
|
||||
|
||||
def unwrap(ob):
|
||||
"""Unwraps an interface.
|
||||
|
||||
Given an interface which wraps up a Gateway, return the object behind
|
||||
the gateway.
|
||||
"""
|
||||
ob = pythoncom.UnwrapObject(ob)
|
||||
# see if the object is a dispatcher
|
||||
if hasattr(ob, 'policy'):
|
||||
ob = ob.policy
|
||||
return ob._obj_
|
||||
|
||||
|
||||
class ListEnumerator:
|
||||
"""A class to expose a Python sequence as an EnumVARIANT.
|
||||
|
||||
Create an instance of this class passing a sequence (list, tuple, or
|
||||
any sequence protocol supporting object) and it will automatically
|
||||
support the EnumVARIANT interface for the object.
|
||||
|
||||
See also the @NewEnum@ function, which can be used to turn the
|
||||
instance into an actual COM server.
|
||||
"""
|
||||
_public_methods_ = [ 'Next', 'Skip', 'Reset', 'Clone' ]
|
||||
|
||||
def __init__(self, data, index=0, iid = pythoncom.IID_IEnumVARIANT):
|
||||
self._list_ = data
|
||||
self.index = index
|
||||
self._iid_ = iid
|
||||
|
||||
def _query_interface_(self, iid):
|
||||
if iid == self._iid_:
|
||||
return 1
|
||||
def Next(self, count):
|
||||
result = self._list_[self.index:self.index+count]
|
||||
self.Skip(count)
|
||||
return result
|
||||
|
||||
def Skip(self, count):
|
||||
end = self.index + count
|
||||
if end > len(self._list_):
|
||||
end = len(self._list_)
|
||||
self.index = end
|
||||
|
||||
def Reset(self):
|
||||
self.index = 0
|
||||
|
||||
def Clone(self):
|
||||
return self._wrap(self.__class__(self._list_, self.index))
|
||||
|
||||
def _wrap(self, ob):
|
||||
return wrap(ob)
|
||||
|
||||
|
||||
class ListEnumeratorGateway(ListEnumerator):
|
||||
"""A List Enumerator which wraps a sequence's items in gateways.
|
||||
|
||||
If a sequence contains items (objects) that have not been wrapped for
|
||||
return through the COM layers, then a ListEnumeratorGateway can be
|
||||
used to wrap those items before returning them (from the Next() method).
|
||||
|
||||
See also the @ListEnumerator@ class and the @NewEnum@ function.
|
||||
"""
|
||||
|
||||
def Next(self, count):
|
||||
result = self._list_[self.index:self.index+count]
|
||||
self.Skip(count)
|
||||
return map(self._wrap, result)
|
||||
|
||||
|
||||
def NewEnum(seq,
|
||||
cls=ListEnumerator,
|
||||
iid=pythoncom.IID_IEnumVARIANT,
|
||||
usePolicy=None,
|
||||
useDispatcher=None):
|
||||
"""Creates a new enumerator COM server.
|
||||
|
||||
This function creates a new COM Server that implements the
|
||||
IID_IEnumVARIANT interface.
|
||||
|
||||
A COM server that can enumerate the passed in sequence will be
|
||||
created, then wrapped up for return through the COM framework.
|
||||
Optionally, a custom COM server for enumeration can be passed
|
||||
(the default is @ListEnumerator@), and the specific IEnum
|
||||
interface can be specified.
|
||||
"""
|
||||
ob = cls(seq, iid=iid)
|
||||
return wrap(ob, iid, usePolicy=usePolicy, useDispatcher=useDispatcher)
|
||||
|
||||
|
||||
class Collection:
|
||||
"A collection of VARIANT values."
|
||||
|
||||
_public_methods_ = [ 'Item', 'Count', 'Add', 'Remove', 'Insert' ]
|
||||
|
||||
def __init__(self, data=None, readOnly=0):
|
||||
if data is None:
|
||||
data = [ ]
|
||||
self.data = data
|
||||
|
||||
# disable Add/Remove if read-only. note that we adjust _public_methods_
|
||||
# on this instance only.
|
||||
if readOnly:
|
||||
self._public_methods_ = [ 'Item', 'Count' ]
|
||||
|
||||
# This method is also used as the "default" method.
|
||||
# Thus "print ob" will cause this to be called with zero
|
||||
# params. Handle this slightly more elegantly here.
|
||||
# Ideally the policy should handle this.
|
||||
def Item(self, *args):
|
||||
if len(args) != 1:
|
||||
raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
|
||||
|
||||
try:
|
||||
return self.data[args[0]]
|
||||
except IndexError as desc:
|
||||
raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
|
||||
|
||||
|
||||
_value_ = Item
|
||||
|
||||
def Count(self):
|
||||
return len(self.data)
|
||||
|
||||
def Add(self, value):
|
||||
self.data.append(value)
|
||||
|
||||
def Remove(self, index):
|
||||
try:
|
||||
del self.data[index]
|
||||
except IndexError as desc:
|
||||
raise COMException(scode=winerror.DISP_E_BADINDEX, desc=str(desc))
|
||||
|
||||
def Insert(self, index, value):
|
||||
try:
|
||||
index = int(index)
|
||||
except (ValueError, TypeError):
|
||||
raise COMException(scode=winerror.DISP_E_TYPEMISMATCH)
|
||||
self.data.insert(index, value)
|
||||
|
||||
def _NewEnum(self):
|
||||
return NewEnum(self.data)
|
||||
|
||||
def NewCollection(seq, cls=Collection):
|
||||
"""Creates a new COM collection object
|
||||
|
||||
This function creates a new COM Server that implements the
|
||||
common collection protocols, including enumeration. (_NewEnum)
|
||||
|
||||
A COM server that can enumerate the passed in sequence will be
|
||||
created, then wrapped up for return through the COM framework.
|
||||
Optionally, a custom COM server for enumeration can be passed
|
||||
(the default is @Collection@).
|
||||
"""
|
||||
return pythoncom.WrapObject(policy.DefaultPolicy(cls(seq)),
|
||||
pythoncom.IID_IDispatch,
|
||||
pythoncom.IID_IDispatch)
|
||||
|
||||
class FileStream:
|
||||
_public_methods_ = [ 'Read', 'Write', 'Clone', 'CopyTo', 'Seek' ]
|
||||
_com_interfaces_ = [ pythoncom.IID_IStream ]
|
||||
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
|
||||
def Read(self, amount):
|
||||
return self.file.read(amount)
|
||||
|
||||
def Write(self, data):
|
||||
self.file.write(data)
|
||||
return len(data)
|
||||
|
||||
def Clone(self):
|
||||
return self._wrap(self.__class__(self.file))
|
||||
|
||||
def CopyTo(self, dest, cb):
|
||||
data=self.file.read(cb)
|
||||
cbread=len(data)
|
||||
dest.Write(data) ## ??? Write does not currently return the length ???
|
||||
return cbread, cbread
|
||||
|
||||
def Seek(self, offset, origin):
|
||||
# how convient that the 'origin' values are the same as the CRT :)
|
||||
self.file.seek(offset, origin)
|
||||
return self.file.tell()
|
||||
|
||||
def _wrap(self, ob):
|
||||
return wrap(ob)
|
||||
45
Lib/site-packages/win32com/servers/PythonTools.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import sys
|
||||
import time
|
||||
|
||||
class Tools:
|
||||
_public_methods_ = [ 'reload', 'adddir', 'echo', 'sleep' ]
|
||||
|
||||
def reload(self, module):
|
||||
if module in sys.modules:
|
||||
try:
|
||||
from imp import reload
|
||||
except ImportError:
|
||||
pass # builtin in py2k
|
||||
reload(sys.modules[module])
|
||||
return "reload succeeded."
|
||||
return "no reload performed."
|
||||
|
||||
def adddir(self, dir):
|
||||
if type(dir) == type(''):
|
||||
sys.path.append(dir)
|
||||
return str(sys.path)
|
||||
|
||||
def echo(self, arg):
|
||||
return repr(arg)
|
||||
|
||||
def sleep(self, t):
|
||||
time.sleep(t)
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.server.register import RegisterServer, UnregisterServer
|
||||
clsid = "{06ce7630-1d81-11d0-ae37-c2fa70000000}"
|
||||
progid = "Python.Tools"
|
||||
verprogid = "Python.Tools.1"
|
||||
if "--unregister" in sys.argv:
|
||||
print("Unregistering...")
|
||||
UnregisterServer(clsid, progid, verprogid)
|
||||
print("Unregistered OK")
|
||||
else:
|
||||
print("Registering COM server...")
|
||||
RegisterServer(clsid,
|
||||
"win32com.servers.PythonTools.Tools",
|
||||
"Python Tools",
|
||||
progid,
|
||||
verprogid)
|
||||
print("Class registered.")
|
||||
0
Lib/site-packages/win32com/servers/__init__.py
Normal file
130
Lib/site-packages/win32com/servers/dictionary.py
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
"""Python.Dictionary COM Server.
|
||||
|
||||
This module implements a simple COM server that acts much like a Python
|
||||
dictionary or as a standard string-keyed VB Collection. The keys of
|
||||
the dictionary are strings and are case-insensitive.
|
||||
|
||||
It uses a highly customized policy to fine-tune the behavior exposed to
|
||||
the COM client.
|
||||
|
||||
The object exposes the following properties:
|
||||
|
||||
int Count (readonly)
|
||||
VARIANT Item(BSTR key) (propget for Item)
|
||||
Item(BSTR key, VARIANT value) (propput for Item)
|
||||
|
||||
Note that 'Item' is the default property, so the following forms of
|
||||
VB code are acceptable:
|
||||
|
||||
set ob = CreateObject("Python.Dictionary")
|
||||
ob("hello") = "there"
|
||||
ob.Item("hi") = ob("HELLO")
|
||||
|
||||
All keys are defined, returning VT_NULL (None) if a value has not been
|
||||
stored. To delete a key, simply assign VT_NULL to the key.
|
||||
|
||||
The object responds to the _NewEnum method by returning an enumerator over
|
||||
the dictionary's keys. This allows for the following type of VB code:
|
||||
|
||||
for each name in ob
|
||||
debug.print name, ob(name)
|
||||
next
|
||||
"""
|
||||
|
||||
import pythoncom
|
||||
from win32com.server import util, policy
|
||||
from win32com.server.exception import COMException
|
||||
import winerror
|
||||
import types
|
||||
import pywintypes
|
||||
|
||||
from pythoncom import DISPATCH_METHOD, DISPATCH_PROPERTYGET
|
||||
from winerror import S_OK
|
||||
|
||||
class DictionaryPolicy(policy.BasicWrapPolicy):
|
||||
### BasicWrapPolicy looks for this
|
||||
_com_interfaces_ = [ ]
|
||||
|
||||
### BasicWrapPolicy looks for this
|
||||
_name_to_dispid_ = {
|
||||
'item' : pythoncom.DISPID_VALUE,
|
||||
'_newenum' : pythoncom.DISPID_NEWENUM,
|
||||
'count' : 1,
|
||||
}
|
||||
|
||||
### Auto-Registration process looks for these...
|
||||
_reg_desc_ = 'Python Dictionary'
|
||||
_reg_clsid_ = '{39b61048-c755-11d0-86fa-00c04fc2e03e}'
|
||||
_reg_progid_ = 'Python.Dictionary'
|
||||
_reg_verprogid_ = 'Python.Dictionary.1'
|
||||
_reg_policy_spec_ = 'win32com.servers.dictionary.DictionaryPolicy'
|
||||
|
||||
def _CreateInstance_(self, clsid, reqIID):
|
||||
self._wrap_({ })
|
||||
return pythoncom.WrapObject(self, reqIID)
|
||||
|
||||
def _wrap_(self, ob):
|
||||
self._obj_ = ob # ob should be a dictionary
|
||||
|
||||
def _invokeex_(self, dispid, lcid, wFlags, args, kwargs, serviceProvider):
|
||||
if dispid == 0: # item
|
||||
l = len(args)
|
||||
if l < 1:
|
||||
raise COMException(desc="not enough parameters", scode=winerror.DISP_E_BADPARAMCOUNT)
|
||||
|
||||
key = args[0]
|
||||
if type(key) not in [str, str]:
|
||||
### the nArgErr thing should be 0-based, not reversed... sigh
|
||||
raise COMException(desc="Key must be a string", scode=winerror.DISP_E_TYPEMISMATCH)
|
||||
|
||||
key = key.lower()
|
||||
|
||||
if wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET):
|
||||
if l > 1:
|
||||
raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
|
||||
try:
|
||||
return self._obj_[key]
|
||||
except KeyError:
|
||||
return None # unknown keys return None (VT_NULL)
|
||||
|
||||
if l != 2:
|
||||
raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
|
||||
if args[1] is None:
|
||||
# delete a key when None is assigned to it
|
||||
try:
|
||||
del self._obj_[key]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
self._obj_[key] = args[1]
|
||||
return S_OK
|
||||
|
||||
if dispid == 1: # count
|
||||
if not wFlags & DISPATCH_PROPERTYGET:
|
||||
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND) # not found
|
||||
if len(args) != 0:
|
||||
raise COMException(scode=winerror.DISP_E_BADPARAMCOUNT)
|
||||
return len(self._obj_)
|
||||
|
||||
if dispid == pythoncom.DISPID_NEWENUM:
|
||||
return util.NewEnum(list(self._obj_.keys()))
|
||||
|
||||
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND)
|
||||
|
||||
def _getidsofnames_(self, names, lcid):
|
||||
### this is a copy of MappedWrapPolicy._getidsofnames_ ...
|
||||
|
||||
name = names[0].lower()
|
||||
try:
|
||||
return (self._name_to_dispid_[name],)
|
||||
except KeyError:
|
||||
raise COMException(scode=winerror.DISP_E_MEMBERNOTFOUND,
|
||||
desc="Member not found")
|
||||
|
||||
|
||||
def Register():
|
||||
from win32com.server.register import UseCommandLine
|
||||
return UseCommandLine(DictionaryPolicy)
|
||||
|
||||
if __name__ == '__main__':
|
||||
Register()
|
||||
52
Lib/site-packages/win32com/servers/interp.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
"""Python.Interpreter COM Server
|
||||
|
||||
This module implements a very very simple COM server which
|
||||
exposes the Python interpreter.
|
||||
|
||||
This is designed more as a demonstration than a full blown COM server.
|
||||
General functionality and Error handling are both limited.
|
||||
|
||||
To use this object, ensure it is registered by running this module
|
||||
from Python.exe. Then, from Visual Basic, use "CreateObject('Python.Interpreter')",
|
||||
and call its methods!
|
||||
"""
|
||||
|
||||
from win32com.server.exception import Exception
|
||||
import winerror
|
||||
|
||||
# Expose the Python interpreter.
|
||||
class Interpreter:
|
||||
"""The interpreter object exposed via COM
|
||||
"""
|
||||
_public_methods_ = [ 'Exec', 'Eval' ]
|
||||
# All registration stuff to support fully automatic register/unregister
|
||||
_reg_verprogid_ = "Python.Interpreter.2"
|
||||
_reg_progid_ = "Python.Interpreter"
|
||||
_reg_desc_ = "Python Interpreter"
|
||||
_reg_clsid_ = "{30BD3490-2632-11cf-AD5B-524153480001}"
|
||||
_reg_class_spec_ = "win32com.servers.interp.Interpreter"
|
||||
|
||||
def __init__(self):
|
||||
self.dict = {}
|
||||
|
||||
def Eval(self, exp):
|
||||
"""Evaluate an expression.
|
||||
"""
|
||||
if type(exp) not in [str, str]:
|
||||
raise Exception(desc="Must be a string",scode=winerror.DISP_E_TYPEMISMATCH)
|
||||
|
||||
return eval(str(exp), self.dict)
|
||||
def Exec(self, exp):
|
||||
"""Execute a statement.
|
||||
"""
|
||||
if type(exp) not in [str, str]:
|
||||
raise Exception(desc="Must be a string",scode=winerror.DISP_E_TYPEMISMATCH)
|
||||
exec(str(exp), self.dict)
|
||||
|
||||
def Register():
|
||||
import win32com.server.register
|
||||
return win32com.server.register.UseCommandLine(Interpreter)
|
||||
|
||||
if __name__=='__main__':
|
||||
print("Registering COM server...")
|
||||
Register()
|
||||
27
Lib/site-packages/win32com/servers/perfmon.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
"""A COM Server which exposes the NT Performance monitor in a very rudimentary way
|
||||
|
||||
Usage from VB:
|
||||
set ob = CreateObject("Python.PerfmonQuery")
|
||||
freeBytes = ob.Query("Memory", "Available Bytes")
|
||||
"""
|
||||
from win32com.server import exception, register
|
||||
import pythoncom, win32pdhutil, winerror
|
||||
|
||||
class PerfMonQuery:
|
||||
_reg_verprogid_ = "Python.PerfmonQuery.1"
|
||||
_reg_progid_ = "Python.PerfmonQuery"
|
||||
_reg_desc_ = "Python Performance Monitor query object"
|
||||
_reg_clsid_ = "{64cef7a0-8ece-11d1-a65a-00aa00125a98}"
|
||||
_reg_class_spec_ = "win32com.servers.perfmon.PerfMonQuery"
|
||||
_public_methods_ = [ 'Query' ]
|
||||
def Query(self, object, counter, instance = None, machine = None):
|
||||
try:
|
||||
return win32pdhutil.GetPerformanceAttributes(object, counter, instance, machine=machine)
|
||||
except win32pdhutil.error as exc:
|
||||
raise exception.Exception(desc=exc.strerror)
|
||||
except TypeError as desc:
|
||||
raise exception.Exception(desc=desc,scode=winerror.DISP_E_TYPEMISMATCH)
|
||||
|
||||
if __name__=='__main__':
|
||||
print("Registering COM server...")
|
||||
register.UseCommandLine(PerfMonQuery)
|
||||
168
Lib/site-packages/win32com/servers/test_pycomtest.py
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
# This is part of the Python test suite.
|
||||
# The object is registered when you first run the test suite.
|
||||
# (and hopefully unregistered once done ;-)
|
||||
|
||||
# Ensure the vtables in the tlb are known.
|
||||
from win32com import universal
|
||||
from win32com.server.exception import COMException
|
||||
from win32com.client import gencache
|
||||
import winerror
|
||||
from win32com.client import constants
|
||||
from win32com.server.util import wrap
|
||||
|
||||
import pythoncom
|
||||
pythoncom.__future_currency__ = True
|
||||
# We use the constants from the module, so must insist on a gencache.
|
||||
# Otherwise, use of gencache is not necessary (tho still advised)
|
||||
gencache.EnsureModule('{6BCDCB60-5605-11D0-AE5F-CADD4C000000}', 0, 1, 1)
|
||||
|
||||
class PyCOMTest:
|
||||
_typelib_guid_ = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
|
||||
_typelib_version = 1,0
|
||||
_com_interfaces_ = ['IPyCOMTest']
|
||||
_reg_clsid_ = "{e743d9cd-cb03-4b04-b516-11d3a81c1597}"
|
||||
_reg_progid_ = "Python.Test.PyCOMTest"
|
||||
|
||||
def DoubleString(self, str):
|
||||
return str*2
|
||||
def DoubleInOutString(self, str):
|
||||
return str*2
|
||||
|
||||
def Fire(self, nID):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def GetLastVarArgs(self):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def GetMultipleInterfaces(self, outinterface1, outinterface2):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def GetSafeArrays(self, attrs, attrs2, ints):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def GetSetDispatch(self, indisp):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
# Result is of type IPyCOMTest
|
||||
def GetSetInterface(self, ininterface):
|
||||
return wrap(self)
|
||||
|
||||
def GetSetVariant(self, indisp):
|
||||
return indisp
|
||||
|
||||
def TestByRefVariant(self, v):
|
||||
return v * 2
|
||||
|
||||
def TestByRefString(self, v):
|
||||
return v * 2
|
||||
|
||||
# Result is of type IPyCOMTest
|
||||
def GetSetInterfaceArray(self, ininterface):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def GetSetUnknown(self, inunk):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
# Result is of type ISimpleCounter
|
||||
def GetSimpleCounter(self):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def GetSimpleSafeArray(self, ints):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def GetStruct(self):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def SetIntSafeArray(self, ints):
|
||||
return len(ints)
|
||||
|
||||
def SetLongLongSafeArray(self, ints):
|
||||
return len(ints)
|
||||
|
||||
def SetULongLongSafeArray(self, ints):
|
||||
return len(ints)
|
||||
|
||||
def SetBinSafeArray(self, buf):
|
||||
return len(buf)
|
||||
|
||||
def SetVarArgs(self, *args):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def SetVariantSafeArray(self, vars):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def Start(self):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def Stop(self, nID):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def StopAll(self):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def TakeByRefDispatch(self, inout):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def TakeByRefTypedDispatch(self, inout):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def Test(self, key, inval):
|
||||
return not inval
|
||||
|
||||
def Test2(self, inval):
|
||||
return inval
|
||||
|
||||
def Test3(self, inval):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def Test4(self, inval):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def Test5(self, inout):
|
||||
if inout == constants.TestAttr1:
|
||||
return constants.TestAttr1_1
|
||||
elif inout == constants.TestAttr1_1:
|
||||
return constants.TestAttr1
|
||||
else:
|
||||
return -1
|
||||
|
||||
def Test6(self, inval):
|
||||
return inval
|
||||
|
||||
def TestOptionals(self, strArg='def', sval=0, lval=1, dval=3.1400001049041748):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def TestOptionals2(self, dval, strval='', sval=1):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def CheckVariantSafeArray(self, data):
|
||||
return 1
|
||||
|
||||
def LongProp(self):
|
||||
return self.longval
|
||||
def SetLongProp(self, val):
|
||||
self.longval = val
|
||||
def ULongProp(self):
|
||||
return self.ulongval
|
||||
def SetULongProp(self, val):
|
||||
self.ulongval = val
|
||||
def IntProp(self):
|
||||
return self.intval
|
||||
def SetIntProp(self, val):
|
||||
self.intval = val
|
||||
|
||||
class PyCOMTestMI(PyCOMTest):
|
||||
_typelib_guid_ = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
|
||||
_typelib_version = 1,0
|
||||
# Interfaces with a interface name, a real IID, and an IID as a string
|
||||
_com_interfaces_ = ['IPyCOMTest',
|
||||
pythoncom.IID_IStream,
|
||||
str(pythoncom.IID_IStorage),
|
||||
]
|
||||
_reg_clsid_ = "{F506E9A1-FB46-4238-A597-FA4EB69787CA}"
|
||||
_reg_progid_ = "Python.Test.PyCOMTestMI"
|
||||
|
||||
if __name__ == '__main__':
|
||||
import win32com.server.register
|
||||
win32com.server.register.UseCommandLine(PyCOMTest)
|
||||
win32com.server.register.UseCommandLine(PyCOMTestMI)
|
||||
142
Lib/site-packages/win32com/storagecon.py
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
"""Constants related to IStorage and related interfaces
|
||||
|
||||
This file was generated by h2py from d:\msdev\include\objbase.h
|
||||
then hand edited, a few extra constants added, etc.
|
||||
"""
|
||||
|
||||
STGC_DEFAULT = 0
|
||||
STGC_OVERWRITE = 1
|
||||
STGC_ONLYIFCURRENT = 2
|
||||
STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 4
|
||||
STGC_CONSOLIDATE = 8
|
||||
|
||||
STGTY_STORAGE = 1
|
||||
STGTY_STREAM = 2
|
||||
STGTY_LOCKBYTES = 3
|
||||
STGTY_PROPERTY = 4
|
||||
STREAM_SEEK_SET = 0
|
||||
STREAM_SEEK_CUR = 1
|
||||
STREAM_SEEK_END = 2
|
||||
|
||||
LOCK_WRITE = 1
|
||||
LOCK_EXCLUSIVE = 2
|
||||
LOCK_ONLYONCE = 4
|
||||
|
||||
# Generated as from here.
|
||||
|
||||
CWCSTORAGENAME = 32
|
||||
STGM_DIRECT = 0x00000000
|
||||
STGM_TRANSACTED = 0x00010000
|
||||
STGM_SIMPLE = 0x08000000
|
||||
STGM_READ = 0x00000000
|
||||
STGM_WRITE = 0x00000001
|
||||
STGM_READWRITE = 0x00000002
|
||||
STGM_SHARE_DENY_NONE = 0x00000040
|
||||
STGM_SHARE_DENY_READ = 0x00000030
|
||||
STGM_SHARE_DENY_WRITE = 0x00000020
|
||||
STGM_SHARE_EXCLUSIVE = 0x00000010
|
||||
STGM_PRIORITY = 0x00040000
|
||||
STGM_DELETEONRELEASE = 0x04000000
|
||||
STGM_NOSCRATCH = 0x00100000
|
||||
STGM_CREATE = 0x00001000
|
||||
STGM_CONVERT = 0x00020000
|
||||
STGM_FAILIFTHERE = 0x00000000
|
||||
STGM_NOSNAPSHOT = 0x00200000
|
||||
ASYNC_MODE_COMPATIBILITY = 0x00000001
|
||||
ASYNC_MODE_DEFAULT = 0x00000000
|
||||
STGTY_REPEAT = 0x00000100
|
||||
STG_TOEND = 0xFFFFFFFF
|
||||
STG_LAYOUT_SEQUENTIAL = 0x00000000
|
||||
STG_LAYOUT_INTERLEAVED = 0x00000001
|
||||
|
||||
## access rights used with COM server ACL's
|
||||
COM_RIGHTS_EXECUTE = 1
|
||||
COM_RIGHTS_EXECUTE_LOCAL = 2
|
||||
COM_RIGHTS_EXECUTE_REMOTE = 4
|
||||
COM_RIGHTS_ACTIVATE_LOCAL = 8
|
||||
COM_RIGHTS_ACTIVATE_REMOTE = 16
|
||||
|
||||
STGFMT_DOCUMENT = 0
|
||||
STGFMT_STORAGE = 0
|
||||
STGFMT_NATIVE = 1
|
||||
STGFMT_FILE = 3
|
||||
STGFMT_ANY = 4
|
||||
STGFMT_DOCFILE = 5
|
||||
|
||||
PID_DICTIONARY = 0
|
||||
PID_CODEPAGE = 1
|
||||
PID_FIRST_USABLE = 2
|
||||
PID_FIRST_NAME_DEFAULT = 4095
|
||||
|
||||
PID_LOCALE = -2147483648
|
||||
PID_MODIFY_TIME = -2147483647
|
||||
PID_SECURITY = -2147483646
|
||||
PID_BEHAVIOR = -2147483645
|
||||
PID_ILLEGAL = -1
|
||||
PID_MIN_READONLY = -2147483648
|
||||
PID_MAX_READONLY = -1073741825
|
||||
|
||||
## DiscardableInformation
|
||||
PIDDI_THUMBNAIL = 0x00000002
|
||||
|
||||
## SummaryInformation
|
||||
PIDSI_TITLE = 2
|
||||
PIDSI_SUBJECT = 3
|
||||
PIDSI_AUTHOR = 4
|
||||
PIDSI_KEYWORDS = 5
|
||||
PIDSI_COMMENTS = 6
|
||||
PIDSI_TEMPLATE = 7
|
||||
PIDSI_LASTAUTHOR = 8
|
||||
PIDSI_REVNUMBER = 9
|
||||
PIDSI_EDITTIME = 10
|
||||
PIDSI_LASTPRINTED = 11
|
||||
PIDSI_CREATE_DTM = 12
|
||||
PIDSI_LASTSAVE_DTM = 13
|
||||
PIDSI_PAGECOUNT = 14
|
||||
PIDSI_WORDCOUNT = 15
|
||||
PIDSI_CHARCOUNT = 16
|
||||
PIDSI_THUMBNAIL = 17
|
||||
PIDSI_APPNAME = 18
|
||||
PIDSI_DOC_SECURITY = 19
|
||||
|
||||
## DocSummaryInformation
|
||||
PIDDSI_CATEGORY = 2
|
||||
PIDDSI_PRESFORMAT = 3
|
||||
PIDDSI_BYTECOUNT = 4
|
||||
PIDDSI_LINECOUNT = 5
|
||||
PIDDSI_PARCOUNT = 6
|
||||
PIDDSI_SLIDECOUNT = 7
|
||||
PIDDSI_NOTECOUNT = 8
|
||||
PIDDSI_HIDDENCOUNT = 9
|
||||
PIDDSI_MMCLIPCOUNT = 10
|
||||
PIDDSI_SCALE = 11
|
||||
PIDDSI_HEADINGPAIR = 12
|
||||
PIDDSI_DOCPARTS = 13
|
||||
PIDDSI_MANAGER = 14
|
||||
PIDDSI_COMPANY = 15
|
||||
PIDDSI_LINKSDIRTY = 16
|
||||
|
||||
|
||||
## MediaFileSummaryInfo
|
||||
PIDMSI_EDITOR = 2
|
||||
PIDMSI_SUPPLIER = 3
|
||||
PIDMSI_SOURCE = 4
|
||||
PIDMSI_SEQUENCE_NO = 5
|
||||
PIDMSI_PROJECT = 6
|
||||
PIDMSI_STATUS = 7
|
||||
PIDMSI_OWNER = 8
|
||||
PIDMSI_RATING = 9
|
||||
PIDMSI_PRODUCTION = 10
|
||||
PIDMSI_COPYRIGHT = 11
|
||||
|
||||
## PROPSETFLAG enum
|
||||
PROPSETFLAG_DEFAULT = 0
|
||||
PROPSETFLAG_NONSIMPLE = 1
|
||||
PROPSETFLAG_ANSI = 2
|
||||
PROPSETFLAG_UNBUFFERED = 4
|
||||
PROPSETFLAG_CASE_SENSITIVE = 8
|
||||
|
||||
## STGMOVE enum
|
||||
STGMOVE_MOVE = 0
|
||||
STGMOVE_COPY = 1
|
||||
STGMOVE_SHALLOWCOPY = 2
|
||||
84
Lib/site-packages/win32com/test/GenTestScripts.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#
|
||||
# Generate scripts needed for serious testing!
|
||||
#
|
||||
import win32com, win32com.client.makepy
|
||||
import win32com.test
|
||||
import pythoncom
|
||||
import sys, os
|
||||
|
||||
genList = [
|
||||
("msword8", "{00020905-0000-0000-C000-000000000046}",1033,8,0),
|
||||
]
|
||||
|
||||
genDir = "Generated4Test"
|
||||
def GetGenPath():
|
||||
import win32api
|
||||
return os.path.join(win32api.GetFullPathName(win32com.test.__path__[0]), genDir)
|
||||
|
||||
def GenerateFromRegistered(fname, *loadArgs):
|
||||
# tlb = apply(pythoncom.LoadRegTypeLib, loadArgs)
|
||||
genPath = GetGenPath()
|
||||
try:
|
||||
os.stat(genPath)
|
||||
except os.error:
|
||||
os.mkdir(genPath)
|
||||
# Ensure an __init__ exists.
|
||||
open(os.path.join(genPath, "__init__.py"), "w").close()
|
||||
print(fname, ": generating -", end=' ')
|
||||
f = open(os.path.join(genPath, fname + ".py"), "w")
|
||||
win32com.client.makepy.GenerateFromTypeLibSpec(loadArgs, f, bQuiet = 1, bGUIProgress = 1)
|
||||
f.close()
|
||||
print("compiling -", end=' ')
|
||||
fullModName = "win32com.test.%s.%s" % (genDir, fname)
|
||||
exec("import " + fullModName)
|
||||
# Inject the generated module as a top level module.
|
||||
sys.modules[fname] = sys.modules[fullModName]
|
||||
print("done")
|
||||
|
||||
|
||||
def GenerateAll():
|
||||
for args in genList:
|
||||
try:
|
||||
GenerateFromRegistered(*args)
|
||||
except KeyboardInterrupt:
|
||||
print("** Interrupted ***")
|
||||
break
|
||||
except pythoncom.com_error:
|
||||
print("** Could not generate test code for ", args[0])
|
||||
|
||||
def CleanAll():
|
||||
print("Cleaning generated test scripts...")
|
||||
try: # Clear exceptions!
|
||||
1/0
|
||||
except:
|
||||
pass
|
||||
genPath = GetGenPath()
|
||||
for args in genList:
|
||||
try:
|
||||
name = args[0]+".py"
|
||||
os.unlink(os.path.join(genPath, name))
|
||||
except os.error as details:
|
||||
if type(details)==type(()) and details[0]!=2:
|
||||
print("Could not deleted generated", name, details)
|
||||
try:
|
||||
name = args[0]+".pyc"
|
||||
os.unlink(os.path.join(genPath, name))
|
||||
except os.error as details:
|
||||
if type(details)==type(()) and details[0]!=2:
|
||||
print("Could not deleted generated", name, details)
|
||||
try:
|
||||
os.unlink(os.path.join(genPath, "__init__.py"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
os.unlink(os.path.join(genPath, "__init__.pyc"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
os.rmdir(genPath)
|
||||
except os.error as details:
|
||||
print("Could not delete test directory -", details)
|
||||
|
||||
if __name__=='__main__':
|
||||
GenerateAll()
|
||||
CleanAll()
|
||||
64
Lib/site-packages/win32com/test/Testpys.sct
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<scriptlet>
|
||||
|
||||
<Registration
|
||||
Description="TestPys"
|
||||
ProgID="TestPys.Scriptlet"
|
||||
Version="1"
|
||||
ClassID="{2eeb6080-cd58-11d1-b81e-00a0240b2fef}">
|
||||
|
||||
<SCRIPT LANGUAGE="VBScript">
|
||||
Function Register()
|
||||
Msgbox "Scriptlet 'Test' registered."
|
||||
End Function
|
||||
|
||||
Function Unregister()
|
||||
Msgbox "Scriptlet 'Test' unregistered."
|
||||
End Function
|
||||
</SCRIPT>
|
||||
</Registration>
|
||||
|
||||
<implements id=Automation type=Automation>
|
||||
<property name=PyProp1>
|
||||
<get/>
|
||||
<put/>
|
||||
</property>
|
||||
<property name=PyProp2>
|
||||
<get/>
|
||||
<put/>
|
||||
</property>
|
||||
<method name=PyMethod1>
|
||||
</method>
|
||||
|
||||
<method name=PyMethod2>
|
||||
</method>
|
||||
</implements>
|
||||
|
||||
<script language=python>
|
||||
|
||||
PyProp1 = "PyScript Property1";
|
||||
PyProp2 = "PyScript Property2";
|
||||
|
||||
def get_PyProp1():
|
||||
return PyProp1
|
||||
|
||||
def put_PyProp1(newValue):
|
||||
global PyProp1
|
||||
PyProp1 = newValue
|
||||
|
||||
def get_PyProp2():
|
||||
return PyProp2
|
||||
|
||||
def put_PyProp2(newValue):
|
||||
global PyProp2
|
||||
PyProp2 = newValue
|
||||
|
||||
def PyMethod1():
|
||||
return "PyMethod1 called"
|
||||
|
||||
def PyMethod2():
|
||||
return "PyMethod2 called"
|
||||
|
||||
</script>
|
||||
|
||||
</scriptlet>
|
||||
|
||||
1
Lib/site-packages/win32com/test/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# Empty file to designate a Python package
|
||||
61
Lib/site-packages/win32com/test/daodump.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# import dao3032
|
||||
# No longer imported here - callers responsibility to load
|
||||
#
|
||||
import win32com.client
|
||||
|
||||
def DumpDB(db, bDeep = 1):
|
||||
# MUST be a DB object.
|
||||
DumpTables(db,bDeep)
|
||||
DumpRelations(db,bDeep)
|
||||
DumpAllContainers(db,bDeep)
|
||||
|
||||
def DumpTables(db, bDeep = 1):
|
||||
for tab in db.TableDefs:
|
||||
tab = db.TableDefs(tab.Name) # Redundant lookup for testing purposes.
|
||||
print("Table %s - Fields: %d, Attributes:%d" % (tab.Name, len(tab.Fields), tab.Attributes))
|
||||
if bDeep: DumpFields(tab.Fields)
|
||||
|
||||
def DumpFields(fields):
|
||||
for field in fields:
|
||||
print(" %s, size=%d, reqd=%d, type=%d, defVal=%s" % (field.Name, field.Size, field.Required, field.Type, str(field.DefaultValue)))
|
||||
|
||||
def DumpRelations(db, bDeep = 1):
|
||||
for relation in db.Relations:
|
||||
print("Relation %s - %s->%s" % (relation.Name, relation.Table, relation.ForeignTable))
|
||||
|
||||
#### This dont work. TLB says it is a Fields collection, but apparently not!
|
||||
#### if bDeep: DumpFields(relation.Fields)
|
||||
|
||||
def DumpAllContainers(db, bDeep = 1):
|
||||
for cont in db.Containers:
|
||||
print("Container %s - %d documents" % (cont.Name, len(cont.Documents)))
|
||||
if bDeep: DumpContainerDocuments(cont)
|
||||
|
||||
def DumpContainerDocuments(container):
|
||||
for doc in container.Documents:
|
||||
import time
|
||||
timeStr = time.ctime(int(doc.LastUpdated))
|
||||
print(" %s - updated %s (" % (doc.Name, timeStr), end=' ')
|
||||
print(doc.LastUpdated,")") # test the _print_ method?
|
||||
|
||||
def TestEngine(engine):
|
||||
import sys
|
||||
if len(sys.argv)>1:
|
||||
dbName = sys.argv[1]
|
||||
else:
|
||||
dbName = "e:\\temp\\TestPython.mdb"
|
||||
db = engine.OpenDatabase(dbName)
|
||||
DumpDB(db)
|
||||
|
||||
def test():
|
||||
for progid in ("DAO.DBEngine.36", "DAO.DBEngine.35", "DAO.DBEngine.30"):
|
||||
try:
|
||||
ob = win32com.client.gencache.EnsureDispatch(progid)
|
||||
except pythoncom.com_error:
|
||||
print(progid, "does not seem to be installed")
|
||||
else:
|
||||
TestEngine(ob)
|
||||
break
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
151
Lib/site-packages/win32com/test/errorSemantics.py
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# errorSemantics.py
|
||||
|
||||
# Test the Python error handling semantics. Specifically:
|
||||
#
|
||||
# * When a Python COM object is called via IDispatch, the nominated
|
||||
# scode is placed in the exception tuple, and the HRESULT is
|
||||
# DISP_E_EXCEPTION
|
||||
# * When the same interface is called via IWhatever, the
|
||||
# nominated scode is returned directly (with the scode also
|
||||
# reflected in the exception tuple)
|
||||
# * In all cases, the description etc end up in the exception tuple
|
||||
# * "Normal" Python exceptions resolve to an E_FAIL "internal error"
|
||||
|
||||
import pythoncom
|
||||
from win32com.server.exception import COMException
|
||||
from win32com.server.util import wrap
|
||||
from win32com.client import Dispatch
|
||||
import winerror
|
||||
from win32com.test.util import CaptureWriter
|
||||
|
||||
class error(Exception):
|
||||
def __init__(self, msg, com_exception=None):
|
||||
Exception.__init__(self, msg, str(com_exception))
|
||||
|
||||
# Our COM server.
|
||||
class TestServer:
|
||||
_public_methods_ = [ 'Clone', 'Commit', 'LockRegion', 'Read']
|
||||
_com_interfaces_ = [ pythoncom.IID_IStream ]
|
||||
|
||||
def Clone(self):
|
||||
raise COMException("Not today", scode=winerror.E_UNEXPECTED)
|
||||
|
||||
def Commit(self, flags):
|
||||
raise Exception("foo")
|
||||
|
||||
def test():
|
||||
# Call via a native interface.
|
||||
com_server = wrap(TestServer(), pythoncom.IID_IStream)
|
||||
try:
|
||||
com_server.Clone()
|
||||
raise error("Expecting this call to fail!")
|
||||
except pythoncom.com_error as com_exc:
|
||||
if com_exc.hresult != winerror.E_UNEXPECTED:
|
||||
raise error("Calling the object natively did not yield the correct scode", com_exc)
|
||||
exc = com_exc.excepinfo
|
||||
if not exc or exc[-1] != winerror.E_UNEXPECTED:
|
||||
raise error("The scode element of the exception tuple did not yield the correct scode", com_exc)
|
||||
if exc[2] != "Not today":
|
||||
raise error("The description in the exception tuple did not yield the correct string", com_exc)
|
||||
cap = CaptureWriter()
|
||||
try:
|
||||
cap.capture()
|
||||
try:
|
||||
com_server.Commit(0)
|
||||
finally:
|
||||
cap.release()
|
||||
raise error("Expecting this call to fail!")
|
||||
except pythoncom.com_error as com_exc:
|
||||
if com_exc.hresult != winerror.E_FAIL:
|
||||
raise error("The hresult was not E_FAIL for an internal error", com_exc)
|
||||
if com_exc.excepinfo[1] != "Python COM Server Internal Error":
|
||||
raise error("The description in the exception tuple did not yield the correct string", com_exc)
|
||||
# Check we saw a traceback in stderr
|
||||
if cap.get_captured().find("Traceback")<0:
|
||||
raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
|
||||
|
||||
# Now do it all again, but using IDispatch
|
||||
com_server = Dispatch(wrap(TestServer()))
|
||||
try:
|
||||
com_server.Clone()
|
||||
raise error("Expecting this call to fail!")
|
||||
except pythoncom.com_error as com_exc:
|
||||
if com_exc.hresult != winerror.DISP_E_EXCEPTION:
|
||||
raise error("Calling the object via IDispatch did not yield the correct scode", com_exc)
|
||||
exc = com_exc.excepinfo
|
||||
if not exc or exc[-1] != winerror.E_UNEXPECTED:
|
||||
raise error("The scode element of the exception tuple did not yield the correct scode", com_exc)
|
||||
if exc[2] != "Not today":
|
||||
raise error("The description in the exception tuple did not yield the correct string", com_exc)
|
||||
|
||||
cap.clear()
|
||||
try:
|
||||
cap.capture()
|
||||
try:
|
||||
com_server.Commit(0)
|
||||
finally:
|
||||
cap.release()
|
||||
raise error("Expecting this call to fail!")
|
||||
except pythoncom.com_error as com_exc:
|
||||
if com_exc.hresult != winerror.DISP_E_EXCEPTION:
|
||||
raise error("Calling the object via IDispatch did not yield the correct scode", com_exc)
|
||||
exc = com_exc.excepinfo
|
||||
if not exc or exc[-1] != winerror.E_FAIL:
|
||||
raise error("The scode element of the exception tuple did not yield the correct scode", com_exc)
|
||||
if exc[1] != "Python COM Server Internal Error":
|
||||
raise error("The description in the exception tuple did not yield the correct string", com_exc)
|
||||
# Check we saw a traceback in stderr
|
||||
if cap.get_captured().find("Traceback")<0:
|
||||
raise error("Could not find a traceback in stderr: %r" % (cap.get_captured(),))
|
||||
|
||||
try:
|
||||
import logging
|
||||
except ImportError:
|
||||
logging = None
|
||||
if logging is not None:
|
||||
import win32com
|
||||
class TestLogHandler(logging.Handler):
|
||||
def __init__(self):
|
||||
self.num_emits = 0
|
||||
logging.Handler.__init__(self)
|
||||
def emit(self, record):
|
||||
self.num_emits += 1
|
||||
return
|
||||
print("--- record start")
|
||||
print(self.format(record))
|
||||
print("--- record end")
|
||||
|
||||
def testLogger():
|
||||
assert not hasattr(win32com, "logger")
|
||||
handler = TestLogHandler()
|
||||
formatter = logging.Formatter('%(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
log = logging.getLogger("win32com_test")
|
||||
log.addHandler(handler)
|
||||
win32com.logger = log
|
||||
# Now throw some exceptions!
|
||||
# Native interfaces
|
||||
com_server = wrap(TestServer(), pythoncom.IID_IStream)
|
||||
try:
|
||||
com_server.Commit(0)
|
||||
raise RuntimeError("should have failed")
|
||||
except pythoncom.error:
|
||||
pass
|
||||
assert handler.num_emits == 1, handler.num_emits
|
||||
handler.num_emits = 0 # reset
|
||||
|
||||
com_server = Dispatch(wrap(TestServer()))
|
||||
try:
|
||||
com_server.Commit(0)
|
||||
raise RuntimeError("should have failed")
|
||||
except pythoncom.error:
|
||||
pass
|
||||
assert handler.num_emits == 1, handler.num_emits
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
if logging is not None:
|
||||
testLogger()
|
||||
from .util import CheckClean
|
||||
CheckClean()
|
||||
print("error semantic tests worked")
|
||||
64
Lib/site-packages/win32com/test/pippo.idl
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// TestServer.idl : IDL source for TestServer.dll
|
||||
//
|
||||
|
||||
// This file will be processed by the MIDL tool to
|
||||
// produce the type library (TestServer.tlb) and marshalling code.
|
||||
|
||||
import "oaidl.idl";
|
||||
import "ocidl.idl";
|
||||
[
|
||||
object,
|
||||
uuid(50086EE8-F535-464B-806E-365ADBB727CF),
|
||||
dual,
|
||||
helpstring("ITestServerApp Interface"),
|
||||
pointer_default(unique)
|
||||
]
|
||||
interface ITestServerApp : IDispatch
|
||||
{
|
||||
[id(1), helpstring("method Test1")] HRESULT Test1([out, retval] ITestServerApp **pVal);
|
||||
[id(2), helpstring("method Test2")] HRESULT Test2([out, retval] VARIANT *pVar);
|
||||
[propget, id(3), helpstring("property MyProp1")] HRESULT MyProp1([out, retval] long *pVal);
|
||||
};
|
||||
[
|
||||
object,
|
||||
uuid(F1A3CC2E-4B2A-4A81-992D-67862076949B),
|
||||
dual,
|
||||
helpstring("IPippo Interface"),
|
||||
pointer_default(unique)
|
||||
]
|
||||
interface IPippo : IDispatch
|
||||
{
|
||||
[id(1), helpstring("method Method1")] HRESULT Method1([out, retval] IPippo **val);
|
||||
[propget, id(2), helpstring("property MyProp1")] HRESULT MyProp1([out, retval] long *pVal);
|
||||
[id(3), helpstring("method Method2")] HRESULT Method2([in] long in1, [in, out] long *inout1,
|
||||
[out, retval] long *val);
|
||||
};
|
||||
|
||||
[
|
||||
uuid(41059C57-975F-4B36-8FF3-C5117426647A),
|
||||
version(1.0),
|
||||
helpstring("TestServer 1.0 Type Library")
|
||||
]
|
||||
library TESTSERVERLib
|
||||
{
|
||||
importlib("stdole32.tlb");
|
||||
importlib("stdole2.tlb");
|
||||
importlib("msado15.dll");
|
||||
|
||||
[
|
||||
uuid(49E44E89-5A72-4456-B1D5-68268A19E798),
|
||||
helpstring("TestServerApp Class")
|
||||
]
|
||||
coclass TestServerApp
|
||||
{
|
||||
[default] interface ITestServerApp;
|
||||
};
|
||||
[
|
||||
uuid(E19C0A68-F61C-450B-A974-A7BA6957829C),
|
||||
helpstring("Pippo Class")
|
||||
]
|
||||
coclass Pippo
|
||||
{
|
||||
[default] interface IPippo;
|
||||
};
|
||||
};
|
||||
80
Lib/site-packages/win32com/test/pippo_server.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# A little test server, complete with typelib, we can use for testing.
|
||||
# Originally submitted with bug:
|
||||
# [ 753154 ] memory leak wrapping object having _typelib_guid_ attribute
|
||||
# but modified by mhammond for use as part of the test suite.
|
||||
import sys, os
|
||||
import pythoncom
|
||||
import win32com
|
||||
import winerror
|
||||
from win32com.server.util import wrap
|
||||
|
||||
class CPippo:
|
||||
#
|
||||
# COM declarations
|
||||
#
|
||||
_reg_clsid_ = "{05AC1CCE-3F9B-4d9a-B0B5-DFE8BE45AFA8}"
|
||||
_reg_desc_ = "Pippo Python test object"
|
||||
_reg_progid_ = "Python.Test.Pippo"
|
||||
#_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
|
||||
###
|
||||
### Link to typelib
|
||||
_typelib_guid_ = '{41059C57-975F-4B36-8FF3-C5117426647A}'
|
||||
_typelib_version_ = 1, 0
|
||||
_com_interfaces_ = ['IPippo']
|
||||
|
||||
def __init__(self):
|
||||
self.MyProp1 = 10
|
||||
|
||||
def Method1(self):
|
||||
return wrap(CPippo())
|
||||
|
||||
def Method2(self, in1, inout1):
|
||||
return in1, inout1 * 2
|
||||
|
||||
def BuildTypelib():
|
||||
from distutils.dep_util import newer
|
||||
this_dir = os.path.dirname(__file__)
|
||||
idl = os.path.abspath(os.path.join(this_dir, "pippo.idl"))
|
||||
tlb=os.path.splitext(idl)[0] + '.tlb'
|
||||
if newer(idl, tlb):
|
||||
print("Compiling %s" % (idl,))
|
||||
rc = os.system ('midl "%s"' % (idl,))
|
||||
if rc:
|
||||
raise RuntimeError("Compiling MIDL failed!")
|
||||
# Can't work out how to prevent MIDL from generating the stubs.
|
||||
# just nuke them
|
||||
for fname in "dlldata.c pippo_i.c pippo_p.c pippo.h".split():
|
||||
os.remove(os.path.join(this_dir, fname))
|
||||
|
||||
print("Registering %s" % (tlb,))
|
||||
tli=pythoncom.LoadTypeLib(tlb)
|
||||
pythoncom.RegisterTypeLib(tli,tlb)
|
||||
|
||||
def UnregisterTypelib():
|
||||
k = CPippo
|
||||
try:
|
||||
pythoncom.UnRegisterTypeLib(k._typelib_guid_,
|
||||
k._typelib_version_[0],
|
||||
k._typelib_version_[1],
|
||||
0,
|
||||
pythoncom.SYS_WIN32)
|
||||
print("Unregistered typelib")
|
||||
except pythoncom.error as details:
|
||||
if details[0]==winerror.TYPE_E_REGISTRYACCESS:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
def main(argv=None):
|
||||
if argv is None: argv = sys.argv[1:]
|
||||
if '--unregister' in argv:
|
||||
# Unregister the type-libraries.
|
||||
UnregisterTypelib()
|
||||
else:
|
||||
# Build and register the type-libraries.
|
||||
BuildTypelib()
|
||||
import win32com.server.register
|
||||
win32com.server.register.UseCommandLine(CPippo)
|
||||
|
||||
if __name__=='__main__':
|
||||
main(sys.argv)
|
||||
90
Lib/site-packages/win32com/test/policySemantics.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import win32com.server.util
|
||||
import win32com.client
|
||||
import pythoncom
|
||||
import winerror
|
||||
import win32com.test.util
|
||||
|
||||
import unittest
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
# An object representing a list of numbers
|
||||
class PythonSemanticClass:
|
||||
_public_methods_ = ["In"] # DISPIDs are allocated.
|
||||
_dispid_to_func_ = { 10: 'Add', 11:'Remove'} # DISPIDs specified by the object.
|
||||
def __init__(self):
|
||||
self.list = []
|
||||
def _NewEnum(self):
|
||||
return win32com.server.util.NewEnum(self.list)
|
||||
def _value_(self):
|
||||
# should return an array.
|
||||
return self.list
|
||||
def _Evaluate(self):
|
||||
# return the sum
|
||||
return sum(self.list)
|
||||
def In(self, value):
|
||||
return value in self.list
|
||||
def Add(self, value):
|
||||
self.list.append(value)
|
||||
def Remove(self, value):
|
||||
self.list.remove(value)
|
||||
|
||||
def DispExTest(ob):
|
||||
if not __debug__: print("WARNING: Tests dressed up as assertions are being skipped!")
|
||||
assert ob.GetDispID("Add", 0)==10, "Policy did not honour the dispid"
|
||||
# Not impl
|
||||
# assert ob.GetMemberName(10, 0)=="add", "Policy did not give me the correct function for the dispid"
|
||||
assert ob.GetDispID("Remove", 0)==11, "Policy did not honour the dispid"
|
||||
assert ob.GetDispID("In", 0)==1000, "Allocated dispid unexpected value"
|
||||
assert ob.GetDispID("_NewEnum", 0)==pythoncom.DISPID_NEWENUM, "_NewEnum() got unexpected DISPID"
|
||||
dispids = []
|
||||
dispid = -1
|
||||
while 1:
|
||||
try:
|
||||
dispid = ob.GetNextDispID(0, dispid)
|
||||
dispids.append(dispid)
|
||||
except pythoncom.com_error as xxx_todo_changeme:
|
||||
(hr, desc, exc, arg) = xxx_todo_changeme.args
|
||||
assert hr==winerror.S_FALSE, "Bad result at end of enum"
|
||||
break
|
||||
dispids.sort()
|
||||
if dispids != [pythoncom.DISPID_EVALUATE, pythoncom.DISPID_NEWENUM, 10, 11, 1000]:
|
||||
raise Error("Got back the wrong dispids: %s" % dispids)
|
||||
|
||||
def SemanticTest(ob):
|
||||
# First just check our object "generally" as expected.
|
||||
ob.Add(1)
|
||||
ob.Add(2)
|
||||
ob.Add(3)
|
||||
# invoke _value_
|
||||
if ob() != (1,2,3):
|
||||
raise Error("Bad result - got %s" % (repr(ob())))
|
||||
|
||||
dispob = ob._oleobj_
|
||||
|
||||
rc = dispob.Invoke(pythoncom.DISPID_EVALUATE, 0, pythoncom.DISPATCH_METHOD|pythoncom.DISPATCH_PROPERTYGET, 1)
|
||||
if rc != 6:
|
||||
raise Error("Evaluate returned %d" % rc)
|
||||
|
||||
|
||||
class Tester(win32com.test.util.TestCase):
|
||||
def setUp(self):
|
||||
debug=0
|
||||
import win32com.server.dispatcher
|
||||
if debug:
|
||||
dispatcher=win32com.server.dispatcher.DefaultDebugDispatcher
|
||||
else:
|
||||
dispatcher=None
|
||||
disp = win32com.server.util.wrap(PythonSemanticClass(), useDispatcher=dispatcher)
|
||||
self.ob = win32com.client.Dispatch(disp)
|
||||
def tearDown(self):
|
||||
self.ob = None
|
||||
def testSemantics(self):
|
||||
SemanticTest(self.ob)
|
||||
def testIDispatchEx(self):
|
||||
dispexob = self.ob._oleobj_.QueryInterface(pythoncom.IID_IDispatchEx)
|
||||
DispExTest(dispexob)
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
18
Lib/site-packages/win32com/test/readme.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
COM Test Suite Readme
|
||||
---------------------
|
||||
|
||||
Running the test suite:
|
||||
-----------------------
|
||||
* Open a command prompt
|
||||
* Change to the "win32com\test" directory.
|
||||
* run "testall.py". This will perform level 1 testing.
|
||||
You may specify 1, 2, or 3 on the command line ("testutil 3")
|
||||
to execute more tests.
|
||||
|
||||
In general, this should just run the best it can, utilizing what is available
|
||||
on the machine. It is likely some tests will refuse to run due to objects not
|
||||
being locally available - this is normal.
|
||||
|
||||
The win32com source tree has source code to a C++ and VB component used purely
|
||||
for testing. You may like to build and register these, particularly if you
|
||||
are doing anything related to argument/result handling.
|
||||
74
Lib/site-packages/win32com/test/testADOEvents.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
from win32com.client import Dispatch, DispatchWithEvents, constants
|
||||
import pythoncom
|
||||
import os
|
||||
import time
|
||||
|
||||
finished = 0 # Flag for the wait loop from (3) to test
|
||||
|
||||
class ADOEvents: # event handler class
|
||||
def OnWillConnect(self, str, user, pw, opt, sts, cn):
|
||||
# Must have this event, as if it is not handled, ADO assumes the
|
||||
# operation is cancelled, and raises an error (Operation cancelled
|
||||
# by the user)
|
||||
pass
|
||||
def OnConnectComplete(self, error, status, connection):
|
||||
# Assume no errors, until we have the basic stuff
|
||||
# working. Now, "connection" should be an open
|
||||
# connection to my data source
|
||||
# Do the "something" from (2). For now, just
|
||||
# print the connection data source
|
||||
print("connection is", connection)
|
||||
print("Connected to", connection.Properties("Data Source"))
|
||||
# OK, our work is done. Let the main loop know
|
||||
global finished
|
||||
finished = 1
|
||||
def OnCommitTransComplete(self, pError, adStatus, pConnection):
|
||||
pass
|
||||
def OnInfoMessage(self, pError, adStatus, pConnection):
|
||||
pass
|
||||
def OnDisconnect(self, adStatus, pConnection):
|
||||
pass
|
||||
def OnBeginTransComplete(self, TransactionLevel, pError, adStatus, pConnection):
|
||||
pass
|
||||
def OnRollbackTransComplete(self, pError, adStatus, pConnection):
|
||||
pass
|
||||
def OnExecuteComplete(self, RecordsAffected, pError, adStatus, pCommand, pRecordset, pConnection):
|
||||
pass
|
||||
def OnWillExecute(self, Source, CursorType, LockType, Options, adStatus, pCommand, pRecordset, pConnection):
|
||||
pass
|
||||
|
||||
def TestConnection(dbname):
|
||||
# Create the ADO connection object, and link the event
|
||||
# handlers into it
|
||||
c = DispatchWithEvents("ADODB.Connection", ADOEvents)
|
||||
|
||||
# Initiate the asynchronous open
|
||||
dsn = "Driver={Microsoft Access Driver (*.mdb)};Dbq=%s" % dbname
|
||||
user = "system"
|
||||
pw = "manager"
|
||||
c.Open(dsn, user, pw, constants.adAsyncConnect)
|
||||
|
||||
# Sit in a loop, until our event handler (above) sets the
|
||||
# "finished" flag or we time out.
|
||||
end_time = time.clock() + 10
|
||||
while time.clock() < end_time:
|
||||
# Pump messages so that COM gets a look in
|
||||
pythoncom.PumpWaitingMessages()
|
||||
if not finished:
|
||||
print("XXX - Failed to connect!")
|
||||
|
||||
def Test():
|
||||
from . import testAccess
|
||||
try:
|
||||
testAccess.GenerateSupport()
|
||||
except pythoncom.com_error:
|
||||
print("*** Can not import the MSAccess type libraries - tests skipped")
|
||||
return
|
||||
dbname = testAccess.CreateTestAccessDatabase()
|
||||
try:
|
||||
TestConnection(dbname)
|
||||
finally:
|
||||
os.unlink(dbname)
|
||||
|
||||
if __name__=='__main__':
|
||||
Test()
|
||||
34
Lib/site-packages/win32com/test/testAXScript.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Test AXScripting the best we can in an automated fashion...
|
||||
import win32api, os, sys
|
||||
|
||||
import win32com.axscript
|
||||
import win32com.axscript.client
|
||||
|
||||
import unittest
|
||||
import win32com.test.util
|
||||
|
||||
verbose = "-v" in sys.argv
|
||||
|
||||
class AXScript(win32com.test.util.TestCase):
|
||||
def setUp(self):
|
||||
file = win32api.GetFullPathName(os.path.join(win32com.axscript.client.__path__[0], "pyscript.py"))
|
||||
from win32com.test.util import RegisterPythonServer
|
||||
self.verbose = verbose
|
||||
RegisterPythonServer(file, 'python', verbose=self.verbose)
|
||||
|
||||
def testHost(self):
|
||||
file = win32api.GetFullPathName(os.path.join(win32com.axscript.__path__[0], "test\\testHost.py"))
|
||||
cmd = '%s "%s"' % (win32api.GetModuleFileName(0), file)
|
||||
if verbose:
|
||||
print("Testing Python Scripting host")
|
||||
win32com.test.util.ExecuteShellCommand(cmd, self)
|
||||
|
||||
def testCScript(self):
|
||||
file = win32api.GetFullPathName(os.path.join(win32com.axscript.__path__[0], "Demos\\Client\\wsh\\test.pys"))
|
||||
cmd = 'cscript.exe "%s"' % (file)
|
||||
if verbose:
|
||||
print("Testing Windows Scripting host with Python script")
|
||||
win32com.test.util.ExecuteShellCommand(cmd, self)
|
||||
|
||||
if __name__=='__main__':
|
||||
win32com.test.util.testmain()
|
||||
169
Lib/site-packages/win32com/test/testAccess.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#
|
||||
# This assumes that you have MSAccess and DAO installed.
|
||||
# You need to run makepy.py over "msaccess.tlb" and
|
||||
# "dao3032.dll", and ensure the generated files are on the
|
||||
# path.
|
||||
|
||||
# You can run this with no args, and a test database will be generated.
|
||||
# You can optionally pass a dbname on the command line, in which case it will be dumped.
|
||||
|
||||
import pythoncom
|
||||
from win32com.client import gencache, constants, Dispatch
|
||||
import win32api
|
||||
import os, sys
|
||||
|
||||
def CreateTestAccessDatabase(dbname = None):
|
||||
# Creates a test access database - returns the filename.
|
||||
if dbname is None:
|
||||
dbname = os.path.join( win32api.GetTempPath(), "COMTestSuiteTempDatabase.mdb" )
|
||||
|
||||
access = Dispatch("Access.Application")
|
||||
dbEngine = access.DBEngine
|
||||
workspace = dbEngine.Workspaces(0)
|
||||
|
||||
try:
|
||||
os.unlink(dbname)
|
||||
except os.error:
|
||||
print("WARNING - Unable to delete old test database - expect a COM exception RSN!")
|
||||
|
||||
newdb = workspace.CreateDatabase( dbname, constants.dbLangGeneral, constants.dbEncrypt )
|
||||
|
||||
# Create one test table.
|
||||
table = newdb.CreateTableDef("Test Table 1")
|
||||
table.Fields.Append( table.CreateField("First Name", constants.dbText ) )
|
||||
table.Fields.Append( table.CreateField("Last Name", constants.dbText ) )
|
||||
|
||||
index = table.CreateIndex("UniqueIndex")
|
||||
index.Fields.Append( index.CreateField("First Name") )
|
||||
index.Fields.Append( index.CreateField("Last Name") )
|
||||
index.Unique = -1
|
||||
table.Indexes.Append(index)
|
||||
|
||||
newdb.TableDefs.Append( table )
|
||||
|
||||
# Create a second test table.
|
||||
table = newdb.CreateTableDef("Test Table 2")
|
||||
table.Fields.Append( table.CreateField("First Name", constants.dbText ) )
|
||||
table.Fields.Append( table.CreateField("Last Name", constants.dbText ) )
|
||||
|
||||
newdb.TableDefs.Append( table )
|
||||
|
||||
# Create a relationship between them
|
||||
relation = newdb.CreateRelation("TestRelationship")
|
||||
relation.Table = "Test Table 1"
|
||||
relation.ForeignTable = "Test Table 2"
|
||||
|
||||
field = relation.CreateField("First Name")
|
||||
field.ForeignName = "First Name"
|
||||
relation.Fields.Append( field )
|
||||
|
||||
field = relation.CreateField("Last Name")
|
||||
field.ForeignName = "Last Name"
|
||||
relation.Fields.Append( field )
|
||||
|
||||
relation.Attributes = constants.dbRelationDeleteCascade + constants.dbRelationUpdateCascade
|
||||
|
||||
newdb.Relations.Append(relation)
|
||||
|
||||
# Finally we can add some data to the table.
|
||||
tab1 = newdb.OpenRecordset("Test Table 1")
|
||||
tab1.AddNew()
|
||||
tab1.Fields("First Name").Value = "Mark"
|
||||
tab1.Fields("Last Name").Value = "Hammond"
|
||||
tab1.Update()
|
||||
|
||||
tab1.MoveFirst()
|
||||
# We do a simple bookmark test which tests our optimized VT_SAFEARRAY|VT_UI1 support.
|
||||
# The bookmark will be a buffer object - remember it for later.
|
||||
bk = tab1.Bookmark
|
||||
|
||||
# Add a second record.
|
||||
tab1.AddNew()
|
||||
tab1.Fields("First Name").Value = "Second"
|
||||
tab1.Fields("Last Name").Value = "Person"
|
||||
tab1.Update()
|
||||
|
||||
# Reset the bookmark to the one we saved.
|
||||
# But first check the test is actually doing something!
|
||||
tab1.MoveLast()
|
||||
if tab1.Fields("First Name").Value != "Second":
|
||||
raise RuntimeError("Unexpected record is last - makes bookmark test pointless!")
|
||||
|
||||
tab1.Bookmark = bk
|
||||
if tab1.Bookmark != bk:
|
||||
raise RuntimeError("The bookmark data is not the same")
|
||||
|
||||
if tab1.Fields("First Name").Value != "Mark":
|
||||
raise RuntimeError("The bookmark did not reset the record pointer correctly")
|
||||
|
||||
return dbname
|
||||
|
||||
|
||||
def DoDumpAccessInfo(dbname):
|
||||
from . import daodump
|
||||
a = forms = None
|
||||
try:
|
||||
sys.stderr.write("Creating Access Application...\n")
|
||||
a=Dispatch("Access.Application")
|
||||
print("Opening database %s" % dbname)
|
||||
a.OpenCurrentDatabase(dbname)
|
||||
db = a.CurrentDb()
|
||||
daodump.DumpDB(db,1)
|
||||
forms = a.Forms
|
||||
print("There are %d forms open." % (len(forms)))
|
||||
# Uncommenting these lines means Access remains open.
|
||||
# for form in forms:
|
||||
# print " %s" % form.Name
|
||||
reports = a.Reports
|
||||
print("There are %d reports open" % (len(reports)))
|
||||
finally:
|
||||
if not a is None:
|
||||
sys.stderr.write("Closing database\n")
|
||||
try:
|
||||
a.CloseCurrentDatabase()
|
||||
except pythoncom.com_error:
|
||||
pass
|
||||
|
||||
# Generate all the support we can.
|
||||
def GenerateSupport():
|
||||
# dao
|
||||
gencache.EnsureModule("{00025E01-0000-0000-C000-000000000046}", 0, 4, 0)
|
||||
# Access
|
||||
# gencache.EnsureModule("{4AFFC9A0-5F99-101B-AF4E-00AA003F0F07}", 0, 8, 0)
|
||||
gencache.EnsureDispatch("Access.Application")
|
||||
|
||||
def DumpAccessInfo(dbname):
|
||||
amod = gencache.GetModuleForProgID("Access.Application")
|
||||
dmod = gencache.GetModuleForProgID("DAO.DBEngine.35")
|
||||
if amod is None and dmod is None:
|
||||
DoDumpAccessInfo(dbname)
|
||||
# Now generate all the support we can.
|
||||
GenerateSupport()
|
||||
else:
|
||||
sys.stderr.write("testAccess not doing dynamic test, as generated code already exists\n")
|
||||
# Now a generated version.
|
||||
DoDumpAccessInfo(dbname)
|
||||
|
||||
def test(dbname = None):
|
||||
if dbname is None:
|
||||
# We need makepy support to create a database (just for the constants!)
|
||||
try:
|
||||
GenerateSupport()
|
||||
except pythoncom.com_error:
|
||||
print("*** Can not import the MSAccess type libraries - tests skipped")
|
||||
return
|
||||
dbname = CreateTestAccessDatabase()
|
||||
print("A test database at '%s' was created" % dbname)
|
||||
|
||||
DumpAccessInfo(dbname)
|
||||
|
||||
if __name__=='__main__':
|
||||
import sys
|
||||
from .util import CheckClean
|
||||
dbname = None
|
||||
if len(sys.argv)>1:
|
||||
dbname = sys.argv[1]
|
||||
|
||||
test(dbname)
|
||||
|
||||
CheckClean()
|
||||
124
Lib/site-packages/win32com/test/testArrays.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
# Originally contributed by Stefan Schukat as part of this arbitrary-sized
|
||||
# arrays patch.
|
||||
from win32com.client import gencache
|
||||
from win32com.test import util
|
||||
import unittest
|
||||
|
||||
ZeroD = 0
|
||||
OneDEmpty = []
|
||||
OneD = [1,2,3]
|
||||
TwoD = [
|
||||
[1,2,3],
|
||||
[1,2,3],
|
||||
[1,2,3]
|
||||
]
|
||||
|
||||
TwoD1 = [
|
||||
[
|
||||
[1,2,3,5],
|
||||
[1,2,3],
|
||||
[1,2,3]
|
||||
],
|
||||
[
|
||||
[1,2,3],
|
||||
[1,2,3],
|
||||
[1,2,3]
|
||||
]
|
||||
]
|
||||
|
||||
OneD1 = [
|
||||
[
|
||||
[1,2,3],
|
||||
[1,2,3],
|
||||
[1,2,3]
|
||||
],
|
||||
[
|
||||
[1,2,3],
|
||||
[1,2,3]
|
||||
]
|
||||
]
|
||||
|
||||
OneD2 = [
|
||||
[1,2,3],
|
||||
[1,2,3,4,5],
|
||||
[
|
||||
[1,2,3,4,5],
|
||||
[1,2,3,4,5],
|
||||
[1,2,3,4,5]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
ThreeD = [
|
||||
[
|
||||
[1,2,3],
|
||||
[1,2,3],
|
||||
[1,2,3]
|
||||
],
|
||||
[
|
||||
[1,2,3],
|
||||
[1,2,3],
|
||||
[1,2,3]
|
||||
]
|
||||
]
|
||||
|
||||
FourD = [
|
||||
[
|
||||
[[1,2,3],[1,2,3],[1,2,3]],
|
||||
[[1,2,3],[1,2,3],[1,2,3]],
|
||||
[[1,2,3],[1,2,3],[1,2,3]]
|
||||
],
|
||||
[
|
||||
[[1,2,3],[1,2,3],[1,2,3]],
|
||||
[[1,2,3],[1,2,3],[1,2,3]],
|
||||
[[1,2,3],[1,2,3],[1,2,3]]
|
||||
]
|
||||
]
|
||||
|
||||
LargeD = [
|
||||
[ [list(range(10))] * 10],
|
||||
] * 512
|
||||
|
||||
def _normalize_array(a):
|
||||
if type(a) != type(()):
|
||||
return a
|
||||
ret = []
|
||||
for i in a:
|
||||
ret.append(_normalize_array(i))
|
||||
return ret
|
||||
|
||||
class ArrayTest(util.TestCase):
|
||||
def setUp(self):
|
||||
self.arr = gencache.EnsureDispatch("PyCOMTest.ArrayTest")
|
||||
def tearDown(self):
|
||||
self.arr = None
|
||||
def _doTest(self, array):
|
||||
self.arr.Array = array
|
||||
self.failUnlessEqual(_normalize_array(self.arr.Array), array)
|
||||
def testZeroD(self):
|
||||
self._doTest(ZeroD)
|
||||
def testOneDEmpty(self):
|
||||
self._doTest(OneDEmpty)
|
||||
def testOneD(self):
|
||||
self._doTest(OneD)
|
||||
def testTwoD(self):
|
||||
self._doTest(TwoD)
|
||||
def testThreeD(self):
|
||||
self._doTest(ThreeD)
|
||||
def testFourD(self):
|
||||
self._doTest(FourD)
|
||||
def testTwoD1(self):
|
||||
self._doTest(TwoD1)
|
||||
def testOneD1(self):
|
||||
self._doTest(OneD1)
|
||||
def testOneD2(self):
|
||||
self._doTest(OneD2)
|
||||
def testLargeD(self):
|
||||
self._doTest(LargeD)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
util.testmain()
|
||||
except SystemExit as rc:
|
||||
if not rc:
|
||||
raise
|
||||
160
Lib/site-packages/win32com/test/testClipboard.py
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# testClipboard.py
|
||||
import unittest
|
||||
import pythoncom
|
||||
import win32con
|
||||
import winerror
|
||||
import win32clipboard
|
||||
|
||||
from pywin32_testutil import str2bytes
|
||||
|
||||
from win32com.server.util import NewEnum, wrap
|
||||
from win32com.server.exception import COMException
|
||||
|
||||
IDataObject_Methods = """GetData GetDataHere QueryGetData
|
||||
GetCanonicalFormatEtc SetData EnumFormatEtc
|
||||
DAdvise DUnadvise EnumDAdvise""".split()
|
||||
|
||||
# A COM object implementing IDataObject used for basic testing.
|
||||
num_do_objects = 0
|
||||
|
||||
def WrapCOMObject(ob, iid=None):
|
||||
return wrap(ob, iid=iid, useDispatcher = 0)
|
||||
|
||||
class TestDataObject:
|
||||
_com_interfaces_ = [pythoncom.IID_IDataObject]
|
||||
_public_methods_ = IDataObject_Methods
|
||||
def __init__(self, strval):
|
||||
global num_do_objects
|
||||
num_do_objects += 1
|
||||
self.strval = strval
|
||||
self.supported_fe = []
|
||||
for cf in (win32con.CF_TEXT, win32con.CF_UNICODETEXT):
|
||||
fe = cf, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL
|
||||
self.supported_fe.append(fe)
|
||||
|
||||
def __del__(self):
|
||||
global num_do_objects
|
||||
num_do_objects -= 1
|
||||
|
||||
def _query_interface_(self, iid):
|
||||
if iid==pythoncom.IID_IEnumFORMATETC:
|
||||
return NewEnum(self.supported_fe, iid=iid)
|
||||
|
||||
def GetData(self, fe):
|
||||
ret_stg = None
|
||||
cf, target, aspect, index, tymed = fe
|
||||
if aspect & pythoncom.DVASPECT_CONTENT and \
|
||||
tymed==pythoncom.TYMED_HGLOBAL:
|
||||
if cf == win32con.CF_TEXT:
|
||||
ret_stg = pythoncom.STGMEDIUM()
|
||||
# ensure always 'bytes' by encoding string.
|
||||
ret_stg.set(pythoncom.TYMED_HGLOBAL, str2bytes(self.strval))
|
||||
elif cf == win32con.CF_UNICODETEXT:
|
||||
ret_stg = pythoncom.STGMEDIUM()
|
||||
ret_stg.set(pythoncom.TYMED_HGLOBAL, str(self.strval))
|
||||
|
||||
if ret_stg is None:
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
return ret_stg
|
||||
|
||||
def GetDataHere(self, fe):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def QueryGetData(self, fe):
|
||||
cf, target, aspect, index, tymed = fe
|
||||
if aspect & pythoncom.DVASPECT_CONTENT == 0:
|
||||
raise COMException(hresult=winerror.DV_E_DVASPECT)
|
||||
if tymed!=pythoncom.TYMED_HGLOBAL:
|
||||
raise COMException(hresult=winerror.DV_E_TYMED)
|
||||
return None # should check better
|
||||
|
||||
def GetCanonicalFormatEtc(self, fe):
|
||||
RaiseCOMException(winerror.DATA_S_SAMEFORMATETC)
|
||||
# return fe
|
||||
|
||||
def SetData(self, fe, medium):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def EnumFormatEtc(self, direction):
|
||||
if direction != pythoncom.DATADIR_GET:
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
return NewEnum(self.supported_fe, iid=pythoncom.IID_IEnumFORMATETC)
|
||||
|
||||
def DAdvise(self, fe, flags, sink):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def DUnadvise(self, connection):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
def EnumDAdvise(self):
|
||||
raise COMException(hresult=winerror.E_NOTIMPL)
|
||||
|
||||
class ClipboardTester(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pythoncom.OleInitialize()
|
||||
def tearDown(self):
|
||||
try:
|
||||
pythoncom.OleFlushClipboard()
|
||||
except pythoncom.com_error:
|
||||
# We never set anything!
|
||||
pass
|
||||
def testIsCurrentClipboard(self):
|
||||
do = TestDataObject("Hello from Python")
|
||||
do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
|
||||
pythoncom.OleSetClipboard(do)
|
||||
self.failUnless(pythoncom.OleIsCurrentClipboard(do))
|
||||
|
||||
def testComToWin32(self):
|
||||
# Set the data via our DataObject
|
||||
do = TestDataObject("Hello from Python")
|
||||
do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
|
||||
pythoncom.OleSetClipboard(do)
|
||||
# Then get it back via the standard win32 clipboard functions.
|
||||
win32clipboard.OpenClipboard()
|
||||
got = win32clipboard.GetClipboardData(win32con.CF_TEXT)
|
||||
# CF_TEXT gives bytes on py3k - use str2bytes() to ensure that's true.
|
||||
expected = str2bytes("Hello from Python")
|
||||
self.assertEqual(got, expected)
|
||||
# Now check unicode
|
||||
got = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
|
||||
self.assertEqual(got, "Hello from Python")
|
||||
win32clipboard.CloseClipboard()
|
||||
|
||||
def testWin32ToCom(self):
|
||||
# Set the data via the std win32 clipboard functions.
|
||||
val = str2bytes("Hello again!") # ensure always bytes, even in py3k
|
||||
win32clipboard.OpenClipboard()
|
||||
win32clipboard.SetClipboardData(win32con.CF_TEXT, val)
|
||||
win32clipboard.CloseClipboard()
|
||||
# and get it via an IDataObject provided by COM
|
||||
do = pythoncom.OleGetClipboard()
|
||||
cf = win32con.CF_TEXT, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL
|
||||
stg = do.GetData(cf)
|
||||
got = stg.data
|
||||
# The data we get back has the \0, as our STGMEDIUM has no way of
|
||||
# knowing if it meant to be a string, or a binary buffer, so
|
||||
# it must return it too.
|
||||
self.failUnlessEqual(got, str2bytes("Hello again!\0"))
|
||||
|
||||
def testDataObjectFlush(self):
|
||||
do = TestDataObject("Hello from Python")
|
||||
do = WrapCOMObject(do, iid=pythoncom.IID_IDataObject)
|
||||
pythoncom.OleSetClipboard(do)
|
||||
self.assertEqual(num_do_objects, 1)
|
||||
|
||||
do = None # clear my ref!
|
||||
pythoncom.OleFlushClipboard()
|
||||
self.assertEqual(num_do_objects, 0)
|
||||
|
||||
def testDataObjectReset(self):
|
||||
do = TestDataObject("Hello from Python")
|
||||
do = WrapCOMObject(do)
|
||||
pythoncom.OleSetClipboard(do)
|
||||
do = None # clear my ref!
|
||||
self.assertEqual(num_do_objects, 1)
|
||||
pythoncom.OleSetClipboard(None)
|
||||
self.assertEqual(num_do_objects, 0)
|
||||
|
||||
if __name__=='__main__':
|
||||
from win32com.test import util
|
||||
util.testmain()
|
||||
147
Lib/site-packages/win32com/test/testCollections.py
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# testCollections.py
|
||||
#
|
||||
# This code tests both the client and server side of collections
|
||||
# and enumerators.
|
||||
#
|
||||
# Also has the side effect of testing some of the PythonCOM error semantics.
|
||||
import sys
|
||||
import win32com.server.util
|
||||
import win32com.test.util
|
||||
import win32com.client
|
||||
import traceback
|
||||
import pythoncom
|
||||
import pywintypes
|
||||
import winerror
|
||||
L=pywintypes.Unicode
|
||||
|
||||
import unittest
|
||||
|
||||
error = "collection test error"
|
||||
|
||||
def MakeEmptyEnum():
|
||||
# create the Python enumerator object as a real COM object
|
||||
o = win32com.server.util.wrap( win32com.server.util.Collection() )
|
||||
return win32com.client.Dispatch(o)
|
||||
|
||||
def MakeTestEnum():
|
||||
# create a sub-collection, just to make sure it works :-)
|
||||
sub = win32com.server.util.wrap( win32com.server.util.Collection( ['Sub1', 2, 'Sub3']) )
|
||||
# create the Python enumerator object as a real COM object
|
||||
o = win32com.server.util.wrap( win32com.server.util.Collection( [1,'Two',3, sub]))
|
||||
return win32com.client.Dispatch(o)
|
||||
|
||||
def TestEnumAgainst(o,check):
|
||||
for i in range(len(check)):
|
||||
if o(i) != check[i]:
|
||||
raise error("Using default method gave the incorrect value - %s/%s" % (repr(o(i)), repr(check[i])))
|
||||
|
||||
for i in range(len(check)):
|
||||
if o.Item(i) != check[i]:
|
||||
raise error("Using Item method gave the incorrect value - %s/%s" % (repr(o(i)), repr(check[i])))
|
||||
|
||||
# First try looping.
|
||||
cmp = []
|
||||
for s in o:
|
||||
cmp.append(s)
|
||||
|
||||
if cmp[:len(check)] != check:
|
||||
raise error("Result after looping isnt correct - %s/%s" % (repr(cmp[:len(check)]), repr(check)))
|
||||
|
||||
for i in range(len(check)):
|
||||
if o[i] != check[i]:
|
||||
raise error("Using indexing gave the incorrect value")
|
||||
|
||||
|
||||
def TestEnum(quiet=None):
|
||||
if quiet is None:
|
||||
quiet = not "-v" in sys.argv
|
||||
if not quiet: print("Simple enum test")
|
||||
o = MakeTestEnum()
|
||||
check = [1,'Two',3]
|
||||
TestEnumAgainst(o, check)
|
||||
|
||||
if not quiet: print("sub-collection test")
|
||||
sub = o[3]
|
||||
TestEnumAgainst(sub ,['Sub1', 2, 'Sub3'])
|
||||
|
||||
# Remove the sublist for this test!
|
||||
o.Remove(o.Count()-1)
|
||||
|
||||
if not quiet: print("Remove item test")
|
||||
del check[1]
|
||||
o.Remove(1)
|
||||
TestEnumAgainst(o, check)
|
||||
|
||||
if not quiet: print("Add item test")
|
||||
o.Add('New Item')
|
||||
check.append('New Item')
|
||||
TestEnumAgainst(o, check)
|
||||
|
||||
if not quiet: print("Insert item test")
|
||||
o.Insert(2, -1)
|
||||
check.insert(2, -1)
|
||||
TestEnumAgainst(o, check)
|
||||
|
||||
### This does not work!
|
||||
# if not quiet: print "Indexed replace item test"
|
||||
# o[2] = 'Replaced Item'
|
||||
# check[2] = 'Replaced Item'
|
||||
# TestEnumAgainst(o, check)
|
||||
|
||||
try:
|
||||
o()
|
||||
raise error("default method with no args worked when it shouldnt have!")
|
||||
except pythoncom.com_error as xxx_todo_changeme:
|
||||
(hr, desc, exc, argErr) = xxx_todo_changeme.args
|
||||
if hr != winerror.DISP_E_BADPARAMCOUNT:
|
||||
raise error("Expected DISP_E_BADPARAMCOUNT - got %d (%s)" % (hr, desc))
|
||||
|
||||
try:
|
||||
o.Insert("foo", 2)
|
||||
raise error("Insert worked when it shouldnt have!")
|
||||
except pythoncom.com_error as xxx_todo_changeme1:
|
||||
(hr, desc, exc, argErr) = xxx_todo_changeme1.args
|
||||
if hr != winerror.DISP_E_TYPEMISMATCH:
|
||||
raise error("Expected DISP_E_TYPEMISMATCH - got %d (%s)" % (hr, desc))
|
||||
|
||||
# Remove the sublist for this test!
|
||||
try:
|
||||
o.Remove(o.Count())
|
||||
raise error("Remove worked when it shouldnt have!")
|
||||
except pythoncom.com_error as xxx_todo_changeme2:
|
||||
(hr, desc, exc, argErr) = xxx_todo_changeme2.args
|
||||
if hr != winerror.DISP_E_BADINDEX:
|
||||
raise error("Expected DISP_E_BADINDEX - got %d (%s)" % (hr, desc))
|
||||
|
||||
# Test an empty collection
|
||||
if not quiet: print("Empty collection test")
|
||||
o = MakeEmptyEnum()
|
||||
for item in o:
|
||||
raise error("Empty list performed an iteration")
|
||||
|
||||
try:
|
||||
ob = o[1]
|
||||
raise error("Empty list could be indexed")
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
try:
|
||||
ob = o[0]
|
||||
raise error("Empty list could be indexed")
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
try:
|
||||
ob = o(0)
|
||||
raise error("Empty list could be indexed")
|
||||
except pythoncom.com_error as xxx_todo_changeme3:
|
||||
(hr, fn, desc, arg) = xxx_todo_changeme3.args
|
||||
if hr != winerror.DISP_E_BADINDEX:
|
||||
raise error("Expected DISP_E_BADINDEX - got %d (%s)" % (hr, desc))
|
||||
|
||||
class TestCase(win32com.test.util.TestCase):
|
||||
def testEnum(self):
|
||||
TestEnum()
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
40
Lib/site-packages/win32com/test/testDCOM.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# testDCOM
|
||||
usage="""\
|
||||
testDCOM.py - Simple DCOM test
|
||||
Usage: testDCOM.py serverName
|
||||
|
||||
Attempts to start the Python.Interpreter object on the named machine,
|
||||
and checks that the object is indeed running remotely.
|
||||
|
||||
Requires the named server be configured to run DCOM (using dcomcnfg.exe),
|
||||
and the Python.Interpreter object installed and registered on that machine.
|
||||
|
||||
The Python.Interpreter object must be installed on the local machine,
|
||||
but no special DCOM configuration should be necessary.
|
||||
"""
|
||||
# NOTE: If you configured the object locally using dcomcnfg, you could
|
||||
# simple use Dispatch rather than DispatchEx.
|
||||
import pythoncom, win32com.client, win32api, string, sys
|
||||
|
||||
def test(serverName):
|
||||
if string.lower(serverName)==string.lower(win32api.GetComputerName()):
|
||||
print("You must specify a remote server name, not the local machine!")
|
||||
return
|
||||
|
||||
# Hack to overcome a DCOM limitation. As the Python.Interpreter object
|
||||
# is probably installed locally as an InProc object, DCOM seems to ignore
|
||||
# all settings, and use the local object.
|
||||
clsctx = pythoncom.CLSCTX_SERVER & ~pythoncom.CLSCTX_INPROC_SERVER
|
||||
ob = win32com.client.DispatchEx("Python.Interpreter", serverName, clsctx=clsctx)
|
||||
ob.Exec("import win32api")
|
||||
actualName = ob.Eval("win32api.GetComputerName()")
|
||||
if string.lower(serverName) != string.lower(actualName):
|
||||
print("Error: The object created on server '%s' reported its name as '%s'" % (serverName, actualName))
|
||||
else:
|
||||
print("Object created and tested OK on server '%s'" % serverName)
|
||||
|
||||
if __name__=='__main__':
|
||||
if len(sys.argv) == 2:
|
||||
test(sys.argv[1])
|
||||
else:
|
||||
print(usage)
|
||||
82
Lib/site-packages/win32com/test/testDictionary.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# testDictionary.py
|
||||
#
|
||||
import sys
|
||||
import win32com.server.util
|
||||
import win32com.test.util
|
||||
import win32com.client
|
||||
import traceback
|
||||
import pythoncom
|
||||
import pywintypes
|
||||
import winerror
|
||||
|
||||
import unittest
|
||||
|
||||
error = "dictionary test error"
|
||||
|
||||
def MakeTestDictionary():
|
||||
return win32com.client.Dispatch("Python.Dictionary")
|
||||
|
||||
def TestDictAgainst(dict,check):
|
||||
for key, value in check.items():
|
||||
if dict(key) != value:
|
||||
raise error("Indexing for '%s' gave the incorrect value - %s/%s" % (repr(key), repr(dict[key]), repr(check[key])))
|
||||
|
||||
# Ensure we have the correct version registered.
|
||||
def Register(quiet):
|
||||
import win32com.servers.dictionary
|
||||
from win32com.test.util import RegisterPythonServer
|
||||
RegisterPythonServer(win32com.servers.dictionary.__file__, 'Python.Dictionary')
|
||||
|
||||
def TestDict(quiet=None):
|
||||
if quiet is None:
|
||||
quiet = not "-v" in sys.argv
|
||||
Register(quiet)
|
||||
|
||||
if not quiet: print("Simple enum test")
|
||||
dict = MakeTestDictionary()
|
||||
checkDict = {}
|
||||
TestDictAgainst(dict, checkDict)
|
||||
|
||||
dict["NewKey"] = "NewValue"
|
||||
checkDict["NewKey"] = "NewValue"
|
||||
TestDictAgainst(dict, checkDict)
|
||||
|
||||
dict["NewKey"] = None
|
||||
del checkDict["NewKey"]
|
||||
TestDictAgainst(dict, checkDict)
|
||||
|
||||
if not quiet:
|
||||
print("Failure tests")
|
||||
try:
|
||||
dict()
|
||||
raise error("default method with no args worked when it shouldnt have!")
|
||||
except pythoncom.com_error as xxx_todo_changeme:
|
||||
(hr, desc, exc, argErr) = xxx_todo_changeme.args
|
||||
if hr != winerror.DISP_E_BADPARAMCOUNT:
|
||||
raise error("Expected DISP_E_BADPARAMCOUNT - got %d (%s)" % (hr, desc))
|
||||
|
||||
try:
|
||||
dict("hi", "there")
|
||||
raise error("multiple args worked when it shouldnt have!")
|
||||
except pythoncom.com_error as xxx_todo_changeme1:
|
||||
(hr, desc, exc, argErr) = xxx_todo_changeme1.args
|
||||
if hr != winerror.DISP_E_BADPARAMCOUNT:
|
||||
raise error("Expected DISP_E_BADPARAMCOUNT - got %d (%s)" % (hr, desc))
|
||||
|
||||
try:
|
||||
dict(0)
|
||||
raise error("int key worked when it shouldnt have!")
|
||||
except pythoncom.com_error as xxx_todo_changeme2:
|
||||
(hr, desc, exc, argErr) = xxx_todo_changeme2.args
|
||||
if hr != winerror.DISP_E_TYPEMISMATCH:
|
||||
raise error("Expected DISP_E_TYPEMISMATCH - got %d (%s)" % (hr, desc))
|
||||
|
||||
if not quiet:
|
||||
print("Python.Dictionary tests complete.")
|
||||
|
||||
class TestCase(win32com.test.util.TestCase):
|
||||
def testDict(self):
|
||||
TestDict()
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
26
Lib/site-packages/win32com/test/testDictionary.vbs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
' Test Pyhon.Dictionary using VBScript - this uses
|
||||
' IDispatchEx, so is an interesting test.
|
||||
|
||||
set ob = CreateObject("Python.Dictionary")
|
||||
ob("hello") = "there"
|
||||
' Our keys are case insensitive.
|
||||
ob.Item("hi") = ob("HELLO")
|
||||
|
||||
dim ok
|
||||
ok = true
|
||||
|
||||
if ob("hello") <> "there" then
|
||||
WScript.Echo "**** The dictionary value was wrong!!"
|
||||
ok = false
|
||||
end if
|
||||
|
||||
if ob("hi") <> "there" then
|
||||
WScript.Echo "**** The other dictionary value was wrong!!"
|
||||
ok = false
|
||||
end if
|
||||
|
||||
if ok then
|
||||
WScript.Echo "VBScript has successfully tested Python.Dictionary"
|
||||
end if
|
||||
|
||||
|
||||
75
Lib/site-packages/win32com/test/testDynamic.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Test dynamic policy, and running object table.
|
||||
|
||||
import pythoncom
|
||||
import winerror
|
||||
|
||||
from win32com.server.exception import Exception
|
||||
|
||||
error = "testDynamic error"
|
||||
|
||||
iid = pythoncom.MakeIID("{b48969a0-784b-11d0-ae71-d23f56000000}")
|
||||
|
||||
class VeryPermissive:
|
||||
def _dynamic_(self, name, lcid, wFlags, args):
|
||||
if wFlags & pythoncom.DISPATCH_METHOD:
|
||||
return getattr(self,name)(*args)
|
||||
|
||||
if wFlags & pythoncom.DISPATCH_PROPERTYGET:
|
||||
try:
|
||||
# to avoid problems with byref param handling, tuple results are converted to lists.
|
||||
ret = self.__dict__[name]
|
||||
if type(ret)==type(()):
|
||||
ret = list(ret)
|
||||
return ret
|
||||
except KeyError: # Probably a method request.
|
||||
raise Exception(scode=winerror.DISP_E_MEMBERNOTFOUND)
|
||||
|
||||
if wFlags & (pythoncom.DISPATCH_PROPERTYPUT | pythoncom.DISPATCH_PROPERTYPUTREF):
|
||||
setattr(self, name, args[0])
|
||||
return
|
||||
|
||||
raise Exception(scode=winerror.E_INVALIDARG, desc="invalid wFlags")
|
||||
|
||||
def write(self, *args):
|
||||
if len(args)==0:
|
||||
raise Exception(scode=winerror.DISP_E_BADPARAMCOUNT) # Probably call as PROPGET.
|
||||
|
||||
for arg in args[:-1]:
|
||||
print(str(arg), end=' ')
|
||||
print(str(args[-1]))
|
||||
|
||||
def Test():
|
||||
import win32com.server.util, win32com.server.policy
|
||||
# import win32dbg;win32dbg.brk()
|
||||
ob = win32com.server.util.wrap(VeryPermissive(),usePolicy=win32com.server.policy.DynamicPolicy)
|
||||
try:
|
||||
handle = pythoncom.RegisterActiveObject(ob, iid, 0)
|
||||
except pythoncom.com_error as details:
|
||||
print("Warning - could not register the object in the ROT:", details)
|
||||
handle = None
|
||||
try:
|
||||
import win32com.client.dynamic
|
||||
client = win32com.client.dynamic.Dispatch(iid)
|
||||
client.ANewAttr = "Hello"
|
||||
if client.ANewAttr != "Hello":
|
||||
raise error("Could not set dynamic property")
|
||||
|
||||
v = ["Hello","From","Python",1.4]
|
||||
client.TestSequence = v
|
||||
if v != list(client.TestSequence):
|
||||
raise error("Dynamic sequences not working! %r/%r" % (repr(v), repr(client.testSequence)))
|
||||
|
||||
client.write("This","output","has","come","via","COM")
|
||||
# Check our new "_FlagAsMethod" works (kinda!)
|
||||
client._FlagAsMethod("NotReallyAMethod")
|
||||
if not callable(client.NotReallyAMethod):
|
||||
raise error("Method I flagged as callable isn't!")
|
||||
|
||||
|
||||
client = None
|
||||
finally:
|
||||
if handle is not None:
|
||||
pythoncom.RevokeActiveObject(handle)
|
||||
|
||||
if __name__=='__main__':
|
||||
Test()
|
||||
112
Lib/site-packages/win32com/test/testExchange.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# TestExchange = Exchange Server Dump
|
||||
# Note that this code uses "CDO", which is unlikely to get the best choice.
|
||||
# You should use the Outlook object model, or
|
||||
# the win32com.mapi examples for a low-level interface.
|
||||
|
||||
from win32com.client import gencache, constants
|
||||
import pythoncom
|
||||
import os
|
||||
|
||||
ammodule = None # was the generated module!
|
||||
|
||||
def GetDefaultProfileName():
|
||||
import win32api, win32con
|
||||
try:
|
||||
key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles")
|
||||
try:
|
||||
return win32api.RegQueryValueEx(key, "DefaultProfile")[0]
|
||||
finally:
|
||||
key.Close()
|
||||
except win32api.error:
|
||||
return None
|
||||
|
||||
#
|
||||
# Recursive dump of folders.
|
||||
#
|
||||
def DumpFolder(folder, indent = 0):
|
||||
print(" " * indent, folder.Name)
|
||||
folders = folder.Folders
|
||||
folder = folders.GetFirst()
|
||||
while folder:
|
||||
DumpFolder(folder, indent+1)
|
||||
folder = folders.GetNext()
|
||||
|
||||
def DumpFolders(session):
|
||||
try:
|
||||
infostores = session.InfoStores
|
||||
except AttributeError:
|
||||
# later outlook?
|
||||
store = session.DefaultStore
|
||||
folder = store.GetRootFolder()
|
||||
DumpFolder(folder)
|
||||
return
|
||||
|
||||
print(infostores)
|
||||
print("There are %d infostores" % infostores.Count)
|
||||
for i in range(infostores.Count):
|
||||
infostore = infostores[i+1]
|
||||
print("Infostore = ", infostore.Name)
|
||||
try:
|
||||
folder = infostore.RootFolder
|
||||
except pythoncom.com_error as details:
|
||||
hr, msg, exc, arg = details
|
||||
# -2147221219 == MAPI_E_FAILONEPROVIDER - a single provider temporarily not available.
|
||||
if exc and exc[-1]==-2147221219:
|
||||
print("This info store is currently not available")
|
||||
continue
|
||||
DumpFolder(folder)
|
||||
|
||||
# Build a dictionary of property tags, so I can reverse look-up
|
||||
#
|
||||
PropTagsById={}
|
||||
if ammodule:
|
||||
for name, val in ammodule.constants.__dict__.items():
|
||||
PropTagsById[val] = name
|
||||
|
||||
|
||||
def TestAddress(session):
|
||||
# entry = session.GetAddressEntry("Skip")
|
||||
# print entry
|
||||
pass
|
||||
|
||||
|
||||
def TestUser(session):
|
||||
ae = session.CurrentUser
|
||||
fields = getattr(ae, "Fields", [])
|
||||
print("User has %d fields" % len(fields))
|
||||
for f in range(len(fields)):
|
||||
field = fields[f+1]
|
||||
try:
|
||||
id = PropTagsById[field.ID]
|
||||
except KeyError:
|
||||
id = field.ID
|
||||
print("%s/%s=%s" % (field.Name, id, field.Value))
|
||||
|
||||
def test():
|
||||
import win32com.client
|
||||
oldcwd = os.getcwd()
|
||||
try:
|
||||
session = gencache.EnsureDispatch("MAPI.Session")
|
||||
try:
|
||||
session.Logon(GetDefaultProfileName())
|
||||
except pythoncom.com_error as details:
|
||||
print("Could not log on to MAPI:", details)
|
||||
return
|
||||
except pythoncom.error:
|
||||
# no mapi.session - let's try outlook
|
||||
app = gencache.EnsureDispatch("Outlook.Application")
|
||||
session = app.Session
|
||||
|
||||
try:
|
||||
TestUser(session)
|
||||
TestAddress(session)
|
||||
DumpFolders(session)
|
||||
finally:
|
||||
session.Logoff()
|
||||
# It appears Exchange will change the cwd on us :(
|
||||
os.chdir(oldcwd)
|
||||
|
||||
if __name__=='__main__':
|
||||
from .util import CheckClean
|
||||
test()
|
||||
CheckClean()
|
||||
106
Lib/site-packages/win32com/test/testExplorer.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# testExplorer -
|
||||
|
||||
import sys
|
||||
import os
|
||||
import win32com.client.dynamic
|
||||
from win32com.client import Dispatch
|
||||
import win32api
|
||||
import win32gui
|
||||
import win32con
|
||||
import winerror
|
||||
import glob
|
||||
import pythoncom
|
||||
import time
|
||||
from win32com.test.util import CheckClean
|
||||
|
||||
bVisibleEventFired = 0
|
||||
|
||||
class ExplorerEvents:
|
||||
def OnVisible(self, visible):
|
||||
global bVisibleEventFired
|
||||
bVisibleEventFired = 1
|
||||
|
||||
def TestExplorerEvents():
|
||||
global bVisibleEventFired
|
||||
iexplore = win32com.client.DispatchWithEvents("InternetExplorer.Application", ExplorerEvents)
|
||||
iexplore.Visible = 1
|
||||
if not bVisibleEventFired:
|
||||
raise RuntimeError("The IE event did not appear to fire!")
|
||||
iexplore.Quit()
|
||||
iexplore = None
|
||||
|
||||
bVisibleEventFired = 0
|
||||
ie = win32com.client.Dispatch("InternetExplorer.Application")
|
||||
ie_events = win32com.client.DispatchWithEvents(ie, ExplorerEvents)
|
||||
ie.Visible = 1
|
||||
if not bVisibleEventFired:
|
||||
raise RuntimeError("The IE event did not appear to fire!")
|
||||
ie.Quit()
|
||||
ie = None
|
||||
print("IE Event tests worked.")
|
||||
|
||||
def TestObjectFromWindow():
|
||||
# Check we can use ObjectFromLresult to get the COM object from the
|
||||
# HWND - see KB Q249232
|
||||
# Locating the HWND is different than the KB says...
|
||||
hwnd = win32gui.FindWindow('IEFrame', None)
|
||||
for child_class in ['TabWindowClass', 'Shell DocObject View',
|
||||
'Internet Explorer_Server']:
|
||||
hwnd = win32gui.FindWindowEx(hwnd, 0, child_class, None)
|
||||
# ack - not working for markh on vista with IE8 (or maybe it is the
|
||||
# lack of the 'accessibility' components mentioned in Q249232)
|
||||
# either way - not working!
|
||||
return
|
||||
# But here is the point - once you have an 'Internet Explorer_Server',
|
||||
# you can send a message and use ObjectFromLresult to get it back.
|
||||
msg = win32gui.RegisterWindowMessage("WM_HTML_GETOBJECT")
|
||||
rc, result = win32gui.SendMessageTimeout(hwnd, msg, 0, 0, win32con.SMTO_ABORTIFHUNG, 1000)
|
||||
ob = pythoncom.ObjectFromLresult(result, pythoncom.IID_IDispatch, 0)
|
||||
doc = Dispatch(ob)
|
||||
# just to prove it works, set the background color of the document.
|
||||
for color in "red green blue orange white".split():
|
||||
doc.bgColor = color
|
||||
time.sleep(0.2)
|
||||
|
||||
def TestExplorer(iexplore):
|
||||
if not iexplore.Visible: iexplore.Visible = -1
|
||||
iexplore.Navigate(win32api.GetFullPathName('..\\readme.htm'))
|
||||
win32api.Sleep(1000)
|
||||
TestObjectFromWindow()
|
||||
win32api.Sleep(3000)
|
||||
try:
|
||||
iexplore.Quit()
|
||||
except (AttributeError, pythoncom.com_error):
|
||||
# User got sick of waiting :)
|
||||
pass
|
||||
|
||||
def TestAll():
|
||||
try:
|
||||
try:
|
||||
iexplore = win32com.client.dynamic.Dispatch("InternetExplorer.Application")
|
||||
TestExplorer(iexplore)
|
||||
|
||||
win32api.Sleep(1000)
|
||||
iexplore = None
|
||||
|
||||
# Test IE events.
|
||||
TestExplorerEvents()
|
||||
# Give IE a chance to shutdown, else it can get upset on fast machines.
|
||||
time.sleep(2)
|
||||
|
||||
# Note that the TextExplorerEvents will force makepy - hence
|
||||
# this gencache is really no longer needed.
|
||||
|
||||
from win32com.client import gencache
|
||||
gencache.EnsureModule("{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1)
|
||||
iexplore = win32com.client.Dispatch("InternetExplorer.Application")
|
||||
TestExplorer(iexplore)
|
||||
except pythoncom.com_error as exc:
|
||||
if exc.hresult!=winerror.RPC_E_DISCONNECTED: # user closed the app!
|
||||
raise
|
||||
finally:
|
||||
iexplore = None
|
||||
|
||||
if __name__=='__main__':
|
||||
TestAll()
|
||||
CheckClean()
|
||||
119
Lib/site-packages/win32com/test/testGIT.py
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
"""Testing pasing object between multiple COM threads
|
||||
|
||||
Uses standard COM marshalling to pass objects between threads. Even
|
||||
though Python generally seems to work when you just pass COM objects
|
||||
between threads, it shouldnt.
|
||||
|
||||
This shows the "correct" way to do it.
|
||||
|
||||
It shows that although we create new threads to use the Python.Interpreter,
|
||||
COM marshalls back all calls to that object to the main Python thread,
|
||||
which must be running a message loop (as this sample does).
|
||||
|
||||
When this test is run in "free threaded" mode (at this stage, you must
|
||||
manually mark the COM objects as "ThreadingModel=Free", or run from a
|
||||
service which has marked itself as free-threaded), then no marshalling
|
||||
is done, and the Python.Interpreter object start doing the "expected" thing
|
||||
- ie, it reports being on the same thread as its caller!
|
||||
|
||||
Python.exe needs a good way to mark itself as FreeThreaded - at the moment
|
||||
this is a pain in the but!
|
||||
|
||||
"""
|
||||
|
||||
import _thread, traceback
|
||||
import win32com.client
|
||||
import win32event, win32api
|
||||
import pythoncom
|
||||
|
||||
def TestInterp(interp):
|
||||
if interp.Eval("1+1") != 2:
|
||||
raise ValueError("The interpreter returned the wrong result.")
|
||||
try:
|
||||
interp.Eval(1+1)
|
||||
raise ValueError("The interpreter did not raise an exception")
|
||||
except pythoncom.com_error as details:
|
||||
import winerror
|
||||
if details[0]!=winerror.DISP_E_TYPEMISMATCH:
|
||||
raise ValueError("The interpreter exception was not winerror.DISP_E_TYPEMISMATCH.")
|
||||
|
||||
|
||||
def TestInterpInThread(stopEvent, cookie):
|
||||
try:
|
||||
DoTestInterpInThread(cookie)
|
||||
finally:
|
||||
win32event.SetEvent(stopEvent)
|
||||
|
||||
def CreateGIT():
|
||||
return pythoncom.CoCreateInstance(pythoncom.CLSID_StdGlobalInterfaceTable,
|
||||
None,
|
||||
pythoncom.CLSCTX_INPROC,
|
||||
pythoncom.IID_IGlobalInterfaceTable)
|
||||
|
||||
def DoTestInterpInThread(cookie):
|
||||
try:
|
||||
pythoncom.CoInitialize()
|
||||
myThread = win32api.GetCurrentThreadId()
|
||||
GIT = CreateGIT()
|
||||
|
||||
interp = GIT.GetInterfaceFromGlobal(cookie, pythoncom.IID_IDispatch)
|
||||
interp = win32com.client.Dispatch(interp)
|
||||
|
||||
TestInterp(interp)
|
||||
interp.Exec("import win32api")
|
||||
print("The test thread id is %d, Python.Interpreter's thread ID is %d" % (myThread, interp.Eval("win32api.GetCurrentThreadId()")))
|
||||
interp = None
|
||||
pythoncom.CoUninitialize()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
def BeginThreadsSimpleMarshal(numThreads, cookie):
|
||||
"""Creates multiple threads using simple (but slower) marshalling.
|
||||
|
||||
Single interpreter object, but a new stream is created per thread.
|
||||
|
||||
Returns the handles the threads will set when complete.
|
||||
"""
|
||||
ret = []
|
||||
for i in range(numThreads):
|
||||
hEvent = win32event.CreateEvent(None, 0, 0, None)
|
||||
_thread.start_new(TestInterpInThread, (hEvent, cookie))
|
||||
ret.append(hEvent)
|
||||
return ret
|
||||
|
||||
|
||||
def test(fn):
|
||||
print("The main thread is %d" % (win32api.GetCurrentThreadId()))
|
||||
GIT = CreateGIT()
|
||||
interp = win32com.client.Dispatch("Python.Interpreter")
|
||||
cookie = GIT.RegisterInterfaceInGlobal(interp._oleobj_, pythoncom.IID_IDispatch)
|
||||
|
||||
events = fn(4, cookie)
|
||||
numFinished = 0
|
||||
while 1:
|
||||
try:
|
||||
rc = win32event.MsgWaitForMultipleObjects(events, 0, 2000, win32event.QS_ALLINPUT)
|
||||
if rc >= win32event.WAIT_OBJECT_0 and rc < win32event.WAIT_OBJECT_0+len(events):
|
||||
numFinished = numFinished + 1
|
||||
if numFinished >= len(events):
|
||||
break
|
||||
elif rc==win32event.WAIT_OBJECT_0 + len(events): # a message
|
||||
# This is critical - whole apartment model demo will hang.
|
||||
pythoncom.PumpWaitingMessages()
|
||||
else: # Timeout
|
||||
print("Waiting for thread to stop with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
GIT.RevokeInterfaceFromGlobal(cookie)
|
||||
del interp
|
||||
del GIT
|
||||
|
||||
if __name__=='__main__':
|
||||
test(BeginThreadsSimpleMarshal)
|
||||
win32api.Sleep(500)
|
||||
# Doing CoUninit here stop Pythoncom.dll hanging when DLLMain shuts-down the process
|
||||
pythoncom.CoUninitialize()
|
||||
if pythoncom._GetInterfaceCount()!=0 or pythoncom._GetGatewayCount()!=0:
|
||||
print("Done with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
|
||||
else:
|
||||
print("Done.")
|
||||
133
Lib/site-packages/win32com/test/testGatewayAddresses.py
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# The purpose of this test is to ensure that the gateways objects
|
||||
# do the right thing WRT COM rules about object identity etc.
|
||||
|
||||
# Also includes a basic test that we support inheritance correctly in
|
||||
# gateway interfaces.
|
||||
|
||||
# For our test, we create an object of type IID_IPersistStorage
|
||||
# This interface derives from IPersist.
|
||||
# Therefore, QI's for IID_IDispatch, IID_IUnknown, IID_IPersist and
|
||||
# IID_IPersistStorage should all return the same gateway object.
|
||||
#
|
||||
# In addition, the interface should only need to declare itself as
|
||||
# using the IPersistStorage interface, and as the gateway derives
|
||||
# from IPersist, it should automatically be available without declaration.
|
||||
#
|
||||
# We also create an object of type IID_I??, and perform a QI for it.
|
||||
# We then jump through a number of hoops, ensuring that the objects
|
||||
# returned by the QIs follow all the rules.
|
||||
#
|
||||
# Here is Gregs summary of the rules:
|
||||
# 1) the set of supported interfaces is static and unchanging
|
||||
# 2) symmetric: if you QI an interface for that interface, it succeeds
|
||||
# 3) reflexive: if you QI against A for B, the new pointer must succeed
|
||||
# for a QI for A
|
||||
# 4) transitive: if you QI for B, then QI that for C, then QI'ing A for C
|
||||
# must succeed
|
||||
#
|
||||
#
|
||||
# Note that 1) Requires cooperation of the Python programmer. The rule to keep is:
|
||||
# "whenever you return an _object_ from _query_interface_(), you must return the
|
||||
# same object each time for a given IID. Note that you must return the same
|
||||
# _wrapped_ object
|
||||
# you
|
||||
# The rest are tested here.
|
||||
|
||||
|
||||
from win32com.server.util import wrap
|
||||
import pythoncom
|
||||
from .util import CheckClean
|
||||
|
||||
numErrors = 0
|
||||
|
||||
# Check that the 2 objects both have identical COM pointers.
|
||||
def CheckSameCOMObject(ob1, ob2):
|
||||
addr1 = repr(ob1).split()[6][:-1]
|
||||
addr2 = repr(ob2).split()[6][:-1]
|
||||
return addr1==addr2
|
||||
|
||||
# Check that the objects conform to COM identity rules.
|
||||
def CheckObjectIdentity(ob1, ob2):
|
||||
u1 = ob1.QueryInterface(pythoncom.IID_IUnknown)
|
||||
u2 = ob2.QueryInterface(pythoncom.IID_IUnknown)
|
||||
return CheckSameCOMObject(u1, u2)
|
||||
|
||||
def FailObjectIdentity(ob1, ob2, when):
|
||||
if not CheckObjectIdentity(ob1, ob2):
|
||||
global numErrors
|
||||
numErrors = numErrors + 1
|
||||
print(when, "are not identical (%s, %s)" % (repr(ob1), repr(ob2)))
|
||||
|
||||
|
||||
class Dummy:
|
||||
_public_methods_ = [] # We never attempt to make a call on this object.
|
||||
_com_interfaces_ = [pythoncom.IID_IPersistStorage]
|
||||
|
||||
class Dummy2:
|
||||
_public_methods_ = [] # We never attempt to make a call on this object.
|
||||
_com_interfaces_ = [pythoncom.IID_IPersistStorage, pythoncom.IID_IExternalConnection]
|
||||
|
||||
class DeletgatedDummy:
|
||||
_public_methods_ = []
|
||||
|
||||
class Dummy3:
|
||||
_public_methods_ = [] # We never attempt to make a call on this object.
|
||||
_com_interfaces_ = [pythoncom.IID_IPersistStorage]
|
||||
def _query_interface_(self, iid):
|
||||
if iid==pythoncom.IID_IExternalConnection:
|
||||
# This will NEVER work - can only wrap the object once!
|
||||
return wrap(DelegatedDummy())
|
||||
|
||||
def TestGatewayInheritance():
|
||||
# By default, wrap() creates and discards a temporary object.
|
||||
# This is not necessary, but just the current implementation of wrap.
|
||||
# As the object is correctly discarded, it doesnt affect this test.
|
||||
o = wrap(Dummy(), pythoncom.IID_IPersistStorage)
|
||||
o2 = o.QueryInterface(pythoncom.IID_IUnknown)
|
||||
FailObjectIdentity(o, o2, "IID_IPersistStorage->IID_IUnknown")
|
||||
|
||||
o3 = o2.QueryInterface(pythoncom.IID_IDispatch)
|
||||
|
||||
FailObjectIdentity(o2, o3, "IID_IUnknown->IID_IDispatch")
|
||||
FailObjectIdentity(o, o3, "IID_IPersistStorage->IID_IDispatch")
|
||||
|
||||
o4 = o3.QueryInterface(pythoncom.IID_IPersistStorage)
|
||||
FailObjectIdentity(o, o4, "IID_IPersistStorage->IID_IPersistStorage(2)")
|
||||
FailObjectIdentity(o2, o4, "IID_IUnknown->IID_IPersistStorage(2)")
|
||||
FailObjectIdentity(o3, o4, "IID_IDispatch->IID_IPersistStorage(2)")
|
||||
|
||||
|
||||
o5 = o4.QueryInterface(pythoncom.IID_IPersist)
|
||||
FailObjectIdentity(o, o5, "IID_IPersistStorage->IID_IPersist")
|
||||
FailObjectIdentity(o2, o5, "IID_IUnknown->IID_IPersist")
|
||||
FailObjectIdentity(o3, o5, "IID_IDispatch->IID_IPersist")
|
||||
FailObjectIdentity(o4, o5, "IID_IPersistStorage(2)->IID_IPersist")
|
||||
|
||||
def TestMultiInterface():
|
||||
o = wrap(Dummy2(), pythoncom.IID_IPersistStorage)
|
||||
o2 = o.QueryInterface(pythoncom.IID_IExternalConnection)
|
||||
|
||||
FailObjectIdentity(o, o2, "IID_IPersistStorage->IID_IExternalConnection")
|
||||
|
||||
# Make the same QI again, to make sure it is stable.
|
||||
o22 = o.QueryInterface(pythoncom.IID_IExternalConnection)
|
||||
FailObjectIdentity(o, o22, "IID_IPersistStorage->IID_IExternalConnection")
|
||||
FailObjectIdentity(o2, o22, "IID_IPersistStorage->IID_IExternalConnection (stability)")
|
||||
|
||||
o3 = o2.QueryInterface(pythoncom.IID_IPersistStorage)
|
||||
FailObjectIdentity(o2, o3, "IID_IExternalConnection->IID_IPersistStorage")
|
||||
FailObjectIdentity(o, o3, "IID_IPersistStorage->IID_IExternalConnection->IID_IPersistStorage")
|
||||
|
||||
|
||||
def test():
|
||||
TestGatewayInheritance()
|
||||
TestMultiInterface()
|
||||
if numErrors==0:
|
||||
print("Worked ok")
|
||||
else:
|
||||
print("There were", numErrors, "errors.")
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
CheckClean()
|
||||
12
Lib/site-packages/win32com/test/testInterp.vbs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
set o = CreateObject("Python.Interpreter")
|
||||
if o.Eval("1+1") <> 2 Then
|
||||
WScript.Echo "Eval('1+1') failed"
|
||||
bFailed = True
|
||||
end if
|
||||
|
||||
if bFailed then
|
||||
WScript.Echo "*********** VBScript tests failed *********"
|
||||
else
|
||||
WScript.Echo "VBScript test worked OK"
|
||||
end if
|
||||
|
||||
132
Lib/site-packages/win32com/test/testIterators.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
from __future__ import generators
|
||||
|
||||
# Some raw iter tests. Some "high-level" iterator tests can be found in
|
||||
# testvb.py and testOutlook.py
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from win32com.client.gencache import EnsureDispatch
|
||||
from win32com.client import Dispatch
|
||||
import win32com.server.util
|
||||
import win32com.test.util
|
||||
import pythoncom
|
||||
|
||||
def yield_iter(iter):
|
||||
while 1:
|
||||
yield next(iter)
|
||||
|
||||
class _BaseTestCase(win32com.test.util.TestCase):
|
||||
def test_enumvariant_vb(self):
|
||||
ob, iter = self.iter_factory()
|
||||
got=[]
|
||||
for v in iter:
|
||||
got.append(v)
|
||||
self.assertEquals(got, self.expected_data)
|
||||
def test_yield(self):
|
||||
ob, i = self.iter_factory()
|
||||
got=[]
|
||||
for v in yield_iter(iter(i)):
|
||||
got.append(v)
|
||||
self.assertEquals(got, self.expected_data)
|
||||
|
||||
def _do_test_nonenum(self, object):
|
||||
try:
|
||||
for i in object:
|
||||
pass
|
||||
self.fail("Could iterate over a non-iterable object")
|
||||
except TypeError:
|
||||
pass # this is expected.
|
||||
self.assertRaises(TypeError, iter, object)
|
||||
self.assertRaises(AttributeError, getattr, object, "next")
|
||||
|
||||
def test_nonenum_wrapper(self):
|
||||
# Check our raw PyIDispatch
|
||||
ob = self.object._oleobj_
|
||||
try:
|
||||
for i in ob:
|
||||
pass
|
||||
self.fail("Could iterate over a non-iterable object")
|
||||
except TypeError:
|
||||
pass # this is expected.
|
||||
self.assertRaises(TypeError, iter, ob)
|
||||
self.assertRaises(AttributeError, getattr, ob, "next")
|
||||
|
||||
# And our Dispatch wrapper
|
||||
ob = self.object
|
||||
try:
|
||||
for i in ob:
|
||||
pass
|
||||
self.fail("Could iterate over a non-iterable object")
|
||||
except TypeError:
|
||||
pass # this is expected.
|
||||
# Note that as our object may be dynamic, we *do* have a __getitem__
|
||||
# method, meaning we *can* call iter() on the object. In this case
|
||||
# actual iteration is what fails.
|
||||
# So either the 'iter(); will raise a type error, or an attempt to
|
||||
# fetch it
|
||||
try:
|
||||
next(iter(ob))
|
||||
self.fail("Expected a TypeError fetching this iterator")
|
||||
except TypeError:
|
||||
pass
|
||||
# And it should never have a 'next' method
|
||||
self.assertRaises(AttributeError, getattr, ob, "next")
|
||||
|
||||
class VBTestCase(_BaseTestCase):
|
||||
def setUp(self):
|
||||
def factory():
|
||||
# Our VB test harness exposes a property with IEnumVariant.
|
||||
ob = self.object.EnumerableCollectionProperty
|
||||
for i in self.expected_data:
|
||||
ob.Add(i)
|
||||
# Get the raw IEnumVARIANT.
|
||||
invkind = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
|
||||
iter = ob._oleobj_.InvokeTypes(pythoncom.DISPID_NEWENUM,0,invkind,(13, 10),())
|
||||
return ob, iter.QueryInterface(pythoncom.IID_IEnumVARIANT)
|
||||
# We *need* generated dispatch semantics, so dynamic __getitem__ etc
|
||||
# don't get in the way of our tests.
|
||||
self.object = EnsureDispatch("PyCOMVBTest.Tester")
|
||||
self.expected_data = [1, "Two", "3"]
|
||||
self.iter_factory = factory
|
||||
|
||||
def tearDown(self):
|
||||
self.object = None
|
||||
|
||||
# Test our client semantics, but using a wrapped Python list object.
|
||||
# This has the effect of re-using our client specific tests, but in this
|
||||
# case is exercising the server side.
|
||||
class SomeObject:
|
||||
_public_methods_ = ["GetCollection"]
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
def GetCollection(self):
|
||||
return win32com.server.util.NewCollection(self.data)
|
||||
|
||||
class WrappedPythonCOMServerTestCase(_BaseTestCase):
|
||||
def setUp(self):
|
||||
def factory():
|
||||
ob = self.object.GetCollection()
|
||||
flags = pythoncom.DISPATCH_METHOD | pythoncom.DISPATCH_PROPERTYGET
|
||||
enum = ob._oleobj_.Invoke(pythoncom.DISPID_NEWENUM, 0, flags, 1)
|
||||
return ob, enum.QueryInterface(pythoncom.IID_IEnumVARIANT)
|
||||
|
||||
self.expected_data = [1,'Two',3]
|
||||
sv = win32com.server.util.wrap(SomeObject(self.expected_data))
|
||||
self.object = Dispatch(sv)
|
||||
self.iter_factory = factory
|
||||
|
||||
def tearDown(self):
|
||||
self.object = None
|
||||
|
||||
def suite():
|
||||
# We dont want our base class run
|
||||
suite = unittest.TestSuite()
|
||||
for item in globals().values():
|
||||
if type(item)==type(unittest.TestCase) and \
|
||||
issubclass(item, unittest.TestCase) and \
|
||||
item != _BaseTestCase:
|
||||
suite.addTest(unittest.makeSuite(item))
|
||||
return suite
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main(argv=sys.argv + ['suite'])
|
||||
163
Lib/site-packages/win32com/test/testMSOffice.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
# Test MSOffice
|
||||
#
|
||||
# Main purpose of test is to ensure that Dynamic COM objects
|
||||
# work as expected.
|
||||
|
||||
# Assumes Word and Excel installed on your machine.
|
||||
|
||||
import win32com, sys, string, win32api, traceback
|
||||
import win32com.client.dynamic
|
||||
from win32com.test.util import CheckClean
|
||||
import pythoncom
|
||||
from win32com.client import gencache
|
||||
from pywintypes import Unicode
|
||||
|
||||
error = "MSOffice test error"
|
||||
|
||||
# Test a few of the MSOffice components.
|
||||
def TestWord():
|
||||
# Try and load the object exposed by Word 8
|
||||
# Office 97 - _totally_ different object model!
|
||||
try:
|
||||
# NOTE - using "client.Dispatch" would return an msword8.py instance!
|
||||
print("Starting Word 8 for dynamic test")
|
||||
word = win32com.client.dynamic.Dispatch("Word.Application")
|
||||
TestWord8(word)
|
||||
|
||||
word = None
|
||||
# Now we will test Dispatch without the new "lazy" capabilities
|
||||
print("Starting Word 8 for non-lazy dynamic test")
|
||||
dispatch = win32com.client.dynamic._GetGoodDispatch("Word.Application")
|
||||
typeinfo = dispatch.GetTypeInfo()
|
||||
attr = typeinfo.GetTypeAttr()
|
||||
olerepr = win32com.client.build.DispatchItem(typeinfo, attr, None, 0)
|
||||
word = win32com.client.dynamic.CDispatch(dispatch, olerepr)
|
||||
dispatch = typeinfo = attr = olerepr = None
|
||||
TestWord8(word)
|
||||
|
||||
except pythoncom.com_error:
|
||||
print("Starting Word 7 for dynamic test")
|
||||
word = win32com.client.Dispatch("Word.Basic")
|
||||
TestWord7(word)
|
||||
|
||||
print("Starting MSWord for generated test")
|
||||
from win32com.client import gencache
|
||||
word = gencache.EnsureDispatch("Word.Application.8")
|
||||
TestWord8(word)
|
||||
|
||||
def TestWord7(word):
|
||||
word.FileNew()
|
||||
# If not shown, show the app.
|
||||
if not word.AppShow(): word._proc_("AppShow")
|
||||
|
||||
for i in range(12):
|
||||
word.FormatFont(Color=i+1, Points=i+12)
|
||||
word.Insert("Hello from Python %d\n" % i)
|
||||
|
||||
word.FileClose(2)
|
||||
|
||||
def TestWord8(word):
|
||||
word.Visible = 1
|
||||
doc = word.Documents.Add()
|
||||
wrange = doc.Range()
|
||||
for i in range(10):
|
||||
wrange.InsertAfter("Hello from Python %d\n" % i)
|
||||
paras = doc.Paragraphs
|
||||
for i in range(len(paras)):
|
||||
p = paras[i]()
|
||||
p.Font.ColorIndex = i+1
|
||||
p.Font.Size = 12 + (4 * i)
|
||||
# XXX - note that
|
||||
# for para in paras:
|
||||
# para().Font...
|
||||
# doesnt seem to work - no error, just doesnt work
|
||||
# Should check if it works for VB!
|
||||
doc.Close(SaveChanges = 0)
|
||||
word.Quit()
|
||||
win32api.Sleep(1000) # Wait for word to close, else we
|
||||
# may get OA error.
|
||||
|
||||
def TestWord8OldStyle():
|
||||
try:
|
||||
import win32com.test.Generated4Test.msword8
|
||||
except ImportError:
|
||||
print("Can not do old style test")
|
||||
|
||||
|
||||
def TextExcel(xl):
|
||||
xl.Visible = 0
|
||||
if xl.Visible: raise error("Visible property is true.")
|
||||
xl.Visible = 1
|
||||
if not xl.Visible: raise error("Visible property not true.")
|
||||
|
||||
if int(xl.Version[0])>=8:
|
||||
xl.Workbooks.Add()
|
||||
else:
|
||||
xl.Workbooks().Add()
|
||||
|
||||
|
||||
xl.Range("A1:C1").Value = (1,2,3)
|
||||
xl.Range("A2:C2").Value = ('x','y','z')
|
||||
xl.Range("A3:C3").Value = ('3','2','1')
|
||||
|
||||
for i in range(20):
|
||||
xl.Cells(i+1,i+1).Value = "Hi %d" % i
|
||||
|
||||
if xl.Range("A1").Value != "Hi 0":
|
||||
raise error("Single cell range failed")
|
||||
|
||||
if xl.Range("A1:B1").Value != ((Unicode("Hi 0"),2),):
|
||||
raise error("flat-horizontal cell range failed")
|
||||
|
||||
if xl.Range("A1:A2").Value != ((Unicode("Hi 0"),),(Unicode("x"),)):
|
||||
raise error("flat-vertical cell range failed")
|
||||
|
||||
if xl.Range("A1:C3").Value != ((Unicode("Hi 0"),2,3),(Unicode("x"),Unicode("Hi 1"),Unicode("z")),(3,2,Unicode("Hi 2"))):
|
||||
raise error("square cell range failed")
|
||||
|
||||
xl.Range("A1:C3").Value =((3,2,1),("x","y","z"),(1,2,3))
|
||||
|
||||
if xl.Range("A1:C3").Value != ((3,2,1),(Unicode("x"),Unicode("y"),Unicode("z")),(1,2,3)):
|
||||
raise error("Range was not what I set it to!")
|
||||
|
||||
# test dates out with Excel
|
||||
xl.Cells(5,1).Value = "Excel time"
|
||||
xl.Cells(5,2).Formula = "=Now()"
|
||||
|
||||
import time
|
||||
xl.Cells(6,1).Value = "Python time"
|
||||
xl.Cells(6,2).Value = pythoncom.MakeTime(time.time())
|
||||
xl.Cells(6,2).NumberFormat = "d/mm/yy h:mm"
|
||||
xl.Columns("A:B").EntireColumn.AutoFit()
|
||||
|
||||
xl.Workbooks(1).Close(0)
|
||||
xl.Quit()
|
||||
|
||||
def TestAll():
|
||||
TestWord()
|
||||
|
||||
print("Starting Excel for Dynamic test...")
|
||||
xl = win32com.client.dynamic.Dispatch("Excel.Application")
|
||||
TextExcel(xl)
|
||||
|
||||
try:
|
||||
print("Starting Excel 8 for generated excel8.py test...")
|
||||
mod = gencache.EnsureModule("{00020813-0000-0000-C000-000000000046}", 0, 1, 2, bForDemand=1)
|
||||
xl = win32com.client.Dispatch("Excel.Application")
|
||||
TextExcel(xl)
|
||||
except ImportError:
|
||||
print("Could not import the generated Excel 97 wrapper")
|
||||
|
||||
try:
|
||||
import xl5en32
|
||||
mod = gencache.EnsureModule("{00020813-0000-0000-C000-000000000046}", 9, 1, 0)
|
||||
xl = win32com.client.Dispatch("Excel.Application.5")
|
||||
print("Starting Excel 95 for makepy test...")
|
||||
TextExcel(xl)
|
||||
except ImportError:
|
||||
print("Could not import the generated Excel 95 wrapper")
|
||||
|
||||
if __name__=='__main__':
|
||||
TestAll()
|
||||
CheckClean()
|
||||
pythoncom.CoUninitialize()
|
||||
115
Lib/site-packages/win32com/test/testMSOfficeEvents.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# OfficeEvents - test/demonstrate events with Word and Excel.
|
||||
from win32com.client import DispatchWithEvents, Dispatch
|
||||
import msvcrt, pythoncom
|
||||
import time, sys
|
||||
import types
|
||||
|
||||
import threading
|
||||
stopEvent = threading.Event()
|
||||
|
||||
def TestExcel():
|
||||
class ExcelEvents:
|
||||
def OnNewWorkbook(self, wb):
|
||||
if type(wb) != types.InstanceType:
|
||||
raise RuntimeError("The transformer doesnt appear to have translated this for us!")
|
||||
self.seen_events["OnNewWorkbook"] = None
|
||||
def OnWindowActivate(self, wb, wn):
|
||||
if type(wb) != types.InstanceType or type(wn) != types.InstanceType:
|
||||
raise RuntimeError("The transformer doesnt appear to have translated this for us!")
|
||||
self.seen_events["OnWindowActivate"] = None
|
||||
def OnWindowDeactivate(self, wb, wn):
|
||||
self.seen_events["OnWindowDeactivate"] = None
|
||||
def OnSheetDeactivate(self, sh):
|
||||
self.seen_events["OnSheetDeactivate"] = None
|
||||
def OnSheetBeforeDoubleClick(self, Sh, Target, Cancel):
|
||||
if Target.Column % 2 == 0:
|
||||
print("You can double-click there...")
|
||||
else:
|
||||
print("You can not double-click there...")
|
||||
# This function is a void, so the result ends up in
|
||||
# the only ByRef - Cancel.
|
||||
return 1
|
||||
|
||||
class WorkbookEvents:
|
||||
def OnActivate(self):
|
||||
print("workbook OnActivate")
|
||||
def OnBeforeRightClick(self, Target, Cancel):
|
||||
print("It's a Worksheet Event")
|
||||
|
||||
e = DispatchWithEvents("Excel.Application", ExcelEvents)
|
||||
e.seen_events = {}
|
||||
e.Visible=1
|
||||
book = e.Workbooks.Add()
|
||||
book = DispatchWithEvents(book, WorkbookEvents)
|
||||
print("Have book", book)
|
||||
# sheet = e.Worksheets(1)
|
||||
# sheet = DispatchWithEvents(sheet, WorksheetEvents)
|
||||
|
||||
print("Double-click in a few of the Excel cells...")
|
||||
print("Press any key when finished with Excel, or wait 10 seconds...")
|
||||
if not _WaitForFinish(e, 10):
|
||||
e.Quit()
|
||||
if not _CheckSeenEvents(e, ["OnNewWorkbook", "OnWindowActivate"]):
|
||||
sys.exit(1)
|
||||
|
||||
def TestWord():
|
||||
class WordEvents:
|
||||
def OnDocumentChange(self):
|
||||
self.seen_events["OnDocumentChange"] = None
|
||||
def OnWindowActivate(self, doc, wn):
|
||||
self.seen_events["OnWindowActivate"] = None
|
||||
def OnQuit(self):
|
||||
self.seen_events["OnQuit"] = None
|
||||
stopEvent.set()
|
||||
|
||||
w = DispatchWithEvents("Word.Application", WordEvents)
|
||||
w.seen_events = {}
|
||||
w.Visible = 1
|
||||
w.Documents.Add()
|
||||
print("Press any key when finished with Word, or wait 10 seconds...")
|
||||
if not _WaitForFinish(w, 10):
|
||||
w.Quit()
|
||||
if not _CheckSeenEvents(w, ["OnDocumentChange", "OnWindowActivate"]):
|
||||
sys.exit(1)
|
||||
|
||||
def _WaitForFinish(ob, timeout):
|
||||
end = time.time() + timeout
|
||||
while 1:
|
||||
if msvcrt.kbhit():
|
||||
msvcrt.getch()
|
||||
break
|
||||
pythoncom.PumpWaitingMessages()
|
||||
stopEvent.wait(.2)
|
||||
if stopEvent.isSet():
|
||||
stopEvent.clear()
|
||||
break
|
||||
try:
|
||||
if not ob.Visible:
|
||||
# Gone invisible - we need to pretend we timed
|
||||
# out, so the app is quit.
|
||||
return 0
|
||||
except pythoncom.com_error:
|
||||
# Excel is busy (eg, editing the cell) - ignore
|
||||
pass
|
||||
if time.time() > end:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
def _CheckSeenEvents(o, events):
|
||||
rc = 1
|
||||
for e in events:
|
||||
if e not in o.seen_events:
|
||||
print("ERROR: Expected event did not trigger", e)
|
||||
rc = 0
|
||||
return rc
|
||||
|
||||
def test():
|
||||
import sys
|
||||
if "noword" not in sys.argv[1:]:
|
||||
TestWord()
|
||||
if "noexcel" not in sys.argv[1:]:
|
||||
TestExcel()
|
||||
print("Word and Excel event tests passed.")
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
140
Lib/site-packages/win32com/test/testMarshal.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
"""Testing pasing object between multiple COM threads
|
||||
|
||||
Uses standard COM marshalling to pass objects between threads. Even
|
||||
though Python generally seems to work when you just pass COM objects
|
||||
between threads, it shouldnt.
|
||||
|
||||
This shows the "correct" way to do it.
|
||||
|
||||
It shows that although we create new threads to use the Python.Interpreter,
|
||||
COM marshalls back all calls to that object to the main Python thread,
|
||||
which must be running a message loop (as this sample does).
|
||||
|
||||
When this test is run in "free threaded" mode (at this stage, you must
|
||||
manually mark the COM objects as "ThreadingModel=Free", or run from a
|
||||
service which has marked itself as free-threaded), then no marshalling
|
||||
is done, and the Python.Interpreter object start doing the "expected" thing
|
||||
- ie, it reports being on the same thread as its caller!
|
||||
|
||||
Python.exe needs a good way to mark itself as FreeThreaded - at the moment
|
||||
this is a pain in the but!
|
||||
|
||||
"""
|
||||
|
||||
import threading, traceback
|
||||
import win32com.client
|
||||
import win32event, win32api
|
||||
import pythoncom
|
||||
import unittest
|
||||
|
||||
from .testServers import InterpCase
|
||||
|
||||
freeThreaded = 1
|
||||
|
||||
class ThreadInterpCase(InterpCase):
|
||||
def _testInterpInThread(self, stopEvent, interp):
|
||||
try:
|
||||
self._doTestInThread(interp)
|
||||
finally:
|
||||
win32event.SetEvent(stopEvent)
|
||||
|
||||
def _doTestInThread(self, interp):
|
||||
pythoncom.CoInitialize()
|
||||
myThread = win32api.GetCurrentThreadId()
|
||||
|
||||
if freeThreaded:
|
||||
interp = pythoncom.CoGetInterfaceAndReleaseStream(interp, pythoncom.IID_IDispatch)
|
||||
interp = win32com.client.Dispatch(interp)
|
||||
|
||||
interp.Exec("import win32api")
|
||||
#print "The test thread id is %d, Python.Interpreter's thread ID is %d" % (myThread, interp.Eval("win32api.GetCurrentThreadId()"))
|
||||
pythoncom.CoUninitialize()
|
||||
|
||||
def BeginThreadsSimpleMarshal(self, numThreads):
|
||||
"""Creates multiple threads using simple (but slower) marshalling.
|
||||
|
||||
Single interpreter object, but a new stream is created per thread.
|
||||
|
||||
Returns the handles the threads will set when complete.
|
||||
"""
|
||||
interp = win32com.client.Dispatch("Python.Interpreter")
|
||||
events = []
|
||||
threads = []
|
||||
for i in range(numThreads):
|
||||
hEvent = win32event.CreateEvent(None, 0, 0, None)
|
||||
events.append(hEvent)
|
||||
interpStream = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, interp._oleobj_)
|
||||
t = threading.Thread(target=self._testInterpInThread, args=(hEvent, interpStream))
|
||||
t.setDaemon(1) # so errors dont cause shutdown hang
|
||||
t.start()
|
||||
threads.append(t)
|
||||
interp = None
|
||||
return threads, events
|
||||
|
||||
#
|
||||
# NOTE - this doesnt quite work - Im not even sure it should, but Greg reckons
|
||||
# you should be able to avoid the marshal per thread!
|
||||
# I think that refers to CoMarshalInterface though...
|
||||
def BeginThreadsFastMarshal(self, numThreads):
|
||||
"""Creates multiple threads using fast (but complex) marshalling.
|
||||
|
||||
The marshal stream is created once, and each thread uses the same stream
|
||||
|
||||
Returns the handles the threads will set when complete.
|
||||
"""
|
||||
interp = win32com.client.Dispatch("Python.Interpreter")
|
||||
if freeThreaded:
|
||||
interp = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, interp._oleobj_)
|
||||
events = []
|
||||
threads = []
|
||||
for i in range(numThreads):
|
||||
hEvent = win32event.CreateEvent(None, 0, 0, None)
|
||||
t = threading.Thread(target=self._testInterpInThread, args=(hEvent, interp))
|
||||
t.setDaemon(1) # so errors dont cause shutdown hang
|
||||
t.start()
|
||||
events.append(hEvent)
|
||||
threads.append(t)
|
||||
return threads, events
|
||||
|
||||
def _DoTestMarshal(self, fn, bCoWait = 0):
|
||||
#print "The main thread is %d" % (win32api.GetCurrentThreadId())
|
||||
threads, events = fn(2)
|
||||
numFinished = 0
|
||||
while 1:
|
||||
try:
|
||||
if bCoWait:
|
||||
rc = pythoncom.CoWaitForMultipleHandles(0, 2000, events)
|
||||
else:
|
||||
# Specifying "bWaitAll" here will wait for messages *and* all events
|
||||
# (which is pretty useless)
|
||||
rc = win32event.MsgWaitForMultipleObjects(events, 0, 2000, win32event.QS_ALLINPUT)
|
||||
if rc >= win32event.WAIT_OBJECT_0 and rc < win32event.WAIT_OBJECT_0+len(events):
|
||||
numFinished = numFinished + 1
|
||||
if numFinished >= len(events):
|
||||
break
|
||||
elif rc==win32event.WAIT_OBJECT_0 + len(events): # a message
|
||||
# This is critical - whole apartment model demo will hang.
|
||||
pythoncom.PumpWaitingMessages()
|
||||
else: # Timeout
|
||||
print("Waiting for thread to stop with interfaces=%d, gateways=%d" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
for t in threads:
|
||||
t.join(2)
|
||||
self.failIf(t.isAlive(), "thread failed to stop!?")
|
||||
threads = None # threads hold references to args
|
||||
# Seems to be a leak here I can't locate :(
|
||||
#self.failUnlessEqual(pythoncom._GetInterfaceCount(), 0)
|
||||
#self.failUnlessEqual(pythoncom._GetGatewayCount(), 0)
|
||||
|
||||
def testSimpleMarshal(self):
|
||||
self._DoTestMarshal(self.BeginThreadsSimpleMarshal)
|
||||
|
||||
def testSimpleMarshalCoWait(self):
|
||||
self._DoTestMarshal(self.BeginThreadsSimpleMarshal, 1)
|
||||
|
||||
# def testFastMarshal(self):
|
||||
# self._DoTestMarshal(self.BeginThreadsFastMarshal)
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main('testMarshal')
|
||||
25
Lib/site-packages/win32com/test/testNetscape.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
## AHH - I cant make this work!!!
|
||||
|
||||
# But this is the general idea.
|
||||
|
||||
import netscape
|
||||
import sys
|
||||
|
||||
error = "Netscape Test Error"
|
||||
|
||||
if __name__=='__main__':
|
||||
n=netscape.CNetworkCX()
|
||||
rc = n.Open("http://d|/temp/apyext.html", 0, None, 0, None)
|
||||
if not rc: raise error("Open method of Netscape failed")
|
||||
while 1:
|
||||
num, str = n.Read(None, 0)
|
||||
print("Got ", num, str)
|
||||
if num==0:
|
||||
break # used to be continue - no idea!!
|
||||
if num==-1:
|
||||
break
|
||||
# sys.stdout.write(str)
|
||||
n.Close()
|
||||
print("Done!")
|
||||
del n
|
||||
sys.last_type = sys.last_value = sys.last_traceback = None
|
||||
212
Lib/site-packages/win32com/test/testPersist.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
import pythoncom
|
||||
import win32com.server.util
|
||||
import time
|
||||
|
||||
import win32com, sys, string, win32api, traceback
|
||||
import win32com.client.dynamic
|
||||
import win32com.client
|
||||
import pythoncom
|
||||
from win32com.axcontrol import axcontrol
|
||||
from pywintypes import Unicode
|
||||
from win32com import storagecon
|
||||
from win32com.test.util import CheckClean
|
||||
|
||||
import pywintypes
|
||||
import win32ui
|
||||
import win32api, os
|
||||
|
||||
from pywin32_testutil import str2bytes
|
||||
|
||||
S_OK = 0
|
||||
|
||||
import datetime
|
||||
if issubclass(pywintypes.TimeType, datetime.datetime):
|
||||
import win32timezone
|
||||
now = win32timezone.now()
|
||||
else:
|
||||
now = pywintypes.Time(time.time())
|
||||
|
||||
class LockBytes:
|
||||
_public_methods_ = [ 'ReadAt', 'WriteAt', 'Flush', 'SetSize', 'LockRegion', 'UnlockRegion', 'Stat' ]
|
||||
_com_interfaces_ = [ pythoncom.IID_ILockBytes ]
|
||||
|
||||
def __init__(self, data = ""):
|
||||
self.data = str2bytes(data)
|
||||
self.ctime = now
|
||||
self.mtime = now
|
||||
self.atime = now
|
||||
|
||||
def ReadAt(self, offset, cb):
|
||||
print("ReadAt")
|
||||
result = self.data[offset:offset + cb]
|
||||
return result
|
||||
|
||||
def WriteAt(self, offset, data):
|
||||
print("WriteAt " +str(offset))
|
||||
print("len " + str(len(data)))
|
||||
print("data:")
|
||||
#print data
|
||||
if len(self.data) >= offset:
|
||||
newdata = self.data[0:offset] + data
|
||||
print(len(newdata))
|
||||
if len(self.data) >= offset + len(data):
|
||||
newdata = newdata + self.data[offset + len(data):]
|
||||
print(len(newdata))
|
||||
self.data = newdata
|
||||
return len(data)
|
||||
|
||||
def Flush(self, whatsthis=0):
|
||||
print("Flush" + str(whatsthis))
|
||||
fname = os.path.join(win32api.GetTempPath(), "persist.doc")
|
||||
open(fname, "wb").write(self.data)
|
||||
return S_OK
|
||||
|
||||
def SetSize(self, size):
|
||||
print("Set Size" + str(size))
|
||||
if size > len(self.data):
|
||||
self.data = self.data + str2bytes("\000" * (size - len(self.data)))
|
||||
else:
|
||||
self.data = self.data[0:size]
|
||||
return S_OK
|
||||
|
||||
def LockRegion(self, offset, size, locktype):
|
||||
print("LockRegion")
|
||||
pass
|
||||
|
||||
def UnlockRegion(self, offset, size, locktype):
|
||||
print("UnlockRegion")
|
||||
pass
|
||||
|
||||
def Stat(self, statflag):
|
||||
print("returning Stat " + str(statflag))
|
||||
return (
|
||||
"PyMemBytes",
|
||||
storagecon.STGTY_LOCKBYTES,
|
||||
len(self.data),
|
||||
self.mtime,
|
||||
self.ctime,
|
||||
self.atime,
|
||||
storagecon.STGM_DIRECT|storagecon.STGM_READWRITE|storagecon.STGM_CREATE ,
|
||||
storagecon.STGM_SHARE_EXCLUSIVE,
|
||||
"{00020905-0000-0000-C000-000000000046}",
|
||||
0, # statebits ?
|
||||
0
|
||||
)
|
||||
|
||||
|
||||
class OleClientSite:
|
||||
_public_methods_ = [ 'SaveObject', 'GetMoniker', 'GetContainer', 'ShowObject', 'OnShowWindow', 'RequestNewObjectLayout' ]
|
||||
_com_interfaces_ = [ axcontrol.IID_IOleClientSite ]
|
||||
|
||||
def __init__(self, data = ""):
|
||||
self.IPersistStorage = None
|
||||
self.IStorage = None
|
||||
|
||||
def SetIPersistStorage(self, IPersistStorage):
|
||||
self.IPersistStorage = IPersistStorage
|
||||
|
||||
def SetIStorage(self, IStorage):
|
||||
self.IStorage = IStorage
|
||||
|
||||
def SaveObject(self):
|
||||
print("SaveObject")
|
||||
if self.IPersistStorage != None and self.IStorage != None:
|
||||
self.IPersistStorage.Save(self.IStorage,1)
|
||||
self.IStorage.Commit(0)
|
||||
return S_OK
|
||||
|
||||
def GetMoniker(self, dwAssign, dwWhichMoniker):
|
||||
print("GetMoniker " + str(dwAssign) + " " + str(dwWhichMoniker))
|
||||
|
||||
def GetContainer(self):
|
||||
print("GetContainer")
|
||||
|
||||
def ShowObject(self):
|
||||
print("ShowObject")
|
||||
|
||||
def OnShowWindow(self, fShow):
|
||||
print("ShowObject" + str(fShow))
|
||||
|
||||
def RequestNewObjectLayout(self):
|
||||
print("RequestNewObjectLayout")
|
||||
|
||||
|
||||
def test():
|
||||
# create a LockBytes object and
|
||||
#wrap it as a COM object
|
||||
# import win32com.server.dispatcher
|
||||
lbcom = win32com.server.util.wrap(LockBytes(), pythoncom.IID_ILockBytes) #, useDispatcher=win32com.server.dispatcher.DispatcherWin32trace)
|
||||
|
||||
# create a structured storage on the ILockBytes object
|
||||
stcom = pythoncom.StgCreateDocfileOnILockBytes(lbcom, storagecon.STGM_DIRECT| storagecon.STGM_CREATE | storagecon.STGM_READWRITE | storagecon.STGM_SHARE_EXCLUSIVE, 0)
|
||||
|
||||
# create our ClientSite
|
||||
ocs = OleClientSite()
|
||||
# wrap it as a COM object
|
||||
ocscom = win32com.server.util.wrap(ocs, axcontrol.IID_IOleClientSite)
|
||||
|
||||
# create a Word OLE Document, connect it to our site and our storage
|
||||
oocom=axcontrol.OleCreate("{00020906-0000-0000-C000-000000000046}",
|
||||
axcontrol.IID_IOleObject,
|
||||
0,
|
||||
(0,),
|
||||
ocscom,
|
||||
stcom,
|
||||
)
|
||||
|
||||
mf=win32ui.GetMainFrame()
|
||||
hwnd=mf.GetSafeHwnd()
|
||||
|
||||
# Set the host and document name
|
||||
# for unknown reason document name becomes hostname, and document name
|
||||
# is not set, debugged it, but don't know where the problem is?
|
||||
oocom.SetHostNames("OTPython", "This is Cool")
|
||||
|
||||
# activate the OLE document
|
||||
oocom.DoVerb( -1, ocscom, 0, hwnd, mf.GetWindowRect())
|
||||
|
||||
# set the hostnames again
|
||||
oocom.SetHostNames("OTPython2", "ThisisCool2")
|
||||
|
||||
# get IDispatch of Word
|
||||
doc=win32com.client.Dispatch(oocom.QueryInterface(pythoncom.IID_IDispatch))
|
||||
|
||||
# get IPersistStorage of Word
|
||||
dpcom=oocom.QueryInterface(pythoncom.IID_IPersistStorage)
|
||||
|
||||
# let our ClientSite know the interfaces
|
||||
ocs.SetIPersistStorage(dpcom)
|
||||
ocs.SetIStorage(stcom)
|
||||
|
||||
# use IDispatch to do the Office Word test
|
||||
# pasted from TestOffice.py
|
||||
|
||||
wrange = doc.Range()
|
||||
for i in range(10):
|
||||
wrange.InsertAfter("Hello from Python %d\n" % i)
|
||||
paras = doc.Paragraphs
|
||||
for i in range(len(paras)):
|
||||
paras[i]().Font.ColorIndex = i+1
|
||||
paras[i]().Font.Size = 12 + (4 * i)
|
||||
# XXX - note that
|
||||
# for para in paras:
|
||||
# para().Font...
|
||||
# doesnt seem to work - no error, just doesnt work
|
||||
# Should check if it works for VB!
|
||||
|
||||
|
||||
dpcom.Save(stcom, 0)
|
||||
dpcom.HandsOffStorage()
|
||||
# oocom.Close(axcontrol.OLECLOSE_NOSAVE) # or OLECLOSE_SAVEIFDIRTY, but it fails???
|
||||
|
||||
#Save the ILockBytes data to "persist2.doc"
|
||||
lbcom.Flush()
|
||||
|
||||
#exiting Winword will automatically update the ILockBytes data
|
||||
#and flush it to "%TEMP%\persist.doc"
|
||||
doc.Application.Quit()
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
pythoncom.CoUninitialize()
|
||||
CheckClean()
|
||||
55
Lib/site-packages/win32com/test/testPippo.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import sys
|
||||
import unittest
|
||||
import pythoncom
|
||||
from win32com.client import Dispatch
|
||||
from win32com.client.gencache import EnsureDispatch
|
||||
|
||||
class PippoTester(unittest.TestCase):
|
||||
def setUp(self):
|
||||
from win32com.test.util import RegisterPythonServer
|
||||
from win32com.test import pippo_server
|
||||
RegisterPythonServer(pippo_server.__file__, "Python.Test.Pippo")
|
||||
# create it.
|
||||
self.object = Dispatch("Python.Test.Pippo")
|
||||
|
||||
def testLeaks(self):
|
||||
try:
|
||||
gtrc = sys.gettotalrefcount
|
||||
except AttributeError:
|
||||
print("Please run this with python_d for leak tests")
|
||||
gtrc = lambda: 0
|
||||
# note creating self.object() should have consumed our "one time" leaks
|
||||
self.object.Method1()
|
||||
start = gtrc()
|
||||
for i in range(1000):
|
||||
object = Dispatch("Python.Test.Pippo")
|
||||
object.Method1()
|
||||
object = None
|
||||
end = gtrc()
|
||||
if end-start > 5:
|
||||
self.fail("We lost %d references!" % (end-start,))
|
||||
|
||||
def testResults(self):
|
||||
rc, out1 = self.object.Method2(123, 111)
|
||||
self.failUnlessEqual(rc, 123)
|
||||
self.failUnlessEqual(out1, 222)
|
||||
|
||||
def testLeaksGencache(self):
|
||||
try:
|
||||
gtrc = sys.gettotalrefcount
|
||||
except AttributeError:
|
||||
print("Please run this with python_d for leak tests")
|
||||
gtrc = lambda: 0
|
||||
# note creating self.object() should have consumed our "one time" leaks
|
||||
object = EnsureDispatch("Python.Test.Pippo")
|
||||
start = gtrc()
|
||||
for i in range(1000):
|
||||
object = EnsureDispatch("Python.Test.Pippo")
|
||||
object.Method1()
|
||||
object = None
|
||||
end = gtrc()
|
||||
if end-start > 10:
|
||||
self.fail("We lost %d references!" % (end-start,))
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||