Update of /cvsroot/pywin32/pywin32/com/win32com/src
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7696/com/win32com/src
Modified Files:
PyGatewayBase.cpp PythonCOM.cpp
Log Message:
Much better support for 'named params' in Python gateways. Fixes
[ 1648655 ] Wrong args order with event handler
Index: PythonCOM.cpp
===================================================================
RCS file: /cvsroot/pywin32/pywin32/com/win32com/src/PythonCOM.cpp,v
retrieving revision 1.48
retrieving revision 1.49
diff -C2 -d -r1.48 -r1.49
*** PythonCOM.cpp 27 Aug 2007 05:12:11 -0000 1.48
--- PythonCOM.cpp 4 Sep 2007 10:53:29 -0000 1.49
***************
*** 29,35 ****
extern PyObject *g_obPyCom_MapInterfaceNameToIID;
! static PyObject *g_obEmpty = NULL;
! static PyObject *g_obMissing = NULL;
! static PyObject *g_obArgNotFound = NULL;
PyObject *PyCom_InternalError = NULL;
--- 29,35 ----
extern PyObject *g_obPyCom_MapInterfaceNameToIID;
! PyObject *g_obEmpty = NULL;
! PyObject *g_obMissing = NULL;
! PyObject *g_obArgNotFound = NULL;
PyObject *PyCom_InternalError = NULL;
Index: PyGatewayBase.cpp
===================================================================
RCS file: /cvsroot/pywin32/pywin32/com/win32com/src/PyGatewayBase.cpp,v
retrieving revision 1.16
retrieving revision 1.17
diff -C2 -d -r1.16 -r1.17
*** PyGatewayBase.cpp 19 Apr 2006 22:37:56 -0000 1.16
--- PyGatewayBase.cpp 4 Sep 2007 10:53:29 -0000 1.17
***************
*** 12,18 ****
--- 12,26 ----
{ 0x25d29cd0, 0x9b98, 0x11d0, { 0xae, 0x79, 0x4c, 0xf1, 0xcf, 0x0, 0x0, 0x0 } };
+ extern PyObject *g_obMissing;
+
extern void PyCom_LogF(const char *fmt, ...);
#define LogF PyCom_LogF
+ #include <malloc.h>
+ #if _MSC_VER < 1400
+ // _malloca is the new 'safe' one
+ #define _malloca _alloca
+ #endif
+
// Internal ErrorUtil helpers we reach in for.
// Free the strings from an excep-info.
***************
*** 342,347 ****
}
! UINT count = PyObject_Length(result);
! if ( count != cNames )
{
PyErr_Clear(); /* ### toss any potential exception */
--- 350,355 ----
}
! Py_ssize_t count = PyObject_Length(result);
! if ( count != PyWin_SAFE_DOWNCAST(cNames, UINT, Py_ssize_t))
{
PyErr_Clear(); /* ### toss any potential exception */
***************
*** 409,413 ****
)
{
! PyObject *argList = PyTuple_New(params->cArgs);
if ( !argList )
{
--- 417,445 ----
)
{
! HRESULT hr = S_OK;
! PyObject * py_lcid = NULL;
! /* Our named arg support is based on a few things. First,
! GetIDsOfNames() documentation states all params are numbered 0->n.
! Secondly, we have always required that all args be present in the
! Python implementation, and the names of those args need not be
! identical to the names in a typelib. Given these 2 facts, we can
! safely treat dispids as indexes into our arg tuple.
! */
! // num args is the greatest of cArgs and all the dispIDs presented.
! UINT i;
! UINT numArgs = params->cArgs;
! // handle 1 special case: 1 named arg with dispid of DISPID_PROPERTYPUT
! // We just treat that as positional, so it ends up at the end (which is
! // what we always did)
! UINT numNamedArgs = params->cNamedArgs!=1 || params->rgdispidNamedArgs[0]!=DISPID_PROPERTYPUT ? params->cNamedArgs : 0;
!
! for (i=0;i<numNamedArgs;i++) {
! // make sure its not a special DISPID we don't understand.
! if (params->rgdispidNamedArgs[i] < 0)
! return DISP_E_PARAMNOTFOUND;
! numArgs = max(numArgs, (UINT)params->rgdispidNamedArgs[i]+1);
! }
!
! PyObject *argList = PyTuple_New(numArgs);
if ( !argList )
{
***************
*** 415,443 ****
return E_OUTOFMEMORY;
}
! PyObject *ob;
! VARIANTARG FAR *pvarg;
! UINT i;
! for ( pvarg = params->rgvarg, i = params->cArgs; i--; ++pvarg )
! {
! ob = PyCom_PyObjectFromVariant(pvarg);
! if ( !ob )
! {
! PyErr_Clear(); /* ### what to do with exceptions? ... */
! Py_DECREF(argList);
! return E_OUTOFMEMORY;
}
!
/* Note: this takes our reference for us (even if it fails) */
! if ( PyTuple_SetItem(argList, i, ob) == -1 )
! {
! PyErr_Clear(); /* ### what to do with exceptions? ... */
! Py_DECREF(argList);
! return E_FAIL;
}
}
/* use the double stuff to keep lcid unsigned... */
! PyObject * py_lcid = PyLong_FromDouble((double)lcid);
if ( !py_lcid )
{
--- 447,492 ----
return E_OUTOFMEMORY;
}
+ // MUST exit from here on error via 'failed:'
! // Fill the positional args - they start at the end.
! for ( i = params->cArgs; i != numNamedArgs; --i) {
! PyObject *ob = PyCom_PyObjectFromVariant(params->rgvarg+i-1);
! if ( !ob ) {
! hr = E_OUTOFMEMORY;
! goto failed;
}
! Py_ssize_t pyndx = params->cArgs - i; // index in Python arg tuple.
/* Note: this takes our reference for us (even if it fails) */
! if ( PyTuple_SetItem(argList, pyndx, ob) == -1 ) {
! hr = E_FAIL;
! goto failed;
! }
! }
! // Fill the named params.
! for (i=0;i<numNamedArgs;i++) {
! UINT ndx = params->rgdispidNamedArgs[i];
! assert(PyTuple_GET_ITEM(argList, ndx)==NULL); // must not have seen it before
! PyObject *ob = PyCom_PyObjectFromVariant(params->rgvarg+i);
! if ( !ob ) {
! hr = E_OUTOFMEMORY;
! goto failed;
! }
! /* Note: this takes our reference for us (even if it fails) */
! if ( PyTuple_SetItem(argList, ndx, ob) == -1 ) {
! hr = E_FAIL;
! goto failed;
! }
! }
! // and any ones missing get 'Missing' - all positional ones must
! // have been done
! for (i=params->cArgs-numNamedArgs;i < numArgs;i++) {
! if (PyTuple_GET_ITEM(argList, i)==NULL) {
! Py_INCREF(g_obMissing);
! PyTuple_SetItem(argList, i, g_obMissing);
}
}
/* use the double stuff to keep lcid unsigned... */
! py_lcid = PyLong_FromDouble((double)lcid);
if ( !py_lcid )
{
***************
*** 449,453 ****
*pPyArgList = argList;
*pPyLCID = py_lcid;
! return S_OK;
}
--- 498,578 ----
*pPyArgList = argList;
*pPyLCID = py_lcid;
! return hr;
! failed:
! PyCom_LoggerException(NULL, "Failed to setup call into Python gateway");
! PyErr_Clear();
! Py_DECREF(argList);
! assert(FAILED(hr)); // must have set this.
! return hr;
! }
!
! // Named support for [out] args is harder - utilities for sorting
! struct NPI {
! DISPID id;
! VARIANT *v;
! unsigned offset;
! };
!
! int qsort_compare( const void *arg1, const void *arg2 )
! {
! // NOTE: We return in DESCENDING order
! return ((NPI *)arg2)->id - ((NPI *)arg1)->id;
! }
!
! // Given the COM params, fill a caller-allocated array of indexes into the
! // params for all the BYREF's. -1 will be set for all non-byref args.
! // The array to fill should be the same size as pDispParams->cArgs.
! // Example: if all args are positional and BYREF, pOffsets will be filled
! // with [3, 2, 1, 0] - indicating pDispParams->rgvarg[3] is the first BYREF,
! // and rgvarg[0] is the last. Another example: only last param is BYREF,
! // pOffsets will be filled with [0, -1, -1, -1].
! // Named params are tricky - IDs could be in any order, but the tuple order
! // is fixed - so we sort the named params by their ID to work out the order.
! static void fill_byref_offsets(DISPPARAMS *pDispParams, unsigned *pOffsets, unsigned noffsets)
! {
! // See above - special case DISPID_PROPERTYPUT. All other negative
! // DISPIDs have already been rejected.
! UINT numNamedArgs = pDispParams->cNamedArgs!=1 || pDispParams->rgdispidNamedArgs[0]!=DISPID_PROPERTYPUT ? pDispParams->cNamedArgs : 0;
! // init all.
! memset(pOffsets, -1, noffsets * sizeof(unsigned));
! unsigned ioffset = 0;
! unsigned idispparam;
! // positional args are in reverse order
! for (idispparam=pDispParams->cArgs;
! idispparam > numNamedArgs && ioffset < noffsets;
! idispparam--) {
! VARIANT *pv = pDispParams->rgvarg+idispparam-1;
! // If this param is not byref, try the following one.
! if (!V_ISBYREF(pv))
! continue;
! pOffsets[ioffset] = idispparam-1;
! ioffset++;
! }
! // named params could have their dispid in any order - so we sort
! // them - but only if necessary
! if (numNamedArgs && ioffset < noffsets) {
! // NOTE: optimizations possible - if only 1 named param its
! // obvious which one it is! If 2 params its very easy to work
! // it out - so we should only qsort for 3 or more.
! NPI *npi = (NPI *)_malloca(sizeof(NPI) * pDispParams->cNamedArgs); // death if we fail :)
! for (unsigned i=0;i < pDispParams->cNamedArgs;i++) {
! npi[i].id = pDispParams->rgdispidNamedArgs[i];
! npi[i].v = &pDispParams->rgvarg[i];
! npi[i].offset = i;
! }
! qsort(npi, pDispParams->cNamedArgs, sizeof(NPI), qsort_compare);
! // Now in descending order - we can just do the same loop again,
! // using our sorted array instead of the original.
! for (;
! idispparam > 0 && ioffset < noffsets;
! idispparam--) {
! VARIANT *pv = npi[idispparam-1].v;
! // If this param is not byref, try the following one.
! if (!V_ISBYREF(pv))
! continue;
! pOffsets[ioffset] = npi[idispparam-1].offset;
! ioffset++;
! }
! }
}
***************
*** 490,494 ****
ob = NULL;
! int count = PyObject_Length(result);
if ( count > 0 )
{
--- 615,619 ----
ob = NULL;
! Py_ssize_t count = PyObject_Length(result);
if ( count > 0 )
{
***************
*** 523,527 ****
// from the same function.
if (PyTuple_Check(userResult)) {
! unsigned cUserResult = PyTuple_Size(userResult);
unsigned firstByRef = 0;
if ( pVarResult )
--- 648,652 ----
// from the same function.
if (PyTuple_Check(userResult)) {
! unsigned cUserResult = PyWin_SAFE_DOWNCAST(PyTuple_Size(userResult), Py_ssize_t, UINT);
unsigned firstByRef = 0;
if ( pVarResult )
***************
*** 534,551 ****
firstByRef = 1;
}
// Now loop over the params, and set any byref's
! unsigned ituple = firstByRef;
! unsigned idispparam;
! // args are in reverse order
! for (idispparam=pDispParams->cArgs;idispparam>0;idispparam--) {
! // If we havent been given enough values, then we are done.
! if (ituple >= cUserResult)
break;
! VARIANT *pv = pDispParams->rgvarg+idispparam-1;
! // If this param is not byref, try the following one.
! if (!V_ISBYREF(pv))
! continue;
// Do the conversion thang
! ob = PyTuple_GetItem(userResult, ituple);
if (!ob) goto done;
Py_INCREF(ob); // tuple fetch doesnt do this!
--- 659,680 ----
firstByRef = 1;
}
+ UINT max_args = min(cUserResult-firstByRef, pDispParams->cArgs);
+ UINT *offsets = (UINT *)_malloca(sizeof(UINT) * max_args);
+ // Get the offsets into our params of all BYREF args, in order.
+ fill_byref_offsets(pDispParams, offsets, max_args);
+
// Now loop over the params, and set any byref's
! UINT i;
! for (i=0;i<max_args;i++) {
! UINT offset = offsets[i];
! if (offset == (UINT)-1) {
! // we've more args than BYREFs.
! PyCom_LoggerWarning(NULL, "Too many results supplied - %d supplied, but only %d can be set", cUserResult, i);
break;
! }
! VARIANT *pv = pDispParams->rgvarg+offset;
! assert(V_ISBYREF(pv));
// Do the conversion thang
! ob = PyTuple_GetItem(userResult, i + firstByRef);
if (!ob) goto done;
Py_INCREF(ob); // tuple fetch doesnt do this!
***************
*** 555,559 ****
arghelper.m_convertDirection = POAH_CONVERT_FROM_VARIANT;
if (!arghelper.MakeObjToVariant(ob, pv)) goto done;
- ituple++;
Py_DECREF(ob);
ob = NULL;
--- 684,687 ----
***************
*** 566,577 ****
// If a Python error, it remains set for handling below...
} else {
! // Single value for the first byref we find.
! unsigned idispparam;
! // args are in reverse order
! for (idispparam=pDispParams->cArgs;idispparam>0;idispparam--) {
! VARIANT *pv = pDispParams->rgvarg+idispparam-1;
! if (!V_ISBYREF(pv))
! continue;
!
PythonOleArgHelper arghelper;
arghelper.m_reqdType = V_VT(pv);
--- 694,705 ----
// If a Python error, it remains set for handling below...
} else {
! // Single value for the first byref we find. Probably
! // only 1, but do the whole byref processing to ensure
! // if there is more than 1, we get the right 1.
! UINT offset;
! fill_byref_offsets(pDispParams, &offset, 1);
! if (offset != (UINT)-1) {
! VARIANT *pv = pDispParams->rgvarg+offset;
! assert(V_ISBYREF(pv));
PythonOleArgHelper arghelper;
arghelper.m_reqdType = V_VT(pv);
***************
*** 579,583 ****
arghelper.MakeObjToVariant(userResult, pv);
// If a Python error, it remains set for handling below...
- break;
}
}
--- 707,710 ----
***************
*** 616,635 ****
V_VT(pVarResult) = VT_EMPTY;
- /* ### for now: no named args unless it is a PUT operation,
- ### OR all args are named args, and have contiguous DISPIDs
- */
- if ( params->cNamedArgs )
- {
- if ( params->cNamedArgs != 1 || params->rgdispidNamedArgs[0] != DISPID_PROPERTYPUT ) {
- if (params->cArgs != params->cNamedArgs)
- // Not all named args.
- return DISP_E_NONAMEDARGS;
- unsigned int argCheck;
- for (argCheck=0;argCheck<params->cNamedArgs;argCheck++)
- if (params->rgdispidNamedArgs[argCheck] != (DISPID)argCheck)
- return DISP_E_NONAMEDARGS;
- // OK - we will let it through.
- }
- }
PY_GATEWAY_METHOD;
PyObject *argList;
--- 743,746 ----
***************
*** 695,714 ****
V_VT(pVarResult) = VT_EMPTY;
- /* ### for now: no named args unless it is a PUT operation,
- ### OR all args are named args, and have contiguous DISPIDs
- */
- if ( params->cNamedArgs )
- {
- if ( params->cNamedArgs != 1 || params->rgdispidNamedArgs[0] != DISPID_PROPERTYPUT ) {
- if (params->cArgs != params->cNamedArgs)
- // Not all named args.
- return DISP_E_NONAMEDARGS;
- unsigned int argCheck;
- for (argCheck=0;argCheck<params->cNamedArgs;argCheck++)
- if (params->rgdispidNamedArgs[argCheck] != (DISPID)argCheck)
- return DISP_E_NONAMEDARGS;
- // OK - we will let it through.
- }
- }
PY_GATEWAY_METHOD;
PyObject *obISP = PyCom_PyObjectFromIUnknown(pspCaller, IID_IServiceProvider, TRUE);
--- 806,809 ----
|