163 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <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>
 |