mirror of https://github.com/tongzx/nt5src
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.
1655 lines
50 KiB
1655 lines
50 KiB
#include "pch.h"
|
|
#include "helpids.h"
|
|
#pragma hdrstop
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ Constants and other helpers
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
//
|
|
// Page size used for paging the result sets (better performance)
|
|
//
|
|
#define PAGE_SIZE 128
|
|
|
|
WCHAR c_szQueryNormal[] = L"(&(objectClass=*)(!showInAdvancedViewOnly=TRUE))";
|
|
WCHAR c_szQueryAll[] = L"(objectClass=*)";
|
|
WCHAR c_szShowInAdvancedViewOnly[] = L"showInAdvancedViewOnly";
|
|
WCHAR c_szObjectClass[] = L"objectClass";
|
|
WCHAR c_szADsPath[] = L"ADsPath";
|
|
WCHAR c_szName[] = L"name";
|
|
WCHAR c_szRDN[] = L"rdn";
|
|
WCHAR c_szLDAPPrefix[] = L"LDAP://";
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ CBrowseDlg class definition
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
class CBrowseDlg
|
|
{
|
|
private:
|
|
|
|
// a UNICODE version of the structure
|
|
DSBROWSEINFOW _bi;
|
|
|
|
// an IADsPathname object for usto use
|
|
IADsPathname* _pPathCracker;
|
|
|
|
// server being referenced (cracked out of the pszRoot path);
|
|
LPWSTR _pServer;
|
|
|
|
// browse information (initialized during startup)
|
|
WCHAR _szFilter[INTERNET_MAX_URL_LENGTH];
|
|
WCHAR _szNameAttribute[MAX_PATH];
|
|
|
|
public:
|
|
CBrowseDlg(PDSBROWSEINFOW pbi);
|
|
~CBrowseDlg();
|
|
|
|
private:
|
|
HRESULT _GetPathCracker(void);
|
|
LPCWSTR _GetSelectedPath(HWND hDlg) const;
|
|
LPCWSTR GetSelectedObjectClass(HWND hDlg) const;
|
|
int _SetSelectedPath(HWND hDlg, LPCWSTR pszADsPath);
|
|
HRESULT _BuildNodeString(LPCWSTR pszADsPath, LPCWSTR pszClass, LPWSTR *ppszResult);
|
|
HRESULT _ExpandNode(LPWSTR pszRootPath, HWND hwndTree, HTREEITEM hParentItem);
|
|
HRESULT _ExpandNode(IADs *pRootObject, HWND hwndTree, HTREEITEM hParentItem);
|
|
HRESULT _EnumerateNode(IADsContainer *pDsContainer, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded);
|
|
HRESULT _SearchNode(IDirectorySearch *pDsSearch, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded);
|
|
HRESULT _AddTreeNode(IADs *pDsObject, LPCWSTR pObjectPath, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem);
|
|
HRESULT _AddTreeNode(LPCWSTR pszPath, LPCWSTR pszClass, LPCWSTR pszName, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem);
|
|
HRESULT _AddTreeNode(LPDOMAINDESC pDomainDesc, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem);
|
|
BOOL _OnInitDlg(HWND hDlg);
|
|
BOOL _OnNotify(HWND hDlg, int idCtrl, LPNMHDR pnmh);
|
|
void _OnOK(HWND hDlg);
|
|
|
|
BOOL _DlgProc(HWND, UINT, WPARAM, LPARAM);
|
|
static INT_PTR CALLBACK s_DlgProc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
friend STDMETHODIMP_(int) DsBrowseForContainerW(PDSBROWSEINFOW pbi);
|
|
};
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ Helper function to create an IADsPathname "path cracker" object
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
HRESULT
|
|
CreatePathCracker(IADsPathname **ppPath)
|
|
{
|
|
HRESULT hr;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CreatePathCracker");
|
|
TraceAssert(ppPath != NULL);
|
|
|
|
hr = CoCreateInstance(CLSID_Pathname,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IADsPathname,
|
|
(LPVOID*)ppPath);
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ DsBrowseForContainer API implementation
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
STDMETHODIMP_(int)
|
|
DsBrowseForContainerA(PDSBROWSEINFOA pbi)
|
|
{
|
|
int nResult = -1;
|
|
DSBROWSEINFOW bi = {0};
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "DsBrowseForContainerA");
|
|
|
|
if (pbi == NULL ||
|
|
IsBadReadPtr(pbi, SIZEOF(DWORD)) ||
|
|
pbi->cbStruct < FIELD_OFFSET(DSBROWSEINFOA, dwReturnFormat) ||
|
|
IsBadReadPtr(pbi, pbi->cbStruct) ||
|
|
pbi->pszPath == NULL ||
|
|
pbi->cchPath == 0 ||
|
|
IsBadWritePtr(pbi->pszPath, pbi->cchPath))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
TraceLeaveValue(nResult);
|
|
}
|
|
|
|
CopyMemory(&bi, pbi, min(SIZEOF(bi), pbi->cbStruct));
|
|
|
|
// Thunk the ANSI strings to UNICODE as required, then
|
|
// call the UNICODE version of this API
|
|
|
|
if (pbi->pszTitle)
|
|
bi.pszTitle = A2CW(pbi->pszTitle);
|
|
|
|
if (pbi->pszCaption)
|
|
bi.pszCaption = A2CW(pbi->pszCaption);
|
|
|
|
nResult = DsBrowseForContainerW(&bi);
|
|
|
|
TraceLeaveValue(nResult);
|
|
}
|
|
|
|
STDMETHODIMP_(int)
|
|
DsBrowseForContainerW(PDSBROWSEINFOW pbi)
|
|
{
|
|
HRESULT hresCoInit;
|
|
int nResult = -1;
|
|
|
|
TraceEnter(TRACE_BROWSE, "DsBrowseForContainerW");
|
|
|
|
hresCoInit = CoInitialize(NULL);
|
|
FailGracefully(hresCoInit, "Failed when calling CoInitialize");
|
|
|
|
if (pbi == NULL ||
|
|
IsBadReadPtr(pbi, SIZEOF(DWORD)) ||
|
|
pbi->cbStruct < FIELD_OFFSET(DSBROWSEINFOW, dwReturnFormat) ||
|
|
IsBadReadPtr(pbi, pbi->cbStruct) ||
|
|
pbi->pszPath == NULL ||
|
|
pbi->cchPath == 0 ||
|
|
IsBadWritePtr(pbi->pszPath, pbi->cchPath * SIZEOF(WCHAR)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
if (pbi->pszRoot == NULL)
|
|
pbi->pszRoot = L"LDAP:";
|
|
|
|
if (pbi->pszRoot != NULL)
|
|
{
|
|
CBrowseDlg dlg(pbi);
|
|
nResult = (int)DialogBoxParam(GLOBAL_HINSTANCE,
|
|
MAKEINTRESOURCE(IDD_DSBROWSEFORCONTAINER),
|
|
pbi->hwndOwner,
|
|
CBrowseDlg::s_DlgProc,
|
|
(LPARAM)&dlg);
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
if (SUCCEEDED(hresCoInit))
|
|
CoUninitialize();
|
|
|
|
TraceLeaveValue(nResult);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ CBrowseDlg class implementation
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
CBrowseDlg::CBrowseDlg(PDSBROWSEINFOW pbi) :
|
|
_pPathCracker(NULL),
|
|
_pServer(NULL)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::CBrowseDlg");
|
|
|
|
TraceAssert(pbi != NULL && !IsBadReadPtr(pbi, pbi->cbStruct));
|
|
TraceAssert(pbi->pszPath != NULL &&
|
|
pbi->cchPath != 0 &&
|
|
!IsBadWritePtr(pbi->pszPath, pbi->cchPath * SIZEOF(WCHAR)));
|
|
|
|
CopyMemory(&_bi, pbi, min(SIZEOF(_bi), pbi->cbStruct));
|
|
|
|
//
|
|
// initialize the fitler we are using
|
|
//
|
|
|
|
StrCpyW(_szFilter, (_bi.dwFlags & DSBI_INCLUDEHIDDEN) ? c_szQueryAll:c_szQueryNormal);
|
|
Trace(TEXT("_szFilter: %s"), W2T(_szFilter));
|
|
|
|
_szNameAttribute[0] = L'\0';
|
|
|
|
TraceLeaveVoid();
|
|
}
|
|
|
|
|
|
CBrowseDlg::~CBrowseDlg()
|
|
{
|
|
LocalFreeStringW(&_pServer);
|
|
DoRelease(_pPathCracker);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CBrowseDlg::_GetPathCracker(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (_pPathCracker == NULL)
|
|
hr = CreatePathCracker(&_pPathCracker);
|
|
|
|
if (SUCCEEDED(hr) && _pPathCracker)
|
|
_pPathCracker->SetDisplayType(ADS_DISPLAY_FULL); // ensure we are set to full
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
LPCWSTR CBrowseDlg::_GetSelectedPath(HWND hDlg) const
|
|
{
|
|
LPCWSTR pszResult = NULL;
|
|
HWND hwndTree;
|
|
HTREEITEM hti = NULL;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_GetSelectedPath");
|
|
TraceAssert(hDlg != NULL);
|
|
|
|
hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
|
|
|
|
if (hwndTree != NULL)
|
|
{
|
|
hti = TreeView_GetSelection(hwndTree);
|
|
}
|
|
|
|
if (hti != NULL)
|
|
{
|
|
TV_ITEM tvi;
|
|
tvi.mask = TVIF_PARAM | TVIF_HANDLE;
|
|
tvi.hItem = hti;
|
|
|
|
if (TreeView_GetItem(hwndTree, &tvi))
|
|
{
|
|
pszResult = (LPCWSTR)tvi.lParam;
|
|
}
|
|
}
|
|
|
|
TraceLeaveValue(pszResult);
|
|
}
|
|
|
|
|
|
LPCWSTR CBrowseDlg::GetSelectedObjectClass(HWND hDlg) const
|
|
{
|
|
LPCWSTR pszResult = NULL;
|
|
HWND hwndTree;
|
|
HTREEITEM hti = NULL;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::GetSelectedObjectClass");
|
|
TraceAssert(hDlg != NULL);
|
|
|
|
hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
|
|
|
|
if (hwndTree != NULL)
|
|
hti = TreeView_GetSelection(hwndTree);
|
|
|
|
if (hti != NULL)
|
|
{
|
|
TV_ITEM tvi;
|
|
tvi.mask = TVIF_PARAM | TVIF_HANDLE;
|
|
tvi.hItem = hti;
|
|
|
|
if (TreeView_GetItem(hwndTree, &tvi))
|
|
{
|
|
LPCWSTR pszPathAndClass = (LPWSTR)tvi.lParam;
|
|
pszResult = (LPCWSTR)ByteOffset(tvi.lParam, StringByteSizeW(pszPathAndClass));
|
|
}
|
|
}
|
|
|
|
TraceLeaveValue(pszResult);
|
|
}
|
|
|
|
|
|
int CBrowseDlg::_SetSelectedPath(HWND hDlg, LPCWSTR pszADsPath)
|
|
{
|
|
int nResult = -1;
|
|
HRESULT hr;
|
|
BSTR bstrPath = NULL;
|
|
UINT nPathLength;
|
|
HWND hwndTree;
|
|
HTREEITEM hItem;
|
|
TV_ITEM tvi;
|
|
tvi.mask = TVIF_HANDLE | TVIF_PARAM;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_SetSelectedPath");
|
|
|
|
// Run the path through a path cracker to get a known format
|
|
hr = _GetPathCracker();
|
|
FailGracefully(hr, "Unable to create ADsPathname object");
|
|
|
|
hr = _pPathCracker->Set((LPWSTR)pszADsPath, ADS_SETTYPE_FULL);
|
|
FailGracefully(hr, "Unable to parse path");
|
|
|
|
hr = _pPathCracker->Retrieve(ADS_FORMAT_WINDOWS, &bstrPath);
|
|
FailGracefully(hr, "Unable to build ADS Windows path");
|
|
|
|
nPathLength = lstrlenW(bstrPath);
|
|
Trace(TEXT("bstrPath: %s (%d)"), W2T(bstrPath), nPathLength);
|
|
|
|
hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
|
|
hItem = TreeView_GetChild(hwndTree, NULL);
|
|
|
|
while (hItem != NULL)
|
|
{
|
|
LPWSTR pszCompare;
|
|
UINT nCompareLength;
|
|
|
|
tvi.hItem = hItem;
|
|
TreeView_GetItem(hwndTree, &tvi);
|
|
|
|
pszCompare = (LPWSTR)tvi.lParam;
|
|
nCompareLength = lstrlenW(pszCompare);
|
|
|
|
Trace(TEXT("Comparing against: %s"), W2T(pszCompare));
|
|
|
|
// Does bstrPath contain pszCompare?
|
|
if (2 == CompareStringW(LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
bstrPath,
|
|
min(nCompareLength, nPathLength),
|
|
pszCompare,
|
|
nCompareLength))
|
|
{
|
|
TraceMsg("Current item, contains bstrPath");
|
|
|
|
TreeView_SelectItem(hwndTree, hItem);
|
|
|
|
if (nCompareLength == nPathLength)
|
|
{
|
|
TraceMsg("... and it was an exact match");
|
|
TreeView_Expand(hwndTree, hItem, TVE_EXPAND);
|
|
nResult = 0;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TraceMsg("... checking children for match");
|
|
TreeView_Expand(hwndTree, hItem, TVE_EXPAND);
|
|
hItem = TreeView_GetChild(hwndTree, hItem);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TraceMsg("Checking sibling as no match found");
|
|
hItem = TreeView_GetNextSibling(hwndTree, hItem);
|
|
}
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
if (bstrPath != NULL)
|
|
SysFreeString(bstrPath);
|
|
|
|
TraceLeaveValue(nResult);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_BuildNodeString(LPCWSTR pszADsPath, LPCWSTR pszClass, LPWSTR *ppszResult)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
INT cbPath, cbClass;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_BuildNodeString");
|
|
TraceAssert(pszADsPath != NULL);
|
|
TraceAssert(pszClass != NULL);
|
|
TraceAssert(ppszResult != NULL);
|
|
|
|
cbPath = StringByteSizeW(pszADsPath);
|
|
cbClass = StringByteSizeW(pszClass);
|
|
|
|
*ppszResult = (LPWSTR)LocalAlloc(LPTR, cbPath+cbClass);
|
|
|
|
if (!*ppszResult)
|
|
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate node string");
|
|
|
|
CopyMemory(*ppszResult, pszADsPath, cbPath);
|
|
CopyMemory(ByteOffset(*ppszResult, cbPath), pszClass, cbClass);
|
|
|
|
exit_gracefully:
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_ExpandNode(LPWSTR pszRootPath, HWND hwndTree, HTREEITEM hParentItem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IADs *pRootObject = NULL;
|
|
HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_ExpandNode");
|
|
TraceAssert(pszRootPath != NULL);
|
|
TraceAssert(hwndTree != NULL);
|
|
|
|
Trace(TEXT("Scope is: %s"), pszRootPath);
|
|
|
|
hr = ADsOpenObject(pszRootPath,
|
|
(_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pUserName:NULL,
|
|
(_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pPassword:NULL,
|
|
(_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION,
|
|
IID_IADs, (LPVOID*)&pRootObject);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _ExpandNode(pRootObject, hwndTree, hParentItem);
|
|
DoRelease(pRootObject);
|
|
}
|
|
|
|
SetCursor(hcur);
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_ExpandNode(IADs *pRootObject, HWND hwndTree, HTREEITEM hParentItem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CLASSCACHEGETINFO ccgi = { 0 };
|
|
LPCLASSCACHEENTRY pCacheEntry = NULL;
|
|
IADsContainer *pDsContainer = NULL;
|
|
IDirectorySearch *pDsSearch = NULL;
|
|
BOOL fIsContainer = TRUE;
|
|
ULONG cAdded = 0;
|
|
TV_ITEM tvi;
|
|
BSTR bstrADsPath = NULL;
|
|
BSTR bstrClass = NULL;
|
|
HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_ExpandNode");
|
|
TraceAssert(pRootObject != NULL);
|
|
TraceAssert(hwndTree != NULL);
|
|
|
|
//
|
|
// The IDirectorySearch method is better, but doesn't work for all objects
|
|
// (e.g. "LDAP:") so try enumerating if IDirectorySearch isn't supported.
|
|
//
|
|
|
|
hr = pRootObject->get_Class(&bstrClass);
|
|
FailGracefully(hr, "Failed to get class from object");
|
|
|
|
hr = pRootObject->get_ADsPath(&bstrADsPath);
|
|
FailGracefully(hr, "Failed to get class from object");
|
|
|
|
ccgi.dwFlags = CLASSCACHE_CONTAINER|CLASSCACHE_TREATASLEAF|CLASSCACHE_DSAVAILABLE;
|
|
ccgi.pPath = bstrADsPath;
|
|
ccgi.pObjectClass = bstrClass;
|
|
ccgi.pServer = _pServer;
|
|
|
|
if (_bi.dwFlags & DSBI_HASCREDENTIALS)
|
|
{
|
|
TraceMsg("Passing credential information to ClassCache API");
|
|
ccgi.pUserName = (LPWSTR)_bi.pUserName;
|
|
ccgi.pPassword = (LPWSTR)_bi.pPassword;
|
|
}
|
|
|
|
if (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE)
|
|
{
|
|
TraceMsg("Setting the CLASSCACHE_SIMPLEAUTHENTICATE flag");
|
|
ccgi.dwFlags |= CLASSCACHE_SIMPLEAUTHENTICATE;
|
|
}
|
|
|
|
if (SUCCEEDED(ClassCache_GetClassInfo(&ccgi, &pCacheEntry)))
|
|
{
|
|
fIsContainer = _IsClassContainer(pCacheEntry, _bi.dwFlags & DSBI_IGNORETREATASLEAF);
|
|
ClassCache_ReleaseClassInfo(&pCacheEntry);
|
|
}
|
|
|
|
if (fIsContainer)
|
|
{
|
|
if (SUCCEEDED(pRootObject->QueryInterface(IID_IDirectorySearch, (LPVOID*)&pDsSearch)))
|
|
{
|
|
hr = _SearchNode(pDsSearch, hwndTree, hParentItem, &cAdded);
|
|
DoRelease(pDsSearch);
|
|
}
|
|
else if (SUCCEEDED(pRootObject->QueryInterface(IID_IADsContainer, (LPVOID*)&pDsContainer)))
|
|
{
|
|
hr = _EnumerateNode(pDsContainer, hwndTree, hParentItem, &cAdded);
|
|
DoRelease(pDsContainer);
|
|
}
|
|
else
|
|
{
|
|
TraceMsg("No IDsSearch, no IDsContainer - were screwed");
|
|
}
|
|
}
|
|
|
|
// If we did not add anything we should update this item to let
|
|
// the user know something happened.
|
|
if (cAdded == 0)
|
|
{
|
|
tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;
|
|
tvi.hItem = hParentItem;
|
|
tvi.cChildren = 0;
|
|
TreeView_SetItem(hwndTree, &tvi);
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
SysFreeString(bstrADsPath);
|
|
SysFreeString(bstrClass);
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_EnumerateNode(IADsContainer *pDsContainer, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IEnumVARIANT *pEnum = NULL;
|
|
VARIANT aVariant[8];
|
|
ULONG cAdded = 0;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_EnumerateNode");
|
|
TraceAssert(pDsContainer != NULL);
|
|
TraceAssert(hwndTree != NULL);
|
|
|
|
hr = ADsBuildEnumerator(pDsContainer, &pEnum);
|
|
FailGracefully(hr, "Unable to build container enumerator object");
|
|
|
|
//
|
|
// Enumerate the given container
|
|
//
|
|
for (;;)
|
|
{
|
|
ULONG cFetched = 0;
|
|
ULONG i;
|
|
|
|
//
|
|
// Get a bunch of child containers and add them to the tree.
|
|
//
|
|
ADsEnumerateNext(pEnum, ARRAYSIZE(aVariant), aVariant, &cFetched);
|
|
|
|
if (cFetched == 0)
|
|
break;
|
|
|
|
for (i = 0; i < cFetched; i++)
|
|
{
|
|
IADs *pDsObject;
|
|
|
|
TraceAssert(V_VT(&aVariant[i]) == VT_DISPATCH);
|
|
TraceAssert(V_DISPATCH(&aVariant[i]));
|
|
|
|
if (SUCCEEDED(V_DISPATCH(&aVariant[i])->QueryInterface(IID_IADs,
|
|
(LPVOID*)&pDsObject)))
|
|
{
|
|
hr = _AddTreeNode(pDsObject, NULL, hwndTree, hParentItem, NULL);
|
|
if (SUCCEEDED(hr))
|
|
cAdded++;
|
|
|
|
DoRelease(pDsObject);
|
|
}
|
|
|
|
VariantClear(&aVariant[i]);
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
exit_gracefully:
|
|
|
|
DoRelease(pEnum);
|
|
|
|
if (pdwAdded != NULL)
|
|
*pdwAdded = cAdded;
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_SearchNode(IDirectorySearch *pDsSearch, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG cAdded = 0;
|
|
ADS_SEARCH_HANDLE hSearch = NULL;
|
|
ADS_SEARCH_COLUMN column;
|
|
ADS_SEARCHPREF_INFO prefInfo[3];
|
|
LPWSTR pszName = NULL;
|
|
LPWSTR pszADsPath = NULL;
|
|
LPWSTR pszObjectClass = NULL;
|
|
BSTR bstrName;
|
|
LPWSTR aProperties[] = { _szNameAttribute, c_szObjectClass, c_szADsPath, c_szName, c_szRDN };
|
|
LPWSTR *pProperties = aProperties;
|
|
INT cProperties = ARRAYSIZE(aProperties);
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_SearchNode");
|
|
TraceAssert(pDsSearch != NULL);
|
|
TraceAssert(hwndTree != NULL);
|
|
|
|
// Set the query prefernece to single level scope, and async retrevial
|
|
// rather than waiting for all objects
|
|
|
|
prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
|
|
prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
|
|
prefInfo[0].vValue.Integer = ADS_SCOPE_ONELEVEL;
|
|
|
|
prefInfo[1].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS;
|
|
prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN;
|
|
prefInfo[1].vValue.Boolean = TRUE;
|
|
|
|
prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; // paged results
|
|
prefInfo[2].vValue.dwType = ADSTYPE_INTEGER;
|
|
prefInfo[2].vValue.Integer = PAGE_SIZE;
|
|
|
|
hr = pDsSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo));
|
|
FailGracefully(hr, "Failed to set search preferences");
|
|
|
|
if (!_szNameAttribute[0])
|
|
{
|
|
TraceMsg("_szNameAttribute is NULL, so removing from query attributes");
|
|
pProperties++;
|
|
cProperties--;
|
|
}
|
|
|
|
hr = pDsSearch->ExecuteSearch(_szFilter, pProperties, cProperties, &hSearch);
|
|
FailGracefully(hr, "Failed in ExecuteSearch");
|
|
|
|
for (;;)
|
|
{
|
|
CLASSCACHEENTRY cce;
|
|
|
|
LocalFreeStringW(&pszObjectClass);
|
|
LocalFreeStringW(&pszADsPath);
|
|
LocalFreeStringW(&pszName);
|
|
|
|
ADsSetLastError(ERROR_SUCCESS, NULL, NULL); // clear the ADSI previous errror
|
|
|
|
hr = pDsSearch->GetNextRow(hSearch);
|
|
FailGracefully(hr, "Failed in GetNextRow");
|
|
|
|
if (hr == S_ADS_NOMORE_ROWS)
|
|
{
|
|
DWORD dwError;
|
|
WCHAR wszError[64], wszName[64];
|
|
|
|
hr = ADsGetLastError(&dwError, wszError, ARRAYSIZE(wszError), wszName, ARRAYSIZE(wszName));
|
|
if (SUCCEEDED(hr) && (dwError != ERROR_MORE_DATA))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the columns for each of the properties we are interested in, if
|
|
// we failed to get any of the base properties for the object then lets
|
|
// just skip this entry as we cannot build a valid IDLIST for it. The
|
|
// properties that we request should be present on all objects.
|
|
//
|
|
|
|
if (FAILED(pDsSearch->GetColumn(hSearch, c_szObjectClass, &column)))
|
|
{
|
|
TraceMsg("Failed to get objectClass from search");
|
|
continue;
|
|
}
|
|
|
|
hr = ObjectClassFromSearchColumn(&column, &pszObjectClass);
|
|
pDsSearch->FreeColumn(&column);
|
|
FailGracefully(hr, "Failed to get the object class from the property");
|
|
|
|
if (FAILED(pDsSearch->GetColumn(hSearch, c_szADsPath, &column)))
|
|
{
|
|
TraceMsg("Failed to get ADsPath from search");
|
|
continue;
|
|
}
|
|
|
|
hr = StringFromSearchColumn(&column, &pszADsPath);
|
|
pDsSearch->FreeColumn(&column);
|
|
FailGracefully(hr, "Failed to convert the ADsPath column to a string");
|
|
|
|
//
|
|
// Try and read the name attribute from the query results, if that fails
|
|
// then lets pass the ADsPath into the pathname API and get the
|
|
// LEAF name (RDN) back.
|
|
//
|
|
|
|
if (_szNameAttribute[0])
|
|
{
|
|
TraceMsg("Name attribute specified, therefore trying to decode that");
|
|
hr = pDsSearch->GetColumn(hSearch, _szNameAttribute, &column);
|
|
}
|
|
|
|
if (!_szNameAttribute[0] || FAILED(hr))
|
|
{
|
|
TraceMsg("Either _szNameAttribute == NULL, or failed to read it");
|
|
|
|
hr = pDsSearch->GetColumn(hSearch, c_szName, &column);
|
|
if (FAILED(hr))
|
|
{
|
|
TraceMsg("'name' not returned as a column, so checking for RDN");
|
|
hr = pDsSearch->GetColumn(hSearch, c_szRDN, &column);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// so that succeeded and we have a search column that we can decode, so lets
|
|
// do so and put that value into a string
|
|
//
|
|
|
|
hr = StringFromSearchColumn(&column, &pszName);
|
|
pDsSearch->FreeColumn(&column);
|
|
FailGracefully(hr, "Failed to convert the name column to a string");
|
|
}
|
|
else
|
|
{
|
|
BSTR bstrName;
|
|
|
|
//
|
|
// so now we attempt to use the path cracker as the string doesn't exist
|
|
//
|
|
|
|
TraceMsg("Failed to get the name, rdn etc, so using the path cracker");
|
|
|
|
hr = _GetPathCracker();
|
|
FailGracefully(hr, "Unable to create ADsPathname object");
|
|
|
|
hr = _pPathCracker->Set((LPWSTR)pszADsPath, ADS_SETTYPE_FULL);
|
|
FailGracefully(hr, "Unable to parse path");
|
|
|
|
hr = _pPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
|
|
FailGracefully(hr, "Failed to set the display type for this value");
|
|
|
|
hr = _pPathCracker->Retrieve(ADS_FORMAT_X500_DN, &bstrName);
|
|
FailGracefully(hr, "Unable to retrieve requested path format");
|
|
|
|
hr = LocalAllocStringW(&pszName, bstrName);
|
|
SysFreeString(bstrName);
|
|
FailGracefully(hr, "Failed to alloc clone of the name");
|
|
}
|
|
|
|
Trace(TEXT("class %s, name %s, ADsPath %s"), W2T(pszObjectClass), W2T(pszName), W2T(pszADsPath));
|
|
|
|
hr = _AddTreeNode(pszADsPath, pszObjectClass, pszName, hwndTree, hParentItem, NULL);
|
|
if (SUCCEEDED(hr))
|
|
cAdded++;
|
|
}
|
|
hr = S_OK;
|
|
|
|
exit_gracefully:
|
|
|
|
if (hSearch != NULL)
|
|
pDsSearch->CloseSearchHandle(hSearch);
|
|
|
|
LocalFreeStringW(&pszObjectClass);
|
|
LocalFreeStringW(&pszADsPath);
|
|
LocalFreeStringW(&pszName);
|
|
|
|
if (pdwAdded != NULL)
|
|
*pdwAdded = cAdded;
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_AddTreeNode(IADs *pDsObject, LPCWSTR pObjectPath, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
BSTR bstrClass = NULL;
|
|
BSTR bstrPath = NULL;
|
|
BSTR bstrName = NULL;
|
|
VARIANT var = {0};
|
|
LPWSTR pszName = NULL;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_AddTreeNode");
|
|
TraceAssert(pDsObject != NULL);
|
|
TraceAssert(hwndTree != NULL);
|
|
|
|
// Do we want to include hidden objects?
|
|
|
|
if (!(_bi.dwFlags & DSBI_INCLUDEHIDDEN))
|
|
{
|
|
if (SUCCEEDED(pDsObject->Get(c_szShowInAdvancedViewOnly, &var)))
|
|
{
|
|
TraceAssert(V_VT(&var) == VT_BOOL);
|
|
|
|
if (!V_BOOL(&var))
|
|
ExitGracefully(hr, E_FAIL, "Hidden node");
|
|
|
|
VariantClear(&var);
|
|
}
|
|
}
|
|
|
|
// Get the path and class name
|
|
if (!pObjectPath)
|
|
pDsObject->get_ADsPath(&bstrPath);
|
|
|
|
pDsObject->get_Class(&bstrClass);
|
|
|
|
// Try to get the name property, if that fails then try RDN (for X5 connectivity)
|
|
|
|
if (SUCCEEDED(pDsObject->Get(c_szName, &var))
|
|
|| SUCCEEDED(pDsObject->Get(c_szRDN, &var))
|
|
|| (_szNameAttribute[0] && SUCCEEDED(pDsObject->Get(_szNameAttribute, &var))))
|
|
{
|
|
TraceAssert(V_VT(&var) == VT_BSTR);
|
|
pszName = V_BSTR(&var);
|
|
}
|
|
else if (SUCCEEDED(pDsObject->get_Name(&bstrName)))
|
|
{
|
|
pszName = bstrName;
|
|
}
|
|
|
|
if (pszName != NULL)
|
|
{
|
|
hr = _AddTreeNode(pObjectPath ? pObjectPath : bstrPath,
|
|
bstrClass, pszName, hwndTree, hParentItem, phitem);
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
if (bstrPath)
|
|
SysFreeString(bstrPath);
|
|
|
|
SysFreeString(bstrClass);
|
|
SysFreeString(bstrName);
|
|
VariantClear(&var);
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_AddTreeNode(LPCWSTR pszPath, LPCWSTR pszClass, LPCWSTR pszName, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem)
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstrWinPath = NULL;
|
|
CLASSCACHEGETINFO ccgi = { 0 };
|
|
LPCLASSCACHEENTRY pCacheEntry = NULL;
|
|
TV_INSERTSTRUCT tvi;
|
|
BSTR bstrName = NULL;
|
|
BOOL fIsContainer = TRUE;
|
|
long nElements;
|
|
INT iResult = 0;
|
|
HTREEITEM hitem = NULL;
|
|
WCHAR szIconLocation[MAX_PATH];
|
|
INT iIconResID = 0;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_AddTreeNode");
|
|
TraceAssert(hwndTree != NULL);
|
|
|
|
if (!pszPath || !*pszPath || !pszClass || !*pszClass)
|
|
ExitGracefully(hr, E_INVALIDARG, "Missing required string parameter");
|
|
|
|
hr = _GetPathCracker();
|
|
FailGracefully(hr, "Failed to get the path cracker");
|
|
|
|
hr = _pPathCracker->Set((LPWSTR)pszPath, ADS_SETTYPE_FULL);
|
|
FailGracefully(hr, "Failed to set the path into the cracker");
|
|
|
|
//
|
|
// we can get the name from the cracker
|
|
//
|
|
|
|
if (!pszName || !*pszName)
|
|
{
|
|
_pPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY); // value only pls.
|
|
|
|
hr = _pPathCracker->Retrieve(ADS_FORMAT_LEAF, &bstrName);
|
|
FailGracefully(hr, "Failed to get leaf name");
|
|
|
|
Trace(TEXT("bstrName: is %s"), W2CT(bstrName));
|
|
pszName = bstrName;
|
|
}
|
|
|
|
tvi.hParent = hParentItem;
|
|
tvi.hInsertAfter = TVI_SORT;
|
|
tvi.item.mask = TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
|
|
TVIF_STATE | TVIF_TEXT | TVIF_PARAM;
|
|
tvi.item.state = 0;
|
|
tvi.item.stateMask = (UINT)-1;
|
|
tvi.item.cchTextMax = 0;
|
|
tvi.item.cChildren = 1;
|
|
tvi.item.pszText = (LPTSTR)W2CT(pszName);
|
|
tvi.item.iImage = 0;
|
|
tvi.item.iSelectedImage = 0;
|
|
tvi.item.lParam = 0;
|
|
|
|
//
|
|
// See if this object is a container, and get its image indexes
|
|
// from the class cache.
|
|
//
|
|
|
|
ccgi.dwFlags = CLASSCACHE_CONTAINER|CLASSCACHE_TREATASLEAF|CLASSCACHE_ICONS|CLASSCACHE_DSAVAILABLE;
|
|
ccgi.pPath = (LPWSTR)pszPath;
|
|
ccgi.pObjectClass = (LPWSTR)pszClass;
|
|
ccgi.pServer = _pServer;
|
|
|
|
if (_bi.dwFlags & DSBI_HASCREDENTIALS)
|
|
{
|
|
TraceMsg("Passing credential information to ClassCache API");
|
|
ccgi.pUserName = (LPWSTR)_bi.pUserName;
|
|
ccgi.pPassword = (LPWSTR)_bi.pPassword;
|
|
}
|
|
|
|
if (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE)
|
|
{
|
|
TraceMsg("Setting the CLASSCACHE_SIMPLEAUTHENTICATE flag");
|
|
ccgi.dwFlags |= CLASSCACHE_SIMPLEAUTHENTICATE;
|
|
}
|
|
|
|
if (SUCCEEDED(ClassCache_GetClassInfo(&ccgi, &pCacheEntry)))
|
|
{
|
|
fIsContainer = _IsClassContainer(pCacheEntry, _bi.dwFlags & DSBI_IGNORETREATASLEAF);
|
|
_GetIconLocation(pCacheEntry, DSGIF_DEFAULTISCONTAINER|DSGIF_GETDEFAULTICON, szIconLocation, ARRAYSIZE(szIconLocation), &iIconResID);
|
|
ClassCache_ReleaseClassInfo(&pCacheEntry);
|
|
}
|
|
|
|
if (!fIsContainer)
|
|
ExitGracefully(hr, E_FAIL, "Not a container");
|
|
|
|
//
|
|
// If we have a callback function then call it taking a note of
|
|
// the changes the caller wants to make to the node we are adding
|
|
// to the tree, internally the LPARAM of this item still points at
|
|
// the ADsPath/Class structure, but the display information has
|
|
// been suitably modified.
|
|
//
|
|
|
|
if (_bi.pfnCallback)
|
|
{
|
|
DSBITEM dsbItem;
|
|
|
|
dsbItem.cbStruct = SIZEOF(dsbItem);
|
|
dsbItem.pszADsPath = pszPath;
|
|
dsbItem.pszClass = pszClass;
|
|
dsbItem.dwMask = DSBF_STATE | DSBF_DISPLAYNAME;
|
|
dsbItem.dwState = 0;
|
|
dsbItem.dwStateMask = DSBS_ROOT;
|
|
StrCpyN(dsbItem.szDisplayName, W2CT(pszName), ARRAYSIZE(dsbItem.szDisplayName));
|
|
StrCpyN(dsbItem.szIconLocation, W2CT(szIconLocation), ARRAYSIZE(dsbItem.szIconLocation));
|
|
dsbItem.iIconResID = iIconResID;
|
|
|
|
if (_bi.dwFlags & DSBI_CHECKBOXES)
|
|
{
|
|
// handle the checked case properly;
|
|
dsbItem.dwStateMask |= DSBS_CHECKED;
|
|
}
|
|
|
|
if ((hParentItem == TVI_ROOT) || !hParentItem)
|
|
{
|
|
dsbItem.dwState |= DSBS_ROOT;
|
|
}
|
|
|
|
iResult = _bi.pfnCallback(GetParent(hwndTree), DSBM_QUERYINSERT, (LPARAM)&dsbItem, _bi.lParam);
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// For the UNICODE version of this call we must check to see if the
|
|
// caller handled it, if not then we will attempt to send an ANSI version
|
|
// and thunk the parameters accordingly.
|
|
//
|
|
|
|
if (!iResult)
|
|
{
|
|
DSBITEMA dsbItemA;
|
|
CopyMemory(&dsbItemA, &dsbItem, SIZEOF(dsbItemA));
|
|
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
pszName, -1,
|
|
dsbItemA.szDisplayName, ARRAYSIZE(dsbItemA.szDisplayName),
|
|
NULL, NULL);
|
|
|
|
WideCharToMultiByte(CP_ACP, 0,
|
|
dsbItem.szIconLocation, -1,
|
|
dsbItemA.szIconLocation, ARRAYSIZE(dsbItemA.szIconLocation),
|
|
NULL, NULL);
|
|
|
|
iResult = _bi.pfnCallback(GetParent(hwndTree), DSBM_QUERYINSERTA, (LPARAM)&dsbItemA, _bi.lParam);
|
|
if (iResult)
|
|
{
|
|
TraceMsg("ANSI DSBM_QUERYINSERT was successful");
|
|
|
|
Assert(SIZEOF(DSBITEMA) == SIZEOF(DSBITEMW));
|
|
CopyMemory(&dsbItem, &dsbItemA, SIZEOF(dsbItem));
|
|
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
dsbItemA.szDisplayName, -1,
|
|
dsbItem.szDisplayName, ARRAYSIZE(dsbItem.szDisplayName));
|
|
|
|
MultiByteToWideChar(CP_ACP, 0,
|
|
dsbItemA.szIconLocation, -1,
|
|
dsbItem.szIconLocation, ARRAYSIZE(dsbItem.szIconLocation));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// iResult == TRUE then the user has modified the structure and we
|
|
// should attempt to apply the changes they have made to the
|
|
// item we are about to add to the view.
|
|
//
|
|
|
|
if (iResult)
|
|
{
|
|
if ((dsbItem.dwMask & DSBF_STATE) &&
|
|
(dsbItem.dwStateMask & DSBS_HIDDEN))
|
|
{
|
|
ExitGracefully(hr, E_FAIL, "Item is hidden, therefore skipping");
|
|
}
|
|
|
|
if ((_bi.dwFlags & DSBI_CHECKBOXES) &&
|
|
(dsbItem.dwMask & DSBF_STATE) &&
|
|
(dsbItem.dwStateMask & DSBS_CHECKED))
|
|
{
|
|
// FEATURE: set the state image
|
|
}
|
|
|
|
if (dsbItem.dwMask & DSBF_ICONLOCATION)
|
|
{
|
|
StrCpyW(szIconLocation, T2CW(dsbItem.szIconLocation));
|
|
iIconResID = dsbItem.iIconResID;
|
|
}
|
|
|
|
if (dsbItem.dwMask & DSBF_DISPLAYNAME)
|
|
{
|
|
dsbItem.szDisplayName[DSB_MAX_DISPLAYNAME_CHARS-1] = TEXT('\0');
|
|
tvi.item.pszText = dsbItem.szDisplayName;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// convert the icon location to an index that we can use in the tree view
|
|
//
|
|
|
|
Trace(TEXT("Icon location is: %s,%d"), W2T(szIconLocation), iIconResID);
|
|
|
|
tvi.item.iImage = tvi.item.iSelectedImage = Shell_GetCachedImageIndex(W2T(szIconLocation), iIconResID, 0x0);
|
|
Trace(TEXT("Index into the shell image list %d"), tvi.item.iImage);
|
|
|
|
//
|
|
// Make a copy of the path to store as the node data.
|
|
// Try the path cracker first, so we get a known format.
|
|
// If that fails, just make a copy of what we've got.
|
|
//
|
|
// The problem with the path cracker is that it just cannot cope
|
|
// with names with no elements, therefore we have to work around
|
|
// this by checking for no elements then looking at the retrieved
|
|
// path to see if it terminates in a bogus way, if it does then
|
|
// lets fix it in a local buffer before creating the tree view node.
|
|
//
|
|
|
|
hr = _pPathCracker->GetNumElements(&nElements);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_pPathCracker->SetDisplayType(ADS_DISPLAY_FULL);
|
|
|
|
Trace(TEXT("nElements %d"), nElements);
|
|
hr = _pPathCracker->Retrieve(ADS_FORMAT_WINDOWS, &bstrWinPath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
int cchWinPath = lstrlenW(bstrWinPath);
|
|
|
|
Trace(TEXT("bstrWinPath %s (%d), nElements %d"),
|
|
W2T(bstrWinPath), cchWinPath, nElements);
|
|
if ((!nElements) &&
|
|
(cchWinPath >= 3) &&
|
|
(bstrWinPath[cchWinPath-1] == L'/') &&
|
|
(bstrWinPath[cchWinPath-2] == L'/') &&
|
|
(bstrWinPath[cchWinPath-3] == L':'))
|
|
{
|
|
LPWSTR pFixedWinPath = NULL;
|
|
|
|
hr = LocalAllocStringW(&pFixedWinPath, bstrWinPath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pFixedWinPath[cchWinPath-2] = L'\0';
|
|
Trace(TEXT("pFixedWinPath %s"), W2T(pFixedWinPath));
|
|
|
|
hr = _BuildNodeString(pFixedWinPath, pszClass, (LPWSTR*)&tvi.item.lParam);
|
|
LocalFreeStringW(&pFixedWinPath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = _BuildNodeString(bstrWinPath, pszClass, (LPWSTR*)&tvi.item.lParam);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
hr = _BuildNodeString(pszPath, pszClass, (LPWSTR*)&tvi.item.lParam);
|
|
|
|
FailGracefully(hr, "Unable to build node data");
|
|
|
|
//
|
|
// finally lets add the item to the tree, if that fails then ensure we free the
|
|
// structure hanging from the TVI.
|
|
//
|
|
|
|
hitem = TreeView_InsertItem(hwndTree, &tvi);
|
|
if (!hitem)
|
|
{
|
|
LocalFreeStringW((LPWSTR*)&tvi.item.lParam);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
SysFreeString(bstrName);
|
|
SysFreeString(bstrWinPath);
|
|
|
|
if (phitem)
|
|
*phitem = hitem;
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
HRESULT CBrowseDlg::_AddTreeNode(LPDOMAINDESC pDomainDesc, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR szBuffer[MAX_PATH];
|
|
DWORD dwIndex;
|
|
HTREEITEM hitem;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_AddTreeNode");
|
|
|
|
while (pDomainDesc)
|
|
{
|
|
StrCpyW(szBuffer, c_szLDAPPrefix);
|
|
|
|
if (_pServer)
|
|
{
|
|
StrCatW(szBuffer, _pServer);
|
|
StrCatW(szBuffer, L"/");
|
|
}
|
|
|
|
StrCatW(szBuffer, pDomainDesc->pszNCName);
|
|
|
|
Trace(TEXT("Scope is: %s"), W2T(szBuffer));
|
|
|
|
hr = _AddTreeNode(szBuffer,
|
|
pDomainDesc->pszObjectClass, NULL,
|
|
hwndTree, hParentItem,
|
|
&hitem);
|
|
|
|
FailGracefully(hr, "Failed to add location node");
|
|
|
|
if (pDomainDesc->pdChildList)
|
|
{
|
|
hr = _AddTreeNode(pDomainDesc->pdChildList,
|
|
hwndTree, hitem,
|
|
NULL);
|
|
|
|
FailGracefully(hr, "Failed to add children");
|
|
}
|
|
|
|
pDomainDesc = pDomainDesc->pdNextSibling;
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
exit_gracefully:
|
|
|
|
if (phitem)
|
|
*phitem = hitem;
|
|
|
|
TraceLeaveResult(hr);
|
|
}
|
|
|
|
|
|
BOOL CBrowseDlg::_OnInitDlg(HWND hDlg)
|
|
{
|
|
HRESULT hr;
|
|
HWND hwndTree;
|
|
IADs *pRoot = NULL;
|
|
IDsBrowseDomainTree* pDsDomains = NULL;
|
|
BSTR bstrServer = NULL;
|
|
LPDOMAINTREE pDomainTree = NULL;
|
|
HTREEITEM hitemRoot = NULL;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_OnInitDlg");
|
|
TraceAssert(hDlg != NULL);
|
|
|
|
if (_bi.pszCaption != NULL)
|
|
SetWindowText(hDlg, W2CT(_bi.pszCaption));
|
|
|
|
if (_bi.pszTitle == NULL)
|
|
{
|
|
RECT rc;
|
|
LONG yPos;
|
|
HWND hwnd;
|
|
|
|
// Get the position of the title window and hide it
|
|
hwnd = GetDlgItem(hDlg, DSBID_BANNER);
|
|
GetWindowRect(hwnd, &rc);
|
|
yPos = rc.top;
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
|
|
// Get the position of the tree control and adjust it
|
|
// to cover the title window.
|
|
hwnd = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
|
|
|
|
GetWindowRect(hwnd, &rc);
|
|
rc.top = yPos;
|
|
|
|
MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2);
|
|
MoveWindow(hwnd,
|
|
rc.left, rc.top,
|
|
rc.right - rc.left, rc.bottom - rc.top,
|
|
FALSE);
|
|
}
|
|
else
|
|
{
|
|
SetDlgItemText(hDlg, DSBID_BANNER, W2CT(_bi.pszTitle));
|
|
}
|
|
|
|
hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
|
|
TraceAssert(hwndTree != NULL);
|
|
|
|
// Update the TreeView style according to what the caller wants
|
|
if (_bi.dwFlags & (DSBI_NOBUTTONS | DSBI_NOLINES | DSBI_NOLINESATROOT | DSBI_CHECKBOXES))
|
|
{
|
|
DWORD dwStyle = GetWindowLong(hwndTree, GWL_STYLE);
|
|
dwStyle &= ~(_bi.dwFlags & (DSBI_NOBUTTONS | DSBI_NOLINES | DSBI_NOLINESATROOT));
|
|
dwStyle |= (_bi.dwFlags & DSBI_CHECKBOXES);
|
|
SetWindowLong(hwndTree, GWL_STYLE, dwStyle);
|
|
}
|
|
|
|
if (_bi.dwFlags & DSBI_CHECKBOXES)
|
|
{
|
|
// load and set the state imagelist (unchecked and checked squares)
|
|
}
|
|
|
|
//
|
|
// ensure we set the shared image list for the tree, this comes from shell32.
|
|
//
|
|
|
|
HIMAGELIST himlSmall;
|
|
Shell_GetImageLists(NULL, &himlSmall);
|
|
TreeView_SetImageList(hwndTree, himlSmall, TVSIL_NORMAL);
|
|
|
|
TraceAssert(_bi.pszRoot != NULL);
|
|
Trace(TEXT("pszRoot is: %s"), W2CT(_bi.pszRoot));
|
|
|
|
//
|
|
// if we have a callback function then we need to call it to get the information we need
|
|
// to browse the DS namespace.
|
|
//
|
|
|
|
if (_bi.pfnCallback)
|
|
{
|
|
DSBROWSEDATA dbd = { 0 };
|
|
|
|
dbd.pszFilter = _szFilter;
|
|
dbd.cchFilter = ARRAYSIZE(_szFilter);
|
|
dbd.pszNameAttribute = _szNameAttribute;
|
|
dbd.cchNameAttribute = ARRAYSIZE(_szNameAttribute);
|
|
|
|
TraceMsg("Calling callback to see if it can provide enumeration information");
|
|
|
|
if (_bi.pfnCallback(hDlg, DSBM_GETBROWSEDATA, (LPARAM)&dbd, _bi.lParam))
|
|
{
|
|
Trace(TEXT("szFilter after DSBM_GETBROWSEDATA: %s"),
|
|
_szFilter[0] ? W2T(_szFilter):TEXT("<not specified>"));
|
|
|
|
Trace(TEXT("szNameAttribute after DSBM_GETBROWSEDATA: %s"),
|
|
_szNameAttribute[0] ? W2T(_szNameAttribute):TEXT("<not specified>"));
|
|
|
|
if (!_szFilter[0])
|
|
ExitGracefully(hr, E_FAIL, "szFilter returned was NULL");
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Bind to the root object (make sure it's a valid object)
|
|
//
|
|
|
|
hr = ADsOpenObject((LPWSTR)_bi.pszRoot,
|
|
(_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pUserName:NULL,
|
|
(_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pPassword:NULL,
|
|
(_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION,
|
|
IID_IADs, (LPVOID*)&pRoot);
|
|
|
|
FailGracefully(hr, "Unable to bind to root object");
|
|
|
|
// attempt to decode the root path we have been given, if this includes a server
|
|
// name then lets store that so that we can call the cache codes. internally
|
|
// we preserver this.
|
|
|
|
hr = _GetPathCracker();
|
|
FailGracefully(hr, "Failed to get the path cracker API");
|
|
|
|
hr = _pPathCracker->Set((LPWSTR)_bi.pszRoot, ADS_SETTYPE_FULL);
|
|
FailGracefully(hr, "Unable to put the path into the path cracker");
|
|
|
|
if (SUCCEEDED(_pPathCracker->Retrieve(ADS_FORMAT_SERVER, &bstrServer)))
|
|
{
|
|
Trace(TEXT("Root path contains a server: %s"), W2T(bstrServer));
|
|
hr = LocalAllocStringW(&_pServer, bstrServer);
|
|
FailGracefully(hr, "Failed to allocate copy of ADsPath");
|
|
}
|
|
|
|
// DSBI_ENTIREDIRECTORY contains 2 bits which means that
|
|
// "if (_bi.dwFlags & DSBI_ENTIREDIRECTORY)" gives a false
|
|
// positive when a client calls with only DSBI_NOROOT set. This
|
|
// causes the entire directory to be displayed when it shouldn't.
|
|
|
|
//if (_bi.dwFlags & DSBI_ENTIREDIRECTORY)
|
|
if (_bi.dwFlags & (DSBI_ENTIREDIRECTORY & ~DSBI_NOROOT))
|
|
{
|
|
TV_ITEM tvi;
|
|
LPDOMAINDESC pDomain;
|
|
|
|
if (!(_bi.dwFlags & DSBI_NOROOT))
|
|
{
|
|
hr = _AddTreeNode(pRoot, _bi.pszRoot, hwndTree, NULL, &hitemRoot);
|
|
FailGracefully(hr, "Failed when adding in root node");
|
|
|
|
tvi.mask = TVIF_STATE|TVIF_HANDLE;
|
|
tvi.hItem = hitemRoot;
|
|
tvi.stateMask = -1;
|
|
TreeView_GetItem(hwndTree, &tvi);
|
|
tvi.state |= TVIS_EXPANDEDONCE;
|
|
TreeView_SetItem(hwndTree, &tvi);
|
|
}
|
|
|
|
hr = CoCreateInstance(CLSID_DsDomainTreeBrowser, NULL,CLSCTX_INPROC_SERVER, IID_IDsBrowseDomainTree, (LPVOID*)&pDsDomains);
|
|
FailGracefully(hr, "Failed to get IDsDomainTreeBrowser");
|
|
|
|
LPCWSTR pUserName = (_bi.dwFlags & DSBI_HASCREDENTIALS) ? _bi.pUserName:NULL;
|
|
LPCWSTR pPassword = (_bi.dwFlags & DSBI_HASCREDENTIALS) ? _bi.pPassword:NULL;
|
|
|
|
hr = pDsDomains->SetComputer(_pServer, pUserName, pPassword);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = pDsDomains->GetDomains(&pDomainTree, DBDTF_RETURNFQDN);
|
|
|
|
if (SUCCEEDED(hr))
|
|
_AddTreeNode(pDomainTree->aDomains, hwndTree, hitemRoot, NULL);
|
|
}
|
|
else if (_bi.dwFlags & DSBI_NOROOT)
|
|
{
|
|
// Skip root node and add its children as toplevel nodes
|
|
hr = _ExpandNode(pRoot, hwndTree, NULL);
|
|
}
|
|
else
|
|
{
|
|
// Add the root node
|
|
hr = _AddTreeNode(pRoot, _bi.pszRoot, hwndTree, NULL, &hitemRoot);
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Set the selected path to expand the tree, this can either be NULL, or
|
|
// a ADSI path. If we fail to do that and we have a root node then
|
|
// lets expand the root node to at least have something highlighted / expanded.
|
|
//
|
|
|
|
if (!(_bi.dwFlags & DSBI_EXPANDONOPEN) || (-1 == _SetSelectedPath(hDlg, _bi.pszPath)))
|
|
{
|
|
if (!(_bi.dwFlags & DSBI_NOROOT))
|
|
{
|
|
TraceMsg("Failed to select node, therefore expanding root");
|
|
_SetSelectedPath(hDlg, _bi.pszRoot);
|
|
}
|
|
}
|
|
|
|
if (_bi.pfnCallback)
|
|
_bi.pfnCallback(hDlg, BFFM_INITIALIZED, 0, _bi.lParam);
|
|
}
|
|
else
|
|
{
|
|
// SetLastError(???);
|
|
EndDialog(hDlg, -1);
|
|
}
|
|
|
|
DoRelease(pRoot);
|
|
DoRelease(pDsDomains);
|
|
|
|
SysFreeString(bstrServer);
|
|
|
|
TraceLeaveValue(TRUE);
|
|
}
|
|
|
|
|
|
BOOL CBrowseDlg::_OnNotify(HWND hDlg, int idCtrl, LPNMHDR pnmh)
|
|
{
|
|
LPNM_TREEVIEW pnmtv = (LPNM_TREEVIEW)pnmh;
|
|
LPWSTR pszClass;
|
|
HRESULT hr;
|
|
USES_CONVERSION;
|
|
|
|
switch (pnmh->code)
|
|
{
|
|
case TVN_DELETEITEM:
|
|
LocalFree((HLOCAL)pnmtv->itemOld.lParam);
|
|
break;
|
|
|
|
case TVN_ITEMEXPANDING:
|
|
if ((pnmtv->action == TVE_EXPAND) &&
|
|
!(pnmtv->itemNew.state & TVIS_EXPANDEDONCE))
|
|
{
|
|
if (pnmtv->itemNew.hItem == NULL)
|
|
{
|
|
pnmtv->itemNew.hItem = TreeView_GetSelection(pnmh->hwndFrom);
|
|
}
|
|
|
|
// Whether we succeed or not, mark the node as having been
|
|
// expanded once.
|
|
pnmtv->itemNew.mask = TVIF_STATE;
|
|
pnmtv->itemNew.stateMask = TVIS_EXPANDEDONCE;
|
|
pnmtv->itemNew.state = TVIS_EXPANDEDONCE;
|
|
|
|
if (!pnmtv->itemNew.lParam ||
|
|
FAILED(_ExpandNode((LPWSTR)pnmtv->itemNew.lParam,
|
|
pnmh->hwndFrom, pnmtv->itemNew.hItem)))
|
|
{
|
|
// Mark this node as having no children
|
|
pnmtv->itemNew.mask |= TVIF_CHILDREN;
|
|
pnmtv->itemNew.cChildren = 0;
|
|
}
|
|
|
|
TreeView_SetItem(pnmh->hwndFrom, &pnmtv->itemNew);
|
|
}
|
|
break;
|
|
|
|
case TVN_ITEMEXPANDED:
|
|
{
|
|
CLASSCACHEGETINFO ccgi = { 0 };
|
|
LPCLASSCACHEENTRY pCacheEntry = NULL;
|
|
|
|
pszClass = (LPWSTR)ByteOffset(pnmtv->itemNew.lParam, StringByteSizeW((LPWSTR)pnmtv->itemNew.lParam));
|
|
if (!pszClass)
|
|
return FALSE;
|
|
|
|
// Switch to the "open" image
|
|
|
|
ccgi.dwFlags = CLASSCACHE_DSAVAILABLE|CLASSCACHE_ICONS|(DSGIF_ISOPEN << CLASSCACHE_IMAGEMASK_BIT);
|
|
ccgi.pPath = (LPWSTR)pnmtv->itemNew.lParam;
|
|
ccgi.pObjectClass = pszClass;
|
|
ccgi.pServer = _pServer;
|
|
|
|
if (_bi.dwFlags & DSBI_HASCREDENTIALS)
|
|
{
|
|
ccgi.pUserName = (LPWSTR)_bi.pUserName;
|
|
ccgi.pPassword = (LPWSTR)_bi.pPassword;
|
|
}
|
|
|
|
if (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE)
|
|
ccgi.dwFlags |= CLASSCACHE_SIMPLEAUTHENTICATE;
|
|
|
|
if ((pnmtv->action == TVE_EXPAND) || (pnmtv->action == TVE_COLLAPSE))
|
|
{
|
|
//
|
|
// handle the expand and colapse of the icon in the tree, assuming that
|
|
// we show the correct state of corse (for those who don't have open states).
|
|
//
|
|
|
|
if (SUCCEEDED(ClassCache_GetClassInfo(&ccgi, &pCacheEntry)))
|
|
{
|
|
WCHAR szIconLocation[MAX_PATH];
|
|
INT iIconResID;
|
|
|
|
hr = _GetIconLocation(pCacheEntry, DSGIF_DEFAULTISCONTAINER|DSGIF_ISOPEN, szIconLocation, ARRAYSIZE(szIconLocation), &iIconResID);
|
|
ClassCache_ReleaseClassInfo(&pCacheEntry);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pnmtv->itemNew.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
|
|
pnmtv->itemNew.iImage = pnmtv->itemNew.iSelectedImage = Shell_GetCachedImageIndex(W2T(szIconLocation), iIconResID, 0x0);
|
|
TreeView_SetItem(pnmh->hwndFrom, &pnmtv->itemNew);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case TVN_SELCHANGED:
|
|
{
|
|
if (_bi.pfnCallback)
|
|
_bi.pfnCallback(hDlg, BFFM_SELCHANGED, (LPARAM)_GetSelectedPath(hDlg), _bi.lParam);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CBrowseDlg::_OnOK(HWND hDlg)
|
|
{
|
|
int nResult = -1;
|
|
LPCWSTR pszADsPath;
|
|
LPCWSTR pszObjectClass;
|
|
BSTR bstrPath = NULL;
|
|
DWORD dwFormat = _pServer ? ADS_FORMAT_X500:ADS_FORMAT_X500_NO_SERVER;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_BROWSE, "CBrowseDlg::_OnNotify");
|
|
TraceAssert(hDlg != NULL);
|
|
|
|
pszADsPath = _GetSelectedPath(hDlg);
|
|
pszObjectClass = GetSelectedObjectClass(hDlg);
|
|
|
|
if (!pszADsPath || !pszObjectClass)
|
|
ExitGracefully(nResult, -1, "Failed to get selected object");
|
|
|
|
// Honor the return type if they gave us one.
|
|
|
|
if ((_bi.dwFlags & DSBI_RETURN_FORMAT) && _bi.dwReturnFormat)
|
|
{
|
|
TraceMsg("Caller specified a dwReturnFormat");
|
|
dwFormat = _bi.dwReturnFormat;
|
|
}
|
|
|
|
// CBrowseDlg uses ADS_FORMAT_WINDOWS internally, so no need to convert if
|
|
// that's what they want.
|
|
|
|
if (dwFormat != ADS_FORMAT_WINDOWS)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = _GetPathCracker();
|
|
FailGracefully(hr, "Unable to create ADsPathname object");
|
|
|
|
hr = _pPathCracker->Set((LPWSTR)pszADsPath, ADS_SETTYPE_FULL);
|
|
FailGracefully(hr, "Unable to parse path");
|
|
|
|
hr = _pPathCracker->Retrieve(dwFormat, &bstrPath);
|
|
FailGracefully(hr, "Unable to retrieve requested path format");
|
|
|
|
pszADsPath = bstrPath;
|
|
}
|
|
|
|
// return the object class to the caller
|
|
|
|
StrCpyNW(_bi.pszPath, pszADsPath, _bi.cchPath);
|
|
|
|
if ((_bi.dwFlags & DSBI_RETURNOBJECTCLASS) && _bi.pszObjectClass)
|
|
{
|
|
Trace(TEXT("Returning objectClass: %s"), W2CT(pszObjectClass));
|
|
StrCpyNW(_bi.pszObjectClass, pszObjectClass, _bi.cchObjectClass);
|
|
}
|
|
|
|
nResult = IDOK;
|
|
|
|
exit_gracefully:
|
|
|
|
SysFreeString(bstrPath);
|
|
EndDialog(hDlg, nResult);
|
|
|
|
TraceLeaveVoid();
|
|
}
|
|
|
|
|
|
BOOL CBrowseDlg::_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
case WM_NOTIFY:
|
|
return _OnNotify(hDlg, (int)wParam, (LPNMHDR)lParam);
|
|
|
|
case BFFM_ENABLEOK:
|
|
EnableWindow(GetDlgItem(hDlg, IDOK), (BOOL)wParam);
|
|
break;
|
|
|
|
case BFFM_SETSELECTIONA:
|
|
case BFFM_SETSELECTIONW:
|
|
_SetSelectedPath(hDlg, (LPCWSTR)lParam); // lParam points to an ADSI path, which we require to be UNICODE
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDOK:
|
|
_OnOK(hDlg);
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, IDCANCEL);
|
|
break;
|
|
|
|
default:
|
|
// Message not handled
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
return _OnInitDlg(hDlg);
|
|
|
|
case WM_HELP:
|
|
case WM_CONTEXTMENU:
|
|
{
|
|
// check to see if we have a callback, if so then lets call so that they
|
|
// can display help on this object.
|
|
|
|
if (!_bi.pfnCallback)
|
|
return FALSE;
|
|
|
|
_bi.pfnCallback(hDlg,
|
|
(uMsg == WM_HELP) ? DSBM_HELP:DSBM_CONTEXTMENU,
|
|
(uMsg == WM_HELP) ? lParam:(LPARAM)wParam,
|
|
_bi.lParam);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return FALSE; // not handled
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
INT_PTR CALLBACK CBrowseDlg::s_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CBrowseDlg *pbd = (CBrowseDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
|
|
|
|
if (uMsg == WM_INITDIALOG)
|
|
{
|
|
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
|
|
pbd = (CBrowseDlg *)lParam;
|
|
}
|
|
|
|
if (pbd != NULL)
|
|
return (INT_PTR)pbd->_DlgProc(hDlg, uMsg, wParam, lParam);
|
|
|
|
return FALSE;
|
|
}
|