Implemented Rtl*ByteSwap() functions, based on a patch by Jon
[wine/multimedia.git] / dlls / oleaut32 / safearray.c
blobef4e035ed08d34862206464887b0bb06ce9d33c2
1 /*************************************************************************
2 * OLE Automation
3 * SafeArray Implementation
5 * This file contains the implementation of the SafeArray interface.
7 * Copyright 1999 Sylvain St-Germain
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 /* Memory Layout of a SafeArray:
25 * -0x10: start of memory.
26 * -0x10: GUID for VT_DISPATCH and VT_UNKNOWN safearrays (if FADF_HAVEIID)
27 * -0x04: DWORD varianttype; (for all others, except VT_RECORD) (if FADF_HAVEVARTYPE)
28 * -0x4: IRecordInfo* iface; (if FADF_RECORD, for VT_RECORD (can be NULL))
29 * 0x00: SAFEARRAY,
30 * 0x10: SAFEARRAYBOUNDS[0...]
33 #include <stdio.h>
34 #include <string.h>
35 #include "windef.h"
36 #include "winerror.h"
37 #include "winbase.h"
38 #include "oleauto.h"
39 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
43 #define SYSDUPSTRING(str) SysAllocStringLen((str), SysStringLen(str))
45 /* Locally used methods */
46 static INT
47 endOfDim(LONG *coor, SAFEARRAYBOUND *mat, LONG dim, LONG realDim);
49 static ULONG
50 calcDisplacement(LONG *coor, SAFEARRAYBOUND *mat, LONG dim);
52 static BOOL
53 isPointer(USHORT feature);
55 static INT
56 getFeatures(VARTYPE vt);
58 static BOOL
59 validCoordinate(LONG *coor, SAFEARRAY *psa);
61 static BOOL
62 resizeSafeArray(SAFEARRAY *psa, LONG lDelta);
64 static BOOL
65 validArg(SAFEARRAY *psa);
67 static ULONG
68 getArraySize(SAFEARRAY *psa);
70 static HRESULT
71 duplicateData(SAFEARRAY *psa, SAFEARRAY *ppsaOut);
73 /* Association between VARTYPE and their size.
74 A size of zero is defined for the unsupported types. */
76 #define VARTYPE_NOT_SUPPORTED 0
77 static const ULONG VARTYPE_SIZE[] =
79 /* this is taken from wtypes.h. Only [S]es are supported by the SafeArray */
80 VARTYPE_NOT_SUPPORTED, /* VT_EMPTY [V] [P] nothing */
81 VARTYPE_NOT_SUPPORTED, /* VT_NULL [V] [P] SQL style Nul */
82 2, /* VT_I2 [V][T][P][S] 2 byte signed int */
83 4, /* VT_I4 [V][T][P][S] 4 byte signed int */
84 4, /* VT_R4 [V][T][P][S] 4 byte real */
85 8, /* VT_R8 [V][T][P][S] 8 byte real */
86 8, /* VT_CY [V][T][P][S] currency */
87 8, /* VT_DATE [V][T][P][S] date */
88 sizeof(BSTR), /* VT_BSTR [V][T][P][S] OLE Automation string*/
89 sizeof(LPDISPATCH), /* VT_DISPATCH [V][T][P][S] IDispatch * */
90 4, /* VT_ERROR [V][T] [S] SCODE */
91 2, /* VT_BOOL [V][T][P][S] True=-1, False=0*/
92 sizeof(VARIANT), /* VT_VARIANT [V][T][P][S] VARIANT * */
93 sizeof(LPUNKNOWN), /* VT_UNKNOWN [V][T] [S] IUnknown * */
94 sizeof(DECIMAL), /* VT_DECIMAL [V][T] [S] 16 byte fixed point */
95 VARTYPE_NOT_SUPPORTED, /* no VARTYPE here..... */
96 1, /* VT_I1 [T] [S] signed char */
97 1, /* VT_UI1 [V][T][P][S] unsigned char */
98 2, /* VT_UI2 [T][P][S] unsigned short */
99 4, /* VT_UI4 [T][P][S] unsigned int */
100 VARTYPE_NOT_SUPPORTED, /* VT_I8 [T][P] signed 64-bit int */
101 VARTYPE_NOT_SUPPORTED, /* VT_UI8 [T][P] unsigned 64-bit int */
102 sizeof(INT), /* VT_INT [T] signed machine int */
103 sizeof(UINT), /* VT_UINT [T] unsigned machine int */
104 VARTYPE_NOT_SUPPORTED, /* VT_VOID [T] C style void */
105 VARTYPE_NOT_SUPPORTED, /* VT_HRESULT [T] Standard return type */
106 VARTYPE_NOT_SUPPORTED, /* VT_PTR [T] pointer type */
107 VARTYPE_NOT_SUPPORTED, /* VT_SAFEARRAY [T] (use VT_ARRAY in VARIANT)*/
108 VARTYPE_NOT_SUPPORTED, /* VT_CARRAY [T] C style array */
109 VARTYPE_NOT_SUPPORTED, /* VT_USERDEFINED [T] user defined type */
110 VARTYPE_NOT_SUPPORTED, /* VT_LPSTR [T][P] null terminated string */
111 VARTYPE_NOT_SUPPORTED, /* VT_LPWSTR [T][P] wide null term string */
112 VARTYPE_NOT_SUPPORTED, /* 32 */
113 VARTYPE_NOT_SUPPORTED, /* 33 */
114 VARTYPE_NOT_SUPPORTED, /* 34 */
115 VARTYPE_NOT_SUPPORTED, /* 35 */
116 VARTYPE_NOT_SUPPORTED, /* VT_RECORD record */
117 VARTYPE_NOT_SUPPORTED, /* 37 */
118 VARTYPE_NOT_SUPPORTED, /* 38 */
119 VARTYPE_NOT_SUPPORTED, /* 39 */
120 VARTYPE_NOT_SUPPORTED, /* 40 */
121 VARTYPE_NOT_SUPPORTED, /* 41 */
122 VARTYPE_NOT_SUPPORTED, /* 42 */
123 VARTYPE_NOT_SUPPORTED, /* 43 */
124 VARTYPE_NOT_SUPPORTED, /* 44 */
125 VARTYPE_NOT_SUPPORTED, /* 45 */
126 VARTYPE_NOT_SUPPORTED, /* 46 */
127 VARTYPE_NOT_SUPPORTED, /* 47 */
128 VARTYPE_NOT_SUPPORTED, /* 48 */
129 VARTYPE_NOT_SUPPORTED, /* 49 */
130 VARTYPE_NOT_SUPPORTED, /* 50 */
131 VARTYPE_NOT_SUPPORTED, /* 51 */
132 VARTYPE_NOT_SUPPORTED, /* 52 */
133 VARTYPE_NOT_SUPPORTED, /* 53 */
134 VARTYPE_NOT_SUPPORTED, /* 54 */
135 VARTYPE_NOT_SUPPORTED, /* 55 */
136 VARTYPE_NOT_SUPPORTED, /* 56 */
137 VARTYPE_NOT_SUPPORTED, /* 57 */
138 VARTYPE_NOT_SUPPORTED, /* 58 */
139 VARTYPE_NOT_SUPPORTED, /* 59 */
140 VARTYPE_NOT_SUPPORTED, /* 60 */
141 VARTYPE_NOT_SUPPORTED, /* 61 */
142 VARTYPE_NOT_SUPPORTED, /* 62 */
143 VARTYPE_NOT_SUPPORTED, /* 63 */
144 VARTYPE_NOT_SUPPORTED, /* VT_FILETIME [P] FILETIME */
145 VARTYPE_NOT_SUPPORTED, /* VT_BLOB [P] Length prefixed bytes */
146 VARTYPE_NOT_SUPPORTED, /* VT_STREAM [P] Name of stream follows */
147 VARTYPE_NOT_SUPPORTED, /* VT_STORAGE [P] Name of storage follows */
148 VARTYPE_NOT_SUPPORTED, /* VT_STREAMED_OBJECT[P] Stream contains an object*/
149 VARTYPE_NOT_SUPPORTED, /* VT_STORED_OBJECT [P] Storage contains object*/
150 VARTYPE_NOT_SUPPORTED, /* VT_BLOB_OBJECT [P] Blob contains an object*/
151 VARTYPE_NOT_SUPPORTED, /* VT_CF [P] Clipboard format */
152 VARTYPE_NOT_SUPPORTED, /* VT_CLSID [P] A Class ID */
155 static const int LAST_VARTYPE = sizeof(VARTYPE_SIZE)/sizeof(VARTYPE_SIZE[0]);
158 /*************************************************************************
159 * SafeArrayAllocDescriptor (OLEAUT32.36)
160 * Allocate the appropriate amount of memory for the SafeArray descriptor
162 HRESULT WINAPI SafeArrayAllocDescriptor(
163 UINT cDims,
164 SAFEARRAY **ppsaOut)
166 SAFEARRAYBOUND *sab;
167 LONG allocSize = 0;
168 char *ptr;
170 if (!cDims || cDims >= 0x10000) /* 65536 appears to be the limit */
171 return E_INVALIDARG;
172 if (!ppsaOut)
173 return E_POINTER;
175 /* GUID + SAFEARRAY + SAFEARRAYBOUND * (cDims -1)
176 * ( -1 because there is already one ( in SAFEARRAY struct
178 allocSize = sizeof(GUID) + sizeof(**ppsaOut) + (sizeof(*sab) * (cDims-1));
180 /* Allocate memory for SAFEARRAY struc */
181 ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, allocSize);
182 if (!ptr)
183 return E_OUTOFMEMORY;
184 *ppsaOut = (SAFEARRAY *)(ptr + sizeof(GUID));
185 (*ppsaOut)->cDims = cDims;
186 TRACE("(%d): %lu bytes allocated for descriptor.\n", cDims, allocSize);
188 return(S_OK);
191 /*************************************************************************
192 * SafeArrayAllocDescriptorEx (OLEAUT32.41)
193 * Allocate the appropriate amount of memory for the SafeArray descriptor
194 * and also store information about the vartype before the returned pointer.
196 HRESULT WINAPI SafeArrayAllocDescriptorEx(
197 VARTYPE vt,
198 UINT cDims,
199 SAFEARRAY **ppsaOut)
201 HRESULT hres;
203 hres = SafeArrayAllocDescriptor (cDims, ppsaOut);
204 if (FAILED(hres))
205 return hres;
207 switch (vt) {
208 case VT_DISPATCH:
209 (*ppsaOut)->fFeatures = FADF_HAVEIID;
210 SafeArraySetIID( *ppsaOut, &IID_IDispatch);
211 break;
212 case VT_UNKNOWN:
213 (*ppsaOut)->fFeatures = FADF_HAVEIID;
214 SafeArraySetIID( *ppsaOut, &IID_IUnknown);
215 break;
216 case VT_RECORD:
217 (*ppsaOut)->fFeatures = FADF_RECORD;
218 break;
219 default:
220 (*ppsaOut)->fFeatures = FADF_HAVEVARTYPE;
221 ((DWORD*)*ppsaOut)[-1] = vt;
222 break;
224 return S_OK;
227 /*************************************************************************
228 * SafeArrayAllocData (OLEAUT32.37)
229 * Allocate the appropriate amount of data for the SafeArray data
231 HRESULT WINAPI SafeArrayAllocData(
232 SAFEARRAY *psa)
234 ULONG ulWholeArraySize; /* to store the size of the whole thing */
236 if(! validArg(psa))
237 return E_INVALIDARG;
239 ulWholeArraySize = getArraySize(psa);
241 /* Allocate memory for the data itself */
242 if((psa->pvData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
243 psa->cbElements*ulWholeArraySize)) == NULL)
244 return(E_UNEXPECTED);
246 TRACE("SafeArray: %lu bytes allocated for data at %p (%lu objects).\n",
247 psa->cbElements*ulWholeArraySize, psa->pvData, ulWholeArraySize);
249 return(S_OK);
252 /*************************************************************************
253 * SafeArrayCreate (OLEAUT32.15)
254 * Create a SafeArray object by encapsulating AllocDescriptor and AllocData
256 SAFEARRAY* WINAPI SafeArrayCreate(
257 VARTYPE vt,
258 UINT cDims,
259 SAFEARRAYBOUND *rgsabound)
261 SAFEARRAY *psa;
262 HRESULT hRes;
263 USHORT cDim;
265 /* Validate supported VARTYPE */
266 if ( (vt >= LAST_VARTYPE) ||
267 ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
268 return NULL;
270 /* Allocate memory for the array descriptor */
271 if( FAILED( hRes = SafeArrayAllocDescriptorEx(vt, cDims, &psa)))
272 return NULL;
274 /* setup data members... */
275 psa->cDims = cDims;
276 switch (vt) {
277 case VT_BSTR: psa->fFeatures |= FADF_BSTR;break;
278 case VT_UNKNOWN: psa->fFeatures |= FADF_UNKNOWN;break;
279 case VT_DISPATCH: psa->fFeatures |= FADF_DISPATCH;break;
280 case VT_VARIANT: psa->fFeatures |= FADF_VARIANT;break;
281 default: break;
283 psa->cLocks = 0;
284 psa->pvData = NULL;
285 psa->cbElements= VARTYPE_SIZE[vt];
287 /* Invert the bounds ... */
288 for(cDim=0; cDim < psa->cDims; cDim++) {
289 psa->rgsabound[cDim].cElements = rgsabound[psa->cDims-cDim-1].cElements;
290 psa->rgsabound[cDim].lLbound = rgsabound[psa->cDims-cDim-1].lLbound;
293 /* allocate memory for the data... */
294 if( FAILED( hRes = SafeArrayAllocData(psa))) {
295 SafeArrayDestroyDescriptor(psa);
296 ERR("() : Failed to allocate the Safe Array data\n");
297 return NULL;
300 return(psa);
303 /*************************************************************************
304 * SafeArrayDestroyDescriptor (OLEAUT32.38)
305 * Frees the memory associated with the descriptor.
307 HRESULT WINAPI SafeArrayDestroyDescriptor(
308 SAFEARRAY *psa)
310 LPVOID ptr;
312 /* Check for lockness before to free... */
313 if(psa->cLocks > 0)
314 return DISP_E_ARRAYISLOCKED;
316 /* The array is unlocked, then, deallocate memory */
317 ptr = ((IID*)psa)-1;
318 if(HeapFree( GetProcessHeap(), 0, ptr) == FALSE)
319 return E_UNEXPECTED;
320 return(S_OK);
324 /*************************************************************************
325 * SafeArrayLock (OLEAUT32.21)
326 * Increment the lock counter
328 * Doc says (MSDN Library ) that psa->pvData should be made available (!= NULL)
329 * only when psa->cLocks is > 0... I don't get it since pvData is allocated
330 * before the array is locked, therefore
332 HRESULT WINAPI SafeArrayLock(
333 SAFEARRAY *psa)
335 if(! validArg(psa))
336 return E_INVALIDARG;
338 psa->cLocks++;
340 return(S_OK);
343 /*************************************************************************
344 * SafeArrayUnlock (OLEAUT32.22)
345 * Decrement the lock counter
347 HRESULT WINAPI SafeArrayUnlock(
348 SAFEARRAY *psa)
350 if(! validArg(psa))
351 return E_INVALIDARG;
353 if (psa->cLocks > 0)
354 psa->cLocks--;
356 return(S_OK);
360 /*************************************************************************
361 * SafeArrayPutElement (OLEAUT32.26)
362 * Set the data at the given coordinate
364 HRESULT WINAPI SafeArrayPutElement(
365 SAFEARRAY *psa,
366 LONG *rgIndices,
367 void *pv)
369 ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
370 the desired one... */
371 PVOID elementStorageAddress = NULL; /* Adress to store the data */
373 /* Validate the index given */
374 if(! validCoordinate(rgIndices, psa))
375 return DISP_E_BADINDEX;
376 if(! validArg(psa))
377 return E_INVALIDARG;
379 if( SafeArrayLock(psa) == S_OK) {
381 /* Figure out the number of items to skip */
382 stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
384 /* Figure out the number of byte to skip ... */
385 elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
387 if(isPointer(psa->fFeatures)) { /* increment ref count for this pointer */
389 *((PVOID*)elementStorageAddress) = *(PVOID*)pv;
390 IUnknown_AddRef( *(IUnknown**)pv);
392 } else {
394 if(psa->fFeatures & FADF_BSTR) { /* Create a new object */
395 BSTR pbstrReAllocStr = NULL;
396 if(pv &&
397 ((pbstrReAllocStr = SYSDUPSTRING( (OLECHAR*)pv )) == NULL)) {
398 SafeArrayUnlock(psa);
399 return E_OUTOFMEMORY;
400 } else
401 *((BSTR*)elementStorageAddress) = pbstrReAllocStr;
403 else if(psa->fFeatures & FADF_VARIANT) {
404 HRESULT hr = VariantCopy(elementStorageAddress, pv);
405 if (FAILED(hr)) {
406 SafeArrayUnlock(psa);
407 return hr;
410 else /* duplicate the memory */
411 memcpy(elementStorageAddress, pv, SafeArrayGetElemsize(psa) );
414 } else {
415 ERR("SafeArray: Cannot lock array....\n");
416 return E_UNEXPECTED; /* UNDOC error condition */
419 TRACE("SafeArray: item put at adress %p.\n",elementStorageAddress);
420 return SafeArrayUnlock(psa);
424 /*************************************************************************
425 * SafeArrayGetElement (OLEAUT32.25)
426 * Return the data element corresponding the the given coordinate
428 HRESULT WINAPI SafeArrayGetElement(
429 SAFEARRAY *psa,
430 LONG *rgIndices,
431 void *pv)
433 ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
434 the desired one... */
435 PVOID elementStorageAddress = NULL; /* Adress to store the data */
437 if(! validArg(psa))
438 return E_INVALIDARG;
440 if(! validCoordinate(rgIndices, psa)) /* Validate the index given */
441 return(DISP_E_BADINDEX);
443 if( SafeArrayLock(psa) == S_OK) {
445 /* Figure out the number of items to skip */
446 stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
448 /* Figure out the number of byte to skip ... */
449 elementStorageAddress = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
451 if( psa->fFeatures & FADF_BSTR) { /* reallocate the obj */
452 BSTR pbstrStoredStr = *(OLECHAR**)elementStorageAddress;
453 BSTR pbstrReturnedStr = NULL;
454 if( pbstrStoredStr &&
455 ((pbstrReturnedStr = SYSDUPSTRING( pbstrStoredStr )) == NULL) ) {
456 SafeArrayUnlock(psa);
457 return E_OUTOFMEMORY;
458 } else
459 *((BSTR*)pv) = pbstrReturnedStr;
461 else if( psa->fFeatures & FADF_VARIANT) {
462 HRESULT hr;
463 VariantInit(pv);
464 hr = VariantCopy(pv, elementStorageAddress);
465 if (FAILED(hr)) {
466 SafeArrayUnlock(psa);
467 return hr;
470 else if( isPointer(psa->fFeatures) ) /* simply copy the pointer */
471 *(PVOID*)pv = *((PVOID*)elementStorageAddress);
472 else /* copy the bytes */
473 memcpy(pv, elementStorageAddress, psa->cbElements );
475 } else {
476 ERR("SafeArray: Cannot lock array....\n");
477 return E_UNEXPECTED; /* UNDOC error condition */
480 return( SafeArrayUnlock(psa) );
483 /*************************************************************************
484 * SafeArrayGetUBound (OLEAUT32.19)
485 * return the UP bound for a given array dimension
487 HRESULT WINAPI SafeArrayGetUBound(
488 SAFEARRAY *psa,
489 UINT nDim,
490 LONG *plUbound)
492 if(! validArg(psa))
493 return E_INVALIDARG;
495 if(nDim > psa->cDims)
496 return DISP_E_BADINDEX;
498 if(0 == nDim)
499 return DISP_E_BADINDEX;
501 *plUbound = psa->rgsabound[nDim-1].lLbound +
502 psa->rgsabound[nDim-1].cElements - 1;
504 return S_OK;
507 /*************************************************************************
508 * SafeArrayGetLBound (OLEAUT32.20)
509 * Return the LO bound for a given array dimension
511 HRESULT WINAPI SafeArrayGetLBound(
512 SAFEARRAY *psa,
513 UINT nDim,
514 LONG *plLbound)
516 if(! validArg(psa))
517 return E_INVALIDARG;
519 if(nDim > psa->cDims)
520 return DISP_E_BADINDEX;
522 if(0 == nDim)
523 return DISP_E_BADINDEX;
525 *plLbound = psa->rgsabound[nDim-1].lLbound;
526 return S_OK;
529 /*************************************************************************
530 * SafeArrayGetDim (OLEAUT32.17)
531 * returns the number of dimension in the array
533 UINT WINAPI SafeArrayGetDim(
534 SAFEARRAY * psa)
537 * A quick test in Windows shows that the behavior here for an invalid
538 * pointer is to return 0.
540 if(! validArg(psa))
541 return 0;
543 return psa->cDims;
546 /*************************************************************************
547 * SafeArrayGetElemsize (OLEAUT32.18)
548 * Return the size of the element in the array
550 UINT WINAPI SafeArrayGetElemsize(
551 SAFEARRAY * psa)
554 * A quick test in Windows shows that the behavior here for an invalid
555 * pointer is to return 0.
557 if(! validArg(psa))
558 return 0;
560 return psa->cbElements;
563 /*************************************************************************
564 * SafeArrayAccessData (OLEAUT32.23)
565 * increment the access count and return the data
567 HRESULT WINAPI SafeArrayAccessData(
568 SAFEARRAY *psa,
569 void **ppvData)
571 HRESULT hRes;
573 if(! validArg(psa))
574 return E_INVALIDARG;
576 hRes = SafeArrayLock(psa);
578 switch (hRes) {
579 case S_OK:
580 (*ppvData) = psa->pvData;
581 break;
582 case E_INVALIDARG:
583 (*ppvData) = NULL;
584 return E_INVALIDARG;
587 return S_OK;
591 /*************************************************************************
592 * SafeArrayUnaccessData (OLEAUT32.24)
593 * Decrement the access count
595 HRESULT WINAPI SafeArrayUnaccessData(
596 SAFEARRAY * psa)
598 if(! validArg(psa))
599 return E_INVALIDARG;
601 return(SafeArrayUnlock(psa));
604 /************************************************************************
605 * SafeArrayPtrOfIndex (OLEAUT32.148)
606 * Return a pointer to the element at rgIndices
608 HRESULT WINAPI SafeArrayPtrOfIndex(
609 SAFEARRAY *psa,
610 LONG *rgIndices,
611 void **ppvData)
613 ULONG stepCountInSAData = 0; /* Number of array item to skip to get to
614 the desired one... */
616 if(! validArg(psa))
617 return E_INVALIDARG;
619 if(! validCoordinate(rgIndices, psa))
620 return DISP_E_BADINDEX;
622 /* Although it is dangerous to do this without having a lock, it is not
623 * illegal. Microsoft do warn of the danger.
626 /* Figure out the number of items to skip */
627 stepCountInSAData = calcDisplacement(rgIndices, psa->rgsabound, psa->cDims);
629 *ppvData = (char *) psa->pvData+(stepCountInSAData*psa->cbElements);
631 return S_OK;
634 /************************************************************************
635 * SafeArrayDestroyData (OLEAUT32.39)
636 * Frees the memory data bloc
638 HRESULT WINAPI SafeArrayDestroyData(
639 SAFEARRAY *psa)
641 HRESULT hRes;
642 ULONG ulWholeArraySize; /* count spot in array */
643 ULONG ulDataIter; /* to iterate the data space */
645 if(! validArg(psa))
646 return E_INVALIDARG;
648 if(psa->cLocks > 0)
649 return DISP_E_ARRAYISLOCKED;
651 if(psa->pvData==NULL)
652 return S_OK;
654 ulWholeArraySize = getArraySize(psa);
656 if(isPointer(psa->fFeatures)) { /* release the pointers */
657 IUnknown *punk;
659 for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
660 punk = *(IUnknown**)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
662 if( punk != NULL)
663 IUnknown_Release(punk);
667 else if(psa->fFeatures & FADF_BSTR) { /* deallocate the obj */
668 BSTR bstr;
670 for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
671 bstr = *(BSTR*)((char *) psa->pvData+(ulDataIter*(psa->cbElements)));
673 if( bstr != NULL)
674 SysFreeString( bstr );
677 else if(psa->fFeatures & FADF_VARIANT) { /* deallocate the obj */
679 for(ulDataIter=0; ulDataIter < ulWholeArraySize; ulDataIter++) {
680 VariantClear((VARIANT*)((char *) psa->pvData+(ulDataIter*(psa->cbElements))));
684 /* check if this array is a Vector, in which case do not free the data
685 block since it has been allocated by AllocDescriptor and therefore
686 deserve to be freed by DestroyDescriptor */
687 if(!(psa->fFeatures & FADF_CREATEVECTOR)) { /* Set when we do CreateVector */
689 /* free the whole chunk */
690 if((hRes = HeapFree( GetProcessHeap(), 0, psa->pvData)) == 0) /*failed*/
691 return E_UNEXPECTED; /* UNDOC error condition */
693 psa->pvData = NULL;
696 return S_OK;
699 /************************************************************************
700 * SafeArrayCopyData (OLEAUT32.412)
701 * Copy the psaSource's data block into psaTarget if dimension and size
702 * permits it.
704 HRESULT WINAPI SafeArrayCopyData(
705 SAFEARRAY *psaSource,
706 SAFEARRAY *psaTarget)
708 USHORT cDimCount; /* looper */
709 LONG lDelta; /* looper */
710 IUnknown *punk;
711 ULONG ulWholeArraySize; /* Number of item in SA */
712 BSTR bstr;
714 if(! (validArg(psaSource) && validArg(psaTarget)) )
715 return E_INVALIDARG;
717 if(SafeArrayGetDim(psaSource) != SafeArrayGetDim(psaTarget))
718 return E_INVALIDARG;
720 ulWholeArraySize = getArraySize(psaSource);
722 /* The two arrays boundaries must be of same lenght */
723 for(cDimCount=0;cDimCount < psaSource->cDims; cDimCount++)
724 if( psaSource->rgsabound[cDimCount].cElements !=
725 psaTarget->rgsabound[cDimCount].cElements)
726 return E_INVALIDARG;
728 if( isPointer(psaTarget->fFeatures) ) { /* the target contains ptr
729 that must be released */
730 for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
731 punk = *(IUnknown**)
732 ((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements));
734 if( punk != NULL)
735 IUnknown_Release(punk);
739 else if( psaTarget->fFeatures & FADF_BSTR) { /* the target contain BSTR
740 that must be freed */
741 for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
742 bstr =
743 *(BSTR*)((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements));
745 if( bstr != NULL)
746 SysFreeString( bstr );
749 else if( psaTarget->fFeatures & FADF_VARIANT) {
751 for(lDelta=0;lDelta < ulWholeArraySize; lDelta++) {
752 VariantClear((VARIANT*)((char *) psaTarget->pvData + (lDelta * psaTarget->cbElements)));
756 return duplicateData(psaSource, psaTarget);
759 /************************************************************************
760 * SafeArrayDestroy (OLEAUT32.16)
761 * Deallocates all memory reserved for the SafeArray
763 HRESULT WINAPI SafeArrayDestroy(
764 SAFEARRAY * psa)
766 HRESULT hRes;
768 if(! validArg(psa))
769 return E_INVALIDARG;
771 if(psa->cLocks > 0)
772 return DISP_E_ARRAYISLOCKED;
774 if((hRes = SafeArrayDestroyData( psa )) == S_OK)
775 if((hRes = SafeArrayDestroyDescriptor( psa )) == S_OK)
776 return S_OK;
778 return E_UNEXPECTED; /* UNDOC error condition */
781 /************************************************************************
782 * SafeArrayCopy (OLEAUT32.27)
783 * Make a dupplicate of a SafeArray
785 HRESULT WINAPI SafeArrayCopy(
786 SAFEARRAY *psa,
787 SAFEARRAY **ppsaOut)
789 HRESULT hRes;
790 DWORD dAllocSize;
791 ULONG ulWholeArraySize; /* size of the thing */
793 if(! validArg(psa))
794 return E_INVALIDARG;
796 if((hRes=SafeArrayAllocDescriptor(psa->cDims, ppsaOut)) == S_OK){
798 /* Duplicate the SAFEARRAY struct */
799 memcpy(*ppsaOut, psa,
800 sizeof(*psa)+(sizeof(*(psa->rgsabound))*(psa->cDims-1)));
802 /* If the features that use storage before the SAFEARRAY struct are
803 * enabled, also copy this memory range. Flags have been copied already.
805 if (psa->fFeatures & (FADF_HAVEIID | FADF_HAVEVARTYPE))
806 memcpy(((GUID*)*ppsaOut)-1, ((GUID*)psa)-1, sizeof(GUID));
808 /* Copy the IRecordInfo* reference */
809 if (psa->fFeatures & FADF_RECORD) {
810 IRecordInfo *ri;
812 ri = ((IRecordInfo**)psa)[-1];
813 if (ri) {
814 ((IRecordInfo**)*ppsaOut)[-1] = ri;
815 IRecordInfo_AddRef(ri);
819 (*ppsaOut)->pvData = NULL; /* do not point to the same data area */
821 /* make sure the new safe array doesn't have the FADF_CREATEVECTOR flag,
822 because the data has not been allocated with the descriptor. */
823 (*ppsaOut)->fFeatures &= ~FADF_CREATEVECTOR;
825 /* Get the allocated memory size for source and allocate it for target */
826 ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
827 dAllocSize = ulWholeArraySize*psa->cbElements;
829 (*ppsaOut)->pvData =
830 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dAllocSize);
831 if( (*ppsaOut)->pvData != NULL) { /* HeapAlloc succeed */
833 if( (hRes=duplicateData(psa, *ppsaOut)) != S_OK) { /* E_OUTOFMEMORY */
834 HeapFree(GetProcessHeap(), 0, (*ppsaOut)->pvData);
835 (*ppsaOut)->pvData = NULL;
836 SafeArrayDestroyDescriptor(*ppsaOut);
837 return hRes;
840 } else { /* failed to allocate or dupplicate... */
841 SafeArrayDestroyDescriptor(*ppsaOut);
842 return E_UNEXPECTED; /* UNDOC error condition */
844 } else { /* failed to allocate mem for descriptor */
845 return E_OUTOFMEMORY; /* UNDOC error condiftion */
848 return S_OK;
851 /************************************************************************
852 * SafeArrayCreateVector (OLEAUT32.411)
853 * Creates a one dimension safearray where the data is next to the
854 * SAFEARRAY structure.
856 SAFEARRAY* WINAPI SafeArrayCreateVector(
857 VARTYPE vt,
858 LONG lLbound,
859 ULONG cElements)
861 SAFEARRAY *psa;
862 LPVOID *ptr;
864 /* Validate supported VARTYPE */
865 if ( (vt >= LAST_VARTYPE) ||
866 ( VARTYPE_SIZE[vt] == VARTYPE_NOT_SUPPORTED ) )
867 return NULL;
869 /* Allocate memory for the array descriptor and data contiguously */
870 ptr = HeapAlloc( GetProcessHeap(),
871 HEAP_ZERO_MEMORY,
872 (sizeof(GUID)+sizeof(*psa)+(VARTYPE_SIZE[vt]*cElements)));
873 if (!ptr)
874 return NULL;
875 psa = (SAFEARRAY*)(ptr+sizeof(GUID));
877 /* setup data members... */
878 psa->cDims = 1; /* always and forever */
879 psa->fFeatures = getFeatures(vt) | FADF_CREATEVECTOR; /* undocumented flag used by Microsoft */
880 psa->cLocks = 0;
881 psa->pvData = (BYTE*)psa + sizeof(*psa);
882 psa->cbElements = VARTYPE_SIZE[vt];
884 psa->rgsabound[0].cElements = cElements;
885 psa->rgsabound[0].lLbound = lLbound;
887 return(psa);
890 /************************************************************************
891 * SafeArrayRedim (OLEAUT32.40)
892 * Changes the caracteristics of the last dimension of the SafeArray
894 HRESULT WINAPI SafeArrayRedim(
895 SAFEARRAY *psa,
896 SAFEARRAYBOUND *psaboundNew)
898 LONG lDelta; /* hold difference in size */
899 USHORT cDims=1; /* dims counter */
901 if( !validArg(psa) )
902 return E_INVALIDARG;
904 if( psa->cLocks > 0 )
905 return DISP_E_ARRAYISLOCKED;
907 if( psa->fFeatures & FADF_FIXEDSIZE )
908 return E_INVALIDARG;
910 if( SafeArrayLock(psa)==E_UNEXPECTED )
911 return E_UNEXPECTED;/* UNDOC error condition */
913 /* find the delta in number of array spot to apply to the new array */
914 lDelta = psaboundNew->cElements - psa->rgsabound[0].cElements;
915 for(; cDims < psa->cDims; cDims++)
916 /* delta in number of spot implied by modifying the last dimension */
917 lDelta *= psa->rgsabound[cDims].cElements;
919 TRACE("elements=%ld, Lbound=%ld (delta=%ld)\n", psaboundNew->cElements, psaboundNew->lLbound, lDelta);
921 if (lDelta == 0) { ;/* same size, maybe a change of lLbound, just set it */
923 } else /* need to enlarge (lDelta +) reduce (lDelta -) */
924 if(! resizeSafeArray(psa, lDelta))
925 return E_UNEXPECTED; /* UNDOC error condition */
927 /* the only modifyable dimension sits in [0] as the dimensions were reversed
928 at array creation time... */
929 psa->rgsabound[0].cElements = psaboundNew->cElements;
930 psa->rgsabound[0].lLbound = psaboundNew->lLbound;
932 return SafeArrayUnlock(psa);
935 /************************************************************************
936 * NOT WINDOWS API - SafeArray* Utility functions
937 ************************************************************************/
939 /************************************************************************
940 * Used to validate the SAFEARRAY type of arg
942 static BOOL validArg(
943 SAFEARRAY *psa)
945 SAFEARRAYBOUND *sab;
946 LONG psaSize = 0;
947 LONG descSize = 0;
948 LONG fullSize = 0;
951 * Let's check for the null pointer just in case.
953 if (psa == NULL)
954 return FALSE;
956 /* Check whether the size of the chunk makes sense... That's the only thing
957 I can think of now... */
959 psaSize = HeapSize(GetProcessHeap(), 0, ((IID*)psa)-1);
960 if (psaSize == -1)
961 /* uh, foreign heap. Better don't mess with it ! */
962 return TRUE;
964 /* size of the descriptor when the SA is not created with CreateVector */
965 descSize = sizeof(GUID) + sizeof(*psa) + (sizeof(*sab) * (psa->cDims-1));
967 /* size of the descriptor + data when created with CreateVector */
968 fullSize = sizeof(*psa) + (psa->cbElements * psa->rgsabound[0].cElements);
970 return((psaSize >= descSize) || (psaSize >= fullSize));
973 /************************************************************************
974 * Used to reallocate memory
976 static BOOL resizeSafeArray(
977 SAFEARRAY *psa,
978 LONG lDelta)
980 ULONG ulWholeArraySize; /* use as multiplicator */
981 PVOID pvNewBlock = NULL;
982 IUnknown *punk;
983 BSTR bstr;
985 ulWholeArraySize = getArraySize(psa);
987 if(lDelta < 0) { /* array needs to be shorthen */
988 if( isPointer(psa->fFeatures)) /* ptr that need to be released */
989 for(;lDelta < 0; lDelta++) {
990 punk = *(IUnknown**)
991 ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
993 if( punk != NULL )
994 IUnknown_Release(punk);
997 else if(psa->fFeatures & FADF_BSTR) /* BSTR that need to be freed */
998 for(;lDelta < 0; lDelta++) {
999 bstr = *(BSTR*)
1000 ((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements));
1002 if( bstr != NULL )
1003 SysFreeString( bstr );
1005 else if(psa->fFeatures & FADF_VARIANT)
1006 for(;lDelta < 0; lDelta++) {
1007 VariantClear((VARIANT*)((char *) psa->pvData+((ulWholeArraySize+lDelta)*psa->cbElements)));
1011 if (!(psa->fFeatures & FADF_CREATEVECTOR))
1013 /* Ok now, if we are enlarging the array, we *MUST* move the whole block
1014 pointed to by pvData. If we are shorthening the array, this move is
1015 optional but we do it anyway becuase the benefit is that we are
1016 releasing to the system the unused memory */
1018 if((pvNewBlock = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, psa->pvData,
1019 (ulWholeArraySize + lDelta) * psa->cbElements)) == NULL)
1020 return FALSE; /* TODO If we get here it means:
1021 SHRINK situation : we've deleted the undesired
1022 data and did not release the memory
1023 GROWING situation: we've been unable to grow the array
1026 else
1028 /* Allocate a new block, because the previous data has been allocated with
1029 the descriptor in SafeArrayCreateVector function. */
1031 if((pvNewBlock = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1032 ulWholeArraySize * psa->cbElements)) == NULL)
1033 return FALSE;
1035 psa->fFeatures &= ~FADF_CREATEVECTOR;
1037 /* reassign to the new block of data */
1038 psa->pvData = pvNewBlock;
1039 return TRUE;
1042 /************************************************************************
1043 * Used to set the fFeatures data member of the SAFEARRAY structure.
1045 static INT getFeatures(VARTYPE vt) {
1046 switch (vt) {
1047 case VT_BSTR: return FADF_BSTR;
1048 case VT_UNKNOWN: return FADF_UNKNOWN;
1049 case VT_DISPATCH: return FADF_DISPATCH;
1050 case VT_VARIANT: return FADF_VARIANT;
1052 return 0;
1055 /************************************************************************
1056 * Used to figure out if the fFeatures data member of the SAFEARRAY
1057 * structure contain any information about the type of data stored...
1059 static BOOL isPointer(
1060 USHORT feature)
1062 switch(feature) {
1063 case FADF_UNKNOWN: return TRUE; /* those are pointers */
1064 case FADF_DISPATCH: return TRUE;
1066 return FALSE;
1069 /************************************************************************
1070 * Used to calculate the displacement when accessing or modifying
1071 * safearray data set.
1073 * Parameters: - LONG *coor is the desired location in the multidimension
1074 * table. Ex for a 3 dim table: coor[] = {1,2,3};
1075 * - ULONG *mat is the format of the table. Ex for a 3 dim
1076 * table mat[] = {4,4,4};
1077 * - USHORT dim is the number of dimension of the SafeArray
1079 static ULONG calcDisplacement(
1080 LONG *coor,
1081 SAFEARRAYBOUND *mat,
1082 LONG dim)
1084 ULONG res = 0;
1085 LONG iterDim;
1087 for(iterDim=0; iterDim<dim; iterDim++)
1088 /* the -mat[dim] bring coor[dim] relative to 0 for calculation */
1089 res += ((coor[iterDim]-mat[iterDim].lLbound) *
1090 endOfDim(coor, mat, iterDim+1, dim));
1092 TRACE("SafeArray: calculated displacement is %lu.\n", res);
1093 return(res);
1096 /************************************************************************
1097 * Recursivity agent for calcDisplacement method. Used within Put and
1098 * Get methods.
1100 static INT endOfDim(
1101 LONG *coor,
1102 SAFEARRAYBOUND *mat,
1103 LONG dim,
1104 LONG realDim)
1106 if(dim==realDim)
1107 return 1;
1108 else
1109 return (endOfDim(coor, mat, dim+1, realDim) * mat[dim].cElements);
1113 /************************************************************************
1114 * Method used to validate the coordinate received in Put and Get
1115 * methods.
1117 static BOOL validCoordinate(
1118 LONG *coor,
1119 SAFEARRAY *psa)
1121 INT iter=0;
1122 LONG lUBound;
1123 LONG lLBound;
1124 HRESULT hRes;
1126 if (!psa->cDims) { FIXME("no dims?\n");return FALSE; }
1127 for(; iter<psa->cDims; iter++) {
1128 TRACE("coor[%d]=%ld\n", iter, coor[iter]);
1129 if((hRes = SafeArrayGetLBound(psa, (iter+1), &lLBound)) != S_OK) {
1130 FIXME("No lbound?\n");
1131 return FALSE;
1133 if((hRes = SafeArrayGetUBound(psa, (iter+1), &lUBound)) != S_OK) {
1134 FIXME("No ubound?\n");
1135 return FALSE;
1137 if(lLBound > lUBound) {
1138 FIXME("lbound larger than ubound?\n");
1139 return FALSE;
1142 if((coor[iter] < lLBound) || (coor[iter] > lUBound)) {
1143 FIXME("coordinate %ld not within %ld - %ld\n",coor[iter], lLBound, lUBound);
1144 return FALSE;
1147 return TRUE;
1150 /************************************************************************
1151 * Method used to calculate the number of cells of the SA
1153 static ULONG getArraySize(
1154 SAFEARRAY *psa)
1156 USHORT cCount;
1157 ULONG ulWholeArraySize = 1;
1159 for(cCount=0; cCount < psa->cDims; cCount++) /* foreach dimensions... */
1160 ulWholeArraySize *= psa->rgsabound[cCount].cElements;
1162 return ulWholeArraySize;
1166 /************************************************************************
1167 * Method used to handle data space dupplication for Copy32 and CopyData32
1169 static HRESULT duplicateData(
1170 SAFEARRAY *psa,
1171 SAFEARRAY *ppsaOut)
1173 ULONG ulWholeArraySize; /* size of the thing */
1174 LONG lDelta;
1176 ulWholeArraySize = getArraySize(psa); /* Number of item in SA */
1178 SafeArrayLock(ppsaOut);
1180 if( isPointer(psa->fFeatures) ) { /* If datatype is object increment
1181 object's reference count */
1182 IUnknown *punk;
1184 for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1185 punk = *(IUnknown**)((char *) psa->pvData+(lDelta * psa->cbElements));
1187 if( punk != NULL)
1188 IUnknown_AddRef(punk);
1191 /* Copy the source array data into target array */
1192 memcpy(ppsaOut->pvData, psa->pvData, ulWholeArraySize*psa->cbElements);
1195 else if( psa->fFeatures & FADF_BSTR ) { /* if datatype is BSTR allocate
1196 the BSTR in the new array */
1197 BSTR pbstrReAllocStr = NULL;
1199 for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1200 if(( pbstrReAllocStr = SYSDUPSTRING(
1201 *(BSTR*)((char *) psa->pvData+(lDelta * psa->cbElements)))) == NULL) {
1203 SafeArrayUnlock(ppsaOut);
1204 return E_OUTOFMEMORY;
1207 *((BSTR*)((char *)ppsaOut->pvData+(lDelta * psa->cbElements))) =
1208 pbstrReAllocStr;
1212 else if( psa->fFeatures & FADF_VARIANT ) {
1214 for(lDelta=0; lDelta < ulWholeArraySize; lDelta++) {
1215 VariantCopy((VARIANT*)((char *) ppsaOut->pvData+(lDelta * psa->cbElements)),
1216 (VARIANT*)((char *) psa->pvData+(lDelta * psa->cbElements)));
1219 } else { /* Simply copy the source array data into target array */
1220 memcpy(ppsaOut->pvData, psa->pvData, ulWholeArraySize*psa->cbElements);
1222 SafeArrayUnlock(ppsaOut);
1223 return S_OK;
1227 /************************************************************************
1228 * SafeArrayGetVartype (OLEAUT32.77)
1229 * Returns the VARTYPE stored in the given safearray
1231 HRESULT WINAPI SafeArrayGetVartype(
1232 SAFEARRAY* psa,
1233 VARTYPE* pvt)
1235 if (psa->fFeatures & FADF_HAVEVARTYPE)
1237 /* VT tag @ negative offset 4 in the array descriptor */
1238 *pvt = ((DWORD*)psa)[-1];
1239 return S_OK;
1242 if (psa->fFeatures & FADF_RECORD)
1244 *pvt = VT_RECORD;
1245 return S_OK;
1248 if (psa->fFeatures & FADF_BSTR)
1250 *pvt = VT_BSTR;
1251 return S_OK;
1254 if (psa->fFeatures & FADF_UNKNOWN)
1256 *pvt = VT_UNKNOWN;
1257 return S_OK;
1260 if (psa->fFeatures & FADF_DISPATCH)
1262 *pvt = VT_UNKNOWN; /* Yes, checked against windows */
1263 return S_OK;
1266 if (psa->fFeatures & FADF_VARIANT)
1268 *pvt = VT_VARIANT;
1269 return S_OK;
1271 if (psa->fFeatures & FADF_HAVEIID)
1273 /* We could check the IID here, but Windows apparently does not
1274 * do that and returns VT_UNKNOWN for VT_DISPATCH too.
1276 *pvt = VT_UNKNOWN;
1277 return S_OK;
1280 WARN("No vt found for safearray\n");
1281 return E_INVALIDARG;
1284 /************************************************************************
1285 * SafeArraySetIID (OLEAUT32.57)
1287 HRESULT WINAPI SafeArraySetIID(SAFEARRAY *arr, REFIID riid) {
1288 IID *xiid = ((IID*)arr)-1;
1289 TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
1291 if (!arr || !(arr->fFeatures & FADF_HAVEIID))
1292 return E_INVALIDARG;
1293 memcpy(xiid, riid, sizeof(GUID));
1294 return S_OK;
1297 /************************************************************************
1298 * SafeArrayGetIID (OLEAUT32.67)
1300 HRESULT WINAPI SafeArrayGetIID(SAFEARRAY *arr, IID *riid) {
1301 IID *xiid = ((IID*)arr)-1;
1302 TRACE("(%p, %s).\n",arr,debugstr_guid(riid));
1304 if (!arr || !(arr->fFeatures & FADF_HAVEIID))
1305 return E_INVALIDARG;
1306 memcpy(riid, xiid, sizeof(GUID));
1307 return S_OK;
1310 /************************************************************************
1311 * SafeArraySetRecordInfo (OLEAUT32.44)
1313 HRESULT WINAPI SafeArraySetRecordInfo(SAFEARRAY *arr, IRecordInfo *iface) {
1314 LPRECORDINFO oldiface;
1316 if (!arr || !(arr->fFeatures & FADF_RECORD))
1317 return E_INVALIDARG;
1318 oldiface = ((IRecordInfo**)arr)[-1];
1319 if (oldiface)
1320 IRecordInfo_Release(oldiface);
1321 ((IRecordInfo**)arr)[-1] = iface;
1322 if (iface)
1323 IRecordInfo_AddRef(iface);
1324 return S_OK;
1327 /************************************************************************
1328 * SafeArrayGetRecordInfo (OLEAUT32.45)
1330 HRESULT WINAPI SafeArrayGetRecordInfo(SAFEARRAY *arr, IRecordInfo** iface) {
1331 if (!arr || !(arr->fFeatures & FADF_RECORD))
1332 return E_INVALIDARG;
1333 *iface = ((IRecordInfo**)arr)[-1];
1334 if (*iface)
1335 IRecordInfo_AddRef(*iface);
1336 return S_OK;