2

I am sort of surprised this hasn't been covered before.

The calling for the method (in C) is:

SetValues(BSTR Keyword, SAFEARRAY * Data)

I have tried:

handle = win32com.client.Dispatch("My.Application")
vals = (1.1, 2.2, 3.3)
safe_vals = win32com.client.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, vals)
handle.SetValues("PUT_IT_HERE", safe_vals)

This gives me the error:

TypeError: Objects for SAFEARRAYS must be sequences (of sequences), or a buffer object.

If I just try entering 'vals':

    res = handle.SetValues("PUT_IT_HERE", vals)
  File "<COMObject My.Application>", line 2, in SetValues
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2147220988), None)

I assume there is some kind of conversion needed to make it compatible with (SAFEARRAY *) but no one has been very clear about this.

Jiminion
  • 5,080
  • 1
  • 31
  • 54
  • You should explain why this https://stackoverflow.com/a/59419622/403671 doesn't work (error? other issue?). It also depends on how your My.Application object's SetValues method is defined. Please show the IDL (using OLEViewer for example https://learn.microsoft.com/en-us/windows/win32/com/ole-com-object-viewer) or some really reproducing code – Simon Mourier Mar 20 '23 at 12:14
  • With this type of C interface, PyWin32 will send a `SAFEARRAY` of `VARIANT`s. So this `handle.SetValues("PUT_IT_HERE", vals)` will simply work. Each element of the array will be a `VARIANT` of the type corresponding to the Python type, so in this case 3 `VARIANT`s of `VT_R8` type. If you pass say `("hello", "world")`, you will get 2 `VARIANT`s of `VT_BSTR` type, etc. – Simon Mourier Mar 20 '23 at 16:59
  • 1
    I've tested what I said and it works fine. If it fails in your case, it means your native interface is not what you said it is or your called object behaves in some way that causes error. Like I said, details are crucial. if you want more precise answer, please show the IDL (using OLEViewer) which is not the same as a C header, or give a real reproducible project. – Simon Mourier Mar 20 '23 at 17:50
  • 1
    @SimonMourier OK, I got it to work. Thank you SO MUCH. Merci beaucoup! If you put out an answer I can accept it and get you the bounty. Thanks again. It does 'simply work', but I had a few things on my end that were goofing things up. – Jiminion Mar 20 '23 at 18:24

1 Answers1

1

With this type of C/C++ method:

SetValues(BSTR Keyword, SAFEARRAY * Data)

It's probable that you have this .IDL file (or any corresponding TLB or COM tooling that ends up with this C/C++ method), as .IDL requires the type of SAFEARRAY argument to be specified:

interface IMyInterface : IDispatch
{
    HRESULT SetValues(BSTR Keyword, SAFEARRAY(VARIANT) Data);
};

So, this is in fact the easiest and versatile way to pass any number of arguments of any type from Python to a COM object as PyWin32 will automatically convert Python objects into "VARIANT-type objects" (e.g: object that can be wrapped by a VARIANT struct)

So this:

handle.SetValues("PUT_IT_HERE", vals)

will simply work. In this case, the native side will get 3 VARIANTs of VT_R8 type. If you pass say ("hello", "world"), you will get 2 VARIANTs of VT_BSTR type, but you can pass arrays of arrays of variants, etc.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298