[pywin32-checkins] /hgrepo/p/py/pywin32/pywin32: 4 new changesets
OLD project page for the Python extensions for Windows
Brought to you by:
mhammond
From: <pyw...@li...> - 2011-10-12 01:15:46
|
changeset b9780b123a70 in /hgrepo/p/py/pywin32/pywin32 details: http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/hgrepo/p/py/pywin32/pywin32?cmd=changeset;node=b9780b123a70 summary: minor doc tweak changeset 4074de7baf6f in /hgrepo/p/py/pywin32/pywin32 details: http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/hgrepo/p/py/pywin32/pywin32?cmd=changeset;node=4074de7baf6f summary: fix win32pipe builds with the new unified swig changeset 0c3b3be90017 in /hgrepo/p/py/pywin32/pywin32 details: http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/hgrepo/p/py/pywin32/pywin32?cmd=changeset;node=0c3b3be90017 summary: refactor testPyComTest to avoid duplicated code and ensure better test coverage changeset 36b16bcc5c9a in /hgrepo/p/py/pywin32/pywin32 details: http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/hgrepo/p/py/pywin32/pywin32?cmd=changeset;node=36b16bcc5c9a summary: Add VARIANT object so explicit variant types can be specified in some contexts diffstat: AutoDuck/pywin32-document.xml | 2 +- CHANGES.txt | 3 + com/win32com/HTML/docindex.html | 1 + com/win32com/HTML/variant.html | 162 ++++++++++++ com/win32com/client/__init__.py | 23 + com/win32com/readme.htm | 6 + com/win32com/src/PyIDispatch.cpp | 45 ++- com/win32com/src/include/PythonCOM.h | 1 + com/win32com/src/oleargs.cpp | 107 ++++++++- com/win32com/test/testPyComTest.py | 438 ++++++++++++++++++++-------------- win32/src/win32pipe.i | 14 +- 11 files changed, 593 insertions(+), 209 deletions(-) diffs (truncated from 1124 to 300 lines): diff -r d812236c54bc -r 36b16bcc5c9a AutoDuck/pywin32-document.xml --- a/AutoDuck/pywin32-document.xml Tue Oct 11 17:06:16 2011 +1100 +++ b/AutoDuck/pywin32-document.xml Wed Oct 12 12:12:12 2011 +1100 @@ -19,7 +19,7 @@ </category> <category id="com" label="Python COM"> <overviews> - <item name="Quick-Starts to Python and COM" href="com/win32com/HTML/docindex.html"/> + <item name="win32com documentation index" href="com/win32com/HTML/docindex.html"/> <item name="win32com readme" href="com/win32com/readme.htm"/> <item name="ADSI Python" href="com/help/adsi.html"/> <item name="Active Directory" href="com/help/active_directory.html"/> diff -r d812236c54bc -r 36b16bcc5c9a CHANGES.txt --- a/CHANGES.txt Tue Oct 11 17:06:16 2011 +1100 +++ b/CHANGES.txt Wed Oct 12 12:12:12 2011 +1100 @@ -6,6 +6,9 @@ Since build 216: ---------------- +* A new win32com.client.VARIANT object can be used for advanced control over + the parameter types passed to some COM methods. See the documentation in + win32com/HTML/variant.html (also included in the help file) * The win32com.adsi and win32com.mapi packages have been upgraded to work on Python 3.x and as a result, there is a slight risk that regressions to diff -r d812236c54bc -r 36b16bcc5c9a com/win32com/HTML/docindex.html --- a/com/win32com/HTML/docindex.html Tue Oct 11 17:06:16 2011 +1100 +++ b/com/win32com/HTML/docindex.html Wed Oct 12 12:12:12 2011 +1100 @@ -13,6 +13,7 @@ <P><A HREF="QuickStartClientCom.html">A Quick Start to Client Side COM</A> (including makepy)</P> <P><A HREF="QuickStartServerCom.html">A Quick Start to Server Side COM</A></P> <P><A HREF="GeneratedSupport.html">Information on generated Python files (ie, what makepy generates)</A></P> +<P><A HREF="variant.html">An advanced VARIANT object which can give more control over parameter types</A></P> <P><A HREF="package.html">A brief description of the win32com package structure</A></P> <P><A HREF="PythonCOM.html">Python COM Implementation documentation</A></P> <P><A HREF="misc.html">Misc stuff I dont know where to put anywhere else</A></P> diff -r d812236c54bc -r 36b16bcc5c9a com/win32com/HTML/variant.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/com/win32com/HTML/variant.html Wed Oct 12 12:12:12 2011 +1100 @@ -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>DumpDispatch</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> diff -r d812236c54bc -r 36b16bcc5c9a com/win32com/client/__init__.py --- a/com/win32com/client/__init__.py Tue Oct 11 17:06:16 2011 +1100 +++ b/com/win32com/client/__init__.py Wed Oct 12 12:12:12 2011 +1100 @@ -514,3 +514,26 @@ 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) diff -r d812236c54bc -r 36b16bcc5c9a com/win32com/readme.htm --- a/com/win32com/readme.htm Tue Oct 11 17:06:16 2011 +1100 +++ b/com/win32com/readme.htm Wed Oct 12 12:12:12 2011 +1100 @@ -18,6 +18,12 @@ 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 diff -r d812236c54bc -r 36b16bcc5c9a com/win32com/src/PyIDispatch.cpp --- a/com/win32com/src/PyIDispatch.cpp Tue Oct 11 17:06:16 2011 +1100 +++ b/com/win32com/src/PyIDispatch.cpp Wed Oct 12 12:12:12 2011 +1100 @@ -140,7 +140,7 @@ // Convert a PyTuple object into a DISPPARAM structure. // numArgs specifies which of the LAST args in the tuple are valid. // To convert all args, pass len(args) -static BOOL PyCom_MakeUntypedDISPPARAMS( PyObject *args, int numArgs, WORD wFlags, DISPPARAMS *pParm) +static BOOL PyCom_MakeUntypedDISPPARAMS( PyObject *args, int numArgs, WORD wFlags, DISPPARAMS *pParm, PythonOleArgHelper **ppHelpers) { int argc = PyObject_Length(args); DISPID dispidNamed = DISPID_PROPERTYPUT; @@ -152,16 +152,19 @@ if ( pParm->cArgs ) { pParm->rgvarg = new VARIANTARG[pParm->cArgs]; + PythonOleArgHelper *pHelpers = *ppHelpers = new PythonOleArgHelper[pParm->cArgs]; for ( UINT i = 0; i < pParm->cArgs; ++i ) { VariantInit(&pParm->rgvarg[i]); // args in reverse order. - if ( !PyCom_VariantFromPyObject(PyTuple_GET_ITEM(args, argc-i-1), &pParm->rgvarg[i]) ) + if (!pHelpers[i].MakeObjToVariant(PyTuple_GET_ITEM(args, argc-i-1), &pParm->rgvarg[i], NULL) ) { if ( !PyErr_Occurred() ) PyErr_Format(PyExc_TypeError, "Bad argument"); while ( i-- > 0 ) VariantClear(&pParm->rgvarg[i]); + delete [] pParm->rgvarg; + delete [] pHelpers; return FALSE; } } @@ -173,12 +176,29 @@ pParm->rgdispidNamedArgs[0] = DISPID_PROPERTYPUT; pParm->cNamedArgs = 1; } + } else { + *ppHelpers = NULL; } return TRUE; } -static void PyCom_FreeUntypedDISPPARAMS( DISPPARAMS *pParm ) +static BOOL PyCom_FinishUntypedDISPPARAMS( DISPPARAMS *pParm, PythonOleArgHelper *pHelpers ) { + BOOL ok = TRUE; + if (pHelpers) { + for ( UINT i = 0; i < pParm->cArgs; ++i ) { + // Do magic so PyVariant objects get updated if appropriate. + if (pHelpers[i].m_bIsOut && pHelpers[i].m_pyVariant) { + PyObject *tmp = pHelpers[i].MakeVariantToObj(pParm->rgvarg+(pParm->cArgs-i-1)); |