⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 safearray.c

📁 ReactOS是一些高手根据Windows XP的内核编写出的类XP。内核实现机理和API函数调用几乎相同。甚至可以兼容XP的程序。喜欢研究系统内核的人可以看一看。
💻 C
📖 第 1 页 / 共 3 页
字号:
/*************************************************************************
 * OLE Automation - SafeArray
 *
 * This file contains the implementation of the SafeArray functions.
 *
 * Copyright 1999 Sylvain St-Germain
 * Copyright 2002-2003 Marcus Meissner
 * Copyright 2003 Jon Griffiths
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */
/* Memory Layout of a SafeArray:
 *
 * -0x10: start of memory.
 * -0x10: GUID for VT_DISPATCH and VT_UNKNOWN safearrays (if FADF_HAVEIID)
 * -0x04: DWORD varianttype; (for all others, except VT_RECORD) (if FADF_HAVEVARTYPE)
 *  -0x4: IRecordInfo* iface;  (if FADF_RECORD, for VT_RECORD (can be NULL))
 *  0x00: SAFEARRAY,
 *  0x10: SAFEARRAYBOUNDS[0...]
 */

#include "config.h"

#include <string.h>
#include <stdarg.h>
#include <stdio.h>

#define COBJMACROS

#include "windef.h"
#include "winerror.h"
#include "winbase.h"
#include "variant.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(variant);

/************************************************************************
 * SafeArray {OLEAUT32}
 *
 * NOTES
 * The SafeArray data type provides the underlying interface for Ole
 * Automations arrays, used for example to represent array types in
 * Visual Basic(tm) and to gather user defined parameters for invocation through
 * an IDispatch interface.
 *
 * Safe arrays provide bounds checking and automatically manage the data
 * types they contain, for example handing reference counting and copying
 * of interface pointers. User defined types can be stored in arrays
 * using the IRecordInfo interface.
 *
 * There are two types of SafeArray, normal and vectors. Normal arrays can have
 * multiple dimensions and the data for the array is allocated separately from
 * the array header. This is the most flexible type of array. Vectors, on the
 * other hand, are fixed in size and consist of a single allocated block, and a
 * single dimension.
 *
 * DATATYPES
 * The following types of data can be stored within a SafeArray.
 * Numeric:
 *|  VT_I1, VT_UI1, VT_I2, VT_UI2, VT_I4, VT_UI4, VT_I8, VT_UI8, VT_INT, VT_UINT,
 *|  VT_R4, VT_R8, VT_CY, VT_DECIMAL
 * Interfaces:
 *|  VT_DISPATCH, VT_UNKNOWN, VT_RECORD
 * Other:
 *|  VT_VARIANT, VT_INT_PTR, VT_UINT_PTR, VT_BOOL, VT_ERROR, VT_DATE, VT_BSTR
 *
 * FUNCTIONS
 *  BstrFromVector()
 *  VectorFromBstr()
 */

/* Undocumented hidden space before the start of a SafeArray descriptor */
#define SAFEARRAY_HIDDEN_SIZE sizeof(GUID)

/* Allocate memory */
static inline LPVOID SAFEARRAY_Malloc(ULONG ulSize)
{
  /* FIXME: Memory should be allocated and freed using a per-thread IMalloc
   *        instance returned from CoGetMalloc().
   */
  return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulSize);
}

/* Free memory */
static inline BOOL SAFEARRAY_Free(LPVOID lpData)
{
  return HeapFree(GetProcessHeap(), 0, lpData);
}

/* Get the size of a supported VT type (0 means unsupported) */
static DWORD SAFEARRAY_GetVTSize(VARTYPE vt)
{
  switch (vt)
  {
    case VT_I1:
    case VT_UI1:      return sizeof(BYTE);
    case VT_BOOL:
    case VT_I2:
    case VT_UI2:      return sizeof(SHORT);
    case VT_I4:
    case VT_UI4:
    case VT_R4:
    case VT_ERROR:    return sizeof(LONG);
    case VT_R8:
    case VT_I8:
    case VT_UI8:      return sizeof(LONG64);
    case VT_INT:
    case VT_UINT:     return sizeof(INT);
    case VT_INT_PTR:
    case VT_UINT_PTR: return sizeof(UINT_PTR);
    case VT_CY:       return sizeof(CY);
    case VT_DATE:     return sizeof(DATE);
    case VT_BSTR:     return sizeof(BSTR);
    case VT_DISPATCH: return sizeof(LPDISPATCH);
    case VT_VARIANT:  return sizeof(VARIANT);
    case VT_UNKNOWN:  return sizeof(LPUNKNOWN);
    case VT_DECIMAL:  return sizeof(DECIMAL);
    /* Note: Return a non-zero size to indicate vt is valid. The actual size
     * of a UDT is taken from the result of IRecordInfo_GetSize().
     */
    case VT_RECORD:   return 32;
  }
  return 0;
}

/* Set the hidden data for an array */
static inline void SAFEARRAY_SetHiddenDWORD(SAFEARRAY* psa, DWORD dw)
{
  /* Implementation data is stored in the 4 bytes before the header */
  LPDWORD lpDw = (LPDWORD)psa;
  lpDw[-1] = dw;
}

/* Get the hidden data from an array */
static inline DWORD SAFEARRAY_GetHiddenDWORD(SAFEARRAY* psa)
{
  LPDWORD lpDw = (LPDWORD)psa;
  return lpDw[-1];
}

/* Get the number of cells in a SafeArray */
static ULONG SAFEARRAY_GetCellCount(const SAFEARRAY *psa)
{
  const SAFEARRAYBOUND* psab = psa->rgsabound;
  USHORT cCount = psa->cDims;
  ULONG ulNumCells = 1;

  while (cCount--)
  {
    /* This is a valid bordercase. See testcases. -Marcus */
    if (!psab->cElements)
      return 0;
    ulNumCells *= psab->cElements;
    psab++;
  }
  return ulNumCells;
}

/* Allocate a descriptor for an array */
static HRESULT SAFEARRAY_AllocDescriptor(ULONG ulSize, SAFEARRAY **ppsaOut)
{
  *ppsaOut = (SAFEARRAY*)((char*)SAFEARRAY_Malloc(ulSize + SAFEARRAY_HIDDEN_SIZE) + SAFEARRAY_HIDDEN_SIZE);

  if (!*ppsaOut)
    return E_UNEXPECTED;

  return S_OK;
}

/* Set the features of an array */
static void SAFEARRAY_SetFeatures(VARTYPE vt, SAFEARRAY *psa)
{
  /* Set the IID if we have one, otherwise set the type */
  if (vt == VT_DISPATCH)
  {
    psa->fFeatures = FADF_HAVEIID;
    SafeArraySetIID(psa, &IID_IDispatch);
  }
  else if (vt == VT_UNKNOWN)
  {
    psa->fFeatures = FADF_HAVEIID;
    SafeArraySetIID(psa, &IID_IUnknown);
  }
  else if (vt == VT_RECORD)
    psa->fFeatures = FADF_RECORD;
  else
  {
    psa->fFeatures = FADF_HAVEVARTYPE;
    SAFEARRAY_SetHiddenDWORD(psa, vt);
  }
}

/* Create an array */
static SAFEARRAY* SAFEARRAY_Create(VARTYPE vt, UINT cDims, const SAFEARRAYBOUND *rgsabound, ULONG ulSize)
{
  SAFEARRAY *psa = NULL;
  int i;

  if (!rgsabound)
    return NULL;

  if (SUCCEEDED(SafeArrayAllocDescriptorEx(vt, cDims, &psa)))
  {
    switch (vt)
    {
      case VT_BSTR:     psa->fFeatures |= FADF_BSTR; break;
      case VT_UNKNOWN:  psa->fFeatures |= FADF_UNKNOWN; break;
      case VT_DISPATCH: psa->fFeatures |= FADF_DISPATCH; break;
      case VT_VARIANT:  psa->fFeatures |= FADF_VARIANT; break;
    }

    for (i = 0; i < cDims; i++)
      memcpy(psa->rgsabound + i, rgsabound + cDims - 1 - i, sizeof(SAFEARRAYBOUND));

    if (ulSize)
      psa->cbElements = ulSize;

    if (!psa->cbElements || FAILED(SafeArrayAllocData(psa)))
    {
      SafeArrayDestroyDescriptor(psa);
      psa = NULL;
    }
  }
  return psa;
}

/* Create an array as a vector */
static SAFEARRAY* SAFEARRAY_CreateVector(VARTYPE vt, LONG lLbound, ULONG cElements, ULONG ulSize)
{
  SAFEARRAY *psa = NULL;

  if (ulSize || (vt == VT_RECORD))
  {
    /* Allocate the header and data together */
    if (SUCCEEDED(SAFEARRAY_AllocDescriptor(sizeof(SAFEARRAY) + ulSize * cElements, &psa)))
    {
      SAFEARRAY_SetFeatures(vt, psa);

      psa->cDims = 1;
      psa->fFeatures |= FADF_CREATEVECTOR;
      psa->pvData = &psa[1]; /* Data follows the header */
      psa->cbElements = ulSize;
      psa->rgsabound[0].cElements = cElements;
      psa->rgsabound[0].lLbound = lLbound;

      switch (vt)
      {
        case VT_BSTR:     psa->fFeatures |= FADF_BSTR; break;
        case VT_UNKNOWN:  psa->fFeatures |= FADF_UNKNOWN; break;
        case VT_DISPATCH: psa->fFeatures |= FADF_DISPATCH; break;
        case VT_VARIANT:  psa->fFeatures |= FADF_VARIANT; break;
      }
    }
  }
  return psa;
}

/* Free data items in an array */
static HRESULT SAFEARRAY_DestroyData(SAFEARRAY *psa, ULONG ulStartCell)
{
  if (psa->pvData && !(psa->fFeatures & FADF_DATADELETED))
  {
    ULONG ulCellCount = SAFEARRAY_GetCellCount(psa);

    if (ulStartCell > ulCellCount) {
      FIXME("unexpted ulcellcount %d, start %d\n",ulCellCount,ulStartCell);
      return E_UNEXPECTED;
    }

    ulCellCount -= ulStartCell;

    if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH))
    {
      LPUNKNOWN *lpUnknown = (LPUNKNOWN *)psa->pvData + ulStartCell;

      while(ulCellCount--)
      {
        if (*lpUnknown)
          IUnknown_Release(*lpUnknown);
        lpUnknown++;
      }
    }
    else if (psa->fFeatures & (FADF_RECORD))
    {
      IRecordInfo *lpRecInfo;

      if (SUCCEEDED(SafeArrayGetRecordInfo(psa, &lpRecInfo)))
      {
        PBYTE pRecordData = (PBYTE)psa->pvData;
        while(ulCellCount--)
        {
          IRecordInfo_RecordClear(lpRecInfo, pRecordData);
          pRecordData += psa->cbElements;
        }
        IRecordInfo_Release(lpRecInfo);
      }
    }
    else if (psa->fFeatures & FADF_BSTR)
    {
      BSTR* lpBstr = (BSTR*)psa->pvData + ulStartCell;

      while(ulCellCount--)
      {
        if (*lpBstr)
          SysFreeString(*lpBstr);
        lpBstr++;
      }
    }
    else if (psa->fFeatures & FADF_VARIANT)
    {
      VARIANT* lpVariant = (VARIANT*)psa->pvData + ulStartCell;

      while(ulCellCount--)
      {
        HRESULT hRet = VariantClear(lpVariant);

        if (FAILED(hRet)) FIXME("VariantClear of element failed!\n");
        lpVariant++;
      }
    }
  }
  return S_OK;
}

/* Copy data items from one array to another */
static HRESULT SAFEARRAY_CopyData(SAFEARRAY *psa, SAFEARRAY *dest)
{
  if (!psa->pvData)
    return S_OK;
  else if (!dest->pvData || psa->fFeatures & FADF_DATADELETED)
    return E_INVALIDARG;
  else
  {
    ULONG ulCellCount = SAFEARRAY_GetCellCount(psa);

    dest->fFeatures = (dest->fFeatures & FADF_CREATEVECTOR) |
                      (psa->fFeatures & ~(FADF_CREATEVECTOR|FADF_DATADELETED));

    if (psa->fFeatures & FADF_VARIANT)
    {
      VARIANT* lpVariant = (VARIANT*)psa->pvData;
      VARIANT* lpDest = (VARIANT*)dest->pvData;

      while(ulCellCount--)
      {
        HRESULT hRet;

        hRet = VariantCopy(lpDest, lpVariant);
        if (FAILED(hRet)) FIXME("VariantCopy failed with 0x%x\n", hRet);
        lpVariant++;
        lpDest++;
      }
    }
    else if (psa->fFeatures & FADF_BSTR)
    {
      BSTR* lpBstr = (BSTR*)psa->pvData;
      BSTR* lpDest = (BSTR*)dest->pvData;

      while(ulCellCount--)
      {
        if (*lpBstr)
        {
          *lpDest = SysAllocStringByteLen((char*)*lpBstr, SysStringByteLen(*lpBstr));
          if (!*lpDest)
            return E_OUTOFMEMORY;
        }
        else
          *lpDest = NULL;
        lpBstr++;
        lpDest++;
      }
    }
    else
    {
      /* Copy the data over */
      memcpy(dest->pvData, psa->pvData, ulCellCount * psa->cbElements);

      if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH))
      {
        LPUNKNOWN *lpUnknown = (LPUNKNOWN *)dest->pvData;

        while(ulCellCount--)
        {
          if (*lpUnknown)
            IUnknown_AddRef(*lpUnknown);
          lpUnknown++;
        }
      }
    }

    if (psa->fFeatures & FADF_RECORD)
    {
      IRecordInfo* pRecInfo = NULL;

      SafeArrayGetRecordInfo(psa, &pRecInfo);
      SafeArraySetRecordInfo(dest, pRecInfo);

      if (pRecInfo)
      {
        /* Release because Get() adds a reference */
        IRecordInfo_Release(pRecInfo);
      }
    }
    else if (psa->fFeatures & FADF_HAVEIID)
    {
      GUID guid;
      SafeArrayGetIID(psa, &guid);
      SafeArraySetIID(dest, &guid);
    }
    else if (psa->fFeatures & FADF_HAVEVARTYPE)
    {
      SAFEARRAY_SetHiddenDWORD(dest, SAFEARRAY_GetHiddenDWORD(psa));
    }
  }
  return S_OK;
}

/*************************************************************************
 *		SafeArrayAllocDescriptor (OLEAUT32.36)
 *
 * Allocate and initialise a descriptor for a SafeArray.
 *
 * PARAMS
 *  cDims   [I] Number of dimensions of the array
 *  ppsaOut [O] Destination for new descriptor
 *
 * RETURNS
 * Success: S_OK. ppsaOut is filled with a newly allocated descriptor.
 * Failure: An HRESULT error code indicating the error.
 *
 * NOTES
 * See SafeArray.
 */
HRESULT WINAPI SafeArrayAllocDescriptor(UINT cDims, SAFEARRAY **ppsaOut)
{
  LONG allocSize;

  TRACE("(%d,%p)\n", cDims, ppsaOut);
  
  if (!cDims || cDims >= 0x10000) /* Maximum 65535 dimensions */
    return E_INVALIDARG;

  if (!ppsaOut)
    return E_POINTER;

  /* We need enough space for the header and its bounds */
  allocSize = sizeof(SAFEARRAY) + sizeof(SAFEARRAYBOUND) * (cDims - 1);

  if (FAILED(SAFEARRAY_AllocDescriptor(allocSize, ppsaOut)))
    return E_UNEXPECTED;

  (*ppsaOut)->cDims = cDims;

  TRACE("(%d): %u bytes allocated for descriptor.\n", cDims, allocSize);
  return S_OK;
}

/*************************************************************************
 *		SafeArrayAllocDescriptorEx (OLEAUT32.41)
 *
 * Allocate and initialise a descriptor for a SafeArray of a given type.
 *
 * PARAMS
 *  vt      [I] The type of items to store in the array
 *  cDims   [I] Number of dimensions of the array
 *  ppsaOut [O] Destination for new descriptor
 *
 * RETURNS
 *  Success: S_OK. ppsaOut is filled with a newly allocated descriptor.
 *  Failure: An HRESULT error code indicating the error.
 *
 * NOTES
 *  - This function does not chack that vt is an allowed VARTYPE.
 *  - Unlike SafeArrayAllocDescriptor(), vt is associated with the array.
 *  See SafeArray.
 */
HRESULT WINAPI SafeArrayAllocDescriptorEx(VARTYPE vt, UINT cDims, SAFEARRAY **ppsaOut)
{
  ULONG cbElements;
  HRESULT hRet = E_UNEXPECTED;

  TRACE("(%d->%s,%d,%p)\n", vt, debugstr_vt(vt), cDims, ppsaOut);
    
  cbElements = SAFEARRAY_GetVTSize(vt);
  if (!cbElements)
    WARN("Creating a descriptor with an invalid VARTYPE!\n");

  hRet = SafeArrayAllocDescriptor(cDims, ppsaOut);

  if (SUCCEEDED(hRet))
  {
    SAFEARRAY_SetFeatures(vt, *ppsaOut);
    (*ppsaOut)->cbElements = cbElements;
  }
  return hRet;
}

/*************************************************************************
 *		SafeArrayAllocData (OLEAUT32.37)
 *
 * Allocate the data area of a SafeArray.
 *
 * PARAMS
 *  psa [I] SafeArray to allocate the data area of.
 *
 * RETURNS
 *  Success: S_OK. The data area is allocated and initialised.
 *  Failure: An HRESULT error code indicating the error.
 *
 * NOTES
 *  See SafeArray.
 */
HRESULT WINAPI SafeArrayAllocData(SAFEARRAY *psa)
{
  HRESULT hRet = E_INVALIDARG;
  
  TRACE("(%p)\n", psa);
  
  if (psa)
  {
    ULONG ulSize = SAFEARRAY_GetCellCount(psa);

    psa->pvData = SAFEARRAY_Malloc(ulSize * psa->cbElements);

    if (psa->pvData)
    {
      hRet = S_OK;
      TRACE("%u bytes allocated for data at %p (%u objects).\n",
            ulSize * psa->cbElements, psa->pvData, ulSize);
    }
    else
      hRet = E_OUTOFMEMORY;
  }
  return hRet;
}

/*************************************************************************
 *		SafeArrayCreate (OLEAUT32.15)
 *
 * Create a new SafeArray.
 *
 * PARAMS
 *  vt        [I] Type to store in the safe array
 *  cDims     [I] Number of array dimensions
 *  rgsabound [I] Bounds of the array dimensions
 *
 * RETURNS
 *  Success: A pointer to a new array object.
 *  Failure: NULL, if any parameter is invalid or memory allocation fails.
 *
 * NOTES
 *  Win32 allows arrays with 0 sized dimensions. This bug is not reproduced
 *  in the Wine implementation.
 *  See SafeArray.
 */
SAFEARRAY* WINAPI SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND *rgsabound)
{
  TRACE("(%d->%s,%d,%p)\n", vt, debugstr_vt(vt), cDims, rgsabound);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -