forked from mhammond/pywin32
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patholeargs.cpp
More file actions
1732 lines (1645 loc) · 60.9 KB
/
oleargs.cpp
File metadata and controls
1732 lines (1645 loc) · 60.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// oleargs.cpp : ole args <--> python object implementation file
//
// $Id$
#include "stdafx.h"
#include "PythonCOM.h"
#include "PyRecord.h"
extern PyObject *PyObject_FromRecordInfo(IRecordInfo *, void *, ULONG);
extern PyObject *PyObject_FromSAFEARRAYRecordInfo(SAFEARRAY *psa);
extern BOOL PyObject_AsVARIANTRecordInfo(PyObject *ob, VARIANT *pv);
extern BOOL PyRecord_Check(PyObject *ob);
// Pointer to class defined in .py file.
static PyObject *PyVariant_Type;
// Do BYREF array's get the existing array backfilled with new elements
// (new behaviour that VB seems to want), or allocate a completely
// new array (old behaviour)
#define BYREF_ARRAY_USE_EXISTING_ARRAY
// Need to put this in pywintypes.h with rest of compatibility macros
#define PYWIN_BUFFER_CHECK(obj) (PyBytes_Check(obj) || PyByteArray_Check(obj) || PyMemoryView_Check(obj))
// A little helper just for this file
static PyObject *OleSetTypeError(TCHAR *msg)
{
PyObject *obMsg = PyWinObject_FromTCHAR(msg);
if (obMsg) {
PyErr_SetObject(PyExc_TypeError, obMsg);
Py_DECREF(obMsg);
}
return NULL;
}
BOOL MaybeExtractPyVariant(PyObject *obj, VARTYPE *vt, PyObject **pObjValue, BOOL *pConverted)
{
// rely on the GIL to ensure there are no races.
if (PyVariant_Type == NULL) {
PyObject *mod = PyImport_ImportModule("win32com.client");
if (mod) {
PyVariant_Type = PyObject_GetAttrString(mod, "VARIANT");
Py_DECREF(mod);
}
if (!PyVariant_Type) // WTF?
return FALSE;
}
int check = PyObject_IsInstance(obj, PyVariant_Type);
if (check == -1)
return FALSE;
if (check != 1) {
// Not that type, so all good but not converted.
*pConverted = FALSE;
return TRUE;
}
PyObject *obvt = PyObject_GetAttrString(obj, "varianttype");
if (!obvt)
return FALSE;
*vt = (VARTYPE)PyLong_AsUnsignedLongMask(obvt);
if (*vt == (VARTYPE)-1 && PyErr_Occurred()) {
Py_DECREF(obvt);
return FALSE;
}
Py_DECREF(obvt);
PyObject *obValue = PyObject_GetAttrString(obj, "value");
if (!obValue)
return FALSE;
// The result is a borrowed ref, but we can still be sure it
// lives as long as obj itself.
Py_DECREF(obValue);
*pObjValue = obValue;
*pConverted = TRUE;
return TRUE;
}
// Returns FALSE on error. If returns TRUE, pConverted may be TRUE or FALSE.
BOOL ConvertPyVariant(PyObject *obj, VARIANT *pResult, BOOL *pConverted)
{
VARTYPE vt;
PyObject *obUse;
if (!MaybeExtractPyVariant(obj, &vt, &obUse, pConverted))
return FALSE;
if (!*pConverted)
return TRUE;
PythonOleArgHelper helper = PythonOleArgHelper();
helper.m_reqdType = vt;
// Here we can't handle BYREF as our 'helper', which holds the buffers,
// doesn't live long enough to keep those buffers valid.
if ((helper.m_reqdType & VT_BYREF) != 0) {
// XXX - this message sucks :)
PyErr_SetString(PyExc_ValueError, "win32com.client.VARIANT can't do VT_BYREF in this context");
return FALSE;
}
helper.m_bParsedTypeInfo = TRUE;
helper.m_convertDirection = POAH_CONVERT_UNKNOWN;
BOOL ok = helper.MakeObjToVariant(obUse, pResult, NULL);
if (ok)
*pConverted = TRUE;
return ok;
}
///////////////////////////////////////////////////////////
//
// Generic Python objects - to/from VARIANTS and Python objects.
//
//
// Given a Python object, make the best (ie, most appropriate) VARIANT.
// Should be used when the specific type of the variant is not known
// NOTE that passing by reference is not supported using this function
// you need to use the complicated ArgHelpers class for that!
BOOL PyCom_VariantFromPyObject(PyObject *obj, VARIANT *var)
{
// First see if a special Python VARIANT object.
BOOL didPyVariant;
if (!ConvertPyVariant(obj, var, &didPyVariant))
return FALSE;
if (didPyVariant)
return TRUE;
BOOL bGoodEmpty = FALSE; // Set if VT_EMPTY should really be used.
V_VT(var) = VT_EMPTY;
if (
// In py3k we don't convert PyBytes_Check objects (ie, bytes) to BSTR...
PyUnicode_Check(obj)) {
if (!PyWinObject_AsBstr(obj, &V_BSTR(var))) {
PyErr_SetString(PyExc_MemoryError, "Making BSTR for variant");
return FALSE;
}
V_VT(var) = VT_BSTR;
}
// For Python 3, bool checks need to be above PyLong_Check, which now succeeds for booleans.
else if (obj == Py_True) {
V_VT(var) = VT_BOOL;
V_BOOL(var) = VARIANT_TRUE;
}
else if (obj == Py_False) {
V_VT(var) = VT_BOOL;
V_BOOL(var) = VARIANT_FALSE;
}
else if (PyLong_Check(obj)) {
int sign = _PyLong_Sign(obj);
size_t nbits = _PyLong_NumBits(obj);
if (nbits == (size_t)-1 && PyErr_Occurred())
return FALSE;
if (64 < nbits) {
// too big for 64 bits! Use a double.
V_VT(var) = VT_R8;
V_R8(var) = PyLong_AsDouble(obj);
if (V_R8(var) == -1.0 && PyErr_Occurred())
return FALSE;
}
else if (32 < nbits) {
// between 32 and 64 use longlong
// signed and using all bits use unsigned
if (sign > 0 && 64 == nbits) {
V_VT(var) = VT_UI8;
V_UI8(var) = PyLong_AsUnsignedLongLong(obj);
if (V_UI8(var) == (unsigned long long)-1 && PyErr_Occurred())
return FALSE;
}
else {
// Negative so use signed
V_VT(var) = VT_I8;
V_I8(var) = PyLong_AsLongLong(obj);
// Problem if value is between LLONG_MIN and -ULLONG_MAX
if (V_I8(var) == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
// Take now double
PyErr_Clear();
V_VT(var) = VT_R8;
V_R8(var) = PyLong_AsDouble(obj);
if (V_R8(var) == -1.0 && PyErr_Occurred())
return FALSE;
}
else {
return FALSE;
}
}
}
}
else {
// less then 32 bit use standard long
// positive and using all bits so unsigned
if (sign > 0 && 32 == nbits) {
V_VT(var) = VT_UI4;
V_UI4(var) = PyLong_AsUnsignedLong(obj);
if (V_UI4(var) == (unsigned long)-1 && PyErr_Occurred())
return FALSE;
}
else {
// Negative so use signed
V_VT(var) = VT_I4;
V_I4(var) = PyLong_AsLong(obj);
// Problem if value is between LONG_MIN and -ULONG_MAX
if (V_I4(var) == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
// Take now double
PyErr_Clear();
V_VT(var) = VT_I8;
V_I8(var) = PyLong_AsLongLong(obj);
if (V_I8(var) == -1 && PyErr_Occurred())
return FALSE;
}
else {
return FALSE;
}
}
}
}
}
else if (PyFloat_Check(obj)) {
V_VT(var) = VT_R8;
V_R8(var) = PyFloat_AsDouble(obj);
if (V_R8(var) == -1.0 && PyErr_Occurred())
return FALSE;
}
else if (obj == Py_None) {
V_VT(var) = VT_NULL;
}
else if (PyObject_HasAttrString(obj, "_oleobj_")) {
if (PyCom_InterfaceFromPyInstanceOrObject(obj, IID_IDispatch, (void **)&V_DISPATCH(var), FALSE))
V_VT(var) = VT_DISPATCH;
else {
PyErr_Clear();
// Try for IUnknown
if (PyCom_InterfaceFromPyInstanceOrObject(obj, IID_IUnknown, (void **)&V_UNKNOWN(var), FALSE))
V_VT(var) = VT_UNKNOWN;
else
PyErr_Clear();
}
}
else if (PyIBase::is_object(obj, &PyIDispatch::type)) {
V_VT(var) = VT_DISPATCH;
V_DISPATCH(var) = PyIDispatch::GetI(obj);
V_DISPATCH(var)->AddRef();
}
else if (PyIBase::is_object(obj, &PyIUnknown::type)) {
V_VT(var) = VT_UNKNOWN;
V_UNKNOWN(var) = PyIUnknown::GetI(obj);
V_UNKNOWN(var)->AddRef();
}
else if (obj->ob_type == &PyOleEmptyType) {
bGoodEmpty = TRUE;
}
// code changed by ssc
else if (obj->ob_type == &PyOleNothingType) {
V_VT(var) = VT_DISPATCH;
V_DISPATCH(var) = NULL;
}
// end code changed by ssc
else if (obj->ob_type == &PyOleArgNotFoundType) {
// use default parameter
// Note the SDK documentation for FUNCDESC describes this behaviour
// as correct. However, IMAPI.Session.Logon, most DAO, etc do _not_ work
// correctly in this case. ..Logon does work if the params are not
// presented at all (ie, argCount < ..) This is supported by the
// PyOleMissing object, but should be handled before here.
// Note that VB seems to use the "missing" rather than "empty"
// behaviour (as logon works there)
// Also note that MAPI still does _not_ work if a valid param with
// VT_EMPTY is passed in, so that is also not an option - the param must
// be _missing_.
V_VT(var) = VT_ERROR;
V_ERROR(var) = DISP_E_PARAMNOTFOUND;
}
else if (PyWinTime_Check(obj)) {
V_VT(var) = VT_DATE;
if (!PyWinObject_AsDATE(obj, &(V_DATE(var))))
return FALSE;
}
else if (PYWIN_BUFFER_CHECK(obj)) {
// We have a buffer object - convert to safe array of VT_UI1
if (!PyCom_SAFEARRAYFromPyObject(obj, &V_ARRAY(var), VT_UI1))
return FALSE;
V_VT(var) = VT_ARRAY | VT_UI1;
}
// NOTE: PySequence_Check may return true for instance objects,
// or ANY object with a __len__ attribute.
// So make sure this check is after anything else which qualifies.
else if (PySequence_Check(obj)) {
V_ARRAY(var) = NULL; // not a valid, existing array.
BOOL is_record_item = false;
if (PyObject_Length(obj) > 0) {
PyObject *obItemCheck = PySequence_GetItem(obj, 0);
is_record_item = PyRecord_Check(obItemCheck);
}
// If the sequence elements are PyRecord objects we do NOT package
// them as VARIANT elements but put them directly into the SAFEARRAY.
if (is_record_item) {
if (!PyCom_SAFEARRAYFromPyObject(obj, &V_ARRAY(var), VT_RECORD))
return FALSE;
V_VT(var) = VT_ARRAY | VT_RECORD;
}
else {
if (!PyCom_SAFEARRAYFromPyObject(obj, &V_ARRAY(var)))
return FALSE;
V_VT(var) = VT_ARRAY | VT_VARIANT;
}
}
else if (PyRecord_Check(obj)) {
if (!PyObject_AsVARIANTRecordInfo(obj, var))
return FALSE;
V_VT(var) = VT_RECORD;
}
// Decimal class from new _decimal module in Python 3.3 shows different name
else if (strcmp(obj->ob_type->tp_name, "Decimal") == 0 || strcmp(obj->ob_type->tp_name, "decimal.Decimal") == 0) {
if (!PyObject_AsCurrency(obj, &V_CY(var)))
return FALSE;
V_VT(var) = VT_CY;
}
else if (obj->ob_type->tp_as_number) {
V_VT(var) = VT_R8;
V_R8(var) = PyFloat_AsDouble(obj);
if (V_R8(var) == -1.0 && PyErr_Occurred())
return FALSE;
}
if (V_VT(var) == VT_EMPTY && !bGoodEmpty) {
// Must ensure we have a Python error set if we fail!
if (!PyErr_Occurred()) {
char *extraMessage = "";
if (obj->ob_type->tp_as_buffer)
extraMessage = " (but obtaining the buffer() of this object could)";
PyErr_Format(PyExc_TypeError, "Objects of type '%s' can not be converted to a COM VARIANT%s",
obj->ob_type->tp_name, extraMessage);
}
return FALSE;
}
return TRUE;
}
// Given a variant, turn it into a Python object of the closest type.
// Note that ByRef params are not supported here.
PyObject *PyCom_PyObjectFromVariant(const VARIANT *var)
{
HRESULT hr;
VARIANT varValue;
PyObject *result = NULL;
if (!var) {
Py_INCREF(Py_None);
return Py_None;
}
/* skip past any variant references to a "real" variant
(Why do we do this? Why is it only a VARIANT? what's the story, morning glory?
*/
while (V_VT(var) == (VT_BYREF | VT_VARIANT)) var = V_VARIANTREF(var);
/* ### note: we shouldn't see this, it is illegal in a VARIANT */
if (V_ISVECTOR(var)) {
return OleSetTypeError(_T("Can't convert vectors!"));
}
if (V_ISARRAY(var)) {
SAFEARRAY FAR *psa;
if (V_ISBYREF(var))
psa = *V_ARRAYREF(var);
else
psa = V_ARRAY(var);
if (psa == NULL) { // A NULL array
Py_INCREF(Py_None);
return Py_None;
}
VARENUM rawVT = (VARENUM)(V_VT(var) & VT_TYPEMASK);
return PyCom_PyObjectFromSAFEARRAY(psa, rawVT);
}
/* get a fully dereferenced copy of the variant */
/* ### we may want to optimize this sometime... avoid copying values */
VariantInit(&varValue);
VariantCopyInd(&varValue, (VARIANT *)var);
switch (V_VT(&varValue)) {
case VT_BOOL:
result = V_BOOL(&varValue) ? Py_True : Py_False;
Py_INCREF(result);
break;
case VT_UI1:
case VT_UI2:
case VT_UI4:
case VT_UINT:
hr = VariantChangeType(&varValue, &varValue, 0, VT_UI4);
if (FAILED(hr)) {
TCHAR buf[200];
wsprintf(buf, _T("Error converting integer variant (%08lx)"), hr);
OleSetTypeError(buf);
break;
}
// The result may be too large for a simple "long". If so,
// we must return a long.
if (V_UI4(&varValue) <= INT_MAX)
result = PyLong_FromLong(V_UI4(&varValue));
else
result = PyLong_FromUnsignedLong(V_UI4(&varValue));
break;
case VT_I1:
case VT_I2:
case VT_I4:
case VT_INT:
hr = VariantChangeType(&varValue, &varValue, 0, VT_I4);
if (FAILED(hr)) {
TCHAR buf[200];
wsprintf(buf, _T("Error converting integer variant (%08lx)"), hr);
OleSetTypeError(buf);
break;
}
result = PyLong_FromLong(V_I4(&varValue));
break;
case VT_UI8:
// The result may be too large for a simple "long". If so,
// we must return a long.
if (V_UI8(&varValue) <= LONG_MAX)
result = PyLong_FromLong((long)V_UI8(&varValue));
else
result = PyLong_FromUnsignedLongLong(V_UI8(&varValue));
break;
case VT_I8:
if ((LONG_MIN <= V_I8(&varValue)) && (V_I8(&varValue) <= LONG_MAX))
result = PyLong_FromLong((long)V_I8(&varValue));
else
result = PyLong_FromLongLong(V_I8(&varValue));
break;
case VT_HRESULT:
case VT_ERROR:
result = PyLong_FromLong(V_ERROR(&varValue));
break;
case VT_R4:
case VT_R8:
hr = VariantChangeType(&varValue, &varValue, 0, VT_R8);
if (FAILED(hr)) {
TCHAR buf[200];
wsprintf(buf, _T("Error converting floating point variant (%08lx)"), hr);
OleSetTypeError(buf);
break;
}
result = PyFloat_FromDouble(V_R8(&varValue));
break;
case VT_DISPATCH: {
IDispatch *pIDispatch = V_DISPATCH(&varValue);
if (pIDispatch)
result = PyCom_PyObjectFromIUnknown(pIDispatch, IID_IDispatch, TRUE);
else {
Py_INCREF(Py_None);
result = Py_None;
}
break;
}
case VT_UNKNOWN: {
IUnknown *punk = V_UNKNOWN(&varValue);
if (punk)
result = PyCom_PyObjectFromIUnknown(punk, IID_IUnknown, TRUE);
else {
Py_INCREF(Py_None);
result = Py_None;
}
break;
}
case VT_BSTR:
result = PyWinObject_FromBstr(V_BSTR(&varValue));
break;
case VT_NULL:
case VT_EMPTY:
Py_INCREF(Py_None);
result = Py_None;
break;
case VT_DATE:
result = PyWinObject_FromDATE(V_DATE(&varValue));
break;
case VT_CY:
result = PyObject_FromCurrency(varValue.cyVal);
break;
case VT_RECORD: {
ULONG cb;
V_RECORDINFO(&varValue)->GetSize(&cb);
result = PyObject_FromRecordInfo(V_RECORDINFO(&varValue), V_RECORD(&varValue), cb);
} break;
default: {
HRESULT hr = VariantChangeType(&varValue, &varValue, 0, VT_BSTR);
if (FAILED(hr)) {
TCHAR buf[200];
wsprintf(buf, _T("The Variant type (0x%x) is not supported, and it can not be converted to a string"),
V_VT(var));
OleSetTypeError(buf);
break;
}
result = PyWinObject_FromBstr(V_BSTR(&varValue));
break;
}
}
VariantClear(&varValue);
return result;
}
///////////////////////////////////////////////////////////
//
// SAFEARRAY support - to/from SAFEARRAYS and Python sequences.
//
//
// PyObject -> SafeArray
static BOOL PyCom_SAFEARRAYFromPyObjectBuildDimension(PyObject *obj, SAFEARRAY *pSA, VARENUM vt, UINT dimNo, UINT nDims,
SAFEARRAYBOUND *pBounds, LONG *pIndices)
{
LONG numElements = pBounds[dimNo - 1].cElements;
if ((LONG)PyObject_Length(obj) != numElements) {
OleSetTypeError(_T("All dimensions must be a sequence of the same size"));
return FALSE;
}
// See if we can take a short-cut for byte arrays - if
// so, we can copy the entire dimension in one hit
// (only support single segment buffers for now)
if (dimNo == nDims && vt == VT_UI1 && obj->ob_type->tp_as_buffer) {
void *sa_buf;
PyWinBufferView pybuf(obj);
if (!pybuf.ok())
return FALSE;
if (pybuf.len() != numElements) {
OleSetTypeError(_T("Internal error - the buffer length is not the sequence length!"));
return FALSE;
}
HRESULT hr = SafeArrayAccessData(pSA, &sa_buf);
if (FAILED(hr)) {
PyCom_BuildPyException(hr);
return FALSE;
}
memcpy(sa_buf, pybuf.ptr(), pybuf.len());
SafeArrayUnaccessData(pSA);
// All done without a single loop :-)
return TRUE;
}
// Otherwise just fall through into the standard mechanisms
BOOL ok = TRUE;
for (int index = 0; index < (int)numElements && ok; index++) {
pIndices[dimNo - 1] = index;
PyObject *item = PySequence_GetItem(obj, index);
if (item == NULL)
return FALSE;
if (dimNo == nDims) { // Last one - fill the data
VARIANT element;
LPVOID pvData;
if (vt == VT_VARIANT) { // simple conversion
ok = PyCom_VariantFromPyObject(item, &element);
pvData = &element;
}
else {
// Complex conversion.
if (vt & VT_ARRAY || vt & VT_BYREF) {
OleSetTypeError(_T("Internal error - unexpected argument - only simple VARIANTTYPE expected"));
ok = FALSE;
}
else {
PythonOleArgHelper helper;
helper.m_reqdType = vt;
ok = helper.MakeObjToVariant(item, &element);
switch (vt) {
case VT_RECORD:
pvData = V_RECORD(&element);
break;
case VT_DISPATCH:
pvData = V_DISPATCH(&element);
break;
case VT_UNKNOWN:
pvData = V_UNKNOWN(&element);
break;
case VT_BSTR:
pvData = V_BSTR(&element);
break;
default:
// The data is in a union - just use an
// arbitary element.
pvData = &V_I4(&element);
break;
}
}
}
if (ok) {
HRESULT hr = SafeArrayPutElement(pSA, pIndices, pvData);
VariantClear(&element);
if (FAILED(hr)) {
PyCom_BuildInternalPyException("Could not set the SAFEARRAY element");
ok = FALSE;
}
}
}
else {
// recurse down dimensions
ok = PyCom_SAFEARRAYFromPyObjectBuildDimension(item, pSA, vt, dimNo + 1, nDims, pBounds, pIndices);
}
Py_DECREF(item);
}
return ok;
}
// Arbitrary-sized array dimensions contributed by Stefan Schukat Feb-2004
static long PyCom_CalculatePyObjectDimension(PyObject *obItemCheck, long lDimension, PyObject *ppyobDimensionDictionary)
{
// Buffers are a special case - they define 1 new dimension.
// Buffers supported sequence semantics in Python 2, but for some reason memoryview objects
// in py3k do not, so check separately
if (PYWIN_BUFFER_CHECK(obItemCheck))
return lDimension + 1;
// Allow arbitrary sequences, but not strings or Unicode objects.
if (PyBytes_Check(obItemCheck) || PyUnicode_Check(obItemCheck) || !PySequence_Check(obItemCheck))
return lDimension;
long lReturnDimension = lDimension;
PyObject *ppyobDimension;
PyObject *ppyobSize;
PyObject *ppyobDimensionSize;
PyObject *ppyobItem;
Py_ssize_t lIndex;
long lMinimalDimension = -1;
long lActualDimension = -1;
Py_ssize_t lObjectSize;
// Retrieve the size of the object
lObjectSize = PySequence_Length(obItemCheck);
if (lObjectSize == -1) {
/* has a __len__, but it failed. Treat as not a sequence */
assert(PyErr_Occurred()); // can't *really* have -1 elems! */
PyErr_Clear();
}
if (lObjectSize != -1) { // A real sequence of size zero should be OK though.
ppyobSize = PyLong_FromSsize_t(lObjectSize);
// Retrieve the stored size in this dimension
ppyobDimension = PyLong_FromLong(lDimension);
// Note: No ref added by PyDict_GetItem
ppyobDimensionSize = PyDict_GetItem(ppyobDimensionDictionary, ppyobDimension);
if (NULL == ppyobDimensionSize) {
// Not found so first element defines the size in this dimension
PyErr_Clear();
PyDict_SetItem(ppyobDimensionDictionary, ppyobDimension, ppyobSize);
}
else {
// Check if stored size in this dimension equals the size of the element to check
Py_ssize_t lStoredSize = PyLong_AsSsize_t(ppyobDimensionSize);
if (lStoredSize != lObjectSize) {
// if not the same size => no new dimension
Py_XDECREF(ppyobSize);
Py_XDECREF(ppyobDimension);
return lReturnDimension;
}
}
Py_XDECREF(ppyobSize);
Py_XDECREF(ppyobDimension);
// A special case for a zero-length sequence - we accept this as
// a new dimension, but no children to check.
// ie an empty list has 1 dimension.
if (lObjectSize == 0)
return lReturnDimension + 1;
// Now check for all elements in this list for their dimensionality
// Their size is compared to the size stored in the dimension dictionary
for (lIndex = 0; lIndex < lObjectSize; lIndex++) {
ppyobItem = PySequence_GetItem(obItemCheck, lIndex);
if (ppyobItem == NULL) {
// Says it is a sequence, but getting the item failed.
// (eg, may be a COM instance that has __getitem__, but fails when attempting)
// Ignore the error, and pretend it is not a sequence.
PyErr_Clear();
break;
}
// Call method recursively
lActualDimension = PyCom_CalculatePyObjectDimension(ppyobItem, lDimension + 1, ppyobDimensionDictionary);
if (-1 == lMinimalDimension) {
// First call so store it
lMinimalDimension = lActualDimension;
lReturnDimension = lActualDimension;
}
else {
// Get the smallest dimension
if (lActualDimension < lMinimalDimension) {
lMinimalDimension = lActualDimension;
}
// Check if all dimensions of the sublist are equal
if (lReturnDimension != lActualDimension) {
// if not set the minimal dimension
lReturnDimension = lMinimalDimension;
}
}
Py_XDECREF(ppyobItem);
}
}
return lReturnDimension;
}
static BOOL PyCom_SAFEARRAYFromPyObjectEx(PyObject *obj, SAFEARRAY **ppSA, bool bAllocNewArray, VARENUM vt)
{
// NOTE: We make no attempt to validate or free any existing array if asked to allocate a new one!
// Seek down searching for total dimension count.
// Item zero of each element will do for now
// (as all must be same)
// First we _will_ allow None here (just don't use it if it crashes :-)
if (obj == Py_None) {
if (bAllocNewArray)
*ppSA = NULL;
// Otherwise we leave it alone!
return TRUE;
}
LONG cDims = 0;
// Arbitrary-sized array dimensions contributed by Stefan Schukat Feb-2004
// Allow arbitrary sized sequences to be transported to a COM server
PyObject *ppyobDimensionDictionary = PyDict_New();
// Calculate the unique dimension of the sequence
cDims = PyCom_CalculatePyObjectDimension(obj, 0, ppyobDimensionDictionary);
Py_DECREF(ppyobDimensionDictionary);
if (cDims == 0) {
OleSetTypeError(_T("Objects for SAFEARRAYS must be sequences (of sequences), or a buffer object."));
return FALSE;
}
if (!bAllocNewArray) {
if (SafeArrayGetDim(*ppSA) != (unsigned)cDims) {
PyErr_SetString(PyExc_ValueError,
"When refilling a safe array, the sequence must have the same number of dimensions as the "
"existing array.");
return FALSE;
}
}
SAFEARRAYBOUND *pBounds = new SAFEARRAYBOUND[cDims];
// Now run down again, setting up the bounds
PyObject *obItemCheck = obj;
Py_INCREF(obItemCheck);
for (LONG dimLook = 1; dimLook <= cDims; dimLook++) {
pBounds[dimLook - 1].lLbound = 0; // always!
// Don't use PySequence_Length due to memoryview not supporting sequence protocol
pBounds[dimLook - 1].cElements = (ULONG)PyObject_Length(obItemCheck);
if (!bAllocNewArray) {
LONG exist_lbound, exist_ubound;
SafeArrayGetLBound(*ppSA, dimLook, &exist_lbound);
SafeArrayGetUBound(*ppSA, dimLook, &exist_ubound);
if ((unsigned long)(exist_ubound - exist_lbound + 1) != pBounds[dimLook - 1].cElements) {
PyErr_SetString(
PyExc_ValueError,
"When refilling a safe array, the sequences must be the same length as the existing array.");
Py_XDECREF(obItemCheck);
delete[] pBounds;
return FALSE;
}
}
// Don't need to do this check if buffer is last dim
if (!PYWIN_BUFFER_CHECK(obItemCheck)) {
PyObject *obSave = obItemCheck;
if (pBounds[dimLook - 1].cElements) {
obItemCheck = PySequence_GetItem(obItemCheck, 0);
Py_DECREF(obSave);
if (obItemCheck == NULL) {
delete[] pBounds;
return FALSE;
}
}
}
}
Py_XDECREF(obItemCheck);
if (bAllocNewArray) {
// OK - Finally can create the array...
if (vt == VT_RECORD) {
// SAFEARRAYS of UDTs need a special treatment.
obItemCheck = PySequence_GetItem(obj, 0);
PyRecord *pyrec = (PyRecord *)obItemCheck;
*ppSA = SafeArrayCreateEx(vt, cDims, pBounds, pyrec->pri);
}
else
*ppSA = SafeArrayCreate(vt, cDims, pBounds);
if (*ppSA == NULL) {
delete[] pBounds;
PyErr_SetString(PyExc_MemoryError, "CreatingSafeArray");
return FALSE;
}
}
LONG *indices = new LONG[cDims];
// Get the data
BOOL bOK = PyCom_SAFEARRAYFromPyObjectBuildDimension(obj, *ppSA, vt, 1, cDims, pBounds, indices);
if (!bOK && bAllocNewArray && *ppSA) {
SafeArrayDestroy(*ppSA);
*ppSA = NULL;
}
delete[] indices;
delete[] pBounds;
return bOK;
}
BOOL PyCom_SAFEARRAYFromPyObject(PyObject *obj, SAFEARRAY **ppSA, VARENUM vt /*= VT_VARIANT*/)
{
return PyCom_SAFEARRAYFromPyObjectEx(obj, ppSA, true, vt);
}
///////////////////////////
//
// SafeArray -> PyObject
/*
Helper - Convert the current index to a Python object.
No iteration - returns a simple object (not a tuple)
*/
static PyObject *PyCom_PyObjectFromSAFEARRAYDimensionItem(SAFEARRAY *psa, VARENUM vt, long *arrayIndices)
{
PyObject *subitem = NULL;
HRESULT hres = 0;
switch (vt) {
case VT_I2: {
short sh;
hres = SafeArrayGetElement(psa, arrayIndices, &sh);
if (FAILED(hres))
break;
subitem = PyLong_FromLong(sh);
break;
}
case VT_I4:
case VT_ERROR: {
long ln;
hres = SafeArrayGetElement(psa, arrayIndices, &ln);
if (FAILED(hres))
break;
subitem = PyLong_FromLong(ln);
break;
}
case VT_I8: {
LARGE_INTEGER ll;
hres = SafeArrayGetElement(psa, arrayIndices, &ll);
if (FAILED(hres))
break;
subitem = PyWinObject_FromPY_LONG_LONG(ll);
break;
}
case VT_R4: {
float fl;
hres = SafeArrayGetElement(psa, arrayIndices, &fl);
if (FAILED(hres))
break;
subitem = PyFloat_FromDouble(fl);
break;
}
case VT_R8: {
double db;
hres = SafeArrayGetElement(psa, arrayIndices, &db);
if (FAILED(hres))
break;
subitem = PyFloat_FromDouble(db);
break;
}
case VT_CY: {
CURRENCY c;
hres = SafeArrayGetElement(psa, arrayIndices, &c);
if (FAILED(hres))
break;
subitem = PyObject_FromCurrency(c);
break;
}
case VT_DATE: {
DATE dt;
hres = SafeArrayGetElement(psa, arrayIndices, &dt);
if (FAILED(hres))
break;
subitem = PyWinObject_FromDATE(dt);
break;
}
case VT_BSTR: {
BSTR str;
hres = SafeArrayGetElement(psa, arrayIndices, &str);
if (FAILED(hres))
break;
subitem = PyWinObject_FromBstr(str);
break;
}
case VT_DISPATCH: {
IDispatch *pDisp;
hres = SafeArrayGetElement(psa, arrayIndices, &pDisp);
if (FAILED(hres))
break;
subitem = PyCom_PyObjectFromIUnknown(pDisp, IID_IDispatch, TRUE);
break;
}
// case VT_ERROR - handled above with I4
case VT_BOOL: {
bool b1;
hres = SafeArrayGetElement(psa, arrayIndices, &b1);
if (FAILED(hres))
break;
subitem = PyBool_FromLong(b1);
break;
}
case VT_VARIANT: {
VARIANT varValue;
VariantInit(&varValue);
hres = SafeArrayGetElement(psa, arrayIndices, &varValue);
if (FAILED(hres))
break;
subitem = PyCom_PyObjectFromVariant(&varValue);
VariantClear(&varValue); // clean this up
break;
}
case VT_UNKNOWN: {
IUnknown *pUnk;
hres = SafeArrayGetElement(psa, arrayIndices, &pUnk);
if (FAILED(hres))
break;
subitem = PyCom_PyObjectFromIUnknown(pUnk, IID_IUnknown, TRUE);
break;
}
// case VT_DECIMAL
// case VT_RECORD
case VT_I1:
case VT_UI1: {
unsigned char i1;
hres = SafeArrayGetElement(psa, arrayIndices, &i1);
if (FAILED(hres))
break;
subitem = PyLong_FromLong(i1);
break;
}
case VT_UI2: {
unsigned short s1;
hres = SafeArrayGetElement(psa, arrayIndices, &s1);
if (FAILED(hres))
break;
subitem = PyLong_FromUnsignedLong(s1);
break;
}
case VT_UI4: {
unsigned long l1;
hres = SafeArrayGetElement(psa, arrayIndices, &l1);
if (FAILED(hres))
break;
subitem = PyLong_FromUnsignedLong(l1);
break;
}
case VT_UI8: {
ULARGE_INTEGER ll;
hres = SafeArrayGetElement(psa, arrayIndices, &ll);
if (FAILED(hres))
break;
subitem = PyWinObject_FromUPY_LONG_LONG(ll);
break;
}
case VT_INT: {
int i1;
hres = SafeArrayGetElement(psa, arrayIndices, &i1);
if (FAILED(hres))
break;
subitem = PyLong_FromLong(i1);
break;
}
case VT_UINT: {
unsigned int i1;
hres = SafeArrayGetElement(psa, arrayIndices, &i1);
if (FAILED(hres))
break;
subitem = PyLong_FromUnsignedLong(i1);
break;
}
default: {
TCHAR buf[200];
wsprintf(buf, _T("The VARIANT type 0x%x is not supported for SAFEARRAYS"), vt);
OleSetTypeError(buf);
}
}
if (FAILED(hres)) {
PyCom_BuildPyException(hres);
Py_XDECREF(subitem);
subitem = NULL;
}
// All done.
return subitem;
}
/* Helper - Convert the specified dimension of the specified safe array to
a Python object (a tuple)
*/
PyObject *PyCom_PyObjectFromSAFEARRAYBuildDimension(SAFEARRAY *psa, VARENUM vt, UINT dimNo, UINT nDims,
long *arrayIndices)
{
long lb, ub;