639 lines
18 KiB
C++
639 lines
18 KiB
C++
#include "pch.h"
|
|
#include "stddef.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ Helper functions (exported)
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
/*----------------------------------------------------------------------------
|
|
/ MergeMenu
|
|
/ ---------
|
|
/ Merge two menus together, taking the first popup menu and merging it into
|
|
/ the target. We use the caption from the pop-up menu as the caption
|
|
/ for the target.
|
|
/
|
|
/ In:
|
|
/ hMenu = handle of menu to merge into
|
|
/ hMenuToInsert = handle of menu to get the popup from
|
|
/ iIndex = index to insert at
|
|
/
|
|
/ Out:
|
|
/ -
|
|
/----------------------------------------------------------------------------*/
|
|
VOID MergeMenu(HMENU hMenu, HMENU hMenuToInsert, INT iIndex)
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
HMENU hPopupMenu = NULL;
|
|
|
|
TraceEnter(TRACE_HANDLER|TRACE_VIEW, "MergeMenu");
|
|
|
|
hPopupMenu = CreatePopupMenu();
|
|
|
|
if ( hPopupMenu )
|
|
{
|
|
GetMenuString(hMenuToInsert, 0, szBuffer, ARRAYSIZE(szBuffer), MF_BYPOSITION);
|
|
InsertMenu(hMenu, iIndex, MF_BYPOSITION|MF_POPUP, (UINT_PTR)hPopupMenu, szBuffer);
|
|
|
|
Shell_MergeMenus(hPopupMenu, GetSubMenu(hMenuToInsert, 0), 0x0, 0x0, 0x7fff, 0);
|
|
}
|
|
|
|
TraceLeave();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ BindToPath
|
|
/ ----------
|
|
/ Given a namespace path bind to it returning the shell object
|
|
/
|
|
/ In:
|
|
/ pszPath -> path to bind to
|
|
/ riid = interface to request
|
|
/ ppvObject -> receives the object pointer
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
HRESULT BindToPath(LPWSTR pszPath, REFIID riid, LPVOID* ppObject)
|
|
{
|
|
HRESULT hres;
|
|
IShellFolder* psfDesktop = NULL;
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
TraceEnter(TRACE_VIEW, "BindToPath");
|
|
|
|
hres = CoCreateInstance(CLSID_ShellDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&psfDesktop);
|
|
FailGracefully(hres, "Failed to get IShellFolder for the desktop object");
|
|
|
|
hres = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL);
|
|
FailGracefully(hres, "Failed when getting root path of DS");
|
|
|
|
if ( ILIsEmpty(pidl) )
|
|
{
|
|
TraceMsg("PIDL is desktop, therefore just QIing for interface");
|
|
hres = psfDesktop->QueryInterface(riid, ppObject);
|
|
}
|
|
else
|
|
{
|
|
TraceMsg("Binding to IDLIST via BindToObject");
|
|
hres = psfDesktop->BindToObject(pidl, NULL, riid, ppObject);
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
if ( FAILED(hres) )
|
|
*ppObject = NULL;
|
|
|
|
DoRelease(psfDesktop);
|
|
DoILFree(pidl);
|
|
|
|
TraceLeaveResult(hres);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ GetColumnHandlerFromProperty
|
|
/ ----------------------------
|
|
/ Given a COLUMN structure allocate the property name appending the
|
|
/ CLSID of the handler if we have one.
|
|
/
|
|
/ In:
|
|
/ pColumn -> column value to decode
|
|
/ pProperty -> property value parse
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
HRESULT GetColumnHandlerFromProperty(LPCOLUMN pColumn, LPWSTR pProperty)
|
|
{
|
|
HRESULT hres;
|
|
LPWSTR pPropertyTemp;
|
|
LPWSTR pColumnHandlerCLSID;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_VIEW, "GetColumnHandlerFromProperty");
|
|
Trace(TEXT("pProperty is: %s"), W2T(pProperty));
|
|
|
|
if ( !pProperty )
|
|
pProperty = pColumn->pProperty;
|
|
|
|
// if we find a ',' then we must parse the GUID as it may be a CLSID for a column handler.
|
|
|
|
pColumnHandlerCLSID = wcschr(pProperty, L',');
|
|
|
|
if ( pColumnHandlerCLSID )
|
|
{
|
|
// attempt to extract the CLSID form the property name
|
|
|
|
*pColumnHandlerCLSID++ = L'\0'; // terminate the property name
|
|
|
|
if ( GetGUIDFromStringW(pColumnHandlerCLSID, &pColumn->clsidColumnHandler) )
|
|
{
|
|
TraceGUID("CLSID for handler is:", pColumn->clsidColumnHandler);
|
|
pColumn->fHasColumnHandler = TRUE;
|
|
}
|
|
else
|
|
{
|
|
TraceMsg("**** Failed to parse CLSID from property name ****");
|
|
}
|
|
|
|
// we truncated the string, so lets re-alloc the buffer with the
|
|
// new string value.
|
|
|
|
if ( SUCCEEDED(LocalAllocStringW(&pPropertyTemp, pProperty)) )
|
|
{
|
|
LocalFreeStringW(&pColumn->pProperty);
|
|
pColumn->pProperty = pPropertyTemp;
|
|
}
|
|
|
|
Trace(TEXT("Property name is now: %s"), W2T(pColumn->pProperty));
|
|
}
|
|
else
|
|
{
|
|
// now CLSID, so just allocate the property string if we need to.
|
|
|
|
if ( pColumn->pProperty != pProperty )
|
|
{
|
|
if ( SUCCEEDED(LocalAllocStringW(&pPropertyTemp, pProperty)) )
|
|
{
|
|
LocalFreeStringW(&pColumn->pProperty);
|
|
pColumn->pProperty = pPropertyTemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
TraceLeaveResult(S_OK);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ GetPropertyFromColumn
|
|
/ ---------------------
|
|
/ Given a COLUMN structure allocate the property name appending the
|
|
/ CLSID of the handler if we have one.
|
|
/
|
|
/ In:
|
|
/ ppProperty -> receives a pointer to the property string
|
|
/ pColumn -> column value to decode
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
HRESULT GetPropertyFromColumn(LPWSTR* ppProperty, LPCOLUMN pColumn)
|
|
{
|
|
HRESULT hres;
|
|
TCHAR szGUID[GUIDSTR_MAX+1];
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_VIEW, "GetPropertyFromColumn");
|
|
|
|
if ( !pColumn->fHasColumnHandler )
|
|
{
|
|
// just copy the property name
|
|
|
|
hres = LocalAllocStringW(ppProperty, pColumn->pProperty);
|
|
FailGracefully(hres, "Failed to allocate property");
|
|
}
|
|
else
|
|
{
|
|
// allocate a buffer and then place the property name and the
|
|
// column handler CLSID.
|
|
|
|
hres = LocalAllocStringLenW(ppProperty, lstrlenW(pColumn->pProperty)+GUIDSTR_MAX+1); // nb: +2 for ","
|
|
FailGracefully(hres, "Failed to allocate buffer for property name");
|
|
|
|
GetStringFromGUID(pColumn->clsidColumnHandler, szGUID, ARRAYSIZE(szGUID));
|
|
|
|
StrCpyW(*ppProperty, pColumn->pProperty);
|
|
StrCatW(*ppProperty, L",");
|
|
StrCatW(*ppProperty, T2W(szGUID));
|
|
}
|
|
|
|
hres = S_OK;
|
|
|
|
exit_gracefully:
|
|
|
|
TraceLeaveResult(hres);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ FreeColumn / FreeColumnValue
|
|
/ ----------------------------
|
|
/ A column consists of the header and filter information including the underlying
|
|
/ property value.
|
|
/
|
|
/ A COLUMNVALUE is the typed information for the column which must be freed
|
|
/ based the iPropertyType value.
|
|
/
|
|
/ In:
|
|
/ pColumn -> LPCOLUMN structure to released
|
|
/ or
|
|
/ pColumnValue ->LPCOLUMNVALUE structure to be released
|
|
/
|
|
/ Out:
|
|
/ -
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
VOID FreeColumnValue(LPCOLUMNVALUE pColumnValue)
|
|
{
|
|
TraceEnter(TRACE_VIEW, "FreeColumnValue");
|
|
|
|
switch ( pColumnValue->iPropertyType )
|
|
{
|
|
case PROPERTY_ISUNDEFINED:
|
|
case PROPERTY_ISBOOL:
|
|
case PROPERTY_ISNUMBER:
|
|
break;
|
|
|
|
case PROPERTY_ISUNKNOWN:
|
|
case PROPERTY_ISSTRING:
|
|
LocalFreeString(&pColumnValue->pszText);
|
|
break;
|
|
|
|
default:
|
|
Trace(TEXT("iPropertyValue is %d"), pColumnValue->iPropertyType);
|
|
TraceAssert(FALSE);
|
|
break;
|
|
}
|
|
|
|
pColumnValue->iPropertyType = PROPERTY_ISUNDEFINED; // no value
|
|
|
|
TraceLeave();
|
|
}
|
|
|
|
INT FreeColumnCB(LPVOID pItem, LPVOID pData)
|
|
{
|
|
FreeColumn((LPCOLUMN)pItem);
|
|
return 1;
|
|
}
|
|
|
|
VOID FreeColumn(LPCOLUMN pColumn)
|
|
{
|
|
TraceEnter(TRACE_VIEW, "FreeQueryResult");
|
|
|
|
if ( pColumn )
|
|
{
|
|
LocalFreeStringW(&pColumn->pProperty);
|
|
LocalFreeString(&pColumn->pHeading);
|
|
FreeColumnValue(&pColumn->filter);
|
|
DoRelease(pColumn->pColumnHandler);
|
|
}
|
|
|
|
TraceLeave();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ FreeQueryResult
|
|
/ ---------------
|
|
/ Given a QUERYRESULT structure free the elements within it
|
|
/
|
|
/ In:
|
|
/ pResult -> result blob to be released
|
|
/ cColumns = number of columns to be released
|
|
/
|
|
/ Out:
|
|
/ -
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
INT FreeQueryResultCB(LPVOID pItem, LPVOID pData)
|
|
{
|
|
FreeQueryResult((LPQUERYRESULT)pItem, PtrToUlong(pData));
|
|
return 1;
|
|
}
|
|
|
|
VOID FreeQueryResult(LPQUERYRESULT pResult, INT cColumns)
|
|
{
|
|
INT i;
|
|
|
|
TraceEnter(TRACE_VIEW, "FreeQueryResult");
|
|
|
|
if ( pResult )
|
|
{
|
|
LocalFreeStringW(&pResult->pObjectClass);
|
|
LocalFreeStringW(&pResult->pPath);
|
|
|
|
for ( i = 0 ; i < cColumns ; i++ )
|
|
FreeColumnValue(&pResult->aColumn[i]);
|
|
}
|
|
|
|
TraceLeave();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ PropertyIsFromAttribute
|
|
/ -----------------------
|
|
/ Get the property is value from the specified attribute.
|
|
/
|
|
/ In:
|
|
/ pszAttributeName -> attribute name
|
|
/ pdds -> IDsDisplaySpecifier
|
|
/
|
|
/ Out:
|
|
/ DWORD dwType
|
|
/----------------------------------------------------------------------------*/
|
|
DWORD PropertyIsFromAttribute(LPCWSTR pszAttributeName, IDsDisplaySpecifier *pdds)
|
|
{
|
|
DWORD dwResult = PROPERTY_ISUNKNOWN;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_CORE, "PropertyIsFromAttribute");
|
|
Trace(TEXT("Fetching attribute type for: %s"), W2CT(pszAttributeName));
|
|
|
|
switch ( pdds->GetAttributeADsType(pszAttributeName) )
|
|
{
|
|
case ADSTYPE_DN_STRING:
|
|
case ADSTYPE_CASE_EXACT_STRING:
|
|
case ADSTYPE_CASE_IGNORE_STRING:
|
|
case ADSTYPE_PRINTABLE_STRING:
|
|
case ADSTYPE_NUMERIC_STRING:
|
|
TraceMsg("Property is a string");
|
|
dwResult = PROPERTY_ISSTRING;
|
|
break;
|
|
|
|
case ADSTYPE_BOOLEAN:
|
|
TraceMsg("Property is a BOOL");
|
|
dwResult = PROPERTY_ISBOOL;
|
|
break;
|
|
|
|
case ADSTYPE_INTEGER:
|
|
TraceMsg("Property is a number");
|
|
dwResult = PROPERTY_ISNUMBER;
|
|
break;
|
|
|
|
default:
|
|
TraceMsg("Property is UNKNOWN");
|
|
break;
|
|
}
|
|
|
|
TraceLeaveValue(dwResult);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ MatchPattern
|
|
/ ------------
|
|
/ Given two strings, one a string the other a pattern match the two
|
|
/ using standard wildcarding "*" == any number of characters, "?" means
|
|
/ single character skip
|
|
/
|
|
/ In:
|
|
/ pString = string to compare
|
|
/ pPattern = pattern to compare against
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
BOOL MatchPattern(LPTSTR pString, LPTSTR pPattern)
|
|
{
|
|
TCHAR c, p, l;
|
|
|
|
for ( ;; )
|
|
{
|
|
switch (p = *pPattern++ )
|
|
{
|
|
case 0: // end of pattern
|
|
return *pString ? FALSE : TRUE; // if end of pString TRUE
|
|
|
|
case TEXT('*'):
|
|
{
|
|
while ( *pString )
|
|
{ // match zero or more char
|
|
if ( MatchPattern(pString++, pPattern) )
|
|
return TRUE;
|
|
}
|
|
|
|
return MatchPattern(pString, pPattern);
|
|
}
|
|
|
|
case TEXT('?'):
|
|
{
|
|
if (*pString++ == 0) // match any one char
|
|
return FALSE; // not end of pString
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if ( *pString++ != p )
|
|
return FALSE; // not a match
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ EnumClassAttrbutes
|
|
/ ------------------
|
|
/ This is a wrapper around the attribute enum functions exposed in
|
|
/ the IDsDisplaySpecifier interface.
|
|
/
|
|
/ We read the attributes into a DPA, then sort them add in the
|
|
/ extra columns exposed from this UI.
|
|
/
|
|
/ In:
|
|
/ pdds -> IDsDisplaySpecifier object
|
|
/ pszObjectClass = object class to enumerate
|
|
/ pcbEnum, lParam = enumeration callback
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
|
|
typedef struct
|
|
{
|
|
LPWSTR pszName;
|
|
LPWSTR pszDisplayName;
|
|
DWORD dwFlags;
|
|
} CLASSATTRIBUTE, * LPCLASSATTRIBUTE;
|
|
|
|
INT _FreeAttribute(LPCLASSATTRIBUTE pca)
|
|
{
|
|
LocalFreeStringW(&pca->pszName);
|
|
LocalFreeStringW(&pca->pszDisplayName);
|
|
LocalFree(pca);
|
|
return 1;
|
|
}
|
|
|
|
INT _FreeAttributeCB(LPVOID pv1, LPVOID pv2)
|
|
{
|
|
return _FreeAttribute((LPCLASSATTRIBUTE)pv1);
|
|
}
|
|
|
|
HRESULT _AddAttribute(HDPA hdpa, LPCWSTR pszName, LPCWSTR pszDisplayName, DWORD dwFlags)
|
|
{
|
|
HRESULT hres;
|
|
LPCLASSATTRIBUTE pca = NULL;
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_CORE, "_AddAttribute");
|
|
Trace(TEXT("Adding %s (%s)"), W2CT(pszDisplayName), W2CT(pszName));
|
|
|
|
pca = (LPCLASSATTRIBUTE)LocalAlloc(LPTR, SIZEOF(CLASSATTRIBUTE));
|
|
if ( !pca )
|
|
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate CLASSATTRIBUTE");
|
|
|
|
// pca->pszName = NULL;
|
|
// pca->pszDisplayName = NULL;
|
|
pca->dwFlags = dwFlags;
|
|
|
|
hres = LocalAllocStringW(&pca->pszName, pszName);
|
|
FailGracefully(hres, "Failed to copy the name");
|
|
|
|
hres = LocalAllocStringW(&pca->pszDisplayName, pszDisplayName);
|
|
FailGracefully(hres, "Failed to copy the name");
|
|
|
|
if ( -1 == DPA_AppendPtr(hdpa, pca) )
|
|
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to append the record to the DPA");
|
|
|
|
hres = S_OK;
|
|
|
|
exit_gracefully:
|
|
|
|
if ( FAILED(hres) && pca )
|
|
_FreeAttribute(pca);
|
|
|
|
TraceLeaveResult(hres);
|
|
}
|
|
|
|
HRESULT _AddAttributeCB(LPARAM lParam, LPCWSTR pszName, LPCWSTR pszDisplayName, DWORD dwFlags)
|
|
{
|
|
return _AddAttribute((HDPA)lParam, pszName, pszDisplayName, dwFlags);
|
|
}
|
|
|
|
INT _CompareAttributeCB(LPVOID pv1, LPVOID pv2, LPARAM lParam)
|
|
{
|
|
LPCLASSATTRIBUTE pca1 = (LPCLASSATTRIBUTE)pv1;
|
|
LPCLASSATTRIBUTE pca2 = (LPCLASSATTRIBUTE)pv2;
|
|
return StrCmpIW(pca1->pszDisplayName, pca2->pszDisplayName);
|
|
}
|
|
|
|
HRESULT EnumClassAttributes(IDsDisplaySpecifier *pdds, LPCWSTR pszObjectClass, LPDSENUMATTRIBUTES pcbEnum, LPARAM lParam)
|
|
{
|
|
HRESULT hres;
|
|
HDPA hdpaAttributes = NULL;
|
|
WCHAR szBuffer[MAX_PATH];
|
|
INT i;
|
|
|
|
TraceEnter(TRACE_CORE, "EnumClassAttributes");
|
|
|
|
hdpaAttributes = DPA_Create(16);
|
|
if ( !hdpaAttributes )
|
|
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate the DPA");
|
|
|
|
//
|
|
// add the stock properties for objectClass and ADsPath
|
|
//
|
|
|
|
LoadStringW(GLOBAL_HINSTANCE, IDS_OBJECTCLASS, szBuffer, ARRAYSIZE(szBuffer));
|
|
hres = _AddAttribute(hdpaAttributes, c_szObjectClassCH, szBuffer, DSECAF_NOTLISTED);
|
|
FailGracefully(hres, "Failed to add the ObjectClass default property");
|
|
|
|
LoadStringW(GLOBAL_HINSTANCE, IDS_ADSPATH, szBuffer, ARRAYSIZE(szBuffer));
|
|
hres = _AddAttribute(hdpaAttributes, c_szADsPathCH, szBuffer, DSECAF_NOTLISTED);
|
|
FailGracefully(hres, "Failed to add the ObjectClass default property");
|
|
|
|
//
|
|
// now call the IDsDisplaySpecifier object to enumerate the properites correctly
|
|
//
|
|
|
|
TraceMsg("Calling IDsDisplaySpecifier::EnumClassAttributes");
|
|
|
|
hres = pdds->EnumClassAttributes(pszObjectClass, _AddAttributeCB, (LPARAM)hdpaAttributes);
|
|
FailGracefully(hres, "Failed to add the attributes");
|
|
|
|
//
|
|
// now sort and return them all to the caller via their callback funtion
|
|
//
|
|
|
|
Trace(TEXT("Sorting %d attributes, to return to the caller"), DPA_GetPtrCount(hdpaAttributes));
|
|
DPA_Sort(hdpaAttributes, _CompareAttributeCB, NULL);
|
|
|
|
for ( i = 0 ; i < DPA_GetPtrCount(hdpaAttributes) ; i++ )
|
|
{
|
|
LPCLASSATTRIBUTE pca = (LPCLASSATTRIBUTE)DPA_FastGetPtr(hdpaAttributes, i);
|
|
TraceAssert(pca);
|
|
|
|
hres = pcbEnum(lParam, pca->pszName, pca->pszDisplayName, pca->dwFlags);
|
|
FailGracefully(hres, "Failed in cb to original caller");
|
|
}
|
|
|
|
hres = S_OK;
|
|
|
|
exit_gracefully:
|
|
|
|
if ( hdpaAttributes )
|
|
DPA_DestroyCallback(hdpaAttributes, _FreeAttributeCB, NULL);
|
|
|
|
TraceLeaveResult(hres);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
/ GetFriendlyAttributeName
|
|
/ ------------------------
|
|
/ Trim the column handler information if needed, and call the
|
|
/ friendly attribute name functions.
|
|
/
|
|
/ In:
|
|
/ pdds -> IDsDisplaySpecifier object
|
|
/ pszObjectClass, pszAttributeName => attribute info to look up
|
|
/ pszBuffer, chh => return buffer
|
|
/
|
|
/ Out:
|
|
/ HRESULT
|
|
/----------------------------------------------------------------------------*/
|
|
HRESULT GetFriendlyAttributeName(IDsDisplaySpecifier *pdds, LPCWSTR pszObjectClass, LPCWSTR pszAttributeName, LPWSTR pszBuffer, UINT cch)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
WCHAR szAttributeName[MAX_PATH];
|
|
USES_CONVERSION;
|
|
|
|
TraceEnter(TRACE_CORE, "GetFriendlyAttributeName");
|
|
|
|
//
|
|
// trim off the attribute suffix if we have one (eg: the GUID for the column handler)
|
|
//
|
|
|
|
if ( wcschr(pszAttributeName, L',') )
|
|
{
|
|
TraceMsg("Has column handler information");
|
|
|
|
StrCpyW(szAttributeName, pszAttributeName);
|
|
LPWSTR pszSeperator = wcschr(szAttributeName, L',');
|
|
*pszSeperator = L'\0';
|
|
|
|
pszAttributeName = szAttributeName;
|
|
}
|
|
|
|
//
|
|
// pick off some special cases before passing onto the COM object to process
|
|
//
|
|
|
|
Trace(TEXT("Looking up name for: %s"), W2CT(pszAttributeName));
|
|
|
|
if ( !StrCmpIW(pszAttributeName, c_szADsPath) )
|
|
{
|
|
LoadStringW(GLOBAL_HINSTANCE, IDS_ADSPATH, pszBuffer, cch);
|
|
}
|
|
else if ( !StrCmpIW(pszAttributeName, c_szObjectClass) )
|
|
{
|
|
LoadStringW(GLOBAL_HINSTANCE, IDS_OBJECTCLASS, pszBuffer, cch);
|
|
}
|
|
else
|
|
{
|
|
hres = pdds->GetFriendlyAttributeName(pszObjectClass, pszAttributeName, pszBuffer, cch);
|
|
}
|
|
|
|
TraceLeaveResult(hres);
|
|
}
|