624 lines
11 KiB
C++
624 lines
11 KiB
C++
|
// Strings.cpp : Implementation of CStrings
|
||
|
#include "stdafx.h"
|
||
|
#include "DevCon2.h"
|
||
|
#include "xStrings.h"
|
||
|
#include "StringsEnum.h"
|
||
|
#include "utils.h"
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CStrings
|
||
|
|
||
|
CStrings::~CStrings()
|
||
|
{
|
||
|
DWORD c;
|
||
|
if(pMultiStrings) {
|
||
|
for(c=0;c<Count;c++) {
|
||
|
SysFreeString(pMultiStrings[c]);
|
||
|
}
|
||
|
delete [] pMultiStrings;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::get_Count(long *pVal)
|
||
|
{
|
||
|
*pVal = (long)Count;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::Item(VARIANT Index, VARIANT *pVal)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD i;
|
||
|
BSTR ValueCopy = NULL;
|
||
|
if(!IsNoArg(&Index)) {
|
||
|
//
|
||
|
// get single value
|
||
|
//
|
||
|
hr = GetIndex(&Index,&i);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
ValueCopy = SysAllocStringLen(pMultiStrings[i],SysStringLen(pMultiStrings[i]));
|
||
|
if(!ValueCopy) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
VariantInit(pVal);
|
||
|
V_VT(pVal) = VT_BSTR;
|
||
|
V_BSTR(pVal) = ValueCopy;
|
||
|
return S_OK;
|
||
|
}
|
||
|
//
|
||
|
// return collection as an array of strings
|
||
|
//
|
||
|
#if 1
|
||
|
SAFEARRAY *pArray;
|
||
|
SAFEARRAYBOUND bounds[1];
|
||
|
LONG ind[1];
|
||
|
bounds[0].lLbound = 1;
|
||
|
bounds[0].cElements = Count;
|
||
|
VARIANT v;
|
||
|
pArray = SafeArrayCreate(VT_VARIANT,1,bounds);
|
||
|
if(!pArray) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
DWORD c;
|
||
|
VariantInit(&v);
|
||
|
for(c=0;c<Count;c++) {
|
||
|
ind[0] = (LONG)(c+1);
|
||
|
V_VT(&v) = VT_BSTR;
|
||
|
V_BSTR(&v) = pMultiStrings[c];
|
||
|
|
||
|
hr = SafeArrayPutElement(pArray,ind,&v);
|
||
|
if(FAILED(hr)) {
|
||
|
SafeArrayDestroy(pArray);
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
VariantInit(pVal);
|
||
|
V_VT(pVal) = VT_ARRAY | VT_VARIANT;
|
||
|
V_ARRAY(pVal) = pArray;
|
||
|
#else
|
||
|
SAFEARRAY *pArray;
|
||
|
SAFEARRAYBOUND bounds[1];
|
||
|
LONG ind[1];
|
||
|
bounds[0].lLbound = 1;
|
||
|
bounds[0].cElements = Count;
|
||
|
pArray = SafeArrayCreate(VT_BSTR,1,bounds);
|
||
|
if(!pArray) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
DWORD c;
|
||
|
for(c=0;c<Count;c++) {
|
||
|
ind[0] = (LONG)(c+1);
|
||
|
hr = SafeArrayPutElement(pArray,ind,pMultiStrings[c]);
|
||
|
if(FAILED(hr)) {
|
||
|
SafeArrayDestroy(pArray);
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
VariantInit(pVal);
|
||
|
V_VT(pVal) = VT_ARRAY | VT_BSTR;
|
||
|
V_ARRAY(pVal) = pArray;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::get__NewEnum(IUnknown **ppUnk)
|
||
|
{
|
||
|
*ppUnk = NULL;
|
||
|
HRESULT hr;
|
||
|
CComObject<CStringsEnum> *pEnum = NULL;
|
||
|
hr = CComObject<CStringsEnum>::CreateInstance(&pEnum);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
if(!pEnum) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
pEnum->AddRef();
|
||
|
if(!pEnum->CopyStrings(pMultiStrings,Count)) {
|
||
|
pEnum->Release();
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
*ppUnk = pEnum;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStrings::InternalAdd(LPCWSTR Value, UINT len)
|
||
|
{
|
||
|
if(len == (UINT)(-1)) {
|
||
|
len = wcslen(Value);
|
||
|
}
|
||
|
BSTR ValueCopy = NULL;
|
||
|
if(!IncreaseArraySize(1)) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
ValueCopy = SysAllocStringLen(Value,len);
|
||
|
if(!ValueCopy) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
pMultiStrings[Count++] = ValueCopy;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::Add(VARIANT Value)
|
||
|
{
|
||
|
return InternalInsert(Count,&Value);
|
||
|
}
|
||
|
|
||
|
BOOL CStrings::IncreaseArraySize(DWORD strings)
|
||
|
{
|
||
|
BSTR* pNewStrings;
|
||
|
DWORD Inc;
|
||
|
DWORD c;
|
||
|
|
||
|
if((ArraySize-Count)>=strings) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
Inc = ArraySize + strings + 32;
|
||
|
pNewStrings = new BSTR[Inc];
|
||
|
if(!pNewStrings) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
for(c=0;c<Count;c++) {
|
||
|
pNewStrings[c] = pMultiStrings[c];
|
||
|
}
|
||
|
delete [] pMultiStrings;
|
||
|
pMultiStrings = pNewStrings;
|
||
|
ArraySize = Inc;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::Insert(VARIANT Index, VARIANT Value)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD i;
|
||
|
hr = GetIndex(&Index,&i);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
//
|
||
|
// allow i==Count
|
||
|
//
|
||
|
if(i>Count) {
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
return InternalInsert(i,&Value);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::Remove(VARIANT Index)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
DWORD i;
|
||
|
hr = GetIndex(&Index,&i);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
if(i>=Count) {
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
DWORD c;
|
||
|
|
||
|
SysFreeString(pMultiStrings[i]);
|
||
|
Count--;
|
||
|
for(c=i;c<Count;c++) {
|
||
|
pMultiStrings[c] = pMultiStrings[c+1];
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::InternalInsert(DWORD Index, LPVARIANT pVal)
|
||
|
{
|
||
|
CComVariant v;
|
||
|
HRESULT hr;
|
||
|
SAFEARRAY *pArray;
|
||
|
VARTYPE vt;
|
||
|
CComPtr<IEnumVARIANT> pEnum;
|
||
|
|
||
|
if(IsArrayVariant(pVal,&pArray,&vt)) {
|
||
|
return InternalInsertArray(Index,vt,pArray);
|
||
|
}
|
||
|
if(IsCollectionVariant(pVal,&pEnum)) {
|
||
|
return InternalInsertCollection(Index,pEnum);
|
||
|
}
|
||
|
//
|
||
|
// now see if we can treat it as a string
|
||
|
//
|
||
|
hr = v.ChangeType(VT_BSTR,pVal);
|
||
|
if(SUCCEEDED(hr)) {
|
||
|
return InternalInsertString(Index,V_BSTR(&v));
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::InternalInsertArray(DWORD Index, VARTYPE vt, SAFEARRAY *pArray)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
UINT dims = SafeArrayGetDim(pArray);
|
||
|
if(!dims) {
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
long *pDims = new long[dims];
|
||
|
if(!pDims) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// write values into a temporary collection
|
||
|
//
|
||
|
CComObject<CStrings> *pStringTemp = NULL;
|
||
|
hr = CComObject<CStrings>::CreateInstance(&pStringTemp);
|
||
|
if(FAILED(hr)) {
|
||
|
delete [] pDims;
|
||
|
return hr;
|
||
|
}
|
||
|
pStringTemp->AddRef();
|
||
|
|
||
|
hr = InternalInsertArrayDim(pStringTemp,vt,pArray,pDims,0,dims);
|
||
|
delete [] pDims;
|
||
|
if(FAILED(hr)) {
|
||
|
pStringTemp->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
//
|
||
|
// now quickly insert pStringTemp strings into this collection
|
||
|
//
|
||
|
DWORD Added = pStringTemp->Count;
|
||
|
if(!IncreaseArraySize(Added)) {
|
||
|
pStringTemp->Release();
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
DWORD c;
|
||
|
for(c=Count;c>Index;c--) {
|
||
|
pMultiStrings[c-1+Added] = pMultiStrings[c-1];
|
||
|
}
|
||
|
for(c=0;c<Added;c++) {
|
||
|
pMultiStrings[Index+c] = pStringTemp->pMultiStrings[c];
|
||
|
}
|
||
|
Count += Added;
|
||
|
//
|
||
|
// throw strings in temp collection away without free'ing them
|
||
|
//
|
||
|
pStringTemp->Count = 0;
|
||
|
pStringTemp->Release();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::InternalInsertArrayDim(CComObject<CStrings> *pStringTemp, VARTYPE vt, SAFEARRAY *pArray,long *pDims,UINT dim,UINT dims)
|
||
|
{
|
||
|
long lower;
|
||
|
long upper;
|
||
|
long aIndex;
|
||
|
HRESULT hr;
|
||
|
UINT nextdim = dim+1;
|
||
|
|
||
|
hr = SafeArrayGetLBound(pArray,nextdim,&lower);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
hr = SafeArrayGetUBound(pArray,nextdim,&upper);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
if(nextdim<dims) {
|
||
|
for(aIndex=lower;aIndex<=upper;aIndex++) {
|
||
|
pDims[dim] = aIndex;
|
||
|
hr = InternalInsertArrayDim(pStringTemp,vt,pArray,pDims,nextdim,dims);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
if(upper-lower<0) {
|
||
|
return S_OK;
|
||
|
}
|
||
|
//
|
||
|
// write values for this vector
|
||
|
//
|
||
|
UINT elemsize = SafeArrayGetElemsize(pArray);
|
||
|
PBYTE buffer = new BYTE[elemsize];
|
||
|
if(!buffer) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
for(aIndex=lower;aIndex<=upper;aIndex++) {
|
||
|
pDims[dim] = aIndex;
|
||
|
hr = SafeArrayGetElement(pArray,pDims,buffer);
|
||
|
if(FAILED(hr)) {
|
||
|
delete [] buffer;
|
||
|
return hr;
|
||
|
}
|
||
|
VARIANT v;
|
||
|
if(elemsize < sizeof(VARIANT)) {
|
||
|
VariantInit(&v);
|
||
|
V_VT(&v) = vt & ~(VT_ARRAY|VT_VECTOR|VT_BYREF);
|
||
|
memcpy(&V_BYREF(&v),buffer,elemsize);
|
||
|
} else {
|
||
|
memcpy(&v,buffer,sizeof(v));
|
||
|
}
|
||
|
if(V_VT(&v)!=VT_EMPTY) {
|
||
|
//
|
||
|
// only add non-empty items
|
||
|
//
|
||
|
hr = pStringTemp->Add(v);
|
||
|
}
|
||
|
VariantClear(&v);
|
||
|
if(FAILED(hr)) {
|
||
|
delete [] buffer;
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::InternalInsertCollection(DWORD Index, IEnumVARIANT *pEnum)
|
||
|
{
|
||
|
pEnum->Reset();
|
||
|
CComVariant ent;
|
||
|
//
|
||
|
// get first item - this allows us to do little work if
|
||
|
// source collection is empty
|
||
|
//
|
||
|
HRESULT hr = pEnum->Next(1,&ent,NULL);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
if(hr != S_OK) {
|
||
|
//
|
||
|
// empty
|
||
|
//
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
//
|
||
|
// create a temporary collection for working
|
||
|
//
|
||
|
CComObject<CStrings> *pStringTemp = NULL;
|
||
|
hr = CComObject<CStrings>::CreateInstance(&pStringTemp);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
pStringTemp->AddRef();
|
||
|
do {
|
||
|
//
|
||
|
// this will recursively process an element of this collection
|
||
|
//
|
||
|
hr = pStringTemp->Add(ent);
|
||
|
if(FAILED(hr)) {
|
||
|
break;
|
||
|
}
|
||
|
//
|
||
|
// next
|
||
|
//
|
||
|
ent.Clear();
|
||
|
hr = pEnum->Next(1,&ent,NULL);
|
||
|
} while(hr == S_OK);
|
||
|
if(FAILED(hr)) {
|
||
|
pStringTemp->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
//
|
||
|
// now quickly insert pStringTemp strings into this collection
|
||
|
//
|
||
|
DWORD Added = pStringTemp->Count;
|
||
|
if(!IncreaseArraySize(Added)) {
|
||
|
pStringTemp->Release();
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
DWORD c;
|
||
|
for(c=Count;c>Index;c--) {
|
||
|
pMultiStrings[c-1+Added] = pMultiStrings[c-1];
|
||
|
}
|
||
|
for(c=0;c<Added;c++) {
|
||
|
pMultiStrings[Index+c] = pStringTemp->pMultiStrings[c];
|
||
|
}
|
||
|
Count += Added;
|
||
|
//
|
||
|
// throw strings in temp collection away without free'ing them
|
||
|
//
|
||
|
pStringTemp->Count = 0;
|
||
|
pStringTemp->Release();
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::InternalInsertString(DWORD Index, BSTR pString)
|
||
|
{
|
||
|
DWORD c;
|
||
|
BSTR ValueCopy = NULL;
|
||
|
|
||
|
if(Index>Count) {
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
if(!IncreaseArraySize(1)) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
ValueCopy = SysAllocStringLen(pString,SysStringLen(pString));
|
||
|
if(!ValueCopy) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
for(c=Count;c>Index;c--) {
|
||
|
pMultiStrings[c] = pMultiStrings[c-1];
|
||
|
}
|
||
|
pMultiStrings[Index] = ValueCopy;
|
||
|
Count++;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::GetMultiSz(LPWSTR *pResult, DWORD *pSize)
|
||
|
{
|
||
|
//
|
||
|
// get a multi-sz buffer from this list of strings
|
||
|
//
|
||
|
DWORD c;
|
||
|
DWORD buflen = 1;
|
||
|
DWORD actlen = 0;
|
||
|
LPWSTR buffer = NULL;
|
||
|
for(c=0;c<Count;c++) {
|
||
|
//
|
||
|
// estimate buffer size
|
||
|
//
|
||
|
DWORD ellen = SysStringLen(pMultiStrings[c]);
|
||
|
if(ellen) {
|
||
|
buflen += ellen+1;
|
||
|
}
|
||
|
}
|
||
|
buffer = new WCHAR[buflen];
|
||
|
if(!buffer) {
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
for(c=0;c<Count;c++) {
|
||
|
//
|
||
|
// first NULL of string might be inside string
|
||
|
// in such a case, terminate string there
|
||
|
//
|
||
|
DWORD ellen = wcslen(pMultiStrings[c]);
|
||
|
if(ellen == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
memcpy(buffer+actlen,pMultiStrings[c],ellen*sizeof(WCHAR));
|
||
|
actlen += ellen;
|
||
|
buffer[actlen++] = 0;
|
||
|
}
|
||
|
buffer[actlen++] = 0;
|
||
|
*pResult = buffer;
|
||
|
*pSize = actlen;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::FromMultiSz(LPCWSTR pMultiSz)
|
||
|
{
|
||
|
//
|
||
|
// append to list from multi-sz
|
||
|
// usually used with a temporary/new CStrings
|
||
|
//
|
||
|
DWORD len = 0;
|
||
|
HRESULT hr;
|
||
|
for(;*pMultiSz;pMultiSz+=len+1) {
|
||
|
len = wcslen(pMultiSz);
|
||
|
hr = InternalAdd(pMultiSz,len);
|
||
|
if(FAILED(hr)) {
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStrings::GetIndex(VARIANT *Index, DWORD *pAt)
|
||
|
{
|
||
|
CComVariant v;
|
||
|
HRESULT hr;
|
||
|
if(IsNumericVariant(Index)) {
|
||
|
hr = v.ChangeType(VT_I4,Index);
|
||
|
if(FAILED(hr)) {
|
||
|
return DISP_E_TYPEMISMATCH;
|
||
|
}
|
||
|
if(V_I4(&v)<1) {
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
*pAt = ((DWORD)V_I4(&v))-1;
|
||
|
return S_OK;
|
||
|
}
|
||
|
//
|
||
|
// user actually supplied instance id
|
||
|
//
|
||
|
hr = v.ChangeType(VT_BSTR,Index);
|
||
|
if(FAILED(hr)) {
|
||
|
return DISP_E_TYPEMISMATCH;
|
||
|
}
|
||
|
if(!Count) {
|
||
|
//
|
||
|
// cannot match anything
|
||
|
//
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
//
|
||
|
// find an existing device that matches this
|
||
|
//
|
||
|
DWORD c;
|
||
|
for(c=0;c<Count;c++) {
|
||
|
if(wcscmp(pMultiStrings[c],V_BSTR(&v))==0) {
|
||
|
*pAt = c;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// none found, run through again ignoring case
|
||
|
//
|
||
|
if(!IsCaseSensative) {
|
||
|
for(c=0;c<Count;c++) {
|
||
|
if(_wcsicmp(pMultiStrings[c],V_BSTR(&v))==0) {
|
||
|
*pAt = c;
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// still none found
|
||
|
//
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
BOOL CStrings::InternalEnum(DWORD index,BSTR *pNext)
|
||
|
{
|
||
|
//
|
||
|
// note that pNext returned is not a unique string
|
||
|
//
|
||
|
if(index>=Count) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
*pNext = pMultiStrings[index];
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::Find(BSTR name, long *pFound)
|
||
|
{
|
||
|
//
|
||
|
// find an existing device that matches this
|
||
|
//
|
||
|
DWORD c;
|
||
|
for(c=0;c<Count;c++) {
|
||
|
if(wcscmp(pMultiStrings[c],name)==0) {
|
||
|
*pFound = (long)(c+1);
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// none found, run through again ignoring case
|
||
|
//
|
||
|
if(!IsCaseSensative) {
|
||
|
for(c=0;c<Count;c++) {
|
||
|
if(_wcsicmp(pMultiStrings[c],name)==0) {
|
||
|
*pFound = (long)(c+1);
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pFound = 0;
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::get_CaseSensative(VARIANT_BOOL *pVal)
|
||
|
{
|
||
|
*pVal = IsCaseSensative ? VARIANT_TRUE : VARIANT_FALSE;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStrings::put_CaseSensative(VARIANT_BOOL newVal)
|
||
|
{
|
||
|
IsCaseSensative = newVal ? VARIANT_TRUE : VARIANT_FALSE;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|