<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>