Wallpaper-Changer/lib/win32com/HTML/variant.html
2020-12-03 00:58:58 +01:00

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>