Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

983 lines
22 KiB

/*++
Module Name:
Connect.cpp
Abstract:
This module contains the implementation for CConnectToDialog.
This is used to display the Connect To Dfs Root dialog box
--*/
#include "stdafx.h"
#include <process.h>
#include "DfsGUI.h"
#include "Utils.h" // For the LoadStringFromResource and SetStandardCursor method
#include "Connect.h"
#include "dfshelp.h"
static const int iFOLDER_IMAGE = 0;
static const int iFOLDER_SELECTED_IMAGE = 1;
static const int iDOMAIN_IMAGE = 2;
static const int iDOMAIN_SELECTED_IMAGE = 2;
static const int iSTANDALONE_DFSROOT_IMAGE = 3;
static const int iFT_DFSROOT_IMAGE = 3;
static const int iOVERLAY_BUSY_IMAGE = 4;
static const int iOVERLAY_ERROR_IMAGE = 5;
static const int OV_BUSY = 1;
static const int OV_ERROR = 2;
CConnectToDialog::CConnectToDialog()
{
CWaitCursor WaitCursor; // Display the wait cursor
m_pBufferManager = NULL;
m_hImageList = NULL;
(void)Get50Domains(&m_50DomainList);
LoadStringFromResource(IDS_DOMAIN_DFSROOTS_LABEL, &m_bstrDomainDfsRootsLabel);
LoadStringFromResource(IDS_ALL_DFSROOTS_LABEL, &m_bstrAllDfsRootsLabel);
}
CConnectToDialog::~CConnectToDialog()
{
CWaitCursor WaitCursor; // An object to set\reset the cursor to wait cursor
if(NULL != m_hImageList)
{
ImageList_Destroy(m_hImageList);
m_hImageList = NULL;
}
// Free Domain List
FreeNetNameList(&m_50DomainList);
if (m_pBufferManager)
{
//
// signal all related running threads to terminate
//
m_pBufferManager->SignalExit();
//
// decrement the reference count on the CBufferManager instance
//
m_pBufferManager->Release();
}
}
LRESULT
CConnectToDialog::OnInitDialog(
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled
)
{
//
// create instance of CBufferManager
// m_pBufferManager will be set to NULL if CreateInstance() failed.
//
(void) CBufferManager::CreateInstance(m_hWnd, &m_pBufferManager);
::SendMessage(GetDlgItem(IDC_EditDfsRoot), EM_LIMITTEXT, DNSNAMELIMIT, 0);
InitTVImageList(); // Get the image list for the TV
FillupTheTreeView(); // Fill up the Tree View
return TRUE; // let the dialog box set the focus to any control it wants.
}
/*++
This function is called when a user clicks the ? in the top right of a property sheet
and then clciks a control, or when they hit F1 in a control.
--*/
LRESULT CConnectToDialog::OnCtxHelp(
IN UINT i_uMsg,
IN WPARAM i_wParam,
IN LPARAM i_lParam,
IN OUT BOOL& io_bHandled
)
{
LPHELPINFO lphi = (LPHELPINFO) i_lParam;
if (!lphi || lphi->iContextType != HELPINFO_WINDOW || lphi->iCtrlId < 0)
return FALSE;
::WinHelp((HWND)(lphi->hItemHandle),
DFS_CTX_HELP_FILE,
HELP_WM_HELP,
(DWORD_PTR)(PVOID)g_aHelpIDs_IDD_DLGCONNECTTO);
return TRUE;
}
/*++
This function handles "What's This" help when a user right clicks the control
--*/
LRESULT CConnectToDialog::OnCtxMenuHelp(
IN UINT i_uMsg,
IN WPARAM i_wParam,
IN LPARAM i_lParam,
IN OUT BOOL& io_bHandled
)
{
::WinHelp((HWND)i_wParam,
DFS_CTX_HELP_FILE,
HELP_CONTEXTMENU,
(DWORD_PTR)(PVOID)g_aHelpIDs_IDD_DLGCONNECTTO);
return TRUE;
}
LRESULT
CConnectToDialog::OnGetDataThreadDone(
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled
)
{
_ASSERT(m_pBufferManager);
bHandled = TRUE;
CEntryData* pEntry = reinterpret_cast<CEntryData*>(wParam);
HRESULT hr = (HRESULT)lParam;
_ASSERT(pEntry);
CComBSTR bstrNode = pEntry->GetNodeName();
HTREEITEM hItem = pEntry->GetTreeItem();
switch (pEntry->GetEntryType())
{
case BUFFER_ENTRY_TYPE_VALID:
(void)InsertData(pEntry, hItem);
ChangeIcon(hItem, ICONTYPE_NORMAL);
break;
case BUFFER_ENTRY_TYPE_ERROR:
ExpandNodeErrorReport(hItem, bstrNode, pEntry->GetEntryHRESULT());
break;
default:
_ASSERT(FALSE);
break;
}
bHandled = TRUE;
return TRUE;
}
void CConnectToDialog::ChangeIcon(
IN HTREEITEM hItem,
IN ICONTYPE IconType
)
{
TVITEM TVItem;
ZeroMemory(&TVItem, sizeof(TVItem));
TVItem.hItem = hItem;
TVItem.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
switch (IconType)
{
case ICONTYPE_BUSY:
TVItem.iImage = iOVERLAY_BUSY_IMAGE;
TVItem.iSelectedImage = iOVERLAY_BUSY_IMAGE;
break;
case ICONTYPE_ERROR:
TVItem.iImage = iOVERLAY_ERROR_IMAGE;
TVItem.iSelectedImage = iOVERLAY_ERROR_IMAGE;
break;
default: // ICONTYPE_NORMAL
{
NODETYPE NodeType = UNASSIGNED;
HRESULT hr = GetNodeInfo(hItem, NULL, &NodeType);
if (FAILED(hr))
return;
switch (NodeType)
{
case TRUSTED_DOMAIN:
TVItem.iImage = iDOMAIN_IMAGE;
TVItem.iSelectedImage = iDOMAIN_SELECTED_IMAGE;
break;
case DOMAIN_DFSROOTS:
case ALL_DFSROOTS:
TVItem.iImage = iFOLDER_IMAGE;
TVItem.iSelectedImage = iFOLDER_SELECTED_IMAGE;
break;
case FTDFS:
TVItem.iImage = iFT_DFSROOT_IMAGE;
TVItem.iSelectedImage = iFT_DFSROOT_IMAGE;
break;
case SADFS:
TVItem.iImage = iSTANDALONE_DFSROOT_IMAGE;
TVItem.iSelectedImage = iSTANDALONE_DFSROOT_IMAGE;
break;
default:
return;
}
}
}
SendDlgItemMessage(IDC_TV, TVM_SETITEM, 0, (LPARAM)&TVItem);
UpdateWindow();
}
/*
void CConnectToDialog::ChangeIcon(
IN HTREEITEM hItem,
IN ICONTYPE IconType
)
{
TVITEM TVItem;
ZeroMemory(&TVItem, sizeof(TVItem));
TVItem.hItem = hItem;
TVItem.mask = TVIF_STATE;
TVItem.stateMask = TVIS_OVERLAYMASK;
switch (IconType)
{
case ICONTYPE_BUSY:
TVItem.state = INDEXTOOVERLAYMASK(OV_BUSY);
break;
case ICONTYPE_ERROR:
TVItem.state = INDEXTOOVERLAYMASK(OV_ERROR);
break;
default:
TVItem.state = 0;
break;
}
SendDlgItemMessage(IDC_TV, TVM_SETITEM, 0, (LPARAM)&TVItem);
UpdateWindow();
}
*/
void CConnectToDialog::ExpandNodeErrorReport(
IN HTREEITEM hItem,
IN PCTSTR pszNodeName,
IN HRESULT hr
)
{
// change the icon to "X"
dfsDebugOut((_T("Failed to expand: %s, hr=%x\n"), pszNodeName, hr));
SetChildrenToZero(hItem);
ChangeIcon(hItem, ICONTYPE_ERROR);
}
void CConnectToDialog::ExpandNode(
IN PCTSTR pszNodeName,
IN NODETYPE nNodeType,
IN HTREEITEM hParentItem
)
{
HRESULT hr = S_OK;
dfsDebugOut((_T("CConnectToDialog::ExpandNode for %s\n"), pszNodeName));
if (m_pBufferManager)
{
//
// change icon to wait
//
ChangeIcon(hParentItem, ICONTYPE_BUSY);
UpdateWindow();
//
// start the thread to calculate a list of servers in the current selected domain
//
CEntryData *pEntry = NULL;
hr = m_pBufferManager->LoadInfo(pszNodeName, nNodeType, hParentItem, &pEntry);
if (SUCCEEDED(hr))
{
//
// Either we get a valid ptr back (ie. data is ready), insert it;
// or, a thread is alreay in progress, wait until a THREAD_DONE message.
//
if (pEntry)
{
_ASSERT(pEntry->GetEntryType() == BUFFER_ENTRY_TYPE_VALID);
(void)InsertData(pEntry, hParentItem);
}
} else
{
ExpandNodeErrorReport(hParentItem, pszNodeName, hr);
}
}
return;
}
HRESULT
CConnectToDialog::InsertData(
IN CEntryData* pEntry,
IN HTREEITEM hParentItem
)
{
_ASSERT(pEntry);
CComBSTR bstrNode = pEntry->GetNodeName();
NODETYPE nNodeType = pEntry->GetNodeType();
NETNAMELIST* pList = pEntry->GetList();
_ASSERT(pList);
HRESULT hr = S_OK;
if (0 == pList->size())
{
SetChildrenToZero(hParentItem);
return hr;
}
int nImageIndex;
int nSelectedImageIndex;
bool bChildren;
nImageIndex = iSTANDALONE_DFSROOT_IMAGE;
nSelectedImageIndex = iSTANDALONE_DFSROOT_IMAGE;
bChildren = false;
for (NETNAMELIST::iterator i = pList->begin(); i != pList->end(); i++)
{
hr = AddSingleItemtoTV(
(*i)->bstrNetName,
nImageIndex,
nSelectedImageIndex,
bChildren,
nNodeType,
hParentItem);
RETURN_IF_FAILED(hr);
}
// make the child items visible
HTREEITEM hChildItem = (HTREEITEM)SendDlgItemMessage(
IDC_TV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hParentItem);
if (hChildItem)
SendDlgItemMessage(IDC_TV, TVM_ENSUREVISIBLE, 0, (LPARAM)hChildItem);
// sort all its child items
SendDlgItemMessage(IDC_TV, TVM_SORTCHILDREN, 0, (LPARAM)hParentItem);
return S_OK;
}
LRESULT
CConnectToDialog :: OnNotify(
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
BOOL& bHandled
)
/*++
Routine Description:
Called on WM_NOTIFY.
Used to set the Edit box depending on the current selection in the TV.
Arguments:
uMsg - The windows message being sent. This is WM_NOTIFY.
lParam - Info about the message like control for which the message is being sent,
what sub type of message, etc
Return value:
TRUE, if we have handled the message
FALSE, if we ignore it. The system handles the message then.
--*/
{
_ASSERTE(WM_NOTIFY == uMsg);
_ASSERTE(lParam != NULL);
LRESULT lr = FALSE; // Set it to true if we handle this message.
LPNM_TREEVIEW pNMTreeView = (NM_TREEVIEW *) lParam;
bHandled = FALSE;
// Check if the message is for our tree control
if (pNMTreeView && IDC_TV == pNMTreeView->hdr.idFrom)
{
// Check if the message is for selection change.
if (TVN_SELCHANGED == pNMTreeView->hdr.code)
{
lr = DoNotifySelectionChanged(pNMTreeView);
}
else if (TVN_ITEMEXPANDING == pNMTreeView->hdr.code)
{
lr = DoNotifyItemExpanding(pNMTreeView);
}
else if (NM_DBLCLK == pNMTreeView->hdr.code)
{
lr = DoNotifyDoubleClick();
} else
{
lr = FALSE;
}
}
return (lr);
}
LRESULT
CConnectToDialog::DoNotifyDoubleClick(
)
/*++
Routine Description:
Handles the WM_NOTIFY for NM_DBLCLK.
This acts like a click on OK, if the current item is a dfsroot.
Arguments:
None
Return value:
TRUE, if we have handled the message
FALSE, if we ignore it. The system handles the message then.
--*/
{
HRESULT hr = E_FAIL;
HTREEITEM hCurrentItem = NULL;
NODETYPE NodeType = UNASSIGNED;
hCurrentItem = TreeView_GetSelection(GetDlgItem(IDC_TV));
if (NULL == hCurrentItem) // Unable to get the current selection
{
return FALSE;
}
hr = GetNodeInfo(hCurrentItem, NULL, &NodeType);
if(FAILED(hr))
return FALSE;
// Take action only on a dfs root
if (FTDFS == NodeType || SADFS == NodeType)
{
int iHandled = TRUE; // A variable used for communication with OnOK
OnOK(NULL, 1, 0, iHandled); // On a double click, we simulate a click on OK.
_ASSERTE(TRUE == iHandled);
return TRUE;
}
return FALSE;
}
LRESULT
CConnectToDialog::DoNotifyItemExpanding(
IN LPNM_TREEVIEW i_pNMTreeView
)
/*++
Routine Description:
Handles the WM_NOTIFY for TVN_ITEMEXPANDING.
If the expand is for "Standalone label", we create another thread
to fill it up.
Else, we get the Fault Tolerant Dfs Roots for the domain name.
Also we removes the '+' sign, if the tree node is empty.
Arguments:
i_pNMTreeView - Information related to the tree and the node for which the message
occurred
Return value:
TRUE, if we have handled the message
FALSE, if we ignore it. The system handles the message then.
--*/
{
HTREEITEM hCurrentItem = (i_pNMTreeView->itemNew).hItem;
_ASSERT(hCurrentItem);
// If children actually exist, we have nothing to do. It is a normal expand
HTREEITEM hItemChild = (HTREEITEM)SendDlgItemMessage(IDC_TV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hCurrentItem);
if (hItemChild)
return FALSE;
NODETYPE NodeType = UNASSIGNED;
HRESULT hr = GetNodeInfo(hCurrentItem, NULL, &NodeType);
if(FAILED(hr))
{
SetChildrenToZero(hCurrentItem);
return TRUE;
}
switch (NodeType)
{
case TRUSTED_DOMAIN:
{
AddSingleItemtoTV(
m_bstrDomainDfsRootsLabel,
iFOLDER_IMAGE,
iFOLDER_SELECTED_IMAGE,
true,
DOMAIN_DFSROOTS,
hCurrentItem);
return TRUE;
}
case DOMAIN_DFSROOTS:
{
CWaitCursor WaitCursor;
// get the domain name
HTREEITEM hParentItem = (HTREEITEM)SendDlgItemMessage(IDC_TV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hCurrentItem);
_ASSERT(hParentItem);
CComBSTR bstrDomainName;
hr = GetNodeInfo(hParentItem, &bstrDomainName, NULL);
if(FAILED(hr))
SetChildrenToZero(hCurrentItem);
ExpandNode(bstrDomainName, ((NodeType == DOMAIN_DFSROOTS) ? FTDFS : SADFS), hCurrentItem);
return TRUE;
}
default:
break;
}
return FALSE;
}
LRESULT
CConnectToDialog::DoNotifySelectionChanged(
IN LPNM_TREEVIEW i_pNMTreeView
)
/*++
Routine Description:
Handles the WM_NOTIFY for TVN_SELCHANGED.
The text in the edit box is set here to the dfs root path.
Arguments:
i_pNMTreeView - Information related to the tree and the node for which the message
occurred
Return value:
TRUE, if we have handled the message
FALSE, if we ignore it. The system handles the message then.
--*/
{
HRESULT hr = S_OK;
CComBSTR bstrNameForEditBox;
CComBSTR bstrDisplayName;
NODETYPE NodeType;
HTREEITEM hItem = (i_pNMTreeView->itemNew).hItem;
hr = GetNodeInfo(hItem, &bstrDisplayName, &NodeType);
if(FAILED(hr))
return FALSE;
switch (NodeType)
{
case FTDFS:
{
// get its parent's display name
HTREEITEM hParentItem =
(HTREEITEM)SendDlgItemMessage(IDC_TV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem);
_ASSERT(hParentItem);
HTREEITEM hGrandParentItem =
(HTREEITEM)SendDlgItemMessage(IDC_TV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hParentItem);
_ASSERT(hGrandParentItem);
CComBSTR bstrDomainName;
hr = GetNodeInfo(hGrandParentItem, &bstrDomainName, NULL);
if(FAILED(hr))
return FALSE;
bstrNameForEditBox = _T("\\\\");
bstrNameForEditBox += bstrDomainName;
bstrNameForEditBox += _T("\\");
bstrNameForEditBox += bstrDisplayName;
}
break;
/* case SADFS:
bstrNameForEditBox = bstrDisplayName;
break; */
default:
bstrNameForEditBox = _T("");
break;
}
return SetDlgItemText(IDC_DLG_EDIT, bstrNameForEditBox);
}
LRESULT
CConnectToDialog::OnOK(
WORD wNotifyCode,
WORD wID,
HWND hWndCtl,
BOOL& bHandled
)
/*++
Routine Description:
Called when the OK button is pressed.
Arguments:
None used.
Return value:
0. As it is a command handler
Calls EndDialog(S_OK). S_OK is passed back as return value of DoModal. This indicates
that the dialog ended on OK being pressed
--*/
{
DWORD dwTextLength = 0;
HRESULT hr = S_OK;
m_bstrDfsRoot.Empty();
hr = GetInputText(GetDlgItem(IDC_DLG_EDIT), &m_bstrDfsRoot, &dwTextLength);
if (FAILED(hr))
{
DisplayMessageBoxForHR(hr);
::SetFocus(GetDlgItem(IDC_DLG_EDIT));
return FALSE;
} else if (0 == dwTextLength)
{
DisplayMessageBoxWithOK(IDS_MSG_EMPTY_DFSROOT);
::SetFocus(GetDlgItem(IDC_DLG_EDIT));
return FALSE;
}
EndDialog(S_OK);
return 0;
}
LRESULT
CConnectToDialog::OnCancel(
WORD wNotifyCode,
WORD wID,
HWND hWndCtl,
BOOL& bHandled
)
/*++
Routine Description:
Called when the Cancel button is pressed.
Arguments:
None used.
Return value:
0. As it is a command handler
Calls EndDialog(S_FALSE). S_FALSE is passed back as return value of DoModal.
This indicates that the dialog ended on Cancel being pressed
--*/
{
EndDialog(S_FALSE);
return 0;
}
BOOL CConnectToDialog :: EndDialog(
IN int i_RetCode
)
/*++
Routine Description:
Overridden method that calls the parent method after some internal processing.
This includes deleting the objects stored in the lparams of the TV items.
Arguments:
None used.
Return value:
The return value of the parent method.
--*/
{
::ShowCursor(FALSE);
SetCursor(::LoadCursor(NULL, IDC_WAIT));
::ShowCursor(TRUE);
// Remove the Imagelist from the tree. We destroy it in the dtor
SendDlgItemMessage(IDC_TV, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)NULL);
return CDialogImpl<CConnectToDialog>::EndDialog(i_RetCode);
}
STDMETHODIMP CConnectToDialog::get_DfsRoot(
OUT BSTR* pVal
)
/*++
Routine Description:
Return the selected DfsRoot name.
Part of the interface IConnectToDialog.
Arguments:
pVal - Return the BSTR in this.
Return value:
S_OK, if successful
E_FAIL, if the value is unavailable
E_INVALIDARG, if the pointer is invalid(NULL)
E_OUTOFMEMORY if we run out of memory
--*/
{
RETURN_INVALIDARG_IF_NULL(pVal);
if ((!m_bstrDfsRoot) || (0 == m_bstrDfsRoot.Length()))
{
return E_FAIL;
}
*pVal = SysAllocString(m_bstrDfsRoot);
RETURN_OUTOFMEMORY_IF_NULL(*pVal);
return S_OK;
}
void
CConnectToDialog::SetChildrenToZero(
IN HTREEITEM i_hItem
)
{
TV_ITEM TVItem;
ZeroMemory(&TVItem, sizeof TVItem);
TVItem.mask = TVIF_CHILDREN;
TVItem.cChildren = 0;
TVItem.hItem = i_hItem;
SendDlgItemMessage( IDC_TV, TVM_SETITEM, 0, (LPARAM)&TVItem);
}
HRESULT CConnectToDialog::InitTVImageList()
{
m_hImageList = ImageList_LoadBitmap(
_Module.GetModuleInstance(),
MAKEINTRESOURCE(IDB_CONNECT_16x16),
16,
8,
CLR_DEFAULT);
if (NULL == m_hImageList)
return E_FAIL;
ImageList_SetOverlayImage(
m_hImageList,
iOVERLAY_BUSY_IMAGE,
OV_BUSY);
ImageList_SetOverlayImage(
m_hImageList,
iOVERLAY_ERROR_IMAGE,
OV_ERROR);
SendDlgItemMessage(
IDC_TV,
TVM_SETIMAGELIST,
TVSIL_NORMAL,
(LPARAM)m_hImageList);
return S_OK;
}
HRESULT
CConnectToDialog::FillupTheTreeView(
)
/*++
Routine Description:
This routine does 2 things, adds the NT 5.0 domain names and the Standalone subtree label.
Also makes the text over the TV invisible.
Arguments:
None.
Return value:
S_OK, On success
HRESULT sent by methods called, if it is not S_OK.
E_FAIL, on other errors.
--*/
{
HRESULT hr = S_OK;
//
// add trusted domains DNS names
// FT dfs roots will be added under these nodes
//
if (m_50DomainList.empty())
return hr;
for(NETNAMELIST::iterator i = m_50DomainList.begin(); i != m_50DomainList.end(); i++)
{
_ASSERTE((*i)->bstrNetName);
hr = AddSingleItemtoTV(
(*i)->bstrNetName,
iDOMAIN_IMAGE,
iDOMAIN_SELECTED_IMAGE,
true, // Children = true
TRUSTED_DOMAIN);
if (FAILED(hr))
break;
}
if (SUCCEEDED(hr))
{
// sort the trusted domains only
SendDlgItemMessage(IDC_TV, TVM_SORTCHILDREN, 0, 0);
}
return hr;
}
HRESULT
CConnectToDialog::AddSingleItemtoTV(
IN const BSTR i_bstrItemLabel,
IN const int i_iImageIndex,
IN const int i_iImageSelectedIndex,
IN const bool i_bChildren,
IN const NODETYPE i_NodeType,
IN HTREEITEM i_hItemParent /* = NULL */
)
{
RETURN_INVALIDARG_IF_NULL(i_bstrItemLabel);
HRESULT hr = S_OK;
TV_INSERTSTRUCT TVInsertData;
TV_ITEM TVItem;
HTREEITEM hCurrentItem = NULL;
ZeroMemory(&TVItem, sizeof(TVItem));
ZeroMemory(&TVInsertData, sizeof(TVInsertData));
TVItem.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
if (true == i_bChildren) // To decide whether we add the '+' or not
{
TVItem.mask |= TVIF_CHILDREN;
TVItem.cChildren = 1;
}
TVItem.pszText = i_bstrItemLabel;
TVItem.cchTextMax = _tcslen(i_bstrItemLabel);
TVItem.iImage = i_iImageIndex;
TVItem.iSelectedImage = i_iImageSelectedIndex;
TVItem.lParam = (LPARAM)i_NodeType;
TVInsertData.hParent = i_hItemParent;
TVInsertData.hInsertAfter = TVI_LAST; // No sorting to improve performance
TVInsertData.item = TVItem;
hCurrentItem = (HTREEITEM) SendDlgItemMessage(IDC_TV, TVM_INSERTITEM, 0, (LPARAM) (LPTV_INSERTSTRUCT) &TVInsertData);
if (NULL == hCurrentItem)
return E_FAIL;
return S_OK;
}
HRESULT
CConnectToDialog::GetNodeInfo(
IN HTREEITEM hItem,
OUT BSTR* o_bstrName,
OUT NODETYPE* pNodeType
)
{
_ASSERT(o_bstrName || pNodeType);
HRESULT hr = S_OK;
TCHAR szName[MAX_PATH];
TVITEM TVItem;
ZeroMemory(&TVItem, sizeof(TVItem));
TVItem.hItem = hItem;
if (o_bstrName)
{
TVItem.mask |= TVIF_TEXT;
TVItem.pszText = szName;
TVItem.cchTextMax = MAX_PATH;
}
if (pNodeType)
TVItem.mask |= TVIF_PARAM;
if ( SendDlgItemMessage(IDC_TV, TVM_GETITEM, 0, (LPARAM)&TVItem) )
{
if (o_bstrName)
{
*o_bstrName = SysAllocString(szName);
if (!*o_bstrName)
hr = E_OUTOFMEMORY;
}
if (pNodeType)
{
*pNodeType = (NODETYPE)TVItem.lParam;
}
} else {
hr = E_FAIL;
}
return hr;
}