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