/******************************************************************************* * * (C) COPYRIGHT MICROSOFT CORP., 1998 * * TITLE: WiaTree.Cpp * * VERSION: 2.0 * * AUTHOR: reedb * * DATE: 27 Apr, 1999 * * DESCRIPTION: * Implementation of the WIA tree class. A folder and file based tree with * a parallel linear list for name based searches. * *******************************************************************************/ #include "precomp.h" #include "stiexe.h" #include "wiamindr.h" #include "helpers.h" HRESULT _stdcall ValidateTreeItem(CWiaTree*); /**************************************************************************\ * CWiaTree * * Constructor for tree item. * * Arguments: * * None * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ CWiaTree::CWiaTree() { m_ulSig = CWIATREE_SIG; m_lFlags = WiaItemTypeFree; m_pNext = NULL; m_pPrev = NULL; m_pParent = NULL; m_pChild = NULL; m_pLinearList = NULL; m_bstrItemName = NULL; m_bstrFullItemName = NULL; m_pData = NULL; m_bInitCritSect = FALSE; } /**************************************************************************\ * Initialize * * Initialize new tree item. * * Arguments: * * lFlags - Object flags for new item. * bstrItemName - Item name. * bstrFullItemName - Full item name, including path. * pData - Pointer to items payload data. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::Initialize( LONG lFlags, BSTR bstrItemName, BSTR bstrFullItemName, void *pData) { HRESULT hr = S_OK; // // Tree items must be either folder or file. // if (!(lFlags & (WiaItemTypeFolder | WiaItemTypeFile))) { DBG_ERR(("CWiaTree::Initialize, bad flags parameter: 0x%08X", lFlags)); return E_INVALIDARG; } // // Initialize the critical section // __try { if(!InitializeCriticalSectionAndSpinCount(&m_CritSect, MINLONG)) { m_bInitCritSect = FALSE; return HRESULT_FROM_WIN32(::GetLastError()); } m_bInitCritSect = TRUE; } __except (EXCEPTION_EXECUTE_HANDLER) { m_bInitCritSect = FALSE; DBG_ERR(("CWiaTree::Initialize, Error initializing critical section")); return E_OUTOFMEMORY; } // // Root items are always valid. Other items are valid only after // insertion in the tree, so set not present flags. // if (!(lFlags & WiaItemTypeRoot)) { lFlags |= WiaItemTypeDeleted; lFlags |= WiaItemTypeDisconnected; } m_lFlags = lFlags; // // Maintain the item names for by name searches. // m_bstrItemName = SysAllocString(bstrItemName); if (!m_bstrItemName) { DBG_ERR(("CWiaTree::Initialize, unable to allocate item name")); return E_OUTOFMEMORY; } m_bstrFullItemName = SysAllocString(bstrFullItemName); if (!m_bstrFullItemName) { DBG_ERR(("CWiaTree::Initialize, unable to allocate full item name")); return E_OUTOFMEMORY; } SetItemData(pData); return hr; } /**************************************************************************\ * ~CWiaTreee * * Destructor for tree item. * * Arguments: * * None * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ CWiaTree::~CWiaTree() { DBG_FN(CWiaTree::~CWiaTree); HRESULT hr; // // Item should be disconnected. // if (m_pNext || m_pPrev || m_pParent || m_pChild) { DBG_ERR(("Destroy Tree Item, item still connected")); } // // Free item names. // if (m_bstrItemName) { SysFreeString(m_bstrItemName); m_bstrItemName = NULL; } if (m_bstrFullItemName) { SysFreeString(m_bstrFullItemName); m_bstrFullItemName = NULL; } // // Delete the critical section // if (m_bInitCritSect) { DeleteCriticalSection(&m_CritSect); } m_bInitCritSect = FALSE; // // Clear all members // m_ulSig = 0; m_lFlags = WiaItemTypeFree; m_pNext = NULL; m_pPrev = NULL; m_pParent = NULL; m_pChild = NULL; m_pLinearList = NULL; m_pData = NULL; } /**************************************************************************\ * GetRootItem * * Walk back up the tree to find the root of this item. * * Arguments: * * None * * Return Value: * * Pointer to this items root item. * * History: * * 1/19/1999 Original Version * \**************************************************************************/ CWiaTree * _stdcall CWiaTree::GetRootItem() { CWiaTree *pRoot = this; CWiaCritSect _CritSect(&m_CritSect); // // walk back up tree to root // while ((pRoot) && (pRoot->m_pParent != NULL)) { pRoot = pRoot->m_pParent; } // // verify root item // if (pRoot) { if (!(pRoot->m_lFlags & WiaItemTypeRoot)) { DBG_ERR(("CWiaTree::GetRootItem, root item doesn't have WiaItemTypeRoot set")); return NULL; } } else { DBG_ERR(("CWiaTree::GetRootItem, root item not found, tree corrupt")); } return pRoot; } /**************************************************************************\ * CWiaTree::AddItemToLinearList * * Add an item to the linear list. Must be called on a root item. * * Arguments: * * pItem - Pointer to the item to be added. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::AddItemToLinearList(CWiaTree *pItem) { CWiaCritSect _CritSect(&m_CritSect); // // Validate the child item. // if (pItem == NULL) { DBG_ERR(("CWiaTree::AddItemToLinearList, NULL input pointer")); return E_POINTER; } // // this must be a root item // if (!(m_lFlags & WiaItemTypeRoot)) { DBG_ERR(("CWiaTree::AddItemToLinearList, caller doesn't have WiaItemTypeRoot set")); return E_INVALIDARG; } // // add to single linked list // pItem->m_pLinearList = m_pLinearList; m_pLinearList = pItem; return S_OK; } /**************************************************************************\ * CWiaTree::RemoveItemFromLinearList * * Remove an item from the linear list. Must be called on a root item. * * Arguments: * * pItem - Pointer to the item to be removed. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::RemoveItemFromLinearList(CWiaTree *pItem) { CWiaCritSect _CritSect(&m_CritSect); HRESULT hr; // // validate // if (pItem == NULL) { DBG_ERR(("CWiaTree::RemoveItemFromLinearList, NULL input pointer")); return E_POINTER; } // // this must be a root item // if (!(m_lFlags & WiaItemTypeRoot)) { DBG_ERR(("CWiaTree::RemoveItemFromLinearList, caller doesn't have WiaItemTypeRoot set")); return E_INVALIDARG; } // // Root item case. // if (pItem == this) { m_pLinearList = NULL; return S_OK; } // // find item in list // CWiaTree* pPrev = this; CWiaTree* pTemp; // // look for match in list // do { // // look for item // if (pPrev->m_pLinearList == pItem) { // // remove from list and exit // pPrev->m_pLinearList = pItem->m_pLinearList; return S_OK; } // // next item // pPrev = pPrev->m_pLinearList; } while (pPrev != NULL); DBG_ERR(("CWiaTree::RemoveItemFromLinearList, item not found: 0x%08X", pItem)); return E_FAIL; } /**************************************************************************\ * CWiaTree::AddChildItem * * Add a child item to the tree. * * Arguments: * * pItem - Pointer to the child item to be added. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::AddChildItem(CWiaTree *pItem) { CWiaCritSect _CritSect(&m_CritSect); // // Validate the child item. // if (!pItem) { DBG_ERR(("CWiaTree::AddChildItem pItem is NULL ")); return E_POINTER; } // // Not using sentinell so check enpty folder case. // if (m_pChild == NULL) { m_pChild = pItem; pItem->m_pNext = pItem; pItem->m_pPrev = pItem; } else { // // Add to end of folder list. // CWiaTree *pTempItem = m_pChild; pTempItem->m_pPrev->m_pNext = pItem; pItem->m_pPrev = pTempItem->m_pPrev; pItem->m_pNext = pTempItem; pTempItem->m_pPrev = pItem; } return S_OK; } /**************************************************************************\ * CWiaTree::AddItemToFolder * * Add a tree item to a folder in the tree. Parent must be a folder. * * Arguments: * * pIParent - Parent of the item. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::AddItemToFolder(CWiaTree *pParent) { CWiaCritSect _CritSect(&m_CritSect); HRESULT hr = S_OK; // // Validate the parent. Parents must be a folder. // if (!pParent) { DBG_ERR(("CWiaTree::AddItemToFolder, NULL parent")); return E_POINTER; } if (!(pParent->m_lFlags & (WiaItemTypeFolder | WiaItemTypeHasAttachments))) { DBG_ERR(("CWiaTree::AddItemToFolder, parent is not a folder")); return E_INVALIDARG; } // // First add item to linear list of items. // CWiaTree *pRoot = pParent->GetRootItem(); if (pRoot == NULL) { return E_FAIL; } hr = pRoot->AddItemToLinearList(this); if (FAILED(hr)) { return hr; } // // Add the item to the tree. // hr = pParent->AddChildItem(this); if (FAILED(hr)) { return hr; } // // Remember parent // m_pParent = pParent; // // Item has been added to the tree, clear not present flags. // m_lFlags &= ~WiaItemTypeDeleted; m_lFlags &= ~WiaItemTypeDisconnected; return hr; } /**************************************************************************\ * RemoveItemFromFolder * * Remove an item from a folder of the tree and mark it so that * no device access can be done through it. * * Arguments: * * lReason - Reason for removal of item. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::RemoveItemFromFolder(LONG lReason) { CWiaCritSect _CritSect(&m_CritSect); HRESULT hr = S_OK; // // Reason for removal must be valid. // if (!(lReason & (WiaItemTypeDeleted | WiaItemTypeDisconnected))) { DBG_ERR(("CWiaTree::RemoveItemFromFolder, invalid lReason: 0x%08X", lReason)); return E_INVALIDARG; } // // Folder items must be empty. // if (m_lFlags & (WiaItemTypeFolder | WiaItemTypeHasAttachments)) { if (m_pChild != NULL) { DBG_ERR(("CWiaTree::RemoveItemFromFolder, trying to remove folder that is not empty")); return E_INVALIDARG; } } // // Remove non-root from linear list // CWiaTree *pRoot = GetRootItem(); if (pRoot == NULL) { DBG_ERR(("CWiaTree::RemoveItemFromFolder, can't find root")); return E_FAIL; } if (pRoot != this) { pRoot->RemoveItemFromLinearList(this); // // remove from list of children // if (m_pNext != this) { // // remove from non-empty list // m_pPrev->m_pNext = m_pNext; m_pNext->m_pPrev = m_pPrev; // // was it head? // if (m_pParent->m_pChild == this) { m_pParent->m_pChild = m_pNext; } } else { // // list contains only this child. mark parent's // child pointer to NULL // m_pParent->m_pChild = NULL; } } // // to prevent accidents, clear connection fields // m_pNext = NULL; m_pPrev = NULL; m_pParent = NULL; m_pChild = NULL; // // Indicate why item was removed from the driver item tree. // m_lFlags |= lReason; return S_OK; } /**************************************************************************\ * CWiaTree::GetFullItemName * * Allocates and fills in a BSTR with this items full name. The full item * name includes item path information. Caller must free. * * Arguments: * * pbstrFullItemName - Pointer to returned full item name. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::GetFullItemName(BSTR *pbstrFullItemName) { if (!pbstrFullItemName) { DBG_ERR(("CWiaTree::GetFullItemName pbstrFullItemName is NULL ")); return E_INVALIDARG; } BSTR bstr = SysAllocString(m_bstrFullItemName); if (bstr) { *pbstrFullItemName = bstr; return S_OK; } return E_OUTOFMEMORY; } /**************************************************************************\ * CWiaTree::GetItemName * * Allocates and fills in a BSTR with this items name. The item name * does not include item path information. Caller must free. * * Arguments: * * pbstrItemName - Pointer to returned item name. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::GetItemName(BSTR *pbstrItemName) { if (!pbstrItemName) { DBG_ERR(("CWiaTree::GetItemName pbstrItemName is NULL ")); return E_INVALIDARG; } BSTR bstr = SysAllocString(m_bstrItemName); if (bstr) { *pbstrItemName = bstr; return S_OK; } return E_OUTOFMEMORY; } /**************************************************************************\ * CWiaTree::DumpTreeData * * Allocate buffer and dump formatted private CWiaTree data into it. * This method is debug only. Free component returns E_NOTIMPL. * * Arguments: * * bstrDrvItemData - Pointer to allocated buffer. Caller must free. * * Return Value: * * Status * * History: * * 1/19/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::DumpTreeData(BSTR *bstrDrvItemData) { #ifdef DEBUG #define BUF_SIZE 1024 #define LINE_SIZE 128 WCHAR szTemp[BUF_SIZE]; LPOLESTR psz = szTemp; wcscpy(szTemp, L""); psz+= wsprintfW(psz, L"Tree item, CWiaTree: %08X\r\n\r\n", this); psz+= wsprintfW(psz, L"Address Member Value\r\n"); psz+= wsprintfW(psz, L"%08X m_ulSig: %08X\r\n", &m_ulSig, m_ulSig); psz+= wsprintfW(psz, L"%08X m_lFlags: %08X\r\n", &m_lFlags, m_lFlags); psz+= wsprintfW(psz, L"%08X m_pNext: %08X\r\n", &m_pNext, m_pNext); psz+= wsprintfW(psz, L"%08X m_pPrev: %08X\r\n", &m_pPrev, m_pPrev); psz+= wsprintfW(psz, L"%08X m_pParent: %08X\r\n", &m_pParent, m_pParent); psz+= wsprintfW(psz, L"%08X m_pChild: %08X\r\n", &m_pChild, m_pChild); psz+= wsprintfW(psz, L"%08X m_pLinearList: %08X\r\n", &m_pLinearList, m_pLinearList); psz+= wsprintfW(psz, L"%08X m_bstrItemName: %08X, %ws\r\n", &m_bstrItemName, m_bstrItemName, m_bstrItemName); psz+= wsprintfW(psz, L"%08X m_bstrFullItemName: %08X, %ws\r\n", &m_bstrFullItemName, m_bstrFullItemName, m_bstrFullItemName); psz+= wsprintfW(psz, L"%08X m_pData: %08X\r\n", &m_pData, m_pData); if (psz > (szTemp + (BUF_SIZE - LINE_SIZE))) { DBG_ERR(("CWiaTree::DumpDrvItemData buffer too small")); } *bstrDrvItemData = SysAllocString(szTemp); if (*bstrDrvItemData) { return S_OK; } return E_OUTOFMEMORY; #else return E_NOTIMPL; #endif } /* HRESULT _stdcall CWiaTree::DumpAllTreeData() { // // start at beginning of item linear list // CWiaTree *pItem = GetRootItem(); // // Find the matching tree item from the linear list. // DBG_OUT(("Start of TREE list:")); while (pItem != NULL) { BSTR bstrInfo = NULL; pItem->DumpTreeData(&bstrInfo); DBG_OUT(("%ws\n", bstrInfo)); pItem = pItem->m_pLinearList; } DBG_OUT((":End of TREE list")); return S_OK; } */ /**************************************************************************\ * CWiaTree::UnlinkChildItemTree * * This method recursively unlinks the tree by calling * RemoveItemFromFolder on each driver item under the root. * * Arguments: * * lReason - Reason for unlink of tree. * * Return Value: * * Status * * History: * * 1/21/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::UnlinkChildItemTree( LONG lReason, PFN_UNLINK_CALLBACK pFunc) { HRESULT hr = S_OK; // // Delete the childern. // CWiaTree *pChild = m_pChild; while (pChild != NULL) { // // If the child is a folder then call // recursively to delete all childern under. // if (pChild->m_lFlags & (WiaItemTypeFolder | WiaItemTypeHasAttachments)) { hr = pChild->UnlinkChildItemTree(lReason, pFunc); if (FAILED(hr)) { break; } } // // remove item from tree // hr = pChild->RemoveItemFromFolder(lReason); if (FAILED(hr)) { break; } // // If a callback has been specified, call it with the // payload as argument // if (pFunc && pChild->m_pData) { pFunc(pChild->m_pData); } pChild = m_pChild; } return hr; } /**************************************************************************\ * CWiaTree::UnlinkItemTree * * This method unlinks the tree. Must be called on the root * driver item. * * Arguments: * * lReason - Reason for unlinking the tree. * * Return Value: * * Status * * History: * * 1/21/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::UnlinkItemTree( LONG lReason, PFN_UNLINK_CALLBACK pFunc) { CWiaCritSect _CritSect(&m_CritSect); // // this must be the root item // if (!(m_lFlags & WiaItemTypeRoot)) { DBG_ERR(("CWiaTree::UnlinkItemTree, caller not root item")); return E_INVALIDARG; } HRESULT hr = S_OK; // // Unlink any childern. // if (m_pChild) { hr = UnlinkChildItemTree(lReason, pFunc); } if (SUCCEEDED(hr)) { // // Remove root item. // hr = RemoveItemFromFolder(lReason); // // If a callback has been specified, call it with the // payload as argument // if (pFunc) { pFunc(m_pData); } } return hr; } /**************************************************************************\ * CWiaTree::FindItemByName * * Locate a tree item by it's full item name. Ref count of the returned * interface is done by the caller. * * Arguments: * * lFlags - Operation flags. * bstrFullItemName - Requested item name. * ppItem - Pointer to returned item, if found. * * Return Value: * * Status * * History: * * 1/27/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::FindItemByName( LONG lFlags, BSTR bstrFullItemName, CWiaTree **ppItem) { CWiaCritSect _CritSect(&m_CritSect); if (ppItem) { *ppItem = NULL; } else { DBG_ERR(("CWiaTree::FindItemByName NULL ppItem")); return E_INVALIDARG; } // // start at beginning of item linear list // CWiaTree *pItem = GetRootItem(); // // Find the matching tree item from the linear list. // while (pItem != NULL) { if (wcscmp(pItem->m_bstrFullItemName, bstrFullItemName) == 0) { // // Item is found. No need to increase ref count, taken care // of by caller. // *ppItem = pItem; return S_OK; } pItem = pItem->m_pLinearList; } return S_FALSE; } /**************************************************************************\ * CWiaTree::FindChildItemByName * * Locate a child item by it's item name. Caller takes care of increasing * the reference count of the returned item interface. * * Arguments: * * bstrItemName - Requested item name. * ppIChildItem - Pointer to returned item, if found. * * Return Value: * * Status * * History: * * 1/27/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::FindChildItemByName( BSTR bstrItemName, CWiaTree **ppIChildItem) { CWiaCritSect _CritSect(&m_CritSect); CWiaTree *pCurItem; pCurItem = m_pChild; if (!pCurItem) { return S_FALSE; } *ppIChildItem = NULL; do { if (wcscmp(bstrItemName, pCurItem->m_bstrItemName) == 0) { // // No need to increase ref count, taken care of by caller. // *ppIChildItem = pCurItem; return S_OK; } pCurItem = pCurItem->m_pNext; } while (pCurItem != m_pChild); return S_FALSE; } /**************************************************************************\ * CWiaTree::GetParent * * Get the parent of this item. Returns S_FALSE and null if * called on root item. * * Arguments: * * ppIParentItem - Pointer to returned parent, if found. * * Return Value: * * Status * * History: * * 1/27/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::GetParentItem(CWiaTree **ppIParentItem) { if (m_lFlags & WiaItemTypeRoot) { *ppIParentItem = NULL; return S_FALSE; } *ppIParentItem = m_pParent; return S_OK; } /**************************************************************************\ * CWiaTree::GetFirstChild * * Return the first child item of this folder. * * Arguments: * * ppIChildItem - Pointer to returned child item, if found. * * Return Value: * * Status * * History: * * 1/27/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::GetFirstChildItem(CWiaTree **ppIChildItem) { HRESULT hr = S_FALSE; // // Check special case: if ppIChildItem == NULL, then just check // whether there are any children. // // S_OK if child exists, else S_FALSE. // if (m_pChild != NULL) { hr = S_OK; } if (ppIChildItem == NULL) { return hr; } *ppIChildItem = NULL; if (!(m_lFlags & (WiaItemTypeFolder | WiaItemTypeHasAttachments))) { DBG_ERR(("CWiaTree::GetFirstChildItem, caller not folder")); return E_INVALIDARG; } *ppIChildItem = m_pChild; return hr; } /**************************************************************************\ * CWiaTree::GetNextSibling * * Find the next sibling of this item. * * Arguments: * * ppSiblingItem - Pointer to the returned sibling item, if found. * * Return Value: * * Status * * History: * * 1/27/1999 Original Version * \**************************************************************************/ HRESULT _stdcall CWiaTree::GetNextSiblingItem(CWiaTree **ppSiblingItem) { CWiaCritSect _CritSect(&m_CritSect); if (!ppSiblingItem) { return S_FALSE; } *ppSiblingItem = NULL; if (m_pNext && m_pParent) { if (m_pNext != m_pParent->m_pChild) { *ppSiblingItem = m_pNext; return S_OK; } } return S_FALSE; } /**************************************************************************\ * ValidateTreeItem * * Validate a tree item. * * Arguments: * * pTreeItem - Pointer to a tree item. * * Return Value: * * Status * * History: * * 1/27/1999 Original Version * \**************************************************************************/ HRESULT _stdcall ValidateTreeItem(CWiaTree *pTreeItem) { if (!pTreeItem) { DBG_ERR(("ValidateTreeItem, NULL tree item pointer")); return E_POINTER; } if (pTreeItem->m_ulSig == CWIATREE_SIG) { return S_OK; } else { DBG_ERR(("ValidateTreeItem, not a tree item")); return E_INVALIDARG; } }