/******************************************************************************* * * (C) COPYRIGHT MICROSOFT CORP., 1998 * * TITLE: IPropItm.Cpp * * VERSION: 2.0 * * AUTHOR: ReedB * * DATE: 19 Feb, 1998 * * DESCRIPTION: * Implementation of WIA item class server properties. * *******************************************************************************/ #include "precomp.h" #include "stiexe.h" #include #include // #include #include "wiapsc.h" #include "helpers.h" // // Strings used to access the registry. REGSTR_* string constants can be // found in sdk\inc\regstr.h // TCHAR g_szREGSTR_PATH_WIA[] = REGSTR_PATH_SETUP TEXT("\\WIA"); /******************************************************************************* * * ReadMultiple * WriteMultiple * ReadPropertyNames * Enum * GetPropertyAttributes * GetCount * * DESCRIPTION: * IWiaPropertyStorage methods. * * PARAMETERS: * *******************************************************************************/ /**************************************************************************\ * CWiaItem::ReadMultiple * * This method reads the specified number of properties from the item's * current value property storage. This method conforms to that standard * OLE IPropertyStorage::ReadMultiple method. * * Arguments: * * cpspec - Number of properties to read. * rgpspec - Array of PropSpec's specifying which properties * are to be read. * rgpropvar - Array where the property values will be copied * to. * * Arguments: * * cpspec * rgpspec * rgpropvar * * Return Value: * * status * * History: * * 9/3/1998 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaItem::ReadMultiple( ULONG cpspec, const PROPSPEC __RPC_FAR rgpspec[], PROPVARIANT __RPC_FAR rgpropvar[]) { DBG_FN(CWiaItem::ReadMultiple); HRESULT hr; LONG lFlags = 0; // // Corresponding driver item must be valid to talk with hardware. // hr = ValidateWiaDrvItemAccess(m_pWiaDrvItem); if (FAILED(hr)) { return hr; } // // rgpropvar must be valid // if (IsBadWritePtr(rgpropvar, sizeof(PROPVARIANT) * cpspec)) { DBG_ERR(("CWiaItem::ReadMultiple, last parameter (rgpropvar) is invalid")); return E_INVALIDARG; } // // Check whether item properties have been initialized // if (!m_bInitialized) { // // Check whether the properties being read are the WIA Managed properties. // If they are, there is still no need to initialize the item. // if (AreWiaInitializedProps(cpspec, (PROPSPEC*) rgpspec)) { return (m_pPropStg->CurStg())->ReadMultiple(cpspec, rgpspec, rgpropvar); } hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::ReadMultiple, InitLazyProps failed")); return hr; } } // // Check whether the properties requested are all cacheable // hr = (m_pPropStg->AccessStg())->ReadMultiple(cpspec, rgpspec, rgpropvar); if (FAILED(hr)) { ReportReadWriteMultipleError(hr, "CWiaItem::ReadMultiple", NULL, TRUE, cpspec, rgpspec); // // Property attributes are not required absolutely, continue without it. // } else { for (ULONG i = 0; i < cpspec; i++) { // // The client requests a property not read yet or non-cacheable // if ((rgpropvar[i].vt == VT_UI4) && (! (rgpropvar[i].lVal & WIA_PROP_CACHEABLE))) { break; } } // // Clear the access flags from the rgpropvar // FreePropVariantArray(cpspec, rgpropvar); // // If all the properties are cacheable, then take the quick path // if (i == cpspec) { hr = (m_pPropStg->CurStg())->ReadMultiple(cpspec, rgpspec, rgpropvar); if (hr == S_OK) { // // Check whether all the properties are retrieved correctly // some properties might not have been read from the storage // for (ULONG i = 0; i < cpspec; i++) { if (rgpropvar[i].vt == VT_EMPTY) { break; } } if (i == cpspec) { // // All the properties requested are found in cache // return (hr); } else { FreePropVariantArray(cpspec, rgpropvar); } } } } if (SUCCEEDED(hr)) { // // Make sure all PropSpecs are using PropID's. This is so that // drivers only have to deal with PropID's. If some of the // PropSpecs are using string names, then convert them. // PROPSPEC *pPropSpec = NULL; hr = m_pPropStg->NamesToPropIDs(cpspec, (PROPSPEC*) rgpspec, &pPropSpec); if (SUCCEEDED(hr)) { // // Give device mini driver a chance to update the device properties. // { LOCK_WIA_DEVICE _LWD(this, &hr); if(SUCCEEDED(hr)) { hr = m_pActiveDevice->m_DrvWrapper.WIA_drvReadItemProperties((BYTE*)this, lFlags, cpspec, (pPropSpec ? pPropSpec : rgpspec), &m_lLastDevErrVal); } } if (pPropSpec) { LocalFree(pPropSpec); pPropSpec = NULL; } } if (SUCCEEDED(hr)) { hr = (m_pPropStg->CurStg())->ReadMultiple(cpspec, rgpspec, rgpropvar); if (FAILED(hr)) { ReportReadWriteMultipleError(hr, "CWiaItem::ReadMultiple", NULL, TRUE, cpspec, rgpspec); } } } return hr; } /**************************************************************************\ * CWiaItem::ReadPropertyNames * * Returns the string name of the specified properties if they exist. * This conforms to the standard OLE IPropertyStorage::ReadPropertyNames * method. * * Arguments: * * pstmProp - Pointer to property stream. * * Return Value: * * Status * * History: * * 9/3/1998 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaItem::ReadPropertyNames( ULONG cpropid, const PROPID __RPC_FAR rgpropid[], LPOLESTR __RPC_FAR rglpwstrName[]) { DBG_FN(CWiaItem::ReadPropertyNames); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::ReadPropertyNames, InitLazyProps failed")); return hr; } } return (m_pPropStg->CurStg())->ReadPropertyNames(cpropid,rgpropid,rglpwstrName); } /**************************************************************************\ * CWiaItem::WritePropertyNames * * Returns the string name of the specified properties if they exist. * This conforms to the standard OLE IPropertyStorage::ReadPropertyNames * method. * * Arguments: * * pstmProp - Pointer to property stream. * * Return Value: * * Status * * History: * * 9/3/1998 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaItem::WritePropertyNames( ULONG cpropid, const PROPID rgpropid[], const LPOLESTR rglpwstrName[]) { DBG_FN(CWiaItem::WritePropertyNames); PROPVARIANT *pv; PROPSPEC *pspec; ULONG index; HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::WritePropertyNames, InitLazyProps failed")); return hr; } } pv = (PROPVARIANT*) LocalAlloc(LPTR, sizeof(PROPVARIANT) * cpropid); if (!pv) { DBG_ERR(("CWiaItem::WritePropertyNames, Out of memory")); return E_OUTOFMEMORY; } pspec = (PROPSPEC*) LocalAlloc(LPTR, sizeof(PROPSPEC) * cpropid); if (!pspec) { DBG_ERR(("CWiaItem::WritePropertyNames, Out of memory")); LocalFree(pv); return E_OUTOFMEMORY; } // // Put PROPIDs into the PROPSPEC array. // for (index = 0; index < cpropid; index++) { pspec[index].ulKind = PRSPEC_PROPID; pspec[index].propid = rgpropid[index]; } hr = (m_pPropStg->AccessStg())->ReadMultiple(cpropid, pspec, pv); if (SUCCEEDED(hr)) { // // Make sure the properties are App. written properties. If a valid // access flag for a property exists, then it was written by the // driver and not the App, so exit. // for (index = 0; index < cpropid; index++) { if (pv[index].vt != VT_EMPTY) { DBG_ERR(("CWiaItem::WritePropertyNames, not allowed to write prop: %d.",rgpropid[index])); hr = E_ACCESSDENIED; break; } } if (SUCCEEDED(hr)) { hr = (m_pPropStg->CurStg())->WritePropertyNames(cpropid, rgpropid, rglpwstrName); if (FAILED(hr)) { DBG_ERR(("CWiaItem::WritePropertyNames, WritePropertyNames failed")); } } } else { DBG_ERR(("CWiaItem::WritePropertyNames, Reading Access values failed")); } LocalFree(pspec); LocalFree(pv); return hr; } /**************************************************************************\ * CWiaItem::Enum * * Returns a IEnumSTATPROPSTG enumerator over the current value property * storage. Conforms to the standard OLE IPRopertyStorage::Enum method. * * Arguments: * * pstmProp - Pointer to property stream. * * Return Value: * * Status * * History: * * 9/3/1998 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaItem::Enum( IEnumSTATPROPSTG __RPC_FAR *__RPC_FAR *ppenum) { DBG_FN(CWiaItem::Enum); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::Enum, InitLazyProps failed")); return hr; } } return (m_pPropStg->CurStg())->Enum(ppenum); } /**************************************************************************\ * CWiaItem::WriteMultiple * * This method writes the specified number of properties into the item's * property storage. Validation will be performed on those property * values. The properties will be restored to their old (valid) values * if validation fails. * * Arguments: * * cpspec - Number of properties to write. * rgpspec - Array of PropSpec's specifying which properties * are to be written. * rgpropvar - Array containing values that the properties * will be set to. * propidNameFirst - Minimum value for property identifiers when * they don't exist and must be allocated. * * Return Value: * * Status - S_OK if writes and validation succeeded. * E_INVALIDARG if validation failed due to an * incorrect property value. * Other error returns are from * ValidateWiaDrvItemAccess, CheckPropertyAccess, * CreatePropertyStorage and CopyProperties. * * History: * * 9/3/1998 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaItem::WriteMultiple( ULONG cpspec, const PROPSPEC __RPC_FAR rgpspec[], const PROPVARIANT __RPC_FAR rgpropvar[], PROPID propidNameFirst) { DBG_FN(CWiaItem::WriteMultiple); HRESULT hr; // // Corresponding driver item must be valid. // hr = ValidateWiaDrvItemAccess(m_pWiaDrvItem); if (FAILED(hr)) { DBG_ERR(("CWiaItem::WriteMultiple, ValidateDrvItemAccess failed")); return hr; } // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::WriteMultiple, InitLazyProps failed")); return hr; } } // // there is no point in going further if there are no properties to // write // if(cpspec == 0) { return S_OK; } // // We do not want to fail users who erroneousely attempt to write // to write to read-only properties IF that the values they // are trying to write are the same as current values. To achive // this, we first current values of the properties they want to // write: // PROPVARIANT *curVals = (PROPVARIANT *) LocalAlloc(LPTR, sizeof(PROPVARIANT) * cpspec); PROPSPEC *newSpecs = (PROPSPEC *) LocalAlloc(LPTR, sizeof(PROPSPEC) * cpspec); PROPVARIANT *newVals = (PROPVARIANT *) LocalAlloc(LPTR, sizeof(PROPVARIANT) * cpspec); ULONG newcpspec = cpspec; if(curVals == NULL || newSpecs == NULL || newVals == NULL) { DBG_ERR(("CWiaItem::WriteMultiple, failed to allocate memory")); goto Cleanup; } CopyMemory(newSpecs, rgpspec, sizeof(PROPSPEC) * cpspec); CopyMemory(newVals, rgpropvar, sizeof(PROPVARIANT) * cpspec); memset(curVals, 0, sizeof(PROPVARIANT) * cpspec); hr = m_pPropStg->CurStg()->ReadMultiple(cpspec, rgpspec, curVals); if(SUCCEEDED(hr)) { // // Now for every property value they want to write we check if // it is the same as the current value // ULONG ulNewEltIndex = 0; for(ULONG i = 0; i < cpspec; i++) { if(curVals[i].vt != rgpropvar[i].vt) continue; if(memcmp(curVals + i, rgpropvar + i, sizeof(PROPVARIANT)) == 0 || (curVals[i].vt == VT_BSTR && !lstrcmp(curVals[i].bstrVal, rgpropvar[i].bstrVal)) || (curVals[i].vt == VT_CLSID && IsEqualGUID(*curVals[i].puuid, *rgpropvar[i].puuid))) { // the value "matches", wipe it from both arrays. if(i != (cpspec - 1)) { // // Move a block of values/propspecs. // The number of elements to move is 1 less than the // remaining number of elements we still have to check. // Move these elements up in the new value array - put // them after the elements we've decided to keep so far // MoveMemory(newVals + ulNewEltIndex, newVals + ulNewEltIndex + 1, (cpspec - i - 1) * sizeof(PROPVARIANT)); MoveMemory(newSpecs + ulNewEltIndex, newSpecs + ulNewEltIndex + 1, (cpspec - i - 1) * sizeof(PROPSPEC)); } newcpspec--; } else { // // We want to keep this element, so increase the element index. // ulNewEltIndex++; } } // It could happen that all values are the same, in which case we // don't want to write anything at all if(newcpspec == 0) { hr = S_OK; goto Cleanup; } } // // Verify write access to all requested properties. If any of the // properties are read ony, the call fails with access denied. // hr = m_pPropStg->CheckPropertyAccess(TRUE, newcpspec, (PROPSPEC*)newSpecs); if (FAILED(hr)) { DBG_ERR(("CWiaItem::WriteMultiple, CheckPropertyAccess failed")); goto Cleanup; } // // First create the backup. // hr = m_pPropStg->Backup(); if (SUCCEEDED(hr)) { // // Write property values. // hr = (m_pPropStg->CurStg())->WriteMultiple(newcpspec, newSpecs, newVals, propidNameFirst); if (SUCCEEDED(hr)) { // // Write was successful, so do validation // LONG lFlags = 0; // // Make sure all PropSpecs are using PropID's. If some of the // PropSpecs are using string names, then convert them. // This is so that drivers only have to deal with PropID's. // PROPSPEC *pPropSpec = NULL; hr = m_pPropStg->NamesToPropIDs(newcpspec, (PROPSPEC*) newSpecs, &pPropSpec); if (SUCCEEDED(hr)) { // // Let the device mini driver know the properties have changed. // Device only gets propspec, must read prop values from item's // property stream. // { LOCK_WIA_DEVICE _LWD(this, &hr); if(SUCCEEDED(hr)) { hr = m_pActiveDevice->m_DrvWrapper.WIA_drvValidateItemProperties((BYTE*)this, lFlags, newcpspec, (pPropSpec ? pPropSpec : newSpecs), &m_lLastDevErrVal); } } if (pPropSpec) { LocalFree(pPropSpec); pPropSpec = NULL; } } else { DBG_ERR(("CWiaItem::WriteMultiple, conversion to PropIDs failed")); } } else { DBG_ERR(("CWiaItem::WriteMultiple, test write failed")); } HRESULT hresult; if (SUCCEEDED(hr)) { // // Validation passed, so free the backups. Use a new // HRESULT, since we don't want to overwrite hr returned by // drvValidateItemProperties. // hresult = m_pPropStg->ReleaseBackups(); if (FAILED(hresult)) { DBG_ERR(("CWiaItem::WriteMultiple, ReleaseBackups failed, continuing anyway...")); } } else { // // Didn't pass validation failed, so restore old values. Use // a new HRESULT, since we don't want to overwrite hr returned // by drvValidateItemProperties. // hresult = m_pPropStg->Undo(); if (FAILED(hresult)) { DBG_ERR(("CWiaItem::WriteMultiple, Undo() failed, could not restore invalid properties to their original values")); } } } else { DBG_ERR(("CWiaItem::WriteMultiple, couldn't make backup copy of properties")); } Cleanup: if(curVals) { FreePropVariantArray(cpspec, curVals); LocalFree(curVals); } if(newVals) LocalFree(newVals); if(newSpecs) LocalFree(newSpecs); return hr; } /**************************************************************************\ * GetPropertyAttributes * * Get the access flags and valid values for a property. * * Arguments: * * pWiasContext - Pointer to WIA item * cPropSpec - The number of properties * pPropSpec - array of property specification. * pulAccessFlags - array of LONGs access flags. * pPropVar - Pointer to returned valid values. * * Return Value: * * Status * * History: * * 1/15/1999 Original Version * 07/19/1999 Moved from iitem to ipropitm to implement IWiaPropertyStorage * interface. * \**************************************************************************/ HRESULT _stdcall CWiaItem::GetPropertyAttributes( ULONG cPropSpec, PROPSPEC pPropSpec[], ULONG pulAccessFlags[], PROPVARIANT ppvValidValues[]) { DBG_FN(CWiaItem::GetPropertyAttributes); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::GetPropertyAttributes, InitLazyProps failed")); return hr; } } // // RPC has already done parameter validation for us, so call // GetPropertyAttributesHelper to do the work. // return GetPropertyAttributesHelper(this, cPropSpec, pPropSpec, pulAccessFlags, ppvValidValues); } /**************************************************************************\ * CWiaItem::GetCount * * Returns the number of properties stored in an item's current value * property storage. * * Arguments: * * pulPropCount - Address to store the property count. * * Return Value: * * Status * * History: * * 9/3/1998 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaItem::GetCount( ULONG* pulPropCount) { DBG_FN(CWiaItem::GetCount); IEnumSTATPROPSTG *pIEnum; STATPROPSTG stg; ULONG ulCount; HRESULT hr = S_OK; if (pulPropCount == NULL) { DBG_ERR(("CWiaItem::GetCount, NULL parameter!")); return E_INVALIDARG; } else { *pulPropCount = 0; } // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::GetCount, InitLazyProps failed")); return hr; } } hr = (m_pPropStg->CurStg())->Enum(&pIEnum); if (SUCCEEDED(hr)) { ulCount = 0; while (pIEnum->Next(1, &stg, NULL) == S_OK) { ulCount++; if(stg.lpwstrName) { CoTaskMemFree(stg.lpwstrName); } } if (SUCCEEDED(hr)) { hr = S_OK; *pulPropCount = ulCount; } else { DBG_ERR(("CWiaItem::GetCount, pIEnum->Next failed (0x%X)", hr)); } pIEnum->Release(); } else { DBG_ERR(("CWiaItem::GetCount, Enum off CurStg failed (0x%X)", hr)); } return hr; } /**************************************************************************\ * CWiaItem::GetPropertyStream * * Get a copy of an items property stream. Caller must free returned * property stream. * * Arguments: * * pCompatibilityId - Address of GUID to receive the device's property * stream CompatibilityId. * ppstmProp - Pointer to returned property stream. * * Return Value: * * Status * * History: * * 09/03/1998 Original Version * 12/12/1999 Modified to use CompatibilityId * \**************************************************************************/ HRESULT _stdcall CWiaItem::GetPropertyStream( GUID *pCompatibilityId, LPSTREAM *ppstmProp) { DBG_FN(CWiaItem::GetPropertyStream); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::GetPropertyStream, InitLazyProps failed")); return hr; } } return m_pPropStg->GetPropertyStream(pCompatibilityId, ppstmProp); } /**************************************************************************\ * CWiaItem::SetPropertyStream * * Set an items property stream. * * Arguments: * * pCompatibilityId - Pointer to a GUID representing the property * stream CompatibilityId. * pstmProp - Pointer to property stream. * * Return Value: * * Status * * History: * * 09/03/1998 Original Version * 12/12/1999 Modified to use CompatibilityId * \**************************************************************************/ HRESULT _stdcall CWiaItem::SetPropertyStream( GUID *pCompatibilityId, LPSTREAM pstmProp) { DBG_FN(CWiaItem::SetPropertyStream); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::SetPropertyStream, InitLazyProps failed")); return hr; } } return m_pPropStg->SetPropertyStream(pCompatibilityId, this, pstmProp); } /**************************************************************************\ * * Methods of IPropertyStorage not directly off IWiaPropertySTorage * * DeleteMultiple * DeletePropertyNames * Commit * Revert * SetTimes * SetClass * Stat * * 9/3/1998 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaItem::DeleteMultiple( ULONG cpspec, const PROPSPEC __RPC_FAR rgpspec[]) { DBG_FN(CWiaItem::DeleteMultiple); return E_ACCESSDENIED; } HRESULT _stdcall CWiaItem::DeletePropertyNames( ULONG cpropid, const PROPID __RPC_FAR rgpropid[]) { DBG_FN(CWiaItem::DeletePropertyNames); return E_ACCESSDENIED; } HRESULT _stdcall CWiaItem::Commit(DWORD grfCommitFlags) { DBG_FN(CWiaItem::Commit); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::Commit, InitLazyProps failed")); return hr; } } hr = (m_pPropStg->CurStg())->Commit(grfCommitFlags); return hr; } HRESULT _stdcall CWiaItem::Revert(void) { DBG_FN(CWiaItem::Revert); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::Revert, InitLazyProps failed")); return hr; } } hr = (m_pPropStg->CurStg())->Revert(); return hr; } HRESULT _stdcall CWiaItem::SetTimes( const FILETIME __RPC_FAR *pctime, const FILETIME __RPC_FAR *patime, const FILETIME __RPC_FAR *pmtime) { DBG_FN(CWiaItem::SetTimes); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::SetTimes, InitLazyProps failed")); return hr; } } hr = (m_pPropStg->CurStg())->SetTimes(pctime,patime,pmtime); return hr; } HRESULT _stdcall CWiaItem::SetClass(REFCLSID clsid) { DBG_FN(CWiaItem::SetClass); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::SetClass, InitLazyProps failed")); return hr; } } return (m_pPropStg->CurStg())->SetClass(clsid); } HRESULT _stdcall CWiaItem::Stat(STATPROPSETSTG *pstatpsstg) { DBG_FN(CWiaItem::Stat); HRESULT hr; // // Check whether item properties have been initialized // if (!m_bInitialized) { hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::Stat, InitLazyProps failed")); return hr; } } hr = (m_pPropStg->CurStg())->Stat(pstatpsstg); return hr; }