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.
 
 
 
 
 
 

2143 lines
70 KiB

#include "pch.h"
#pragma hdrstop
/*-----------------------------------------------------------------------------
/ Local functions / data
/----------------------------------------------------------------------------*/
#define CCLV_CHECKED 0x00002000
#define CCLV_UNCHECKED 0x00001000
#define DLU_EDGE 6
#define DLU_SEPERATOR 2
#define DLU_FIXEDELEMENT 80
#define CLID_OTHER 1 // other...
#define CLID_FIRST 2
static TCHAR c_szItems[] = TEXT("Items");
static TCHAR c_szObjectClassN[] = TEXT("ObjectClass%d");
static TCHAR c_szProperty[] = TEXT("Property%d");
static TCHAR c_szCondition[] = TEXT("Condition%d");
static TCHAR c_szValue[] = TEXT("Value%d");
static COLUMNINFO columns[] =
{
0, 0, IDS_CN, 0, c_szName,
0, 0, IDS_OBJECTCLASS, DSCOLUMNPROP_OBJECTCLASS, NULL,
0, DEFAULT_WIDTH_DESCRIPTION, IDS_DESCRIPTION, 0, c_szDescription,
};
static struct
{
DWORD dwPropertyType;
BOOL fNoValue;
INT iFilter;
INT idsFilter;
}
// NTRAID#NTBUG9-618605-2002/09/17-lucios
// Added PROPERTY_ISDNSTRING entries
conditions[] =
{
PROPERTY_ISUNKNOWN, 0, FILTER_IS, IDS_IS,
PROPERTY_ISUNKNOWN, 0, FILTER_ISNOT, IDS_ISNOT,
PROPERTY_ISUNKNOWN, 1, FILTER_DEFINED, IDS_DEFINED,
PROPERTY_ISUNKNOWN, 1, FILTER_UNDEFINED, IDS_NOTDEFINED,
PROPERTY_ISSTRING, 0, FILTER_STARTSWITH, IDS_STARTSWITH,
PROPERTY_ISSTRING, 0, FILTER_ENDSWITH, IDS_ENDSWITH,
PROPERTY_ISSTRING, 0, FILTER_IS, IDS_IS,
PROPERTY_ISSTRING, 0, FILTER_ISNOT, IDS_ISNOT,
PROPERTY_ISSTRING, 1, FILTER_DEFINED, IDS_DEFINED,
PROPERTY_ISSTRING, 1, FILTER_UNDEFINED, IDS_NOTDEFINED,
PROPERTY_ISDNSTRING, 0, FILTER_IS, IDS_IS,
PROPERTY_ISDNSTRING, 0, FILTER_ISNOT, IDS_ISNOT,
PROPERTY_ISDNSTRING, 1, FILTER_DEFINED, IDS_DEFINED,
PROPERTY_ISDNSTRING, 1, FILTER_UNDEFINED, IDS_NOTDEFINED,
PROPERTY_ISNUMBER, 0, FILTER_LESSEQUAL, IDS_LESSTHAN,
PROPERTY_ISNUMBER, 0, FILTER_GREATEREQUAL, IDS_GREATERTHAN,
PROPERTY_ISNUMBER, 0, FILTER_IS, IDS_IS,
PROPERTY_ISNUMBER, 0, FILTER_ISNOT, IDS_ISNOT,
PROPERTY_ISNUMBER, 1, FILTER_DEFINED, IDS_DEFINED,
PROPERTY_ISNUMBER, 1, FILTER_UNDEFINED, IDS_NOTDEFINED,
PROPERTY_ISBOOL, 1, FILTER_ISTRUE, IDS_ISTRUE,
PROPERTY_ISBOOL, 1, FILTER_ISFALSE, IDS_ISFALSE,
PROPERTY_ISBOOL, 1, FILTER_DEFINED, IDS_DEFINED,
PROPERTY_ISBOOL, 1, FILTER_UNDEFINED, IDS_NOTDEFINED,
};
static struct
{
INT cx;
INT fmt;
}
view_columns[] =
{
128, LVCFMT_LEFT,
128, LVCFMT_LEFT,
128, LVCFMT_LEFT,
};
// Class list used for building the property chooser menu
typedef struct
{
LPWSTR pName;
LPTSTR pDisplayName;
INT cReferences;
} CLASSENTRY, * LPCLASSENTRY;
// State maintained by the property well view
typedef struct
{
LPCLASSENTRY pClassEntry; // class entry reference
LPWSTR pProperty;
LPWSTR pValue; // can be NULL
INT iCondition;
} PROPERTYWELLITEM, * LPPROPERTYWELLITEM;
typedef struct
{
LPCQPAGE pQueryPage;
HDPA hdpaItems;
HDSA hdsaClasses;
INT cxEdge;
INT cyEdge;
HWND hwnd;
HWND hwndProperty;
HWND hwndPropertyLabel;
HWND hwndCondition;
HWND hwndConditionLabel;
HWND hwndValue;
HWND hwndValueLabel;
HWND hwndAdd;
HWND hwndRemove;
HWND hwndList;
LPCLASSENTRY pClassEntry;
LPWSTR pPropertyName;
IDsDisplaySpecifier *pdds;
} PROPERTYWELL, * LPPROPERTYWELL;
BOOL PropertyWell_OnInitDialog(HWND hwnd, LPCQPAGE pQueryPage);
BOOL PropertyWell_OnNCDestroy(LPPROPERTYWELL ppw);
BOOL PropertyWell_OnSize(LPPROPERTYWELL ppw, INT cxWindow, INT cyWindow);
VOID PropertyWell_OnDrawItem(LPPROPERTYWELL ppw, LPDRAWITEMSTRUCT pDrawItem);
VOID PropertyWell_OnChooseProperty(LPPROPERTYWELL ppw);
HRESULT PropertyWell_GetClassList(LPPROPERTYWELL ppw);
VOID PropertyWell_FreeClassList(LPPROPERTYWELL ppw);
LPCLASSENTRY PropertyWell_FindClassEntry(LPPROPERTYWELL ppw, LPWSTR pObjectClass);
HRESULT PropertyWell_AddItem(LPPROPERTYWELL ppw, LPCLASSENTRY pClassEntry, LPWSTR pProperty, INT iCondition, LPWSTR pValue);
VOID PropertyWell_RemoveItem(LPPROPERTYWELL ppw, INT iItem, BOOL fDeleteItem);
VOID PropertyWell_EditItem(LPPROPERTYWELL ppw, INT iItem);
HRESULT PropertyWell_EditProperty(LPPROPERTYWELL ppw, LPCLASSENTRY pClassEntry, LPWSTR pPropertyName, INT iCondition);
VOID PropertyWell_ClearControls(LPPROPERTYWELL ppw);
BOOL PropertyWell_EnableControls(LPPROPERTYWELL ppw, BOOL fEnable);
VOID PropertyWell_SetColumnWidths(LPPROPERTYWELL ppw);
HRESULT PropertyWell_GetQuery(LPPROPERTYWELL ppw, LPWSTR* ppQuery);
HRESULT PropertyWell_Persist(LPPROPERTYWELL ppw, IPersistQuery* pPersistQuery, BOOL fRead);
#define CONDITION_FROM_COMBO(hwnd) \
(int)ComboBox_GetItemData(hwnd, ComboBox_GetCurSel(hwnd))
//
// Control help meppings
//
static DWORD const aFormHelpIDs[] =
{
IDC_PROPERTYLABEL, IDH_FIELD,
IDC_PROPERTY, IDH_FIELD,
IDC_CONDITIONLABEL, IDH_CONDITION,
IDC_CONDITION, IDH_CONDITION,
IDC_VALUELABEL, IDH_VALUE,
IDC_VALUE, IDH_VALUE,
IDC_ADD, IDH_ADD,
IDC_REMOVE, IDH_REMOVE,
IDC_CONDITIONLIST, IDH_CRITERIA,
0, 0,
};
/*-----------------------------------------------------------------------------
/ PageProc_PropertyWell
/ ---------------------
/ PageProc for handling the messages for this object.
/
/ In:
/ pPage -> instance data for this form
/ hwnd = window handle for the form dialog
/ uMsg, wParam, lParam = message parameters
/
/ Out:
/ HRESULT (E_NOTIMPL) if not handled
/----------------------------------------------------------------------------*/
HRESULT CALLBACK PageProc_PropertyWell(LPCQPAGE pPage, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HRESULT hr = S_OK;
LPPROPERTYWELL ppw = (LPPROPERTYWELL)GetWindowLongPtr(hwnd, DWLP_USER);
LPWSTR pQuery = NULL;
TraceEnter(TRACE_FORMS, "PageProc_PropertyWell");
// Only handle page messages if we have a property well object,
// which is created when the PropWell dlg is init'd.
if (ppw)
{
switch ( uMsg )
{
case CQPM_INITIALIZE:
case CQPM_RELEASE:
break;
case CQPM_GETPARAMETERS:
{
LPDSQUERYPARAMS* ppDsQueryParams = (LPDSQUERYPARAMS*)lParam;
// if the add button is enabled then we must prompt the user and see if they
// want to add the current criteria to the query
if ( IsWindowEnabled(ppw->hwndAdd) )
{
TCHAR szProperty[MAX_PATH];
TCHAR szValue[MAX_PATH];
INT id;
LoadString(GLOBAL_HINSTANCE, IDS_WINDOWTITLE, szProperty, ARRAYSIZE(szProperty));
LoadString(GLOBAL_HINSTANCE, IDS_ENTERCRITERIA, szValue, ARRAYSIZE(szValue));
id = MessageBox(hwnd, szValue, szProperty, MB_YESNOCANCEL|MB_ICONWARNING);
Trace(TEXT("MessageBox returned %08x"), id);
if ( id == IDCANCEL )
{
ExitGracefully(hr, S_FALSE, "*** Aborting query ****");
}
else if ( id == IDYES )
{
GetWindowText(ppw->hwndValue, szValue, ARRAYSIZE(szValue));
id = CONDITION_FROM_COMBO(ppw->hwndCondition);
hr = PropertyWell_AddItem(ppw, ppw->pClassEntry, ppw->pPropertyName, id, szValue);
FailGracefully(hr, "Failed to add the item to the current query");
}
}
// zap anything in these fields and ensure the controls reflect the new state
PropertyWell_ClearControls(ppw);
if ( SUCCEEDED(PropertyWell_GetQuery(ppw, &pQuery)) && pQuery )
{
if ( !*ppDsQueryParams )
hr = QueryParamsAlloc(ppDsQueryParams, pQuery, GLOBAL_HINSTANCE, ARRAYSIZE(columns), columns);
else
hr = QueryParamsAddQueryString(ppDsQueryParams, pQuery);
LocalFreeStringW(&pQuery);
}
break;
}
case CQPM_ENABLE:
PropertyWell_EnableControls(ppw, (BOOL)wParam);
break;
case CQPM_CLEARFORM:
ListView_DeleteAllItems(ppw->hwndList);
PropertyWell_ClearControls(ppw);
break;
case CQPM_PERSIST:
hr = PropertyWell_Persist(ppw, (IPersistQuery*)lParam, (BOOL)wParam);
break;
case CQPM_SETDEFAULTPARAMETERS:
{
LPOPENQUERYWINDOW poqw = (LPOPENQUERYWINDOW)lParam;
//
// if we recieve this message we should ensure that we have the IDsDsiplaySpecifier
// object and then we can set the credential information.
//
if ( ppw && poqw->pHandlerParameters )
{
LPDSQUERYINITPARAMS pdqip = (LPDSQUERYINITPARAMS)poqw->pHandlerParameters;
if ( pdqip->dwFlags & DSQPF_HASCREDENTIALS )
{
Trace(TEXT("pUserName : %s"), pdqip->pUserName ? pdqip->pUserName:TEXT("<not specified>"));
Trace(TEXT("pServer : %s"), pdqip->pServer ? pdqip->pServer:TEXT("<not specified>"));
hr = ppw->pdds->SetServer(pdqip->pServer, pdqip->pUserName, pdqip->pPassword, DSSSF_DSAVAILABLE);
FailGracefully(hr, "Failed to set the server information");
}
}
break;
}
case DSQPM_GETCLASSLIST:
{
DWORD cbStruct, offset;
LPDSQUERYCLASSLIST pDsQueryClassList = NULL;
INT i;
if ( wParam & DSQPM_GCL_FORPROPERTYWELL )
{
TraceMsg("Property well calling property well, ignore!");
break;
}
if ( !lParam )
ExitGracefully(hr, E_FAIL, "lParam == NULL, not supported");
// Get the list of classes that the user can/has selected properties from,
// having done this we can then can then generate a suitable query.
hr = PropertyWell_GetClassList(ppw);
FailGracefully(hr, "Failed to get the class list");
cbStruct = SIZEOF(DSQUERYCLASSLIST) + (DSA_GetItemCount(ppw->hdsaClasses)*SIZEOF(DWORD));
offset = cbStruct;
for ( i = 0 ; i < DSA_GetItemCount(ppw->hdsaClasses) ; i++ )
{
LPCLASSENTRY pCE = (LPCLASSENTRY)DSA_GetItemPtr(ppw->hdsaClasses, i);
TraceAssert(pCE);
cbStruct += StringByteSizeW(pCE->pName);
}
// Allocate the blob we need to pass out and fill it.
Trace(TEXT("Allocating class structure %d"), cbStruct);
pDsQueryClassList = (LPDSQUERYCLASSLIST)CoTaskMemAlloc(cbStruct);
TraceAssert(pDsQueryClassList);
if ( !pDsQueryClassList )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate class list structure");
Trace(TEXT("pDsQueryClassList %08x"), pDsQueryClassList);
pDsQueryClassList->cbStruct = cbStruct;
pDsQueryClassList->cClasses = DSA_GetItemCount(ppw->hdsaClasses);
for ( i = 0 ; i < DSA_GetItemCount(ppw->hdsaClasses) ; i++ )
{
LPCLASSENTRY pCE = (LPCLASSENTRY)DSA_GetItemPtr(ppw->hdsaClasses, i);
TraceAssert(pCE);
Trace(TEXT("Adding class: %s"), pCE->pName);
pDsQueryClassList->offsetClass[i] = offset;
StringByteCopyW(pDsQueryClassList, offset, pCE->pName);
offset += StringByteSizeW(pCE->pName);
}
TraceAssert(pDsQueryClassList);
*((LPDSQUERYCLASSLIST*)lParam) = pDsQueryClassList;
break;
}
case CQPM_HELP:
{
LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
WinHelp((HWND)pHelpInfo->hItemHandle,
DSQUERY_HELPFILE,
HELP_WM_HELP,
(DWORD_PTR)aFormHelpIDs);
break;
}
case DSQPM_HELPTOPICS:
{
HWND hwndFrame = (HWND)lParam;
HtmlHelp(hwndFrame, TEXT("omc.chm"), HH_HELP_FINDER, 0);
break;
}
default:
hr = E_NOTIMPL;
break;
}
}
exit_gracefully:
TraceLeaveResult(hr);
}
/*-----------------------------------------------------------------------------
/ Dialog helper functions
/----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ DlgProc_PropertyWell
/ --------------------
/ Standard dialog proc for the form, handle any special buttons and other
/ such nastyness we must here.
/
/ In:
/ hwnd, uMsg, wParam, lParam = standard parameters
/
/ Out:
/ INT_PTR
/----------------------------------------------------------------------------*/
INT_PTR CALLBACK DlgProc_PropertyWell(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
INT_PTR fResult = FALSE;
LPPROPERTYWELL ppw = NULL;
if ( uMsg == WM_INITDIALOG )
{
fResult = PropertyWell_OnInitDialog(hwnd, (LPCQPAGE)lParam);
}
else
{
ppw = (LPPROPERTYWELL)GetWindowLongPtr(hwnd, DWLP_USER);
switch ( uMsg )
{
case WM_NCDESTROY:
PropertyWell_OnNCDestroy(ppw);
break;
case WM_COMMAND:
{
switch ( LOWORD(wParam) )
{
case IDC_PROPERTYLABEL:
{
if ( HIWORD(wParam) == BN_CLICKED )
PropertyWell_OnChooseProperty(ppw);
break;
}
case IDC_PROPERTY:
case IDC_CONDITION:
case IDC_VALUE:
{
if ( (HIWORD(wParam) == EN_CHANGE) || (HIWORD(wParam) == CBN_SELCHANGE) )
PropertyWell_EnableControls(ppw, TRUE);
break;
}
case IDC_ADD:
{
TCHAR szProperty[MAX_PATH] = { TEXT('\0') };
TCHAR szValue[MAX_PATH] = { TEXT('\0') };
INT iCondition;
iCondition = CONDITION_FROM_COMBO(ppw->hwndCondition);
if ( IsWindowEnabled(ppw->hwndValue) )
GetWindowText(ppw->hwndValue, szValue, ARRAYSIZE(szValue));
PropertyWell_AddItem(ppw, ppw->pClassEntry, ppw->pPropertyName, iCondition, szValue);
break;
}
case IDC_REMOVE:
{
INT item = ListView_GetNextItem(ppw->hwndList, -1, LVNI_ALL|LVNI_SELECTED);
PropertyWell_RemoveItem(ppw, item, TRUE);
}
}
break;
}
case WM_DRAWITEM:
PropertyWell_OnDrawItem(ppw, (LPDRAWITEMSTRUCT)lParam);
break;
case WM_NOTIFY:
{
LPNMHDR pNotifyGeneric = (LPNMHDR)lParam;
switch ( pNotifyGeneric->code )
{
case LVN_DELETEITEM:
{
NM_LISTVIEW* pNotifyLVDel = (NM_LISTVIEW*)lParam;
PropertyWell_RemoveItem(ppw, pNotifyLVDel->iItem, FALSE);
break;
}
case LVN_ITEMCHANGED:
{
PropertyWell_EnableControls(ppw, TRUE);
break;
}
case NM_DBLCLK:
{
INT item = ListView_GetNextItem(ppw->hwndList, -1, LVNI_ALL|LVNI_SELECTED);
PropertyWell_EditItem(ppw, item);
break;
}
case LVN_GETEMPTYTEXT:
{
NMLVDISPINFO* pNotifyLVGetText = (NMLVDISPINFO*)lParam;
if ( pNotifyLVGetText->item.mask & LVIF_TEXT )
{
LoadString(
GLOBAL_HINSTANCE,
IDS_CRITERIAEMPTY,
pNotifyLVGetText->item.pszText,
pNotifyLVGetText->item.cchTextMax
);
SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
fResult = TRUE;
}
break;
}
}
break;
}
case WM_SIZE:
return PropertyWell_OnSize(ppw, LOWORD(lParam), HIWORD(lParam));
case WM_CONTEXTMENU:
WinHelp((HWND)wParam, DSQUERY_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)aFormHelpIDs);
fResult = TRUE;
break;
}
}
return fResult;
}
/*-----------------------------------------------------------------------------
/ PropertyWell_OnInitDlg
/ ----------------------
/ Initialize the dialog, constructing the property DPA so that we can
/ build the store the query.
/
/ In:
/ hwnd = window handle being initialized
/ pDsQuery -> CDsQuery object to associate with
/
/ Out:
/ BOOL
/----------------------------------------------------------------------------*/
BOOL PropertyWell_OnInitDialog(HWND hwnd, LPCQPAGE pQueryPage)
{
HRESULT hr;
LPPROPERTYWELL ppw;
TCHAR szBuffer[MAX_PATH];
LV_COLUMN lvc;
INT i;
TraceEnter(TRACE_PWELL, "PropertyWell_OnInitDialog");
// Allocate the state structure and fill it
ppw = (LPPROPERTYWELL)LocalAlloc(LPTR, SIZEOF(PROPERTYWELL));
if ( !ppw )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to alloc PROPERTYWELL struct");
Trace(TEXT("ppw = %08x"), ppw);
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)ppw);
// now initialize the structure
ppw->pQueryPage = pQueryPage;
//ppw->hdpaItems = NULL;
//ppw->hdsaClasses = NULL;
ppw->cxEdge = GetSystemMetrics(SM_CXEDGE);
ppw->cyEdge = GetSystemMetrics(SM_CYEDGE);
ppw->hwnd = hwnd;
ppw->hwndProperty = GetDlgItem(hwnd, IDC_PROPERTY);
ppw->hwndPropertyLabel = GetDlgItem(hwnd, IDC_PROPERTYLABEL);
ppw->hwndCondition = GetDlgItem(hwnd, IDC_CONDITION);
ppw->hwndConditionLabel = GetDlgItem(hwnd, IDC_CONDITIONLABEL);
ppw->hwndValue = GetDlgItem(hwnd, IDC_VALUE);
ppw->hwndValueLabel = GetDlgItem(hwnd, IDC_VALUELABEL);
ppw->hwndAdd = GetDlgItem(hwnd, IDC_ADD);
ppw->hwndRemove = GetDlgItem(hwnd, IDC_REMOVE);
ppw->hwndList = GetDlgItem(hwnd, IDC_CONDITIONLIST);
ppw->hdpaItems = DPA_Create(16);
if ( !ppw->hdpaItems )
ExitGracefully(hr, E_FAIL, "Failed to create DPA");
// ppw->pClassItem = NULL;
// ppw->pPropertyName = NULL;
hr = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&ppw->pdds);
FailGracefully(hr, "Failed to CoCreate the IDsDisplaySpecifier object");
ListView_SetExtendedListViewStyle(ppw->hwndList, LVS_EX_FULLROWSELECT|LVS_EX_LABELTIP);
// Add the conditions to the condition picker, then add the columns to the
// condition list.
for ( i = 0 ; i < ARRAYSIZE(view_columns) ; i++ )
{
lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT;
lvc.fmt = view_columns[i].fmt;
lvc.cx = view_columns[i].cx;
lvc.pszText = TEXT("Bla");
ListView_InsertColumn(ppw->hwndList, i, &lvc);
}
Edit_LimitText(ppw->hwndValue, MAX_PATH);
PropertyWell_EnableControls(ppw, TRUE);
exit_gracefully:
TraceLeaveValue(TRUE);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_OnNCDestroy
/ ------------------------
/ The dialog is being nuked, therefore remove our reference to the CDsQuery
/ and free any allocations we have with this window.
/
/ In:
/ ppw -> window defn to use
/
/ Out:
/ BOOL
/----------------------------------------------------------------------------*/
BOOL PropertyWell_OnNCDestroy(LPPROPERTYWELL ppw)
{
BOOL fResult = TRUE;
TraceEnter(TRACE_PWELL, "PropertyWell_OnNCDestroy");
if ( ppw )
{
if ( ppw->hdpaItems )
{
TraceAssert(0 == DPA_GetPtrCount(ppw->hdpaItems));
DPA_Destroy(ppw->hdpaItems);
}
PropertyWell_FreeClassList(ppw);
LocalFreeStringW(&ppw->pPropertyName);
DoRelease(ppw->pdds);
SetWindowLongPtr(ppw->hwnd, DWLP_USER, (LONG_PTR)NULL);
LocalFree((HLOCAL)ppw);
}
TraceLeaveValue(fResult);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_OnSize
/ -------------------
/ The property well dialog is being sized, therefore lets move the
/ controls around within it to reflect the new size.
/
/ In:
/ ppw -> property well to size
/ cx, cy = new size
/
/ Out:
/ BOOL
/----------------------------------------------------------------------------*/
BOOL PropertyWell_OnSize(LPPROPERTYWELL ppw, INT cxWindow, INT cyWindow)
{
RECT rect;
SIZE size;
INT x, cx;
INT xProperty, xCondition, xValue;
INT iSeperator, iEdge, iElement, iFixedElement;
TraceEnter(TRACE_PWELL, "PropertyWell_OnSize");
Trace(TEXT("New size cxWindow %d, cyWindow %d"), cxWindow, cyWindow);
iSeperator = (DLU_SEPERATOR * LOWORD(GetDialogBaseUnits())) / 4;
iEdge = (DLU_EDGE * LOWORD(GetDialogBaseUnits())) / 4;
iFixedElement = (DLU_FIXEDELEMENT * LOWORD(GetDialogBaseUnits())) / 4;
x = cxWindow - (iEdge*2) - (iSeperator*2);
iElement = x / 3;
iFixedElement = min(iElement, iFixedElement);
iElement = x - (iFixedElement*2);
// Move the controls around accordingly
xProperty = iEdge;
GetRealWindowInfo(ppw->hwndProperty, &rect, &size);
SetWindowPos(ppw->hwndProperty, NULL, xProperty, rect.top, iFixedElement, size.cy, SWP_NOZORDER);
GetRealWindowInfo(ppw->hwndPropertyLabel, &rect, &size);
SetWindowPos(ppw->hwndPropertyLabel, NULL, xProperty, rect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
xCondition = iEdge + iFixedElement + iSeperator;
GetRealWindowInfo(ppw->hwndCondition, &rect, &size);
SetWindowPos(ppw->hwndCondition, NULL, xCondition, rect.top, iFixedElement, size.cy, SWP_NOZORDER);
GetRealWindowInfo(ppw->hwndConditionLabel, &rect, &size);
SetWindowPos(ppw->hwndConditionLabel, NULL, xCondition, rect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
xValue = cxWindow - iEdge - iElement;
GetRealWindowInfo(ppw->hwndValue, &rect, &size);
SetWindowPos(ppw->hwndValue, NULL, xValue, rect.top, iElement, size.cy, SWP_NOZORDER);
GetRealWindowInfo(ppw->hwndValueLabel, &rect, &size);
SetWindowPos(ppw->hwndValueLabel, NULL, xValue, rect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
// Move the add / remove buttons
GetRealWindowInfo(ppw->hwndRemove, &rect, &size);
x = cxWindow - iEdge - size.cx;
SetWindowPos(ppw->hwndRemove, NULL, x, rect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
GetRealWindowInfo(ppw->hwndAdd, &rect, &size);
x -= size.cx + iSeperator;
SetWindowPos(ppw->hwndAdd, NULL, x, rect.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
// Move the list view control + size accordingly
GetRealWindowInfo(ppw->hwndList, &rect, &size);
SetWindowPos(ppw->hwndList, NULL, iEdge, rect.top, cxWindow - (iEdge*2), size.cy, SWP_NOZORDER);
PropertyWell_SetColumnWidths(ppw);
TraceLeaveValue(FALSE);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_OnDrawItem
/ -----------------------
/ The property button is owner drawn, therefore lets handle rendering that
/ we assume that the base implementation (eg. the button control) is
/ handling storing the text, font and other interesting information we
/ will just render the face as required.
/
/ In:
/ ppw -> property well to size
/ pDrawItem -> DRAWITEMSTRUCT used for rendering
/
/ Out:
/ void
/----------------------------------------------------------------------------*/
VOID PropertyWell_OnDrawItem(LPPROPERTYWELL ppw, LPDRAWITEMSTRUCT pDrawItem)
{
SIZE thin = { ppw->cxEdge / 2, ppw->cyEdge / 2 };
RECT rc = pDrawItem->rcItem;
HDC hdc = pDrawItem->hDC;
BOOL fDisabled = pDrawItem->itemState & ODS_DISABLED;
BOOL fSelected = pDrawItem->itemState & ODS_SELECTED;
BOOL fFocus = (pDrawItem->itemState & ODS_FOCUS)
#if (_WIN32_WINNT >= 0x0500)
&& !(pDrawItem->itemState & ODS_NOFOCUSRECT)
#endif
&& !(pDrawItem->itemState & ODS_DISABLED);
TCHAR szBuffer[64];
HBRUSH hbr;
INT i, x, y;
SIZE sz;
UINT fuFlags = DST_PREFIXTEXT;
TraceEnter(TRACE_PWELL, "PropertyWell_OnDrawItem");
if ( pDrawItem->CtlID != IDC_PROPERTYLABEL )
goto exit_gracefully;
// render the button edges (assumes that we have an NT4 look)
thin.cx = max(thin.cx, 1);
thin.cy = max(thin.cy, 1);
if ( fSelected )
{
DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT|BF_ADJUST);
OffsetRect(&rc, 1, 1);
}
else
{
DrawEdge(hdc, &rc, EDGE_RAISED, BF_RECT|BF_ADJUST);
}
FillRect(hdc, &rc, GetSysColorBrush(COLOR_3DFACE));
// put the focus rect in if we are focused...
if ( fFocus )
{
InflateRect(&rc, -thin.cx, -thin.cy);
DrawFocusRect(hdc, &rc);
InflateRect(&rc, thin.cx, thin.cy);
}
InflateRect(&rc, 1-thin.cx, -ppw->cyEdge);
rc.left += ppw->cxEdge*2;
// paint the arrow to the right of the control
x = rc.right - ppw->cxEdge - 13;
y = rc.top + ((rc.bottom - rc.top)/2) - 2;
if ( fDisabled )
{
hbr = (HBRUSH)GetSysColorBrush(COLOR_3DHILIGHT);
hbr = (HBRUSH)SelectObject(hdc, hbr);
x++;
y++;
PatBlt(hdc, x+1, y, 7, 1, PATCOPY);
PatBlt(hdc, x+2, y+1, 5, 1, PATCOPY);
PatBlt(hdc, x+3, y+2, 3, 1, PATCOPY);
PatBlt(hdc, x+4, y+3, 1, 1, PATCOPY);
SelectObject(hdc, hbr);
x--;
y--;
}
hbr = (HBRUSH)GetSysColorBrush(fDisabled ? COLOR_3DSHADOW : COLOR_BTNTEXT);
hbr = (HBRUSH)SelectObject(hdc, hbr);
PatBlt(hdc, x, y+1, 7, 1, PATCOPY);
PatBlt(hdc, x+1, y+2, 5, 1, PATCOPY);
PatBlt(hdc, x+2, y+3, 3, 1, PATCOPY);
PatBlt(hdc, x+3, y+4, 1, 1, PATCOPY);
SelectObject(hdc, hbr);
rc.right = x;
// render the label in the remaining area (clipped accordingly)
i = GetWindowText(ppw->hwndPropertyLabel, szBuffer, ARRAYSIZE(szBuffer));
GetTextExtentPoint(hdc, szBuffer, i, &sz);
x = rc.left+(((rc.right-rc.left)-sz.cx)/2);
if ( fDisabled )
fuFlags |= DSS_DISABLED;
#if (_WIN32_WINNT >= 0x0500)
if ( pDrawItem->itemState & ODS_NOACCEL )
fuFlags |= DSS_HIDEPREFIX;
#endif
DrawState(hdc, NULL, NULL,
(LPARAM)szBuffer, (WPARAM)0,
x, rc.top, sz.cx, sz.cy,
fuFlags);
exit_gracefully:
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ PropertyWell_OnChooseProperty
/ -----------------------------
/ Display the class / property list and build the menu from it, this calls on
/ several helper functions.
/
/ In:
/ ppw -> property well to size
/
/ Out:
/ void
/----------------------------------------------------------------------------*/
//
// call the property enumerator and populate the our DPA
//
typedef struct
{
UINT wID;
HDPA hdpaAttributes;
INT iClass;
HMENU hMenu;
} PROPENUMSTRUCT, * LPPROPENUMSTRUCT;
HRESULT CALLBACK _FillMenuCB(LPARAM lParam, LPCWSTR pAttributeName, LPCWSTR pDisplayName, DWORD dwFlags)
{
HRESULT hres = S_OK;
PROPENUMSTRUCT *ppes = (PROPENUMSTRUCT *)lParam;
MENUITEMINFO mii = { 0 };
UINT_PTR iProperty = 0;
TraceEnter(TRACE_PWELL, "_FillMenuCB");
if ( !(dwFlags & DSECAF_NOTLISTED) )
{
hres = StringDPA_AppendStringW(ppes->hdpaAttributes, pAttributeName, &iProperty);
FailGracefully(hres, "Failed to add the attribute to the DPA");
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_TYPE|MIIM_ID|MIIM_DATA;
mii.dwItemData = MAKELPARAM(ppes->iClass, iProperty);
mii.fType = MFT_STRING;
mii.wID = ppes->wID++;
mii.dwTypeData = (LPTSTR)pDisplayName; // why this takes as non-const is beyond me
mii.cch = lstrlenW(pDisplayName);
if ( !InsertMenuItem(ppes->hMenu, 0x7fff, TRUE, &mii) )
ExitGracefully(hres, E_FAIL, "Failed to add the item to the menu");
}
else
{
TraceMsg("Property marked as hidden, so not appending to the DPA");
}
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
VOID PropertyWell_OnChooseProperty(LPPROPERTYWELL ppw)
{
HRESULT hr;
HMENU hMenuToTrack, hMenu = NULL;
PROPENUMSTRUCT pes = { 0 };
RECT rcItem;
LPCLASSENTRY pCE;
LPWSTR pszAttribute;
UINT uID;
INT iItem, iClass;
MENUITEMINFO mii = { 0 };
DECLAREWAITCURSOR;
TraceEnter(TRACE_PWELL, "PropertyWell_OnChooseProperty");
SetWaitCursor();
// construct a menu, and populate with the elements from teh class list,
// which we store in a DSA assocaited with this query form
hr = PropertyWell_GetClassList(ppw);
FailGracefully(hr, "Failed to get the class list");
pes.wID = CLID_FIRST;
pes.hdpaAttributes = DPA_Create(4);
if ( !pes.hdpaAttributes )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate string DPA");
hMenuToTrack = hMenu = CreatePopupMenu();
TraceAssert(hMenu);
if ( !hMenu )
ExitGracefully(hr, E_FAIL, "Failed to create class menu");
for ( pes.iClass = 0; pes.iClass < DSA_GetItemCount(ppw->hdsaClasses); pes.iClass++ )
{
pCE = (LPCLASSENTRY)DSA_GetItemPtr(ppw->hdsaClasses, pes.iClass);
TraceAssert(pCE);
// Create the sub-menu for this entry in the cache and populate it with the list of
// properties we picked from the schema.
pes.hMenu = CreatePopupMenu();
TraceAssert(pes.hMenu);
if ( !pes.hMenu )
ExitGracefully(hr, E_FAIL, "Failed when creating the sub menu for the property list");
if ( FAILED(EnumClassAttributes(ppw->pdds, pCE->pName, _FillMenuCB, (LPARAM)&pes)) )
{
DestroyMenu(pes.hMenu);
ExitGracefully(hr, E_FAIL, "Failed when building the property menu");
}
// Now add that sub-menu to the main menu with a caption that reflects the name of
// the class we are picking from.
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_SUBMENU|MIIM_TYPE;
mii.fType = MFT_STRING;
mii.hSubMenu = pes.hMenu;
mii.dwTypeData = pCE->pDisplayName;
mii.cch = MAX_PATH;
if ( !InsertMenuItem(hMenu, 0x7fff, TRUE, &mii) )
{
DestroyMenu(pes.hMenu);
ExitGracefully(hr, E_FAIL, "Failed when building the class menu");
}
}
ResetWaitCursor();
// having constructed the menu lets display it just below the button
// we are invoked from, if the user selects something then lets put
// it into the edit line which will enable the rest of the UI.
GetWindowRect(ppw->hwndPropertyLabel, &rcItem);
if ( GetMenuItemCount(hMenu) == 1 )
{
TraceMsg("Single class in menu, therefore just showing properties");
hMenuToTrack = GetSubMenu(hMenu, 0);
TraceAssert(hMenuToTrack);
}
uID = TrackPopupMenu(hMenuToTrack,
TPM_TOPALIGN|TPM_RETURNCMD,
rcItem.left, rcItem.bottom,
0, ppw->hwnd, NULL);
if ( !uID )
{
TraceMsg("Menu canceled nothing selected");
}
else
{
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_DATA;
if ( !GetMenuItemInfo(hMenu, uID, FALSE, &mii) )
ExitGracefully(hr, E_FAIL, "Failed to get item data");
// unpick the item data and get the iClass and iProperty of the item
// we have selected, that way we can then populate the control
// with the property name.
pCE = (LPCLASSENTRY)DSA_GetItemPtr(ppw->hdsaClasses, LOWORD(mii.dwItemData));
TraceAssert(pCE);
pszAttribute = StringDPA_GetStringW(pes.hdpaAttributes, HIWORD(mii.dwItemData));
Trace(TEXT("Attribute selected : %s"), pszAttribute);
hr = PropertyWell_EditProperty(ppw, pCE, pszAttribute, -1);
FailGracefully(hr, "Failed to set edit property");
}
hr = S_OK; // success
exit_gracefully:
if ( hMenu )
DestroyMenu(hMenu);
StringDPA_Destroy(&pes.hdpaAttributes);
ResetWaitCursor();
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ Class/Property maps
/----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ PropertyWell_GetClassList
/ -------------------------
/ Obtain the list of visible classes for the for this user. If the
/ list is already present then just return S_OK.
/
/ In:
/ ppw -> property well structure
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
//
// Return all display specifiers who have a class display name and a list of
// attributes to be displayed in the UI.
//
WCHAR c_szQuery[] = L"(&(classDisplayName=*)(attributeDisplayNames=*))";
LPWSTR pProperties[] =
{
L"name",
L"classDisplayName",
};
#define PAGE_SIZE 128
HRESULT PropertyWell_GetClassList(LPPROPERTYWELL ppw)
{
HRESULT hr;
IQueryFrame* pQueryFrame = NULL;
IDirectorySearch* pds = NULL;
ADS_SEARCH_COLUMN column;
ADS_SEARCHPREF_INFO prefInfo[3];
ADS_SEARCH_HANDLE hSearch = NULL;
CLASSENTRY ce;
LPDSQUERYCLASSLIST pDsQueryClassList = NULL;
LPWSTR pName = NULL;
LPWSTR pDisplayName = NULL;
WCHAR szBufferW[MAX_PATH];
INT i;
DECLAREWAITCURSOR;
TraceEnter(TRACE_PWELL, "PropertyWell_GetClassList");
SetWaitCursor();
if ( !ppw->hdsaClasses )
{
// Construct a DSA for us to store the class information we need.
ppw->hdsaClasses = DSA_Create(SIZEOF(CLASSENTRY), 4);
TraceAssert(ppw->hdsaClasses);
if ( !ppw->hdsaClasses )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to create class DSA");
// Call the query form we are part of to see if they want to declare any classes
// for us to show in the drop down. We use the CQFWM_GETFRAME to get the
// IQueryFrame interface from the form and then call all the forms
// with a magic bit so we (the property well) ignore the
// request for the class list.
if ( SendMessage(GetParent(ppw->hwnd), CQFWM_GETFRAME, 0, (LPARAM)&pQueryFrame) )
{
if ( SUCCEEDED(pQueryFrame->CallForm(NULL, DSQPM_GETCLASSLIST,
DSQPM_GCL_FORPROPERTYWELL, (LPARAM)&pDsQueryClassList)) )
{
if ( pDsQueryClassList )
{
for ( i = 0 ; i < pDsQueryClassList->cClasses ; i++ )
{
LPWSTR pObjectClass = (LPWSTR)ByteOffset(pDsQueryClassList, pDsQueryClassList->offsetClass[i]);
TraceAssert(pObjectClass);
TraceAssert(ppw->pdds != NULL);
ppw->pdds->GetFriendlyClassName(pObjectClass, szBufferW, ARRAYSIZE(szBufferW));
ce.pName = NULL;
ce.pDisplayName = NULL;
ce.cReferences = 0;
if ( FAILED(LocalAllocStringW(&ce.pName, pObjectClass)) ||
FAILED(LocalAllocStringW(&ce.pDisplayName, szBufferW)) ||
( -1 == DSA_AppendItem(ppw->hdsaClasses, &ce)) )
{
LocalFreeStringW(&ce.pName);
LocalFreeString(&ce.pDisplayName);
}
}
}
}
}
// if we didn't get anything from the form we are hosted on then let us
// troll around in the display specifier container collecting all the
// objects from there.
if ( DSA_GetItemCount(ppw->hdsaClasses) == 0 )
{
// Set the query prefernece to single level scope, and async retrevial rather
// than waiting for all objects
TraceAssert(ppw->pdds);
hr = ppw->pdds->GetDisplaySpecifier(NULL, IID_IDirectorySearch, (LPVOID*)&pds);
FailGracefully(hr, "Failed to get IDsSearch on the display-spec container");
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 = pds->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo));
FailGracefully(hr, "Failed to set search preferences");
hr = pds->ExecuteSearch(c_szQuery, pProperties, ARRAYSIZE(pProperties), &hSearch);
FailGracefully(hr, "Failed in ExecuteSearch");
while ( TRUE )
{
LocalFreeStringW(&pName);
LocalFreeStringW(&pDisplayName);
// Get the next row from the result set, it consists of
// two columns. The first column is the class name of
// the object (<className-Display>) and the second
// is the friendly name of the class we are trying
// to display.
hr = pds->GetNextRow(hSearch);
FailGracefully(hr, "Failed to get the next row");
if ( hr == S_ADS_NOMORE_ROWS )
{
TraceMsg("No more results, no more rows");
break;
}
if ( SUCCEEDED(pds->GetColumn(hSearch, pProperties[0], &column)) )
{
hr = StringFromSearchColumn(&column, &pName);
pds->FreeColumn(&column);
FailGracefully(hr, "Failed to get the name object");
if (!pName)
{
break;
}
}
if ( SUCCEEDED(pds->GetColumn(hSearch, pProperties[1], &column)) )
{
hr = StringFromSearchColumn(&column, &pDisplayName);
pds->FreeColumn(&column);
FailGracefully(hr, "Failed to get the display name from the object");
}
Trace(TEXT("Display name %s for class %s"), pDisplayName, pName);
// now allocate an item and put it into the menu so we can
// allow the user to select an object from the class.
TraceAssert(pName);
// NTRAID#NTBUG9-344860-2001/10/16-lucios
wchar_t *tailString=L"-Display";
int tailLen=wcslen(tailString);
int pNameLen=wcslen(pName);
TraceAssert(pNameLen >= tailLen);
TraceAssert(lstrcmpi(pName+pNameLen-tailLen,tailString)==0);
if
(
(pNameLen < tailLen) ||
lstrcmpi(pName+pNameLen-tailLen,tailString)!=0
)
{
hr=E_FAIL;
FailGracefully
(
hr,
"Display name from the object is not terminated by -Display"
);
}
pName[pNameLen-tailLen]=0;
ce.pName = NULL;
ce.pDisplayName = NULL;
ce.cReferences = 0;
if ( *pName )
{
if ( FAILED(LocalAllocStringW(&ce.pName, pName)) ||
FAILED(LocalAllocStringW(&ce.pDisplayName, pDisplayName)) ||
( -1 == DSA_AppendItem(ppw->hdsaClasses, &ce)) )
{
LocalFreeStringW(&ce.pName);
LocalFreeString(&ce.pDisplayName);
}
}
}
}
}
hr = S_OK;
exit_gracefully:
LocalFreeStringW(&pName);
LocalFreeStringW(&pDisplayName);
if ( pDsQueryClassList )
CoTaskMemFree(pDsQueryClassList);
if ( hSearch )
pds->CloseSearchHandle(hSearch);
DoRelease(pQueryFrame);
DoRelease(pds);
PropertyWell_EnableControls(ppw, TRUE);
ResetWaitCursor();
TraceLeaveResult(hr);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_FreeClassList
/ --------------------------
/ Tidy up the class list by walking the DSA if we have one allocated
/ and release all dangling elements.
/
/ In:
/ ppw -> property well structure
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
INT _FreeClassListCB(LPVOID pItem, LPVOID pData)
{
LPCLASSENTRY pCE = (LPCLASSENTRY)pItem;
LocalFreeStringW(&pCE->pName);
LocalFreeString(&pCE->pDisplayName);
return 1;
}
VOID PropertyWell_FreeClassList(LPPROPERTYWELL ppw)
{
HRESULT hr;
TraceEnter(TRACE_PWELL, "PropertyWell_FreeClassList");
if ( ppw->hdsaClasses )
DSA_DestroyCallback(ppw->hdsaClasses, _FreeClassListCB, NULL);
ppw->hdsaClasses = NULL;
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ PropertyWell_FindClass
/ ----------------------
/ Find the class the caller wants. They give us a property well
/ and a class name, we return them a class entry structure or NULL.
/
/ In:
/ ppw -> property well structure
/ pObjectClass = class to locate
/
/ Out:
/ LPCLASSETNRY
/----------------------------------------------------------------------------*/
LPCLASSENTRY PropertyWell_FindClassEntry(LPPROPERTYWELL ppw, LPWSTR pObjectClass)
{
LPCLASSENTRY pResult = NULL;
INT i;
TraceEnter(TRACE_PWELL, "PropertyWell_FindClass");
if ( SUCCEEDED(PropertyWell_GetClassList(ppw)) )
{
for ( i = 0 ; i < DSA_GetItemCount(ppw->hdsaClasses) ; i++ )
{
LPCLASSENTRY pClassEntry = (LPCLASSENTRY)DSA_GetItemPtr(ppw->hdsaClasses, i);
TraceAssert(pClassEntry);
if ( !StrCmpIW(pClassEntry->pName, pObjectClass) )
{
pResult = pClassEntry;
break;
}
}
}
TraceLeaveValue(pResult);
}
/*-----------------------------------------------------------------------------
/ Rule list helper functions
/----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ PropertyWell_AddItem
/ --------------------
/ Add an item to the list of rules.
/
/ In:
/ ppw -> window defn to use
/ pProperty = property name
/ iCondition = id of condition to apply
/ pValue = string value to compare against
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT PropertyWell_AddItem(LPPROPERTYWELL ppw, LPCLASSENTRY pClassEntry, LPWSTR pProperty, INT iCondition, LPWSTR pValue)
{
HRESULT hr;
INT item = -1;
LV_ITEM lvi;
LPPROPERTYWELLITEM pItem = NULL;
TCHAR szBuffer[80];
WCHAR szBufferW[80];
TraceEnter(TRACE_PWELL, "PropertyWell_AddItem");
Trace(TEXT("Property: %s, Condition: %d, Value: %s"), pProperty, iCondition, pValue);
// Allocate an item structure to be stored into the list view DPA.
pItem = (LPPROPERTYWELLITEM)LocalAlloc(LPTR, SIZEOF(PROPERTYWELLITEM));
TraceAssert(pItem);
Trace(TEXT("pItem %0x8"), pItem);
if ( !pItem )
ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate item");
pItem->pClassEntry = pClassEntry;
pClassEntry->cReferences += 1;
// pItem->pProperty = NULL;
// pItem->pValue = NULL;
pItem->iCondition = iCondition;
hr = LocalAllocStringW(&pItem->pProperty, pProperty);
FailGracefully(hr, "Failed to add property to DPA item");
if ( pValue && pValue[0] )
{
hr = LocalAllocStringW(&pItem->pValue, pValue);
FailGracefully(hr, "Failed to add value to DPA item");
}
// Add the item to the list view, lParam pItem structure we just allocated,
// therefore when calling delete we can tidy up accordingly
TraceAssert(ppw->pdds);
hr = GetFriendlyAttributeName(ppw->pdds, pClassEntry->pName, pProperty, szBufferW, ARRAYSIZE(szBufferW));
lvi.mask = LVIF_TEXT|LVIF_STATE|LVIF_PARAM;
lvi.iItem = 0x7fffffff;
lvi.iSubItem = 0;
lvi.state = LVIS_SELECTED;
lvi.stateMask = LVIS_SELECTED;
lvi.pszText = szBufferW;
lvi.lParam = (LPARAM)pItem;
item = ListView_InsertItem(ppw->hwndList, &lvi);
Trace(TEXT("item %d"), item);
if ( item < 0 )
ExitGracefully(hr, E_FAIL, "Failed to put item into list view");
LoadString(GLOBAL_HINSTANCE, conditions[iCondition].idsFilter, szBuffer, ARRAYSIZE(szBuffer));
ListView_SetItemText(ppw->hwndList, item, 1, szBuffer);
if ( pValue )
ListView_SetItemText(ppw->hwndList, item, 2, pValue);
DPA_InsertPtr(ppw->hdpaItems, item, pItem);
hr = S_OK; // succeeeded
exit_gracefully:
if ( FAILED(hr) && (item == -1) && pItem )
{
LocalFreeStringW(&pItem->pProperty);
LocalFreeStringW(&pItem->pValue);
LocalFree((HLOCAL)pItem);
}
if ( SUCCEEDED(hr) )
{
PropertyWell_ClearControls(ppw);
ListView_EnsureVisible(ppw->hwndList, item, FALSE);
PropertyWell_SetColumnWidths(ppw);
}
TraceLeaveResult(hr);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_RemoveItem
/ -----------------------
/ Remvoe the given item from the list. If fDeleteItem is true then we
/ delete the list view entry, which in turn will call us again to
/ remove the actual data from our DPA.
/
/ In:
/ ppw -> window defn to use
/ iItem = item to be removed
/ fDelelete = call ListView_DeleteItem
/
/ Out:
/ BOOL
/----------------------------------------------------------------------------*/
void PropertyWell_RemoveItem(LPPROPERTYWELL ppw, INT iItem, BOOL fDeleteItem)
{
INT item;
LV_ITEM lvi;
LPPROPERTYWELLITEM pItem;
TraceEnter(TRACE_PWELL, "PropertyWell_RemoveItem");
Trace(TEXT("iItem %d, fDeleteItem %s"), iItem, fDeleteItem ? TEXT("TRUE"):TEXT("FALSE"));
if ( ppw && (iItem >= 0) )
{
if ( fDeleteItem )
{
// Now delete the item from the view, note that as a result of this we will
// be called again (from the WM_NOTIFY handler) to tidy up the structure.
item = ListView_GetNextItem(ppw->hwndList, iItem, LVNI_BELOW);
if ( item == -1 )
item = ListView_GetNextItem(ppw->hwndList, iItem, LVNI_ABOVE);
if ( item != -1 )
{
ListView_SetItemState(ppw->hwndList, item, LVIS_SELECTED, LVIS_SELECTED);
ListView_EnsureVisible(ppw->hwndList, item, FALSE);
}
ListView_DeleteItem(ppw->hwndList, iItem);
PropertyWell_SetColumnWidths(ppw);
PropertyWell_EnableControls(ppw, TRUE);
}
else
{
// Get the item from that index in the DPA, release the memory that it
// owns and then release it.
pItem = (LPPROPERTYWELLITEM)DPA_FastGetPtr(ppw->hdpaItems, iItem);
TraceAssert(pItem);
if ( pItem )
{
pItem->pClassEntry->cReferences -= 1;
TraceAssert(pItem->pClassEntry->cReferences >= 0);
LocalFreeStringW(&pItem->pProperty);
LocalFreeStringW(&pItem->pValue);
LocalFree((HLOCAL)pItem);
DPA_DeletePtr(ppw->hdpaItems, iItem);
}
}
}
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ PropertyWell_EditItem
/ ---------------------
/ Edit the given item in the list. In doing so we remove from the list
/ and populate the edit controls with data that represents this
/ rule.
/
/ In:
/ ppw -> window defn to use
/ iItem = item to edit
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
void PropertyWell_EditItem(LPPROPERTYWELL ppw, INT iItem)
{
TraceEnter(TRACE_PWELL, "PropertyWell_EditItem");
if ( ppw && (iItem >= 0) )
{
LPPROPERTYWELLITEM pItem = (LPPROPERTYWELLITEM)DPA_FastGetPtr(ppw->hdpaItems, iItem);
TraceAssert(pItem);
PropertyWell_EditProperty(ppw, pItem->pClassEntry, pItem->pProperty, pItem->iCondition);
if ( pItem->pValue )
SetWindowText(ppw->hwndValue, pItem->pValue);
PropertyWell_RemoveItem(ppw, iItem, TRUE);
PropertyWell_EnableControls(ppw, TRUE);
}
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ PropertyWell_EditProperty
/ -------------------------
/ Set the property edit control and reflect that change into the
/ other controls in the dialog (the conditions and editor).
/
/ In:
/ ppw -> property well
/ pClassEntry -> class entry structure
/ pPropertyName -> property name to edit
/ iCondition = condition to select
/
/ Out:
/ void
/----------------------------------------------------------------------------*/
HRESULT PropertyWell_EditProperty(LPPROPERTYWELL ppw, LPCLASSENTRY pClassEntry, LPWSTR pPropertyName, INT iCondition)
{
HRESULT hr;
TCHAR szBuffer[MAX_PATH];
WCHAR szBufferW[MAX_PATH];
INT i, iItem, iCurSel = 0;
DWORD dwPropertyType;
TraceEnter(TRACE_PWELL, "PropertyWell_EditProperty");
Trace(TEXT("Property name '%s', iCondition %d"), pPropertyName, iCondition);
// set the property name for this value, then look it up in the cache to get
// information about the operators we can apply.
ppw->pClassEntry = pClassEntry; // set state for the item we are editing
LocalFreeStringW(&ppw->pPropertyName);
hr = LocalAllocStringW(&ppw->pPropertyName, pPropertyName);
FailGracefully(hr, "Failed to alloc the property name");
TraceAssert(ppw->pdds);
GetFriendlyAttributeName(ppw->pdds, pClassEntry->pName, pPropertyName, szBufferW, ARRAYSIZE(szBufferW));
SetWindowText(ppw->hwndProperty, szBufferW);
ComboBox_ResetContent(ppw->hwndCondition);
SetWindowText(ppw->hwndValue, TEXT(""));
dwPropertyType = PropertyIsFromAttribute(pPropertyName, ppw->pdds);
for ( i = 0 ; i < ARRAYSIZE(conditions); i++ )
{
if ( conditions[i].dwPropertyType == dwPropertyType )
{
LoadString(GLOBAL_HINSTANCE, conditions[i].idsFilter, szBuffer, ARRAYSIZE(szBuffer));
iItem = ComboBox_AddString(ppw->hwndCondition, szBuffer);
if ( iItem >= 0 )
{
ComboBox_SetItemData(ppw->hwndCondition, iItem, i); // i == condition index
if ( i == iCondition )
{
Trace(TEXT("Setting current selection to %d"), iItem);
iCurSel = iItem;
}
}
}
}
ComboBox_SetCurSel(ppw->hwndCondition, iCurSel);
SetWindowText(ppw->hwndValue, TEXT(""));
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_EnableControls
/ ---------------------------
/ Check the controls within the view and determine what controls
/ should be enabled within it. If fDisable == TRUE then disable all the
/ controls in the dialog regardless of the dependancies on other controls.
/
/ The return value indicates if the control sare in a state whereby
/ we can add the criteria to the query.
/
/ In:
/ ppw -> window defn to use
/ fEnable = FALSE then disable all controls in dialog
/
/ Out:
/ BOOL
/----------------------------------------------------------------------------*/
// NTRAID#NTBUG9-650930-2002/07/30-artm
// Factored out common code and fixed focus bug.
//
// PropertyWell_ShiftFocus() : helper fctn for PropertyWell_EnableControls
//
// Sets the focus to the control whose ID == first_choice, but only if that
// control can be retrieved. If not, tries to set the focus to the control whose
// ID == second_choice.
//
HRESULT
PropertyWell_ShiftFocus(LPPROPERTYWELL ppw, int first_choice, int second_choice)
{
HRESULT hr = S_OK;
do // false loop
{
if (!ppw)
{
hr = E_INVALIDARG;
break;
}
HWND hWndParent = GetParent(ppw->hwnd);
if (!hWndParent)
{
hr = S_FALSE;
break;
}
HWND focusControl = GetDlgItem(hWndParent, first_choice);
int control_id = first_choice;
if (!focusControl)
{
focusControl = GetDlgItem(hWndParent, second_choice);
control_id = second_choice;
}
if (focusControl)
{
SendDlgItemMessage(
hWndParent,
control_id,
BM_SETSTYLE,
MAKEWPARAM(BS_DEFPUSHBUTTON, 0),
MAKELPARAM(TRUE, 0)
);
SendMessage(hWndParent, WM_NEXTDLGCTL, (WPARAM)focusControl, TRUE);
SetFocus(focusControl);
}
else
{
hr = E_FAIL;
}
}
while (false);
return hr;
}
BOOL PropertyWell_EnableControls(LPPROPERTYWELL ppw, BOOL fEnable)
{
BOOL fEnableCondition = FALSE;
BOOL fEnableValue = FALSE;
BOOL fEnableAdd = FALSE;
BOOL fEnableRemove = FALSE;
INT iCondition;
DWORD dwButtonStyle;
HWND hWndParent;
TraceEnter(TRACE_PWELL, "PropertyWell_EnableControls");
EnableWindow(ppw->hwndPropertyLabel, fEnable);
EnableWindow(ppw->hwndProperty, fEnable);
EnableWindow(ppw->hwndList, fEnable);
if ( fEnable )
{
fEnableCondition = (ppw->pPropertyName != NULL);
if ( fEnableCondition )
{
iCondition = CONDITION_FROM_COMBO(ppw->hwndCondition);
fEnableValue = !conditions[iCondition].fNoValue;
if ( !fEnableValue || GetWindowTextLength(ppw->hwndValue) )
fEnableAdd = TRUE;
}
if ( ListView_GetSelectedCount(ppw->hwndList) &&
( -1 != ListView_GetNextItem(ppw->hwndList, -1, LVNI_SELECTED|LVNI_ALL)) )
{
fEnableRemove = TRUE;
}
}
if ( !fEnableAdd && !fEnableValue )
{
dwButtonStyle = (DWORD) GetWindowLong(ppw->hwndAdd, GWL_STYLE);
if (dwButtonStyle & BS_DEFPUSHBUTTON)
{
SendMessage(ppw->hwndAdd, BM_SETSTYLE, MAKEWPARAM(BS_PUSHBUTTON, 0), MAKELPARAM(TRUE, 0));
// NTRAID#NTBUG9-650930-2002/07/30-artm
HRESULT hr = PropertyWell_ShiftFocus(
ppw,
CQID_FINDNOW, // 1st choice for new focus
IDOK); // 2nd choice for new focus
}
}
if (!fEnableRemove) {
dwButtonStyle = (DWORD) GetWindowLong(ppw->hwndRemove, GWL_STYLE);
if (dwButtonStyle & BS_DEFPUSHBUTTON) {
SendMessage(
ppw->hwndRemove,
BM_SETSTYLE,
MAKEWPARAM(BS_PUSHBUTTON, 0),
MAKELPARAM(TRUE, 0)
);
// NTRAID#NTBUG9-650930-2002/07/30-artm
HRESULT hr = PropertyWell_ShiftFocus(
ppw,
CQID_FINDNOW, // 1st choice for new focus
IDOK); // 2nd choice for new focus
}
}
// If disabling control that has focus, we need to move the focus to
// avoid losing it.
EnableWindow(ppw->hwndConditionLabel, fEnableCondition);
EnableWindow(ppw->hwndCondition, fEnableCondition);
EnableWindow(ppw->hwndValueLabel, fEnableValue);
EnableWindow(ppw->hwndValue, fEnableValue);
EnableWindow(ppw->hwndAdd, fEnableAdd);
EnableWindow(ppw->hwndRemove, fEnableRemove);
if ( fEnableAdd )
SetDefButton(ppw->hwnd, IDC_ADD);
TraceLeaveValue(fEnableAdd);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_ClearControls
/ --------------------------
/ Zap the contents of the edit controls.
/
/ In:
/ ppw -> window defn to use
/
/ Out:
/ BOOL
/----------------------------------------------------------------------------*/
VOID PropertyWell_ClearControls(LPPROPERTYWELL ppw)
{
TraceEnter(TRACE_PWELL, "PropertyWell_ClearControls");
LocalFreeStringW(&ppw->pPropertyName);
SetWindowText(ppw->hwndProperty, TEXT(""));
ComboBox_ResetContent(ppw->hwndCondition);
SetWindowText(ppw->hwndValue, TEXT(""));
PropertyWell_EnableControls(ppw, TRUE);
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ PropertyWell_SetColumnWidths
/ ----------------------------
/ Fix the widths of the columns in the list view section of the property
/ well so that the most is visible.
/
/ In:
/ ppw -> window defn to use
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
VOID PropertyWell_SetColumnWidths(LPPROPERTYWELL ppw)
{
RECT rect2;
INT cx;
TraceEnter(TRACE_PWELL, "PropertyWell_SetColumnWidths");
GetClientRect(ppw->hwndList, &rect2);
InflateRect(&rect2, -GetSystemMetrics(SM_CXBORDER)*2, 0);
cx = MulDiv((rect2.right - rect2.left), 20, 100);
ListView_SetColumnWidth(ppw->hwndList, 0, cx);
ListView_SetColumnWidth(ppw->hwndList, 1, cx);
ListView_SetColumnWidth(ppw->hwndList, 2, rect2.right - (cx*2));
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ PropertyWell_GetQuery
/ ---------------------
/ Take the items in the property well and construct a query from them,
/ the query is an AND of all the fields present in the list. The conditon
/ table in lists the prefix, condition and postfix for each of the possible
/ conditions in the combo box.
/
/ In:
/ ppw -> property well to construct from
/ ppQuery -> receives the query string
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
static void _GetQuery(LPPROPERTYWELL ppw, LPWSTR pQuery, UINT* pLen)
{
INT i;
TraceEnter(TRACE_PWELL, "_GetQuery");
if ( pQuery )
*pQuery = TEXT('\0');
TraceAssert(ppw->hdsaClasses);
TraceAssert(ppw->hdpaItems);
for ( i = 0 ; i < DSA_GetItemCount(ppw->hdsaClasses); i++ )
{
LPCLASSENTRY pClassEntry = (LPCLASSENTRY)DSA_GetItemPtr(ppw->hdsaClasses, i);
TraceAssert(pClassEntry);
if ( pClassEntry->cReferences )
{
Trace(TEXT("Class %s referenced %d times"), pClassEntry->pName, pClassEntry->cReferences);
GetFilterString(pQuery, pLen, FILTER_IS, L"objectCategory", pClassEntry->pName);
}
}
for ( i = 0 ; i < DPA_GetPtrCount(ppw->hdpaItems); i++ )
{
LPPROPERTYWELLITEM pItem = (LPPROPERTYWELLITEM)DPA_FastGetPtr(ppw->hdpaItems, i);
TraceAssert(pItem);
// NTRAID#NTBUG9- 669515-2002/02/20-lucios
// On the very specific case below, when we are searching for a computer
// That has samAccountName being exactly something, we need to search for
// name + "$" because there is a convention that the samAccountName should be
// created with the $ and displayed without it.
// Since this is not necessarily true we still search for name.
// The calls to PutStringElementW inside the if, would normally belong
// inside lower level functions like GetFilterString, but this is the
// most appropriate place for this patch.
// The magic strings "computer" and "samAccountName" should not be
// localized. 9 and 15 are being used for additional safety in memmory
// access provided by wcsncmp as opposed to wcscmp. +2 is for $ and \0.
// Finally, if the 3 first conditions succeed, but the allocation fails
// or newValue is NULL we fallback in the else, which is the code we had
// before the fix.
// After this fix was originally designed it was revisited to include
// ENDSWITH and ISNOT. ISNOT doesn't require the extra or "(|)" inside
// the if, since the default "&" is good enough.
LPWSTR newValue=NULL;
if(
(
conditions[pItem->iCondition].idsFilter == IDS_IS ||
conditions[pItem->iCondition].idsFilter == IDS_ENDSWITH ||
conditions[pItem->iCondition].idsFilter == IDS_ISNOT
) &&
_wcsnicmp(pItem->pClassEntry->pName,L"computer",9)==0 &&
_wcsnicmp(pItem->pProperty,L"samAccountName",15)==0 &&
SUCCEEDED(LocalAllocStringLenW(&newValue, wcslen(pItem->pValue)+2)) &&
newValue!=NULL
)
{
if(conditions[pItem->iCondition].idsFilter != IDS_ISNOT)
PutStringElementW( pQuery, pLen, L"(|" );
GetFilterString(pQuery, pLen, conditions[pItem->iCondition].iFilter,
pItem->pProperty, pItem->pValue);
wcscpy(newValue,pItem->pValue);
wcscat(newValue,L"$");
GetFilterString(pQuery, pLen, conditions[pItem->iCondition].iFilter,
pItem->pProperty, newValue);
if(conditions[pItem->iCondition].idsFilter != IDS_ISNOT)
PutStringElementW( pQuery, pLen, L")" );
}
else
{
GetFilterString(pQuery, pLen, conditions[pItem->iCondition].iFilter, pItem->pProperty,
pItem->pValue);
}
}
TraceLeave();
}
HRESULT PropertyWell_GetQuery(LPPROPERTYWELL ppw, LPWSTR* ppQuery)
{
HRESULT hr;
UINT cchQuery = 0;
TraceEnter(TRACE_PWELL, "PropertyWell_GetQuery");
*ppQuery = NULL;
hr = PropertyWell_GetClassList(ppw);
FailGracefully(hr, "Failed to get the class list");
_GetQuery(ppw, NULL, &cchQuery);
Trace(TEXT("cchQuery %d"), cchQuery);
if ( cchQuery )
{
hr = LocalAllocStringLenW(ppQuery, cchQuery);
FailGracefully(hr, "Failed to allocate buffer for query string");
_GetQuery(ppw, *ppQuery, NULL);
Trace(TEXT("Resulting query is %s"), *ppQuery);
}
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
/*-----------------------------------------------------------------------------
/ PropertyWell_Persist
/ --------------------
/ Persist the contents of the property well, either read them or write
/ them depending on the given flag.
/
/ In:
/ ppw -> property well to work with
/ pPersistQuery -> IPersistQuery structure to work with
/ fRead = read or write
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT PropertyWell_Persist(LPPROPERTYWELL ppw, IPersistQuery* pPersistQuery, BOOL fRead)
{
HRESULT hr;
LPPROPERTYWELLITEM pItem;
TCHAR szBuffer[80];
INT iItems;
INT i;
TraceEnter(TRACE_PWELL, "PropertyWell_Persist");
if ( !pPersistQuery )
ExitGracefully(hr, E_INVALIDARG, "No persist object");
if ( fRead )
{
// Read the items from the IPersistQuery object, first get the number of items
// we need to get back. Then loop through them all getting the property, condition
// and value.
hr = pPersistQuery->ReadInt(c_szMsPropertyWell, c_szItems, &iItems);
FailGracefully(hr, "Failed to get item count");
Trace(TEXT("Attempting to read %d items"), iItems);
for ( i = 0 ; i < iItems ; i++ )
{
LPCLASSENTRY pClassEntry;
TCHAR szObjectClass[MAX_PATH];
TCHAR szProperty[MAX_PATH];
TCHAR szValue[MAX_PATH];
INT iCondition;
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szObjectClassN, i);
hr = pPersistQuery->ReadString(c_szMsPropertyWell, szBuffer, szObjectClass, ARRAYSIZE(szObjectClass));
FailGracefully(hr, "Failed to read object class");
pClassEntry = PropertyWell_FindClassEntry(ppw, szObjectClass);
TraceAssert(pClassEntry);
if ( !pClassEntry )
ExitGracefully(hr, E_FAIL, "Failed to get objectClass / map to available class");
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szProperty, i);
hr = pPersistQuery->ReadString(c_szMsPropertyWell, szBuffer, szProperty, ARRAYSIZE(szProperty));
FailGracefully(hr, "Failed to read property");
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szCondition, i);
hr = pPersistQuery->ReadInt(c_szMsPropertyWell, szBuffer, &iCondition);
FailGracefully(hr, "Failed to write condition");
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szValue, i);
if ( FAILED(pPersistQuery->ReadString(c_szMsPropertyWell, szBuffer, szValue, ARRAYSIZE(szValue))) )
{
TraceMsg("No value defined in incoming stream");
szValue[0] = TEXT('\0');
}
hr = PropertyWell_AddItem(ppw, pClassEntry, szProperty, iCondition, szValue);
FailGracefully(hr, "Failed to add search criteria to query");
}
}
else
{
// Write the content of the property well out, store the items then for
// each store Condition%d, Value%d, Property%d.
iItems = DPA_GetPtrCount(ppw->hdpaItems);
Trace(TEXT("Attempting to store %d items"), iItems);
hr = pPersistQuery->WriteInt(c_szMsPropertyWell, c_szItems, iItems);
FailGracefully(hr, "Failed to write item count");
for ( i = 0 ; i < iItems ; i++ )
{
pItem = (LPPROPERTYWELLITEM)DPA_FastGetPtr(ppw->hdpaItems, i);
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szObjectClassN, i);
hr = pPersistQuery->WriteString(c_szMsPropertyWell, szBuffer, pItem->pClassEntry->pName);
FailGracefully(hr, "Failed to write property");
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szProperty, i);
hr = pPersistQuery->WriteString(c_szMsPropertyWell, szBuffer, pItem->pProperty);
FailGracefully(hr, "Failed to write property");
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szCondition, i);
hr = pPersistQuery->WriteInt(c_szMsPropertyWell, szBuffer, pItem->iCondition);
FailGracefully(hr, "Failed to write condition");
if ( pItem->pValue )
{
// NTRAID#NTBUG9-554458-2002/02/20-lucios. Pending fix.
wsprintf(szBuffer, c_szValue, i);
hr = pPersistQuery->WriteString(c_szMsPropertyWell, szBuffer, pItem->pValue);
FailGracefully(hr, "Failed to write value");
}
}
}
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}