Added an unknown VxD error code.
[wine] / dlls / oleaut32 / safearray.c
1 /*************************************************************************
2  * OLE Automation
3  * SafeArray Implementation
4  *
5  * This file contains the implementation of the SafeArray interface.
6  *
7  * Copyright 1999 Sylvain St-Germain
8  */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include "windef.h"
13 #include "winerror.h"
14 #include "winbase.h"
15 #include "oleauto.h"
16 #include "wine/obj_base.h"
17 #include "debugtools.h"
18
19 DEFAULT_DEBUG_CHANNEL(ole);
20
21 /* Localy used methods */
22 static INT  
23 endOfDim(LONG *coor, SAFEARRAYBOUND *mat, LONG dim, LONG realDim);
24
25 static ULONG   
26 calcDisplacement(LONG *coor, SAFEARRAYBOUND *mat, LONG dim);
27
28 static BOOL  
29 isPointer(USHORT feature);
30
31 static INT   
32 getFeatures(VARTYPE vt);
33
34 static BOOL  
35 validCoordinate(LONG *coor, SAFEARRAY *psa);
36
37 static BOOL  
38 resizeSafeArray(SAFEARRAY *psa, LONG lDelta);
39
40 static BOOL  
41 validArg(SAFEARRAY *psa);
42
43 static ULONG   
44 getArraySize(SAFEARRAY *psa);
45
46 static HRESULT 
47 duplicateData(SAFEARRAY *psa, SAFEARRAY **ppsaOut);
48
49 /* Association between VARTYPE and their size.
50    A size of zero is defined for the unsupported types.  */
51
52 #define VARTYPE_NOT_SUPPORTED 0
53 static const ULONG VARTYPE_SIZE[] =
54 {
55   /* this is taken from wtypes.h.  Only [S]es are supported by the SafeArray */
56 VARTYPE_NOT_SUPPORTED,  /* VT_EMPTY    [V]   [P]    nothing                     */
57 VARTYPE_NOT_SUPPORTED,  /* VT_NULL     [V]   [P]    SQL style Nul       */
58 2,                          /* VT_I2       [V][T][P][S] 2 byte signed int */
59 4,                          /* VT_I4       [V][T][P][S] 4 byte signed int */
60 4,                          /* VT_R4       [V][T][P][S] 4 byte real     */
61 8,                          /* VT_R8       [V][T][P][S] 8 byte real     */
62 8,                      /* VT_CY       [V][T][P][S] currency */
63 8,                          /* VT_DATE     [V][T][P][S] date */
64 4,                          /* VT_BSTR     [V][T][P][S] OLE Automation string*/
65 4,                          /* VT_DISPATCH [V][T][P][S] IDispatch *     */
66 4,                      /* VT_ERROR    [V][T]   [S] SCODE       */
67 4,                          /* VT_BOOL     [V][T][P][S] True=-1, False=0*/
68 24,                     /* VT_VARIANT  [V][T][P][S] VARIANT *   */
69 4,                          /* VT_UNKNOWN  [V][T]   [S] IUnknown * */
70 16,                         /* VT_DECIMAL  [V][T]   [S] 16 byte fixed point     */
71 VARTYPE_NOT_SUPPORTED,                         /* no VARTYPE here..... */
72 VARTYPE_NOT_SUPPORTED,  /* VT_I1          [T]       signed char */
73 1,                          /* VT_UI1      [V][T][P][S] unsigned char                   */
74 VARTYPE_NOT_SUPPORTED,  /* VT_UI2         [T][P]    unsigned short      */
75 VARTYPE_NOT_SUPPORTED,  /* VT_UI4         [T][P]    unsigned short      */
76 VARTYPE_NOT_SUPPORTED,  /* VT_I8          [T][P]    signed 64-bit int                   */
77 VARTYPE_NOT_SUPPORTED,  /* VT_UI8         [T][P]    unsigned 64-bit int         */
78 VARTYPE_NOT_SUPPORTED,  /* VT_INT         [T]       signed machine int          */
79 VARTYPE_NOT_SUPPORTED,  /* VT_UINT        [T]       unsigned machine int        */
80 VARTYPE_NOT_SUPPORTED,  /* VT_VOID        [T]       C style void                        */
81 VARTYPE_NOT_SUPPORTED,  /* VT_HRESULT     [T]       Standard return type        */
82 VARTYPE_NOT_SUPPORTED,  /* VT_PTR         [T]       pointer type                        */
83 VARTYPE_NOT_SUPPORTED,  /* VT_SAFEARRAY   [T]       (use VT_ARRAY in VARIANT)*/
84 VARTYPE_NOT_SUPPORTED,  /* VT_CARRAY      [T]       C style array                       */
85 VARTYPE_NOT_SUPPORTED,  /* VT_USERDEFINED [T]       user defined type                   */
86 VARTYPE_NOT_SUPPORTED,  /* VT_LPSTR       [T][P]    null terminated string      */
87 VARTYPE_NOT_SUPPORTED,  /* VT_LPWSTR      [T][P]    wide null term string               */
88 VARTYPE_NOT_SUPPORTED,  /* VT_FILETIME       [P]    FILETIME                    */
89 VARTYPE_NOT_SUPPORTED,  /* VT_BLOB           [P]    Length prefixed bytes */
90 VARTYPE_NOT_SUPPORTED,  /* VT_STREAM         [P]    Name of stream follows              */
91 VARTYPE_NOT_SUPPORTED,  /* VT_STORAGE        [P]    Name of storage follows     */
92 VARTYPE_NOT_SUPPORTED,  /* VT_STREAMED_OBJECT[P]    Stream contains an object*/
93 VARTYPE_NOT_SUPPORTED,  /* VT_STORED_OBJECT  [P]    Storage contains object*/
94 VARTYPE_NOT_SUPPORTED,  /* VT_BLOB_OBJECT    [P]    Blob contains an object*/
95 VARTYPE_NOT_SUPPORTED,  /* VT_CF             [P]    Clipboard format                    */
96 VARTYPE_NOT_SUPPORTED,  /* VT_CLSID          [P]    A Class ID                  */
97 VARTYPE_NOT_SUPPORTED,  /* VT_VECTOR         [P]    simple counted array                */
98 VARTYPE_NOT_SUPPORTED,  /* VT_ARRAY    [V]          SAFEARRAY*                  */
99 VARTYPE_NOT_SUPPORTED   /* VT_BYREF    [V]          void* for local use */
100 };
101
102 static const int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(ULONG);
103
104
105 /*************************************************************************
106  *              SafeArrayAllocDescriptor
107  * Allocate the appropriate amount of memory for the SafeArray descriptor
108  */
109 HRESULT WINAPI SafeArrayAllocDescriptor( 
110   UINT    cDims, 
111   SAFEARRAY **ppsaOut) 
112 {
113   SAFEARRAYBOUND *sab;
114   LONG allocSize = 0;
115
116   /* SAFEARRAY + SAFEARRAYBOUND * (cDims -1) ( -1 because there is already one
117                                              ( in SAFEARRAY struct */
118   allocSize = sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
119
120   /* Allocate memory for SAFEARRAY struc */
121   if(( (*ppsaOut)=HeapAlloc( 
122         GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize)) == NULL){
123     return(E_UNEXPECTED);
124   }
125   TRACE("SafeArray: %lu bytes allocated for descriptor.\n", allocSize);
126
127   return(S_OK);
128 }
129
130 /*************************************************************************
131  *              SafeArrayAllocData
132  * Allocate the appropriate amount of data for the SafeArray data
133  */
134 HRESULT WINAPI SafeArrayAllocData(
135   SAFEARRAY *psa) 
136 {
137   ULONG  ulWholeArraySize;   /* to store the size of the whole thing */
138
139   if(! validArg(psa)) 
140     return E_INVALIDARG;
141
142   ulWholeArraySize = getArraySize(psa);
143
144   /* Allocate memory for the data itself */
145   if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
146         psa->cbElements*ulWholeArraySize)) == NULL)
147     return(E_UNEXPECTED);
148
149   TRACE("SafeArray: %lu bytes allocated for data at %p (%lu objects).\n", 
150     psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
151
152   return(S_OK);
153 }
154
155 /*************************************************************************
156  *              SafeArrayCreate
157  * Create a SafeArray object by encapsulating AllocDescriptor and AllocData 
158  */
159 SAFEARRAY* WINAPI SafeArrayCreate(
160   VARTYPE        vt, 
161   UINT         cDims, 
162   SAFEARRAYBOUND *rgsabound)
163 {
164   SAFEARRAY *psa;
165   HRESULT   hRes;
166   USHORT    cDim;
167
168   /* Validate supported VARTYPE */
169   if ( (vt >= LAST_VARTYPE) ||
170        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
171     return NULL;
172
173   /* Allocate memory for the array descriptor */
174   if( FAILED( hRes = SafeArrayAllocDescriptor(cDims, &psa)))
175     return NULL;
176
177   /* setup data members... */ 
178   psa->cDims     = cDims;
179   psa->fFeatures = getFeatures(vt);
180   psa->cLocks    = 0;
181   psa->pvData    = NULL;
182   psa->cbElements= VARTYPE_SIZE[vt];
183
184   /* Invert the bounds ... */
185   for(cDim=0; cDim < psa->cDims; cDim++) {
186     psa->rgsabound[cDim].cElements = rgsabound[psa->cDims-cDim-1].cElements;
187     psa->rgsabound[cDim].lLbound   = rgsabound[psa->cDims-cDim-1].lLbound;
188   }
189
190   /* allocate memory for the data... */ 
191   if( FAILED( hRes = SafeArrayAllocData(psa))) {
192     SafeArrayDestroyDescriptor(psa); 
193     ERR("() : Failed to allocate the Safe Array data\n");
194     return NULL;
195   }
196
197   return(psa); 
198 }
199
200 /*************************************************************************
201  *              SafeArrayDestroyDescriptor
202  * Frees the memory associated with the descriptor.
203  */
204 HRESULT WINAPI SafeArrayDestroyDescriptor(
205   SAFEARRAY *psa)
206 {
207   /* Check for lockness before to free... */
208   if(psa->cLocks > 0) 
209     return DISP_E_ARRAYISLOCKED;
210
211   /* The array is unlocked, then, deallocate memory */
212   if(HeapFree( GetProcessHeap(), 0, psa) == FALSE) 
213     return E_UNEXPECTED;
214   
215   return(S_OK);
216 }
217
218
219 /*************************************************************************
220  *              SafeArrayLock
221  * Increment the lock counter
222  *
223  * Doc says (MSDN Library ) that psa->pvData should be made available (!= NULL)
224  * only when psa->cLocks is > 0... I don't get it since pvData is allocated 
225  * before the array is locked, therefore  
226  */
227 HRESULT WINAPI SafeArrayLock(
228   SAFEARRAY *psa)
229 {
230   if(! validArg(psa))     
231     return E_INVALIDARG;
232
233   psa->cLocks++;
234
235   return(S_OK);
236 }
237
238 /*************************************************************************
239  *              SafeArrayUnlock
240  * Decrement the lock counter
241  */
242 HRESULT WINAPI SafeArrayUnlock(
243   SAFEARRAY *psa)
244 {
245   if(! validArg(psa)) 
246     return E_INVALIDARG;
247
248   if (psa->cLocks > 0) 
249     psa->cLocks--;
250
251   return(S_OK);
252 }
253
254
255 /*************************************************************************
256  *              SafeArrayPutElement
257  * Set the data at the given coordinate
258  */
259 HRESULT WINAPI SafeArrayPutElement(
260   SAFEARRAY *psa, 
261   LONG      *rgIndices, 
262   void      *pv)
263 {
264   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
265                                          the desired one... */
266   PVOID elementStorageAddress = NULL; /* Adress to store the data */
267   BSTR  pbstrReAllocStr     = NULL; /* BSTR reallocated */
268
269   /* Validate the index given */
270   if(! validCoordinate(rgIndices, psa)) 
271     return DISP_E_BADINDEX;
272   if(! validArg(psa))
273     return E_INVALIDARG;
274
275   if( SafeArrayLock(psa) == S_OK) {
276
277     /* Figure out the number of items to skip */
278     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
279   
280     /* Figure out the number of byte to skip ... */
281     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
282   
283     if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
284
285       *((VOID**)elementStorageAddress) = *(VOID**)pv; 
286       IUnknown_AddRef( *(IUnknown**)pv);
287
288     } else { 
289
290       if(psa->fFeatures == FADF_BSTR) { /* Create a new object */
291
292         if((pbstrReAllocStr = SysAllocString( (OLECHAR*)pv )) == NULL) {
293           SafeArrayUnlock(psa);  
294           return E_OUTOFMEMORY;
295         } else 
296           *((BSTR*)elementStorageAddress) = pbstrReAllocStr;
297
298       } else /* dupplicate the memory */
299         memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
300     }
301
302   } else {
303     ERR("SafeArray: Cannot lock array....\n");
304     return E_UNEXPECTED; /* UNDOC error condition */
305   }
306
307   TRACE("SafeArray: item put at adress %p.\n",elementStorageAddress);
308   return SafeArrayUnlock(psa);  
309 }
310
311
312 /*************************************************************************
313  *              SafeArrayGetElement
314  * Return the data element corresponding the the given coordinate
315  */
316 HRESULT WINAPI SafeArrayGetElement(
317   SAFEARRAY *psa, 
318   LONG      *rgIndices, 
319   void      *pv)
320 {
321   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
322                                          the desired one... */
323   PVOID elementStorageAddress = NULL; /* Adress to store the data */
324   BSTR  pbstrReturnedStr    = NULL; /* BSTR reallocated */
325
326   if(! validArg(psa)) 
327     return E_INVALIDARG;
328   
329   if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
330     return(DISP_E_BADINDEX);
331
332   if( SafeArrayLock(psa) == S_OK) {
333
334     /* Figure out the number of items to skip */
335     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
336   
337     /* Figure out the number of byte to skip ... */
338     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
339   
340     if( psa->fFeatures == FADF_BSTR) {           /* reallocate the obj */
341       if( (pbstrReturnedStr = 
342           SysAllocString( *(OLECHAR**)elementStorageAddress )) == NULL) {
343         SafeArrayUnlock(psa);
344         return E_OUTOFMEMORY;
345       } else 
346         *((BSTR*)pv) = pbstrReturnedStr; 
347         
348     } else if( isPointer(psa->fFeatures) )       /* simply copy the pointer */
349        pv = *((PVOID*)elementStorageAddress); 
350     else                                         /* copy the bytes */
351       memcpy(pv, elementStorageAddress, SafeArrayGetElemsize(psa) );
352
353   } else {
354     ERR("SafeArray: Cannot lock array....\n");
355     return E_UNEXPECTED; /* UNDOC error condition */
356   }
357
358   return( SafeArrayUnlock(psa) );  
359 }
360
361 /*************************************************************************
362  *              SafeArrayGetUBound
363  * return the UP bound for a given array dimension
364  */
365 HRESULT WINAPI SafeArrayGetUBound(
366   SAFEARRAY *psa, 
367   UINT    nDim,
368   LONG      *plUbound)
369 {
370   if(! validArg(psa))   
371     return E_INVALIDARG;
372
373   if(nDim > psa->cDims) 
374     return DISP_E_BADINDEX;
375
376   if(0 == nDim)
377     return DISP_E_BADINDEX;
378
379   *plUbound = psa->rgsabound[nDim-1].lLbound + 
380               psa->rgsabound[nDim-1].cElements - 1;
381
382   return S_OK;
383 }
384
385 /*************************************************************************
386  *              SafeArrayGetLBound
387  * Return the LO bound for a given array dimension 
388  */
389 HRESULT WINAPI SafeArrayGetLBound(
390   SAFEARRAY *psa,
391   UINT    nDim, 
392   LONG      *plLbound)
393 {
394   if(! validArg(psa))   
395     return E_INVALIDARG;
396
397   if(nDim > psa->cDims) 
398     return DISP_E_BADINDEX;
399
400   if(0 == nDim)
401     return DISP_E_BADINDEX;
402   
403   *plLbound = psa->rgsabound[nDim-1].lLbound;
404   return S_OK;
405 }
406
407 /*************************************************************************
408  *              SafeArrayGetDim
409  * returns the number of dimension in the array
410  */
411 UINT WINAPI SafeArrayGetDim(
412   SAFEARRAY * psa)
413
414   /*
415    * A quick test in Windows shows that the behavior here for an invalid
416    * pointer is to return 0.
417    */
418   if(! validArg(psa)) 
419     return 0;
420
421   return psa->cDims;
422 }
423
424 /*************************************************************************
425  *              SafeArrayGetElemsize
426  * Return the size of the element in the array
427  */
428 UINT WINAPI SafeArrayGetElemsize(
429   SAFEARRAY * psa)
430
431   /*
432    * A quick test in Windows shows that the behavior here for an invalid
433    * pointer is to return 0.
434    */
435   if(! validArg(psa)) 
436     return 0;
437
438   return psa->cbElements;
439 }
440
441 /*************************************************************************
442  *              SafeArrayAccessData
443  * increment the access count and return the data 
444  */
445 HRESULT WINAPI SafeArrayAccessData(
446   SAFEARRAY *psa, 
447   void      **ppvData)
448
449   HRESULT hRes;
450
451   if(! validArg(psa)) 
452     return E_INVALIDARG;
453
454   hRes = SafeArrayLock(psa);
455
456   switch (hRes) {
457     case S_OK: 
458       (*ppvData) = psa->pvData;
459       break;
460     case E_INVALIDARG:
461       (*ppvData) = NULL;
462       return E_INVALIDARG;
463   }
464   
465   return S_OK;
466 }
467
468
469 /*************************************************************************
470  *              SafeArrayUnaccessData
471  * Decrement the access count
472  */
473 HRESULT WINAPI SafeArrayUnaccessData(
474   SAFEARRAY * psa)
475
476   if(! validArg(psa)) 
477     return E_INVALIDARG;
478
479   return(SafeArrayUnlock(psa));
480 }
481
482 /************************************************************************ 
483  *              SafeArrayPtrOfIndex
484  * Return a pointer to the element at rgIndices
485  */
486 HRESULT WINAPI SafeArrayPtrOfIndex(
487   SAFEARRAY *psa, 
488   LONG      *rgIndices, 
489   void      **ppvData)
490
491   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
492                                          the desired one... */
493
494   if(! validArg(psa))                   
495     return E_INVALIDARG;
496
497   if(! validCoordinate(rgIndices, psa)) 
498     return DISP_E_BADINDEX;
499
500   /* Figure out the number of items to skip */
501   stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
502   
503   *ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
504
505   return S_OK;
506 }
507
508 /************************************************************************ 
509  *              SafeArrayDestroyData
510  * Frees the memory data bloc
511  */
512 HRESULT WINAPI SafeArrayDestroyData(
513   SAFEARRAY *psa)
514
515   HRESULT  hRes;
516   ULONG    ulWholeArraySize; /* count spot in array  */
517   ULONG    ulDataIter;       /* to iterate the data space */
518   IUnknown *punk;
519   BSTR   bstr;
520
521   if(! validArg(psa)) 
522     return E_INVALIDARG;
523
524   if(psa->cLocks > 0) 
525     return DISP_E_ARRAYISLOCKED;
526
527   ulWholeArraySize = getArraySize(psa);
528
529   if(isPointer(psa->fFeatures)) {           /* release the pointers */
530
531     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
532       punk = *(IUnknown**)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));        
533
534       if( punk != NULL) 
535         IUnknown_Release(punk);
536     }
537
538   } else if(psa->fFeatures & FADF_BSTR) {  /* deallocate the obj */
539
540     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
541       bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
542
543       if( bstr != NULL) 
544         SysFreeString( bstr );
545     }
546   }
547       
548   /* check if this array is a Vector, in which case do not free the data 
549      block since it has been allocated by AllocDescriptor and therefore
550      deserve to be freed by DestroyDescriptor */
551   if(!(psa->fFeatures & FADF_CREATEVECTOR)) { /* Set when we do CreateVector */
552
553     /* free the whole chunk */
554     if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*falied*/
555       return E_UNEXPECTED; /* UNDOC error condition */
556
557     psa->pvData = NULL;
558   }
559   
560   return S_OK;
561 }
562
563 /************************************************************************ 
564  *              SafeArrayCopyData
565  * Copy the psaSource's data block into psaTarget if dimension and size
566  * permits it.
567  */
568 HRESULT WINAPI SafeArrayCopyData(
569   SAFEARRAY *psaSource,
570   SAFEARRAY **psaTarget)
571
572   USHORT   cDimCount;        /* looper */
573   LONG     lDelta;           /* looper */
574   IUnknown *punk;   
575   ULONG    ulWholeArraySize; /* Number of item in SA */
576   BSTR   bstr;
577
578   if(! (validArg(psaSource) && validArg(*psaTarget)) ) 
579     return E_INVALIDARG;
580
581   if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(*psaTarget))
582     return E_INVALIDARG;
583
584   ulWholeArraySize = getArraySize(psaSource); 
585
586   /* The two arrays boundaries must be of same lenght */
587   for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
588     if( psaSource->rgsabound[cDimCount].cElements != 
589       (*psaTarget)->rgsabound[cDimCount].cElements)
590       return E_INVALIDARG;
591
592   if( isPointer((*psaTarget)->fFeatures) ) {         /* the target contains ptr 
593                                                         that must be released */
594     for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
595       punk = *(IUnknown**)
596         ((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
597
598       if( punk != NULL) 
599         IUnknown_Release(punk);
600     }
601
602   } else if( (*psaTarget)->fFeatures & FADF_BSTR) {  /* the target contain BSTR
603                                                         that must be freed */ 
604     for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
605       bstr = 
606         *(BSTR*)((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
607
608       if( bstr != NULL) 
609         SysFreeString( bstr );
610     }
611   }
612
613   return duplicateData(psaSource, psaTarget);
614 }
615
616 /************************************************************************ 
617  *              SafeArrayDestroy
618  * Deallocates all memory reserved for the SafeArray
619  */
620 HRESULT WINAPI SafeArrayDestroy(
621   SAFEARRAY * psa)
622
623   HRESULT hRes;
624
625   if(! validArg(psa)) 
626     return E_INVALIDARG;
627
628   if(psa->cLocks > 0) 
629     return DISP_E_ARRAYISLOCKED;
630
631   if((hRes = SafeArrayDestroyData( psa )) == S_OK)
632     if((hRes = SafeArrayDestroyDescriptor( psa )) == S_OK)
633       return S_OK;
634
635   return E_UNEXPECTED; /* UNDOC error condition */
636 }
637
638 /************************************************************************ 
639  *              SafeArrayCopy
640  * Make a dupplicate of a SafeArray
641  */
642 HRESULT WINAPI SafeArrayCopy(
643   SAFEARRAY *psa, 
644   SAFEARRAY **ppsaOut)
645
646   HRESULT hRes;
647   DWORD   dAllocSize;
648   ULONG   ulWholeArraySize; /* size of the thing */
649
650   if(! validArg(psa)) 
651     return E_INVALIDARG;
652
653   if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
654
655     /* Duplicate the SAFEARRAY struc */
656     memcpy(*ppsaOut, psa, 
657             sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
658
659     (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
660
661     /* make sure the new safe array doesn't have the FADF_CREATEVECTOR flag,
662        because the data has not been allocated with the descriptor. */
663     (*ppsaOut)->fFeatures &= ~FADF_CREATEVECTOR;  
664  
665     /* Get the allocated memory size for source and allocate it for target */ 
666     ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
667     dAllocSize = ulWholeArraySize*psa->cbElements;
668
669     (*ppsaOut)->pvData = 
670       HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
671     if( (*ppsaOut)->pvData != NULL) {   /* HeapAlloc succeed */
672
673       if( (hRes=duplicateData(psa, ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
674         HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
675         (*ppsaOut)->pvData = NULL;
676         SafeArrayDestroyDescriptor(*ppsaOut);
677         return hRes;
678       }
679         
680     } else { /* failed to allocate or dupplicate... */
681       SafeArrayDestroyDescriptor(*ppsaOut);
682       return E_UNEXPECTED; /* UNDOC error condition */
683     }
684   } else { /* failed to allocate mem for descriptor */
685     return E_OUTOFMEMORY; /* UNDOC error condiftion */
686   }
687
688   return S_OK;
689 }
690
691 /************************************************************************ 
692  *              SafeArrayCreateVector
693  * Creates a one dimension safearray where the data is next to the 
694  * SAFEARRAY structure.
695  */
696 SAFEARRAY* WINAPI SafeArrayCreateVector(
697   VARTYPE vt, 
698   LONG    lLbound, 
699   ULONG   cElements) 
700
701   SAFEARRAY *psa;
702
703   /* Validate supported VARTYPE */
704   if ( (vt >= LAST_VARTYPE) ||
705        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
706     return NULL;
707
708   /* Allocate memory for the array descriptor and data contiguously  */
709   if( FAILED( psa = HeapAlloc( GetProcessHeap(), 
710                       HEAP_ZERO_MEMORY, 
711                       (sizeof(*psa) + (VARTYPE_SIZE[vt] * cElements))))) {
712     return NULL;
713   }
714                                                                                 
715   /* setup data members... */ 
716   psa->cDims      = 1; /* always and forever */
717   psa->fFeatures  = getFeatures(vt) | FADF_CREATEVECTOR;  /* undocumented flag used by Microsoft */
718   psa->cLocks     = 0;
719   psa->pvData     = (BYTE*)psa + sizeof(*psa);
720   psa->cbElements = VARTYPE_SIZE[vt];
721
722   psa->rgsabound[0].cElements = cElements;
723   psa->rgsabound[0].lLbound   = lLbound;
724
725   return(psa);                            
726
727
728 /************************************************************************ 
729  *              SafeArrayRedim
730  * Changes the caracteristics of the last dimension of the SafeArray
731  */
732 HRESULT WINAPI SafeArrayRedim(
733   SAFEARRAY      *psa, 
734   SAFEARRAYBOUND *psaboundNew)
735
736   LONG   lDelta;  /* hold difference in size */
737   USHORT cDims=1; /* dims counter */
738
739   if( !validArg(psa) )                    
740     return E_INVALIDARG;
741
742   if( psa->cLocks > 0 )                    
743     return DISP_E_ARRAYISLOCKED;
744
745   if( psa->fFeatures & FADF_FIXEDSIZE )    
746     return E_INVALIDARG;
747
748   if( SafeArrayLock(psa)==E_UNEXPECTED ) 
749     return E_UNEXPECTED;/* UNDOC error condition */
750
751   /* find the delta in number of array spot to apply to the new array */
752   lDelta = psaboundNew->cElements - psa->rgsabound[0].cElements;
753   for(; cDims < psa->cDims; cDims++)
754     /* delta in number of spot implied by modifying the last dimension */
755     lDelta *= psa->rgsabound[cDims].cElements;
756
757   if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
758
759   } else /* need to enlarge (lDelta +) reduce (lDelta -) */
760     if(! resizeSafeArray(psa, lDelta)) 
761       return E_UNEXPECTED; /* UNDOC error condition */
762
763   /* the only modifyable dimension sits in [0] as the dimensions were reversed  
764      at array creation time... */
765   psa->rgsabound[0].cElements = psaboundNew->cElements;
766   psa->rgsabound[0].lLbound   = psaboundNew->lLbound;
767
768   return SafeArrayUnlock(psa);
769 }
770
771 /************************************************************************
772  * NOT WINDOWS API - SafeArray* Utility functions
773  ************************************************************************/
774
775 /************************************************************************ 
776  * Used to validate the SAFEARRAY type of arg
777  */
778 static BOOL validArg(
779   SAFEARRAY *psa) 
780 {
781   SAFEARRAYBOUND *sab;
782   LONG psaSize  = 0;
783   LONG descSize = 0;
784   LONG fullSize = 0;
785
786   /*
787    * Let's check for the null pointer just in case.
788    */
789   if (psa == NULL)
790     return FALSE;
791
792   /* Check whether the size of the chunk makes sense... That's the only thing
793      I can think of now... */
794
795   psaSize = HeapSize(GetProcessHeap(), 0, psa);
796   if (psaSize == -1)
797     /* uh, foreign heap. Better don't mess with it ! */
798     return TRUE;
799
800   /* size of the descriptor when the SA is not created with CreateVector */
801   descSize = sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
802
803   /* size of the descriptor + data when created with CreateVector */
804   fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
805
806   return((psaSize >= descSize) || (psaSize >= fullSize));
807 }
808
809 /************************************************************************ 
810  * Used to reallocate memory
811  */
812 static BOOL resizeSafeArray(
813   SAFEARRAY *psa, 
814   LONG lDelta)
815 {
816   ULONG    ulWholeArraySize;  /* use as multiplicator */
817   PVOID    pvNewBlock = NULL;  
818   IUnknown *punk;
819   BSTR   bstr;
820
821   ulWholeArraySize = getArraySize(psa);
822
823   if(lDelta < 0) {                    /* array needs to be shorthen  */
824     if( isPointer(psa->fFeatures))    /* ptr that need to be released */
825             for(;lDelta < 0; lDelta++) {
826               punk = *(IUnknown**)
827           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
828         
829         if( punk != NULL )
830           IUnknown_Release(punk);
831             }
832
833     else if(psa->fFeatures & FADF_BSTR)  /* BSTR that need to be freed */
834             for(;lDelta < 0; lDelta++) {
835         bstr = *(BSTR*)
836           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
837
838         if( bstr != NULL )
839           SysFreeString( bstr );
840       }
841   }
842
843   if (!(psa->fFeatures & FADF_CREATEVECTOR))
844   {
845     /* Ok now, if we are enlarging the array, we *MUST* move the whole block 
846        pointed to by pvData.   If we are shorthening the array, this move is
847        optional but we do it anyway becuase the benefit is that we are 
848        releasing to the system the unused memory */
849
850     if((pvNewBlock = HeapReAlloc(GetProcessHeap(), 0, psa->pvData, 
851        (ulWholeArraySize + lDelta) * psa->cbElements)) == NULL) 
852         return FALSE; /* TODO If we get here it means:
853                          SHRINK situation :  we've deleted the undesired
854                                              data and did not release the memory
855                          GROWING situation:  we've been unable to grow the array
856                       */
857   }
858   else
859   {
860     /* Allocate a new block, because the previous data has been allocated with 
861        the descriptor in SafeArrayCreateVector function. */
862
863     if((pvNewBlock = HeapAlloc(GetProcessHeap(), 0,
864        ulWholeArraySize * psa->cbElements)) == NULL) 
865         return FALSE;
866
867     psa->fFeatures &= ~FADF_CREATEVECTOR;
868   }
869   /* reassign to the new block of data */
870   psa->pvData = pvNewBlock;
871   return TRUE;
872 }
873
874 /************************************************************************ 
875  * Used to set the fFeatures data member of the SAFEARRAY structure. 
876  */
877 static INT getFeatures(
878   VARTYPE vt) 
879 {
880   switch(vt) {
881     case VT_UNKNOWN:   return FADF_UNKNOWN;
882     case VT_DISPATCH:  return FADF_DISPATCH;
883     case VT_BSTR:      return FADF_BSTR;
884   }
885   return 0;
886 }
887
888 /************************************************************************ 
889  * Used to figure out if the fFeatures data member of the SAFEARRAY 
890  * structure contain any information about the type of data stored... 
891  */
892 static BOOL isPointer(
893   USHORT feature) 
894 {
895   switch(feature) {
896     case FADF_UNKNOWN:  return TRUE; /* those are pointers */
897     case FADF_DISPATCH: return TRUE;
898   }
899   return FALSE;
900 }
901
902 /************************************************************************ 
903  * Used to calculate the displacement when accessing or modifying 
904  * safearray data set.
905  *
906  *  Parameters: - LONG *coor is the desired location in the multidimension
907  *              table.  Ex for a 3 dim table: coor[] = {1,2,3};
908  *              - ULONG *mat is the format of the table.  Ex for a 3 dim
909  *              table mat[] = {4,4,4};
910  *              - USHORT dim is the number of dimension of the SafeArray
911  */
912 static ULONG calcDisplacement(
913   LONG           *coor, 
914   SAFEARRAYBOUND *mat, 
915   LONG           dim) 
916 {
917   ULONG res = 0;
918   LONG  iterDim;
919
920   for(iterDim=0; iterDim<dim; iterDim++) 
921     /* the -mat[dim] bring coor[dim] relative to 0 for calculation */
922     res += ((coor[iterDim]-mat[iterDim].lLbound) * 
923             endOfDim(coor, mat, iterDim+1, dim));
924
925   TRACE("SafeArray: calculated displacement is %lu.\n", res);
926   return(res);
927 }
928
929 /************************************************************************ 
930  * Recursivity agent for calcDisplacement method.  Used within Put and 
931  * Get methods.
932  */
933 static INT endOfDim(
934   LONG           *coor, 
935   SAFEARRAYBOUND *mat, 
936   LONG           dim, 
937   LONG           realDim) 
938 {
939   if(dim==realDim) 
940     return 1;
941   else 
942     return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
943 }
944
945
946 /************************************************************************ 
947  * Method used to validate the coordinate received in Put and Get 
948  * methods.
949  */
950 static BOOL validCoordinate(
951   LONG      *coor, 
952   SAFEARRAY *psa) 
953 {
954   INT   iter=0;
955   LONG    lUBound;
956   LONG    lLBound;
957   HRESULT hRes;
958
959   for(; iter<psa->cDims; iter++) {
960     if((hRes = SafeArrayGetLBound(psa, (iter+1), &lLBound)) != S_OK)
961       return FALSE;
962     if((hRes = SafeArrayGetUBound(psa, (iter+1), &lUBound)) != S_OK)
963       return FALSE;
964  
965     if(lLBound == lUBound) 
966       return FALSE; 
967    
968     if((coor[iter] >= lLBound) && (coor[iter] <= lUBound))
969       return TRUE;
970     else
971       return FALSE;
972   }
973   return FALSE;
974 }  
975
976 /************************************************************************ 
977  * Method used to calculate the number of cells of the SA
978  */
979 static ULONG getArraySize(
980   SAFEARRAY *psa) 
981 {
982   USHORT cCount; 
983   ULONG  ulWholeArraySize = 1;
984
985   for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
986     ulWholeArraySize *= psa->rgsabound[cCount].cElements;
987
988   return ulWholeArraySize;  
989 }
990
991
992 /************************************************************************ 
993  * Method used to handle data space dupplication for Copy32 and CopyData32
994  */
995 static HRESULT duplicateData(
996   SAFEARRAY *psa, 
997   SAFEARRAY **ppsaOut) 
998 {
999   ULONG    ulWholeArraySize; /* size of the thing */
1000   LONG     lDelta;
1001   IUnknown *punk;
1002   BSTR   pbstrReAllocStr = NULL; /* BSTR reallocated */
1003
1004   ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
1005   
1006   SafeArrayLock(*ppsaOut);
1007
1008   if( isPointer(psa->fFeatures) ) {  /* If datatype is object increment 
1009                                         object's reference count */
1010
1011     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1012       punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
1013
1014       if( punk != NULL)
1015         IUnknown_AddRef(punk);
1016     }
1017
1018     /* Copy the source array data into target array */
1019     memcpy((*ppsaOut)->pvData, psa->pvData, 
1020       ulWholeArraySize*psa->cbElements);
1021
1022   } else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate 
1023                                                the BSTR in the new array */
1024
1025     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1026       if(( pbstrReAllocStr = SysAllocString(
1027             *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
1028
1029         SafeArrayUnlock(*ppsaOut);
1030         return E_OUTOFMEMORY;
1031       }
1032
1033       *((BSTR*)((char *) (*ppsaOut)->pvData+(lDelta * psa->cbElements))) = 
1034         pbstrReAllocStr;
1035     }
1036
1037   } else { /* Simply copy the source array data into target array */
1038
1039     memcpy((*ppsaOut)->pvData, psa->pvData, 
1040       ulWholeArraySize*psa->cbElements);
1041   }
1042
1043   SafeArrayUnlock(*ppsaOut);
1044
1045   return S_OK;
1046 }
1047
1048
1049 /************************************************************************ 
1050  *              SafeArrayGetVarType
1051  * Returns the VARTYPE stored in the given safearray
1052  */
1053 HRESULT WINAPI SafeArrayGetVarType(
1054   SAFEARRAY* psa,
1055   VARTYPE*   pvt)
1056 {
1057   HRESULT hr = E_INVALIDARG;
1058   VARTYPE vt = VT_EMPTY;
1059
1060   /* const short VARTYPE_OFFSET = -4; */
1061
1062   if (psa->fFeatures & FADF_HAVEVARTYPE)
1063   {
1064     /* VT tag @ negative offset 4 in the array descriptor */
1065     FIXME("Returning VT_BSTR instead of VT_...\n");
1066     vt = VT_BSTR;
1067   }
1068   else if (psa->fFeatures & FADF_RECORD)
1069   {
1070     vt = VT_RECORD;
1071   }
1072   else if (psa->fFeatures & FADF_DISPATCH)
1073   {
1074     vt = VT_DISPATCH;
1075   }
1076   else if (psa->fFeatures & FADF_UNKNOWN)
1077   {
1078     vt = VT_UNKNOWN;
1079   }
1080
1081   if (vt != VT_EMPTY)
1082   {
1083     *pvt = vt;
1084     hr = S_OK;
1085   }
1086
1087   TRACE("HRESULT = %08lx\n", hr);
1088   return hr;
1089 }