Avoid a refcount leak in QueryInterface when delegating.
[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 const static 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 const static int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(ULONG);
103
104
105 /*************************************************************************
106  * Allocate the appropriate amount of memory for the SafeArray descriptor
107  */
108 HRESULT WINAPI SafeArrayAllocDescriptor( 
109   UINT    cDims, 
110   SAFEARRAY **ppsaOut) 
111 {
112   SAFEARRAYBOUND *sab;
113   LONG allocSize = 0;
114
115   /* SAFEARRAY + SAFEARRAYBOUND * (cDims -1) ( -1 because there is already one
116                                              ( in SAFEARRAY struct */
117   allocSize = sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
118
119   /* Allocate memory for SAFEARRAY struc */
120   if(( (*ppsaOut)=HeapAlloc( 
121         GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize)) == NULL){
122     return(E_UNEXPECTED);
123   }
124   TRACE("SafeArray: %lu bytes allocated for descriptor.\n", allocSize);
125
126   return(S_OK);
127 }
128
129 /*************************************************************************
130  * Allocate the appropriate amount of data for the SafeArray data
131  */
132 HRESULT WINAPI SafeArrayAllocData(
133   SAFEARRAY *psa) 
134 {
135   ULONG  ulWholeArraySize;   /* to store the size of the whole thing */
136
137   if(! validArg(psa)) 
138     return E_INVALIDARG;
139
140   ulWholeArraySize = getArraySize(psa);
141
142   /* Allocate memory for the data itself */
143   if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
144         psa->cbElements*ulWholeArraySize)) == NULL)
145     return(E_UNEXPECTED);
146
147   TRACE("SafeArray: %lu bytes allocated for data at %p (%lu objects).\n", 
148     psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
149
150   return(S_OK);
151 }
152
153 /*************************************************************************
154  * Create a SafeArray object by encapsulating AllocDescriptor and AllocData 
155  */
156 SAFEARRAY* WINAPI SafeArrayCreate(
157   VARTYPE        vt, 
158   UINT         cDims, 
159   SAFEARRAYBOUND *rgsabound)
160 {
161   SAFEARRAY *psa;
162   HRESULT   hRes;
163   USHORT    cDim;
164
165   /* Validate supported VARTYPE */
166   if ( (vt >= LAST_VARTYPE) ||
167        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
168     return NULL;
169
170   /* Allocate memory for the array descriptor */
171   if( FAILED( hRes = SafeArrayAllocDescriptor(cDims, &psa)))
172     return NULL;
173
174   /* setup data members... */ 
175   psa->cDims     = cDims;
176   psa->fFeatures = getFeatures(vt);
177   psa->cLocks    = 0;
178   psa->pvData    = NULL;
179   psa->cbElements= VARTYPE_SIZE[vt];
180
181   /* Invert the bounds ... */
182   for(cDim=0; cDim < psa->cDims; cDim++) {
183     psa->rgsabound[cDim].cElements = rgsabound[psa->cDims-cDim-1].cElements;
184     psa->rgsabound[cDim].lLbound   = rgsabound[psa->cDims-cDim-1].lLbound;
185   }
186
187   /* allocate memory for the data... */ 
188   if( FAILED( hRes = SafeArrayAllocData(psa))) {
189     SafeArrayDestroyDescriptor(psa); 
190     ERR("() : Failed to allocate the Safe Array data\n");
191     return NULL;
192   }
193
194   return(psa); 
195 }
196
197 /*************************************************************************
198  * Frees the memory associated with the descriptor.
199  */
200 HRESULT WINAPI SafeArrayDestroyDescriptor(
201   SAFEARRAY *psa)
202 {
203   /* Check for lockness before to free... */
204   if(psa->cLocks > 0) 
205     return DISP_E_ARRAYISLOCKED;
206
207   /* The array is unlocked, then, deallocate memory */
208   if(HeapFree( GetProcessHeap(), 0, psa) == FALSE) 
209     return E_UNEXPECTED;
210   
211   return(S_OK);
212 }
213
214
215 /*************************************************************************
216  * Increment the lock counter
217  *
218  * Doc says (MSDN Library ) that psa->pvData should be made available (!= NULL)
219  * only when psa->cLocks is > 0... I don't get it since pvData is allocated 
220  * before the array is locked, therefore  
221  */
222 HRESULT WINAPI SafeArrayLock(
223   SAFEARRAY *psa)
224 {
225   if(! validArg(psa))     
226     return E_INVALIDARG;
227
228   psa->cLocks++;
229
230   return(S_OK);
231 }
232
233 /*************************************************************************
234  * Decrement the lock counter
235  */
236 HRESULT WINAPI SafeArrayUnlock(
237   SAFEARRAY *psa)
238 {
239   if(! validArg(psa)) 
240     return E_INVALIDARG;
241
242   if (psa->cLocks > 0) 
243     psa->cLocks--;
244
245   return(S_OK);
246 }
247
248
249 /*************************************************************************
250  * Set the data at the given coordinate
251  */
252 HRESULT WINAPI SafeArrayPutElement(
253   SAFEARRAY *psa, 
254   LONG      *rgIndices, 
255   void      *pv)
256 {
257   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
258                                          the desired one... */
259   PVOID elementStorageAddress = NULL; /* Adress to store the data */
260   BSTR  pbstrReAllocStr     = NULL; /* BSTR reallocated */
261
262   /* Validate the index given */
263   if(! validCoordinate(rgIndices, psa)) 
264     return DISP_E_BADINDEX;
265   if(! validArg(psa))
266     return E_INVALIDARG;
267
268   if( SafeArrayLock(psa) == S_OK) {
269
270     /* Figure out the number of items to skip */
271     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
272   
273     /* Figure out the number of byte to skip ... */
274     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
275   
276     if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
277
278       *((VOID**)elementStorageAddress) = *(VOID**)pv; 
279       IUnknown_AddRef( *(IUnknown**)pv);
280
281     } else { 
282
283       if(psa->fFeatures == FADF_BSTR) { /* Create a new object */
284
285         if((pbstrReAllocStr = SysAllocString( (OLECHAR*)pv )) == NULL) {
286           SafeArrayUnlock(psa);  
287           return E_OUTOFMEMORY;
288         } else 
289           *((BSTR*)elementStorageAddress) = pbstrReAllocStr;
290
291       } else /* dupplicate the memory */
292         memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
293     }
294
295   } else {
296     ERR("SafeArray: Cannot lock array....\n");
297     return E_UNEXPECTED; /* UNDOC error condition */
298   }
299
300   TRACE("SafeArray: item put at adress %p.\n",elementStorageAddress);
301   return SafeArrayUnlock(psa);  
302 }
303
304
305 /*************************************************************************
306  * Return the data element corresponding the the given coordinate
307  */
308 HRESULT WINAPI SafeArrayGetElement(
309   SAFEARRAY *psa, 
310   LONG      *rgIndices, 
311   void      *pv)
312 {
313   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
314                                          the desired one... */
315   PVOID elementStorageAddress = NULL; /* Adress to store the data */
316   BSTR  pbstrReturnedStr    = NULL; /* BSTR reallocated */
317
318   if(! validArg(psa)) 
319     return E_INVALIDARG;
320   
321   if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
322     return(DISP_E_BADINDEX);
323
324   if( SafeArrayLock(psa) == S_OK) {
325
326     /* Figure out the number of items to skip */
327     stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
328   
329     /* Figure out the number of byte to skip ... */
330     elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
331   
332     if( psa->fFeatures == FADF_BSTR) {           /* reallocate the obj */
333       if( (pbstrReturnedStr = 
334           SysAllocString( *(OLECHAR**)elementStorageAddress )) == NULL) {
335         SafeArrayUnlock(psa);
336         return E_OUTOFMEMORY;
337       } else 
338         *((BSTR*)pv) = pbstrReturnedStr; 
339         
340     } else if( isPointer(psa->fFeatures) )       /* simply copy the pointer */
341        pv = *((PVOID*)elementStorageAddress); 
342     else                                         /* copy the bytes */
343       memcpy(pv, elementStorageAddress, SafeArrayGetElemsize(psa) );
344
345   } else {
346     ERR("SafeArray: Cannot lock array....\n");
347     return E_UNEXPECTED; /* UNDOC error condition */
348   }
349
350   return( SafeArrayUnlock(psa) );  
351 }
352
353 /*************************************************************************
354  * return the UP bound for a given array dimension
355  */
356 HRESULT WINAPI SafeArrayGetUBound(
357   SAFEARRAY *psa, 
358   UINT    nDim,
359   LONG      *plUbound)
360 {
361   if(! validArg(psa))   
362     return E_INVALIDARG;
363
364   if(nDim > psa->cDims) 
365     return DISP_E_BADINDEX;
366
367   *plUbound = psa->rgsabound[nDim-1].lLbound + 
368               psa->rgsabound[nDim-1].cElements - 1;
369
370   return S_OK;
371 }
372
373 /*************************************************************************
374  * Return the LO bound for a given array dimension 
375  */
376 HRESULT WINAPI SafeArrayGetLBound(
377   SAFEARRAY *psa,
378   UINT    nDim, 
379   LONG      *plLbound)
380 {
381   if(! validArg(psa))   
382     return E_INVALIDARG;
383
384   if(nDim > psa->cDims) 
385     return DISP_E_BADINDEX;
386
387   *plLbound = psa->rgsabound[nDim-1].lLbound;
388   return S_OK;
389 }
390
391 /*************************************************************************
392  * returns the number of dimension in the array
393  */
394 UINT WINAPI SafeArrayGetDim(
395   SAFEARRAY * psa)
396
397   /*
398    * A quick test in Windows shows that the behavior here for an invalid
399    * pointer is to return 0.
400    */
401   if(! validArg(psa)) 
402     return 0;
403
404   return psa->cDims;
405 }
406
407 /*************************************************************************
408  * Return the size of the element in the array
409  */
410 UINT WINAPI SafeArrayGetElemsize(
411   SAFEARRAY * psa)
412
413   /*
414    * A quick test in Windows shows that the behavior here for an invalid
415    * pointer is to return 0.
416    */
417   if(! validArg(psa)) 
418     return 0;
419
420   return psa->cbElements;
421 }
422
423 /*************************************************************************
424  * increment the access count and return the data 
425  */
426 HRESULT WINAPI SafeArrayAccessData(
427   SAFEARRAY *psa, 
428   void      **ppvData)
429
430   HRESULT hRes;
431
432   if(! validArg(psa)) 
433     return E_INVALIDARG;
434
435   hRes = SafeArrayLock(psa);
436
437   switch (hRes) {
438     case S_OK: 
439       (*ppvData) = psa->pvData;
440       break;
441     case E_INVALIDARG:
442       (*ppvData) = NULL;
443       return E_INVALIDARG;
444   }
445   
446   return S_OK;
447 }
448
449
450 /*************************************************************************
451  * Decrement the access count
452  */
453 HRESULT WINAPI SafeArrayUnaccessData(
454   SAFEARRAY * psa)
455
456   if(! validArg(psa)) 
457     return E_INVALIDARG;
458
459   return(SafeArrayUnlock(psa));
460 }
461
462 /************************************************************************ 
463  * Return a pointer to the element at rgIndices
464  */
465 HRESULT WINAPI SafeArrayPtrOfIndex(
466   SAFEARRAY *psa, 
467   LONG      *rgIndices, 
468   void      **ppvData)
469
470   ULONG stepCountInSAData     = 0;    /* Number of array item to skip to get to 
471                                          the desired one... */
472
473   if(! validArg(psa))                   
474     return E_INVALIDARG;
475
476   if(! validCoordinate(rgIndices, psa)) 
477     return DISP_E_BADINDEX;
478
479   /* Figure out the number of items to skip */
480   stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
481   
482   *ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
483
484   return S_OK;
485 }
486
487 /************************************************************************ 
488  * Frees the memory data bloc
489  */
490 HRESULT WINAPI SafeArrayDestroyData(
491   SAFEARRAY *psa)
492
493   HRESULT  hRes;
494   ULONG    ulWholeArraySize; /* count spot in array  */
495   ULONG    ulDataIter;       /* to iterate the data space */
496   IUnknown *punk;
497   BSTR   bstr;
498
499   if(! validArg(psa)) 
500     return E_INVALIDARG;
501
502   if(psa->cLocks > 0) 
503     return DISP_E_ARRAYISLOCKED;
504
505   ulWholeArraySize = getArraySize(psa);
506
507   if(isPointer(psa->fFeatures)) {           /* release the pointers */
508
509     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
510       punk = *(IUnknown**)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));        
511
512       if( punk != NULL) 
513         IUnknown_Release(punk);
514     }
515
516   } else if(psa->fFeatures & FADF_BSTR) {  /* deallocate the obj */
517
518     for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
519       bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
520
521       if( bstr != NULL) 
522         SysFreeString( bstr );
523     }
524   }
525       
526   /* check if this array is a Vector, in which case do not free the data 
527      block since it has been allocated by AllocDescriptor and therefore
528      deserve to be freed by DestroyDescriptor */
529   if(!(psa->fFeatures & FADF_FIXEDSIZE)) { /* Set when we do CreateVector */
530
531     /* free the whole chunk */
532     if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*falied*/
533       return E_UNEXPECTED; /* UNDOC error condition */
534
535     psa->pvData = NULL;
536   }
537   
538   return S_OK;
539 }
540
541 /************************************************************************ 
542  * Copy the psaSource's data block into psaTarget if dimension and size
543  * permits it.
544  */
545 HRESULT WINAPI SafeArrayCopyData(
546   SAFEARRAY *psaSource,
547   SAFEARRAY **psaTarget)
548
549   USHORT   cDimCount;        /* looper */
550   LONG     lDelta;           /* looper */
551   IUnknown *punk;   
552   ULONG    ulWholeArraySize; /* Number of item in SA */
553   BSTR   bstr;
554
555   if(! (validArg(psaSource) && validArg(*psaTarget)) ) 
556     return E_INVALIDARG;
557
558   if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(*psaTarget))
559     return E_INVALIDARG;
560
561   ulWholeArraySize = getArraySize(psaSource); 
562
563   /* The two arrays boundaries must be of same lenght */
564   for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
565     if( psaSource->rgsabound[cDimCount].cElements != 
566       (*psaTarget)->rgsabound[cDimCount].cElements)
567       return E_INVALIDARG;
568
569   if( isPointer((*psaTarget)->fFeatures) ) {         /* the target contains ptr 
570                                                         that must be released */
571     for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
572       punk = *(IUnknown**)
573         ((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
574
575       if( punk != NULL) 
576         IUnknown_Release(punk);
577     }
578
579   } else if( (*psaTarget)->fFeatures & FADF_BSTR) {  /* the target contain BSTR
580                                                         that must be freed */ 
581     for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
582       bstr = 
583         *(BSTR*)((char *) (*psaTarget)->pvData + (lDelta * (*psaTarget)->cbElements));
584
585       if( bstr != NULL) 
586         SysFreeString( bstr );
587     }
588   }
589
590   return duplicateData(psaSource, psaTarget);
591 }
592
593 /************************************************************************ 
594  * Deallocates all memory reserved for the SafeArray
595  */
596 HRESULT WINAPI SafeArrayDestroy(
597   SAFEARRAY * psa)
598
599   HRESULT hRes;
600
601   if(! validArg(psa)) 
602     return E_INVALIDARG;
603
604   if(psa->cLocks > 0) 
605     return DISP_E_ARRAYISLOCKED;
606
607   if((hRes = SafeArrayDestroyData( psa )) == S_OK)
608     if((hRes = SafeArrayDestroyDescriptor( psa )) == S_OK)
609       return S_OK;
610
611   return E_UNEXPECTED; /* UNDOC error condition */
612 }
613
614 /************************************************************************ 
615  * Make a dupplicate of a SafeArray
616  */
617 HRESULT WINAPI SafeArrayCopy(
618   SAFEARRAY *psa, 
619   SAFEARRAY **ppsaOut)
620
621   HRESULT hRes;
622   DWORD   dAllocSize;
623
624   if(! validArg(psa)) 
625     return E_INVALIDARG;
626
627   if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
628
629     /* Duplicate the SAFEARRAY struc */
630     memcpy(*ppsaOut, psa, 
631             sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
632
633     (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
634   
635     /* Get the allocated memory size for source and allocate it for target */ 
636     dAllocSize = HeapSize(GetProcessHeap(), 0, psa->pvData);
637     (*ppsaOut)->pvData = 
638       HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
639
640     if( (*ppsaOut)->pvData != NULL) {   /* HeapAlloc succeed */
641
642       if( (hRes=duplicateData(psa, ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
643         HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
644         (*ppsaOut)->pvData = NULL;
645         SafeArrayDestroyDescriptor(*ppsaOut);
646         return hRes;
647       }
648         
649     } else { /* failed to allocate or dupplicate... */
650       SafeArrayDestroyDescriptor(*ppsaOut);
651       return E_UNEXPECTED; /* UNDOC error condition */
652     }
653   } else { /* failed to allocate mem for descriptor */
654     return E_OUTOFMEMORY; /* UNDOC error condiftion */
655   }
656
657   return S_OK;
658 }
659
660 /************************************************************************ 
661  * Creates a one dimension safearray where the data is next to the 
662  * SAFEARRAY structure.
663  */
664 SAFEARRAY* WINAPI SafeArrayCreateVector(
665   VARTYPE vt, 
666   LONG    lLbound, 
667   ULONG   cElements) 
668
669   SAFEARRAY *psa;
670
671   /* Validate supported VARTYPE */
672   if ( (vt >= LAST_VARTYPE) ||
673        ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
674     return NULL;
675
676   /* Allocate memory for the array descriptor and data contiguously  */
677   if( FAILED( psa = HeapAlloc( GetProcessHeap(), 
678                       HEAP_ZERO_MEMORY, 
679                       (sizeof(*psa) + (VARTYPE_SIZE[vt] * cElements))))) {
680     return NULL;
681   }
682
683   /* setup data members... */ 
684   psa->cDims      = 1; /* always and forever */
685   psa->fFeatures  = getFeatures(vt) | FADF_FIXEDSIZE;
686   psa->cLocks     = 0;
687   psa->pvData     = psa+sizeof(*psa);
688   psa->cbElements = VARTYPE_SIZE[vt];
689
690   psa->rgsabound[0].cElements = cElements;
691   psa->rgsabound[0].lLbound   = lLbound;
692   
693   return(psa); 
694
695
696 /************************************************************************ 
697  * Changes the caracteristics of the last dimension of the SafeArray
698  */
699 HRESULT WINAPI SafeArrayRedim(
700   SAFEARRAY      *psa, 
701   SAFEARRAYBOUND *psaboundNew)
702
703   LONG   lDelta;  /* hold difference in size */
704   USHORT cDims=1; /* dims counter */
705
706   if( !validArg(psa) )                    
707     return E_INVALIDARG;
708
709   if( psa->cLocks > 0 )                    
710     return DISP_E_ARRAYISLOCKED;
711
712   if( psa->fFeatures & FADF_FIXEDSIZE )    
713     return E_INVALIDARG;
714
715   if( SafeArrayLock(psa)==E_UNEXPECTED ) 
716     return E_UNEXPECTED;/* UNDOC error condition */
717
718   /* find the delta in number of array spot to apply to the new array */
719   lDelta = psaboundNew->cElements - psa->rgsabound[0].cElements;
720   for(; cDims < psa->cDims; cDims++)
721     /* delta in number of spot implied by modifying the last dimension */
722     lDelta *= psa->rgsabound[cDims].cElements;
723
724   if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
725
726   } else /* need to enlarge (lDelta +) reduce (lDelta -) */
727     if(! resizeSafeArray(psa, lDelta)) 
728       return E_UNEXPECTED; /* UNDOC error condition */
729
730   /* the only modifyable dimension sits in [0] as the dimensions were reversed  
731      at array creation time... */
732   psa->rgsabound[0].cElements = psaboundNew->cElements;
733   psa->rgsabound[0].lLbound   = psaboundNew->lLbound;
734
735   return SafeArrayUnlock(psa);
736 }
737
738 /************************************************************************
739  * NOT WINDOWS API - SafeArray* Utility functions
740  ************************************************************************/
741
742 /************************************************************************ 
743  * Used to validate the SAFEARRAY type of arg
744  */
745 static BOOL validArg(
746   SAFEARRAY *psa) 
747 {
748   SAFEARRAYBOUND *sab;
749   LONG psaSize  = 0;
750   LONG descSize = 0;
751   LONG fullSize = 0;
752
753   /*
754    * Let's check for the null pointer just in case.
755    */
756   if (psa == NULL)
757     return FALSE;
758
759   /* Check whether the size of the chunk make sens... That's the only thing
760      I can think of now... */
761
762   psaSize   = HeapSize(GetProcessHeap(), 0, psa);
763
764   /* size of the descriptor when the SA is not created with CreateVector */
765   descSize = sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
766
767   /* size of the descriptor + data when created with CreateVector */
768   fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
769
770   return((psaSize >= descSize) || (psaSize >= fullSize));
771 }
772
773 /************************************************************************ 
774  * Used to reallocate memory
775  */
776 static BOOL resizeSafeArray(
777   SAFEARRAY *psa, 
778   LONG lDelta)
779 {
780   ULONG    ulWholeArraySize;  /* use as multiplicator */
781   PVOID    pvNewBlock = NULL;  
782   IUnknown *punk;
783   BSTR   bstr;
784
785   ulWholeArraySize = getArraySize(psa);
786
787   if(lDelta < 0) {                    /* array needs to be shorthen  */
788     if( isPointer(psa->fFeatures))    /* ptr that need to be released */
789             for(;lDelta < 0; lDelta++) {
790               punk = *(IUnknown**)
791           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
792         
793         if( punk != NULL )
794           IUnknown_Release(punk);
795             }
796
797     else if(psa->fFeatures & FADF_BSTR)  /* BSTR that need to be freed */
798             for(;lDelta < 0; lDelta++) {
799         bstr = *(BSTR*)
800           ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
801
802         if( bstr != NULL )
803           SysFreeString( bstr );
804       }
805   }
806
807   /* Ok now, if we are enlarging the array, we *MUST* move the whole block 
808      pointed to by pvData.   If we are shorthening the array, this move is
809      optional but we do it anyway becuase the benefit is that we are 
810      releasing to the system the unused memory */
811   
812   if((pvNewBlock = HeapReAlloc(GetProcessHeap(), 0, psa->pvData, 
813      (ulWholeArraySize + lDelta) * psa->cbElements)) == NULL) 
814       return FALSE; /* TODO If we get here it means:
815                        SHRINK situation :  we've deleted the undesired
816                                            data and did not release the memory
817                        GROWING situation:  we've been unable to grow the array
818                     */
819
820   /* reassign to the new block of data */
821   psa->pvData = pvNewBlock;
822   return TRUE;
823 }
824
825 /************************************************************************ 
826  * Used to set the fFeatures data member of the SAFEARRAY structure. 
827  */
828 static INT getFeatures(
829   VARTYPE vt) 
830 {
831   switch(vt) {
832     case VT_UNKNOWN:   return FADF_UNKNOWN;
833     case VT_DISPATCH:  return FADF_DISPATCH;
834     case VT_BSTR:      return FADF_BSTR;
835   }
836   return 0;
837 }
838
839 /************************************************************************ 
840  * Used to figure out if the fFeatures data member of the SAFEARRAY 
841  * structure contain any information about the type of data stored... 
842  */
843 static BOOL isPointer(
844   USHORT feature) 
845 {
846   switch(feature) {
847     case FADF_UNKNOWN:  return TRUE; /* those are pointers */
848     case FADF_DISPATCH: return TRUE;
849   }
850   return FALSE;
851 }
852
853 /************************************************************************ 
854  * Used to calculate the displacement when accessing or modifying 
855  * safearray data set.
856  *
857  *  Parameters: - LONG *coor is the desired location in the multidimension
858  *              table.  Ex for a 3 dim table: coor[] = {1,2,3};
859  *              - ULONG *mat is the format of the table.  Ex for a 3 dim
860  *              table mat[] = {4,4,4};
861  *              - USHORT dim is the number of dimension of the SafeArray
862  */
863 static ULONG calcDisplacement(
864   LONG           *coor, 
865   SAFEARRAYBOUND *mat, 
866   LONG           dim) 
867 {
868   ULONG res = 0;
869   LONG  iterDim;
870
871   for(iterDim=0; iterDim<dim; iterDim++) 
872     /* the -mat[dim] bring coor[dim] relative to 0 for calculation */
873     res += ((coor[iterDim]-mat[iterDim].lLbound) * 
874             endOfDim(coor, mat, iterDim+1, dim));
875
876   TRACE("SafeArray: calculated displacement is %lu.\n", res);
877   return(res);
878 }
879
880 /************************************************************************ 
881  * Recursivity agent for calcDisplacement method.  Used within Put and 
882  * Get methods.
883  */
884 static INT endOfDim(
885   LONG           *coor, 
886   SAFEARRAYBOUND *mat, 
887   LONG           dim, 
888   LONG           realDim) 
889 {
890   if(dim==realDim) 
891     return 1;
892   else 
893     return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
894 }
895
896
897 /************************************************************************ 
898  * Method used to validate the coordinate received in Put and Get 
899  * methods.
900  */
901 static BOOL validCoordinate(
902   LONG      *coor, 
903   SAFEARRAY *psa) 
904 {
905   INT   iter=0;
906   LONG    lUBound;
907   LONG    lLBound;
908   HRESULT hRes;
909
910   for(; iter<psa->cDims; iter++) {
911     if((hRes = SafeArrayGetLBound(psa, iter, &lLBound)) != S_OK)
912       return FALSE;
913     if((hRes = SafeArrayGetUBound(psa, iter, &lUBound)) != S_OK)
914       return FALSE;
915  
916     if(lLBound == lUBound) 
917       return FALSE; 
918    
919     if((coor[iter] >= lLBound) && (coor[iter] <= lUBound))
920       return TRUE;
921     else
922       return FALSE;
923   }
924   return FALSE;
925 }  
926
927 /************************************************************************ 
928  * Method used to calculate the number of cells of the SA
929  */
930 static ULONG getArraySize(
931   SAFEARRAY *psa) 
932 {
933   USHORT cCount; 
934   ULONG  ulWholeArraySize = 1;
935
936   for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
937     ulWholeArraySize *= psa->rgsabound[cCount].cElements;
938
939   return ulWholeArraySize;  
940 }
941
942
943 /************************************************************************ 
944  * Method used to handle data space dupplication for Copy32 and CopyData32
945  */
946 static HRESULT duplicateData(
947   SAFEARRAY *psa, 
948   SAFEARRAY **ppsaOut) 
949 {
950   ULONG    ulWholeArraySize; /* size of the thing */
951   LONG     lDelta;
952   IUnknown *punk;
953   BSTR   pbstrReAllocStr = NULL; /* BSTR reallocated */
954
955   ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
956   
957   SafeArrayLock(*ppsaOut);
958
959   if( isPointer(psa->fFeatures) ) {  /* If datatype is object increment 
960                                         object's reference count */
961
962     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
963       punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
964
965       if( punk != NULL)
966         IUnknown_AddRef(punk);
967     }
968
969     /* Copy the source array data into target array */
970     memcpy((*ppsaOut)->pvData, psa->pvData, 
971       ulWholeArraySize*psa->cbElements);
972
973   } else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate 
974                                                the BSTR in the new array */
975
976     for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
977       if(( pbstrReAllocStr = SysAllocString(
978             *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
979
980         SafeArrayUnlock(*ppsaOut);
981         return E_OUTOFMEMORY;
982       }
983
984       *((BSTR*)((char *) (*ppsaOut)->pvData+(lDelta * psa->cbElements))) = 
985         pbstrReAllocStr;
986     }
987
988   } else { /* Simply copy the source array data into target array */
989
990     memcpy((*ppsaOut)->pvData, psa->pvData, 
991       ulWholeArraySize*psa->cbElements);
992   }
993
994   SafeArrayUnlock(*ppsaOut);
995
996   return S_OK;
997 }
998