//
// © Copyright 1996 National Instruments
// 
// CCWSafeArray encapsulates OLE SafeArrays and Variants holding SafeArrays
// its primary purpose is to aid in constructing parameters to be passed 
// to ComponentWorks(tm) contorls.
//

#include "stdafx.h"
#include "cwsafearray.h"

//-----------------------------------------------------------------------------------
// Constructors that initialize the SafeArray wht an inital VARIANT of SAFEARRAY
CCWSafeArray::CCWSafeArray(VARTYPE vt, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	AllocateSafeArray(vt, lDim1, lDim2, lDim3);
}

//-----------------------------------------------------------------------------------
// Constructors that initialize the SafeArray with an existing buffer of data
CCWSafeArray::CCWSafeArray(unsigned char  *pData, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	InitSafeArrayFromData(VT_UI1, pData, lDim1, lDim2, lDim3);
}

CCWSafeArray::CCWSafeArray(signed char  *pData, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	InitSafeArrayFromData(VT_I1, pData, lDim1, lDim2, lDim3);
}

CCWSafeArray::CCWSafeArray(unsigned short  *pData, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	InitSafeArrayFromData(VT_UI2, pData, lDim1, lDim2, lDim3);
}

CCWSafeArray::CCWSafeArray(short  *pData, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	InitSafeArrayFromData(VT_I2, pData, lDim1, lDim2, lDim3);
}

CCWSafeArray::CCWSafeArray(long  *pData, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	InitSafeArrayFromData(VT_I4, pData, lDim1, lDim2, lDim3);
}

CCWSafeArray::CCWSafeArray(double  *pData, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	InitSafeArrayFromData(VT_R8, pData, lDim1, lDim2, lDim3);
}

CCWSafeArray::CCWSafeArray(float  *pData, long lDim1, long lDim2, long lDim3)
{
	Preconstruct();
	InitSafeArrayFromData(VT_R4, pData, lDim1, lDim2, lDim3);
}

//-----------------------------------------------------------------------------------
void CCWSafeArray::Preconstruct()
{
	m_pSafeArray = NULL;
	VariantInit(&m_vVariant);

	// By default this class owns the data and releases it when the destructor 
	// is called.
	m_bCWSAOwnsData = TRUE;		
}

//-----------------------------------------------------------------------------------
CCWSafeArray::CCWSafeArray(VARIANT vVariant)
{
	Preconstruct();
	if (!(vVariant.vt & VT_ARRAY))
		CheckHRESULT(E_INVALIDARG);

	m_pSafeArray = vVariant.parray;
	m_vVariant = vVariant;
	m_bCWSAOwnsData = FALSE;	
}

//-----------------------------------------------------------------------------------
CCWSafeArray::CCWSafeArray(SAFEARRAY *pSafeArray, VARTYPE vt)
{
	Preconstruct();

	// Configure the VARIANT to point to it as well.
	m_pSafeArray = pSafeArray;

	// Configure the VARIANT to point to it as well.
	m_vVariant.vt = vt | VT_ARRAY;
	m_vVariant.parray = m_pSafeArray;
	m_bCWSAOwnsData = FALSE;	
}

//-----------------------------------------------------------------------------------
void CCWSafeArray::InitSafeArrayFromData(VARTYPE vt, void* pvData, long lDim1, long lDim2, long lDim3)
{
	AllocateSafeArray(vt, lDim1, lDim2, lDim3);
	memcpy(AccessData(), pvData, GetDimSize()*GetEltSize());
	UnaccessData();
}

//-----------------------------------------------------------------------------------
void CCWSafeArray::AllocateSafeArray(VARTYPE vt, long lDim1, long lDim2, long lDim3)
{
	SAFEARRAYBOUND	sab[3];
	long			dims[3];
	long			nDims;

	dims[0] = lDim1;
	dims[1] = lDim2;
	dims[2] = lDim3;

	for (nDims = 0; nDims < 3 && dims[nDims] > 0; nDims++) {
		sab[nDims].cElements = dims[nDims];
		sab[nDims].lLbound = 0;
	}

	m_pSafeArray = SafeArrayCreate(vt, nDims, sab);
	if (!m_pSafeArray)
		CheckHRESULT(E_OUTOFMEMORY);

	// Configure the VARIANT to point to it as well.
	m_vVariant.vt = vt | VT_ARRAY;
	m_vVariant.parray = m_pSafeArray;
	m_bCWSAOwnsData = TRUE;
}

//-----------------------------------------------------------------------------------
CCWSafeArray::~CCWSafeArray()
{
	Clear();
}

//-----------------------------------------------------------------------------------
void CCWSafeArray::Clear()
{
	m_pSafeArray = NULL;
	if (m_bCWSAOwnsData)
		CheckHRESULT(VariantClear(&m_vVariant));
	else
		VariantInit(&m_vVariant);
}


//-----------------------------------------------------------------------------------
// GetDimSize(long) returns the size of one dimesnion. The first dim is 1, not 0.
long CCWSafeArray::GetDimSize(long lDim)
{
	long 	lLowerB, lUpperB;
	HRESULT hr;
	hr = SafeArrayGetLBound(m_pSafeArray, lDim, &lLowerB);
	CheckHRESULT(hr);
	hr = SafeArrayGetUBound(m_pSafeArray, lDim, &lUpperB);
	CheckHRESULT(hr);
	return (lUpperB - lLowerB + 1);
}

// GetDimSize() returns the total number of elements in the array.
long CCWSafeArray::GetDimSize()
{
	long lDimSize;

	lDimSize = 1;
	for (long i = GetNDims(); i>0 ; i--) {
		lDimSize = lDimSize * GetDimSize(i);
	}
	return lDimSize;
}

//-----------------------------------------------------------------------------------
// Simple wraper methods for SAFEARRAY utility funcitons. 
long CCWSafeArray::GetNDims()
{
	return SafeArrayGetDim(m_pSafeArray);
}

long CCWSafeArray::GetEltSize()
{
	return SafeArrayGetElemsize(m_pSafeArray);
}

void* CCWSafeArray::AccessData()
{
	void*	pvData = NULL;
	CheckHRESULT(SafeArrayAccessData(m_pSafeArray, &pvData));
	return pvData;
}

void CCWSafeArray::UnaccessData()
{
	CheckHRESULT(SafeArrayUnaccessData(m_pSafeArray));
}

void CCWSafeArray::Lock()
{
	CheckHRESULT(SafeArrayLock(m_pSafeArray));
}

void CCWSafeArray::Unlock()
{
	CheckHRESULT(SafeArrayUnlock(m_pSafeArray));
}

//-----------------------------------------------------------------------------------
// GetVARIANTTransferOwnership & GetSAFEARRAYTransferOwnership return a the 
// corespopnding handle to the array of data and clears the CCWSafeArray object's
// connecttion to it so the array will not be destroied when the CCWSafeArray is.
//
// Use these when you need to return the SafeArray or Variant this object 
// is holding from a function.

VARIANT CCWSafeArray::GetVARIANTTransferOwnership()
{
	VARIANT var  = m_vVariant;
	m_vVariant.vt = VT_EMPTY;
	m_pSafeArray = NULL;
	return var;
}

SAFEARRAY* CCWSafeArray::GetSAFEARRAYTransferOwnership()
{
	SAFEARRAY* psa = m_pSafeArray;
	m_vVariant.vt   = VT_EMPTY;
	m_pSafeArray   = NULL;
	return psa;
}

//-----------------------------------------------------------------------------------
// CheckHRESULT converts OLE HRESULTS to MFC exceptions. This method should be changed
// if you want to use a different class library.
#ifndef _CW_USE_CWTHROWEXCEPTION
#include "afxdisp.h"
#define CWThrowMemoryException() AfxThrowMemoryException()
#define CWThrowOleException(_hr) AfxThrowOleException(_hr)
#endif

void CCWSafeArray::CheckHRESULT(HRESULT hr)
{
	if (FAILED(hr)) {
		if (E_OUTOFMEMORY == hr)
			CWThrowMemoryException();
		else	
			CWThrowOleException(hr);
	}
}
