I've come into a problem when trying to implement an interface that has a
single out parameter which happens to be a safearray of structs/VT_RECORD.
I've been able to find a workaround to what looks like a limitation, which
I'm submitting for your review.

 

The interface I need to implement in Python receives a safearray of a Pair
structs (defined below) and has a single out parameter which also provides a
safearray of Pair structs.

 

      [

            uuid(41A8A003-C9FE-4ad8-866E-71CC5BAD0EA5)

      ]

      struct Pair {

            BSTR Key;

            BSTR Value;

      };

 

      [

            object,

            uuid(8232CB78-EEB8-4965-A275-2399F2EE497D),

            dual,

            nonextensible,

            helpstring("ITest Interface"),

            pointer_default(unique)

      ]

      interface ITest : IDispatch{

            [id(1), helpstring("method Test")] HRESULT Test([in]
SAFEARRAY(struct Pair) in, [out] SAFEARRAY(struct Pair)* out);

      };

 

comtypes.client.GetModule generates the following code for the structure,
that includes a _recordinfo_ member:

 

      class Pair(Structure):

            _recordinfo_ = ('{D97ED183-D361-40cf-80AA-759686079F96}', 1, 0,
0L, '{41A8A003-C9FE-4ad8-866E-71CC5BAD0EA5}')

      Pair._fields_ = [

            ('Key', BSTR),

            ('Value', BSTR),

      ]

      assert sizeof(Pair) == 8, sizeof(Pair)

      assert alignment(Pair) == 4, alignment(Pair)

 

The generated code for the interface is:

 

      class
ITest(comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0.IDispatch):

            _case_insensitive_ = True

            u'ITest Interface'

            _iid_ = GUID('{8232CB78-EEB8-4965-A275-2399F2EE497D3}')

            _idlflags_ = ['nonextensible', 'dual', 'oleautomation']

 

      ITest._methods_ = [

            COMMETHOD([dispid(1), helpstring(u'method Test')], HRESULT,
'Test',

                          ( ['in'], _midlSAFEARRAY(Pair), 'in' ),

                          ( ['out'], POINTER(_midlSAFEARRAY(Pair)), 'out'
)),

      ]

 

      ################################################################

      ## code template for ITest implementation

      ##class ITest_Impl(object):

      ##    def Test(self, in):

      ##        u'method Test'

      ##        #return out

      ##

 

The COM object that implements the interface tries to return a list of Pair
objects:

 

      class Test(comtypes.COMObject):

            _com_interfaces_ = [test_tlb.ITest]

 

            # [id(1), helpstring("method Test")] HRESULT Test([in]
SAFEARRAY(struct Pair) in, [out] SAFEARRAY(struct Pair)* out);

            def ITest_Test(self, in, out):

                  l = []

                  l.append(test_tlb.Pair(u'1', u'2'))

                  l.append(test_tlb.Pair(u'2', u'3'))

                  return l

 

This triggers the following error:

 

      Traceback (most recent call last):

        File "C:\Python25\Lib\site-packages\comtypes\_comobject.py", line
123, in call_without_this

            args[args_out_idx[0]][0] = result

        File "C:\Python25\lib\site-packages\comtypes\safearray.py", line
322, in __setitem__

            pa = self._type_.create(value)

        File "C:\Python25\lib\site-packages\comtypes\safearray.py", line
101, in create

            raise TypeError("Cannot create SAFEARRAY type VT_RECORD without
IRecordInfo.")

      TypeError: Cannot create SAFEARRAY type VT_RECORD without IRecordInfo.

 

When _comobject.py tries to assign the method's return value to the single
out argument:

 

    args[args_out_idx[0]][0] = result

 

it ends up calling '__setitem__' which needs to create a saferray that
contains the elements in the return value through the 'create' method:

 

    pa = self._type_.create(value)

 

Then, because this is a safearray of structs, SafeArrayCreateVectorEx needs
to receive an IRecordInfo interface to be able to allocate enough memory for
the array elements:

 

    pa = _safearray.SafeArrayCreateVectorEx(cls._vartype_,

                                            0,

                                            len(value),

                                            extra)

 

However, as shown in the call to create, no 'extra' parameter is specified
and thus the call to SafeArrayCreateVectorEx fails.

 

I've been able to workaround the issue by trying to obtain IRecordInfo
inside the 'create' method, when no extra parameter is provided. Because the
function will receive an array or container of the structs to be copied into
the safearray, if there is at least one element in value, we can check
whether it has a '_recordinfo_' member and, if that's available, use
comtypes.typeinfo.GetRecordInfoFromGuids to obtain the missing IRecordInfo
for SafeArrayCreateVectorEx.

 

The change in comtypes/safearray.py is shown below (just before the call to
SafeArrayCreateVectorEx).

 

            # (...)

 

            # try to obtain IRecordInfo if it's a SAFEARRAY struct of
VT_RECORD and IRecordInfo

            # has not been provided through 'extra'. check whether an
element in value contains

            # _recordinfo_ and if that's the case, use
comtypes.typeinfo.GetRecordInfoFromGuids

            # to obtain IRecordInfo

 

            if cls._vartype_ == VT_RECORD and extra is None and len(value) >
0 and hasattr(value[0], '_recordinfo_'):

                import comtypes.typeinfo

                extra =
comtypes.typeinfo.GetRecordInfoFromGuids(*value[0]._recordinfo_)

 

            # For VT_UNKNOWN or VT_DISPATCH, extra must be a pointer to

            # the GUID of the interface.

            #

            # For VT_RECORD, extra must be a pointer to an IRecordInfo

            # describing the record.

 

            pa = _safearray.SafeArrayCreateVectorEx(cls._vartype_,

                                                    0,

                                                    len(value),

                                                    extra)

            # (...)

 

I'd like to know whether you think this approach is ok, or if there's any
other way I'd have been able to have an out parameter provide a safearray of
structs/VT_RECORD which doesn't require this change.

 

Saludos,

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with 
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
comtypes-users mailing list
comtypes-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/comtypes-users

Reply via email to