Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

5363 lines
170 KiB

#include "pch.h"
#include "stddef.h"
#pragma hdrstop
// class that implements the query UI
class CDsQuery : public IQueryHandler, IQueryForm, IObjectWithSite, IDsQueryHandler, IShellFolder
{
public:
CDsQuery();
~CDsQuery();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IQueryForms
STDMETHOD(Initialize)(HKEY hkForm);
STDMETHOD(AddForms)(LPCQADDFORMSPROC pAddFormsProc, LPARAM lParam);
STDMETHOD(AddPages)(LPCQADDPAGESPROC pAddPagesProc, LPARAM lParam);
// IQueryHandler
STDMETHOD(Initialize)(IQueryFrame* pQueryFrame, DWORD dwOQWFlags, LPVOID pParameters);
STDMETHOD(GetViewInfo)(LPCQVIEWINFO pViewInfo);
STDMETHOD(AddScopes)();
STDMETHOD(BrowseForScope)(HWND hwndParent, LPCQSCOPE pCurrentScope, LPCQSCOPE* ppScope);
STDMETHOD(CreateResultView)(HWND hwndParent, HWND* phWndView);
STDMETHOD(ActivateView)(UINT uState, WPARAM wParam, LPARAM lParam);
STDMETHOD(InvokeCommand)(HWND hwndParent, UINT uID);
STDMETHOD(GetCommandString)(UINT uID, DWORD dwFlags, LPTSTR pBuffer, INT cchBuffer);
STDMETHOD(IssueQuery)(LPCQPARAMS pQueryParams);
STDMETHOD(StopQuery)();
STDMETHOD(GetViewObject)(UINT uScope, REFIID riid, void **ppvOut);
STDMETHOD(LoadQuery)(IPersistQuery* pPersistQuery);
STDMETHOD(SaveQuery)(IPersistQuery* pPersistQuery, LPCQSCOPE pScope);
// IObjectWithSite
STDMETHODIMP SetSite(IUnknown* punk);
STDMETHODIMP GetSite(REFIID riid, void **ppv);
// IDsQueryHandler
STDMETHOD(UpdateView)(DWORD dwType, LPDSOBJECTNAMES pdon);
// IShellFolder
STDMETHOD(ParseDisplayName)(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
{ return E_NOTIMPL; }
STDMETHOD(EnumObjects)(HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST * ppEnumIDList)
{ return E_NOTIMPL; }
STDMETHOD(BindToObject)(LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, void **ppv)
{ return E_NOTIMPL; }
STDMETHOD(BindToStorage)(LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, void **ppv)
{ return E_NOTIMPL; }
STDMETHOD(CompareIDs)(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{ return E_NOTIMPL; }
STDMETHOD(CreateViewObject)(HWND hwndOwner, REFIID riid, void **ppv)
{ return E_NOTIMPL; }
STDMETHOD(GetAttributesOf)(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut)
{ *rgfInOut &= SFGAO_HASPROPSHEET; return S_OK; }
STDMETHOD(GetUIObjectOf)(HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv)
{ return GetViewObject(0x0, riid, ppv); }
STDMETHOD(GetDisplayNameOf)(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET pName)
{ return E_NOTIMPL; }
STDMETHOD(SetNameOf)(HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST* ppidlOut)
{ return E_NOTIMPL; }
private:
LRESULT OnSize(INT cx, INT cy);
LRESULT OnNotify(HWND hWnd, WPARAM wParam, LPARAM lParam);
HRESULT OnAddResults(DWORD dwQueryReference, HDPA hdpaResults);
LRESULT OnContextMenu(HWND hwndMenu, LPARAM lParam);
HRESULT OnFileProperties(VOID);
HRESULT OnFileSaveQuery(VOID);
HRESULT OnEditSelectAll(VOID);
HRESULT OnEditInvertSelection(VOID);
HRESULT OnPickColumns(HWND hwndParent);
static int s_BrowseForScopeCB(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
static LRESULT s_ResultViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT s_BannerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT _SetDataObjectData(IDataObject* pDataObject, UINT cf, LPVOID pData, DWORD cbSize);
HRESULT _SetDispSpecOptions(IDataObject *pdo);
HRESULT _InitNewQuery(LPDSQUERYPARAMS pDsQueryParams, BOOL fRefreshColumnTable);
HRESULT _GetFilterValue(INT iColumn, HD_ITEM* pitem);
HRESULT _FilterView(BOOL fCheck);
HRESULT _PopulateView(INT iFirstItem, INT iLast);
VOID _FreeResults(VOID);
DWORD _SetViewMode(INT uID);
VOID _SortResults(INT iColumn);
VOID _SetFilter(BOOL fFilter);
VOID _ShowBanner(UINT flags, UINT idPrompt);
VOID _InitViewMenuItems(HMENU hMenu);
HRESULT _GetQueryFormKey(REFCLSID clsidForm, HKEY* phKey);
HRESULT _GetColumnTable(REFCLSID clsidForm, LPDSQUERYPARAMS pDsQueryParams, HDSA* pHDSA, BOOL fSetInView);
VOID _SaveColumnTable(VOID);
HRESULT _SaveColumnTable(REFCLSID clsidForm, HDSA hdsaColumns);
HRESULT _AddResultToDataObject(HDSA hdsa, INT i);
HRESULT _GetDataObjectFromSelection(BOOL fGetAll, IDataObject **pdo);
HRESULT _GetContextMenu();
VOID _GetContextMenuVerbs(HMENU hMenu, DWORD dwFlags);
HRESULT _CopyCredentials(LPWSTR *ppszUserName, LPWSTR *ppszPassword, LPWSTR *ppszServer);
VOID _DeleteViewItems(LPDSOBJECTNAMES pdon);
private:
LONG _cRef; // lifetime
IQueryFrame* _pqf; // our parent window
IUnknown* _punkSite; // site object
IContextMenu* _pcm; // Curerntly displayed context menu / == NULL if none
DWORD _dwOQWFlags; // flags passed to OpenQueryWindow
DWORD _dwFlags; // flags as part of the ds query parameters
LPWSTR _pDefaultScope; // default scope passed
LPWSTR _pDefaultSaveLocation; // directory to save queries into by default
LPTSTR _pDefaultSaveName; // default save name (from the query form)
LPWSTR _pServer; // server to target
LPWSTR _pUserName; // user name and password to authenticate with
LPWSTR _pPassword;
BOOL _fNoSelection:1; // the IContextMenu was from no selection
BOOL _fColumnsModified:1; // settings of the view modified
BOOL _fSortDescending:1; // sort the results descending
BOOL _fFilter:1; // filter enabled
BOOL _fFilterSupported:1; // is the filter available, eg: comctl32 > 5.0
INT _idViewMode; // default view mode
INT _iSortColumn; // sort column
HWND _hwnd; // container window
HWND _hwndView; // listview window (child of parent)
HWND _hwndBanner; // banner window which is a child of the list view
DWORD _dwQueryReference; // reference value passed to query
HANDLE _hThread; // worker thread handle
DWORD _dwThreadId; // thread ID for the Query processing thread
CLSID _clsidForm; // form being used for column table
HDSA _hdsaColumns; // column information (size, filters, etc)
HDPA _hdpaResults; // results for tr the query we have issued
LPTSTR _pFilter; // current filter
HMENU _hFrameMenuBar; // stored frame menu bar, stored from activate
HMENU _hFileMenu; // added to the frames view menu
HMENU _hEditMenu; // inserted into the menu bar
HMENU _hViewMenu; // inserted into the menu bar
HMENU _hHelpMenu; // inserted into the menu bar
};
//
// Window classes we create to show the results
//
#define VIEW_CLASS TEXT("ActiveDsQueryView")
#define BANNER_CLASS TEXT("ActiveDsQueryBanner")
//
// Registry values used for the settings
//
#define VIEW_SETTINGS_VALUE TEXT("ViewSettings")
#define ADMIN_VIEW_SETTINGS_VALUE TEXT("AdminViewSettings");
//
// When filtering we populate the view using PostMessage, doing so many items
// at a time
//
#define FILTER_UPDATE_COUNT 128
//
// All items within the list view contain the following LPARAM structure used
// for storing the magic properties we are interested in.
//
#define ENABLE_MENU_ITEM(hMenu, id, fEnabled) \
EnableMenuItem(hMenu, id, (fEnabled) ? (MF_BYCOMMAND|MF_ENABLED):(MF_BYCOMMAND|MF_GRAYED))
//
// Persisted column data, this is stored in the registry under the CLSID for the
// form we are interested in.
//
typedef struct
{
DWORD cbSize; // offset to the next column / == 0 if none
DWORD dwFlags; // flags
DWORD offsetProperty; // offset to property name (UNICODE)
DWORD offsetHeading; // offset to column heading
INT cx; // pixel width of the column
INT fmt; // format of the column
} SAVEDCOLUMN, * LPSAVEDCOLUMN;
//
// Table to map property types to useful information
//
struct
{
LPCTSTR pMenuName;
INT idOperator;
INT hdft;
}
property_type_table[] =
{
0, 0, 0,
MAKEINTRESOURCE(IDR_OP_STRING), FILTER_CONTAINS, HDFT_ISSTRING,
MAKEINTRESOURCE(IDR_OP_STRING), FILTER_CONTAINS, HDFT_ISSTRING,
MAKEINTRESOURCE(IDR_OP_NUMBER), FILTER_IS, HDFT_ISNUMBER,
MAKEINTRESOURCE(IDR_OP_NUMBER), FILTER_IS, HDFT_ISNUMBER, // PROPERTY_ISBOOL
};
//
// Help information for the frame and the control
//
static DWORD const aHelpIDs[] =
{
CQID_LOOKFORLABEL, IDH_FIND,
CQID_LOOKFOR, IDH_FIND,
CQID_LOOKINLABEL, IDH_IN,
CQID_LOOKIN, IDH_IN,
CQID_BROWSE, IDH_BROWSE,
CQID_FINDNOW, IDH_FIND_NOW,
CQID_STOP, IDH_STOP,
CQID_CLEARALL, IDH_CLEAR_ALL,
IDC_RESULTS, IDH_RESULTS,
IDC_STATUS, IDH_NO_HELP,
0, 0,
};
static DWORD const aBrowseHelpIDs[] =
{
DSBID_BANNER, (DWORD)-1,
DSBID_CONTAINERLIST, IDH_BROWSE_CONTAINER,
0, 0,
};
// Query object
CDsQuery::CDsQuery() :
_cRef(1), _fNoSelection(TRUE), _iSortColumn(-1), _idViewMode(DSQH_VIEW_DETAILS)
{
if ( CheckDsPolicy(NULL, c_szEnableFilter) )
{
TraceMsg("QuickFilter enabled in policy");
_fFilter = TRUE;
}
DllAddRef();
}
CDsQuery::~CDsQuery()
{
// persist the column information if we need to
if ( _hdsaColumns )
{
if ( _fColumnsModified )
{
_SaveColumnTable(_clsidForm, _hdsaColumns);
_fColumnsModified = FALSE;
}
_SaveColumnTable();
}
// discard all the other random state we have
LocalFreeStringW(&_pDefaultScope);
LocalFreeStringW(&_pDefaultSaveLocation);
LocalFreeString(&_pDefaultSaveName);
LocalFreeStringW(&_pUserName);
LocalFreeStringW(&_pPassword);
LocalFreeStringW(&_pServer);
if ( IsWindow(_hwnd) )
DestroyWindow(_hwnd);
if ( IsMenu(_hFileMenu) )
DestroyMenu(_hFileMenu);
if ( IsMenu(_hEditMenu) )
DestroyMenu(_hEditMenu);
if ( IsMenu(_hViewMenu) )
DestroyMenu(_hViewMenu);
if ( IsMenu(_hHelpMenu) )
DestroyMenu(_hHelpMenu);
// tell the thread its time to die
if ( _hThread )
{
PostThreadMessage(_dwThreadId, RVTM_STOPQUERY, 0, 0);
PostThreadMessage(_dwThreadId, WM_QUIT, 0, 0);
CloseHandle(_hThread);
}
DoRelease(_pqf);
DoRelease(_punkSite);
DoRelease(_pcm);
DllRelease();
}
// IUnknown bits
ULONG CDsQuery::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CDsQuery::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
HRESULT CDsQuery::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CDsQuery, IQueryForm), // IID_IQueryForm
QITABENT(CDsQuery, IQueryHandler), // IID_IQueryHandler
QITABENT(CDsQuery, IObjectWithSite), // IID_IObjectWIthSite
QITABENT(CDsQuery, IDsQueryHandler), // IID_IDsQueryHandler
QITABENT(CDsQuery, IShellFolder), // IID_IShellFolder
{0, 0 },
};
return QISearch(this, qit, riid, ppv);
}
//
// Handle creating an instance of CLSID_DsQuery
//
STDAPI CDsQuery_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
CDsQuery *pdq = new CDsQuery();
if ( !pdq )
return E_OUTOFMEMORY;
HRESULT hres = pdq->QueryInterface(IID_IUnknown, (void **)ppunk);
pdq->Release();
return hres;
}
/// IQueryForm
STDMETHODIMP CDsQuery::Initialize(HKEY hkForm)
{
return S_OK;
}
// query forms exposed from this object
struct
{
CLSID const * clsidForm;
INT idsTitle;
DWORD dwFlags;
}
forms[] =
{
&CLSID_DsFindPeople, IDS_FINDUSER, 0,
&CLSID_DsFindComputer, IDS_FINDCOMPUTER, 0,
&CLSID_DsFindPrinter, IDS_FINDPRINTERS, 0,
&CLSID_DsFindVolume, IDS_FINDSHAREDFOLDERS, 0,
&CLSID_DsFindContainer, IDS_FINDOU, 0,
&CLSID_DsFindAdvanced, IDS_CUSTOMSEARCH, CQFF_NOGLOBALPAGES,
&CLSID_DsFindDomainController, IDS_FINDDOMCTL, CQFF_ISNEVERLISTED|CQFF_NOGLOBALPAGES,
&CLSID_DsFindFrsMembers, IDS_FINDFRSMEMBER, CQFF_ISNEVERLISTED|CQFF_NOGLOBALPAGES,
};
STDMETHODIMP CDsQuery::AddForms(LPCQADDFORMSPROC pAddFormsProc, LPARAM lParam)
{
HRESULT hres;
TCHAR szBuffer[MAX_PATH];
INT i;
TraceEnter(TRACE_FORMS, "CDsQuery::AddForms");
if ( !pAddFormsProc )
ExitGracefully(hres, E_INVALIDARG, "No AddFormsProc");
for ( i = 0; i < ARRAYSIZE(forms); i++ )
{
CQFORM qf = { 0 };
qf.cbStruct = SIZEOF(qf);
qf.dwFlags = forms[i].dwFlags;
qf.clsid = *forms[i].clsidForm;
qf.pszTitle = szBuffer;
LoadString(GLOBAL_HINSTANCE, forms[i].idsTitle, szBuffer, ARRAYSIZE(szBuffer));
hres = (*pAddFormsProc)(lParam, &qf);
FailGracefully(hres, "Failed to add form (calling pAddFormsFunc)");
}
hres = S_OK; // success
exit_gracefully:
TraceLeaveResult(hres);
}
// page information for this object
struct
{
CLSID const * clisdForm;
LPCQPAGEPROC pPageProc;
DLGPROC pDlgProc;
INT idPageTemplate;
INT idPageName;
DWORD dwFlags;
}
pages[] =
{
//
// Page list for the default forms that we add
//
&CLSID_DsFindPeople, PageProc_User, DlgProc_User, IDD_FINDUSER, IDS_FINDUSER, 0,
&CLSID_DsFindComputer, PageProc_Computer, DlgProc_Computer, IDD_FINDCOMPUTER, IDS_FINDCOMPUTER, 0,
&CLSID_DsFindPrinter, PageProc_Printers, DlgProc_Printers, IDD_FINDPRINT1, IDS_FINDPRINTERS, 0,
&CLSID_DsFindPrinter, PageProc_PrintersMore, DlgProc_PrintersMore, IDD_FINDPRINT2, IDS_MORECHOICES, 0,
&CLSID_DsFindVolume, PageProc_Volume, DlgProc_Volume, IDD_FINDVOLUME, IDS_FINDSHAREDFOLDERS, 0,
&CLSID_DsFindContainer, PageProc_Container, DlgProc_Container, IDD_FINDCONTAINER, IDS_FINDOU, 0,
&CLSID_DsFindAdvanced, PageProc_PropertyWell, DlgProc_PropertyWell, IDD_PROPERTYWELL, IDS_CUSTOMSEARCH, 0,
&CLSID_DsFindAdvanced, PageProc_RawLDAP, DlgProc_RawLDAP, IDD_FINDUSINGLDAP, IDS_ADVANCED, 0,
&CLSID_DsFindDomainController, PageProc_DomainController, DlgProc_DomainController, IDD_FINDDOMCTL, IDS_FINDDOMCTL, 0,
&CLSID_DsFindFrsMembers, PageProc_FrsMember, DlgProc_FrsMember, IDD_FINDFRSMEMBER, IDS_FINDFRSMEMBER, 0,
//
// Make the property well available on all pages (using the magic CQPF_ADDTOALLFORMS bit)
//
&CLSID_DsFindAdvanced, PageProc_PropertyWell, DlgProc_PropertyWell, IDD_PROPERTYWELL, IDS_ADVANCED, CQPF_ISGLOBAL,
};
STDMETHODIMP CDsQuery::AddPages(LPCQADDPAGESPROC pAddPagesProc, LPARAM lParam)
{
HRESULT hres;
INT i;
TraceEnter(TRACE_FORMS, "CDsQuery::AddPages");
if ( !pAddPagesProc )
ExitGracefully(hres, E_INVALIDARG, "No AddPagesProc");
for ( i = 0 ; i < ARRAYSIZE(pages) ; i++ )
{
CQPAGE qp = { 0 };
qp.cbStruct = SIZEOF(qp);
qp.dwFlags = pages[i].dwFlags;
qp.pPageProc = pages[i].pPageProc;
qp.hInstance = GLOBAL_HINSTANCE;
qp.idPageName = pages[i].idPageName;
qp.idPageTemplate = pages[i].idPageTemplate;
qp.pDlgProc = pages[i].pDlgProc;
hres = (*pAddPagesProc)(lParam, *pages[i].clisdForm, &qp);
FailGracefully(hres, "Failed to add page (calling pAddPagesFunc)");
}
hres = S_OK;
exit_gracefully:
TraceLeaveResult(S_OK);
}
// IQueryHandler
STDMETHODIMP CDsQuery::Initialize(IQueryFrame* pQueryFrame, DWORD dwOQWFlags, LPVOID pParameters)
{
HRESULT hres;
LPDSQUERYINITPARAMS pDsQueryInitParams = (LPDSQUERYINITPARAMS)pParameters;
TCHAR szGUID[GUIDSTR_MAX];
TCHAR szBuffer[MAX_PATH];
HINSTANCE hInstanceComCtl32 = NULL;
USES_CONVERSION;
TraceEnter(TRACE_HANDLER, "CDsQuery::Initialize");
// Keep the IQueryFrame interface, we need it for menu negotiation and other
// view -> frame interactions.
_pqf = pQueryFrame;
_pqf->AddRef();
_dwOQWFlags = dwOQWFlags;
// If we have a parameter block then lets take copies of the interesting
// fields from there.
if ( pDsQueryInitParams )
{
_dwFlags = pDsQueryInitParams->dwFlags;
// did the user specify a default scope?
if ( pDsQueryInitParams->pDefaultScope && pDsQueryInitParams->pDefaultScope[0] )
{
Trace(TEXT("Default scope:"), W2T(pDsQueryInitParams->pDefaultScope));
hres = LocalAllocStringW(&_pDefaultScope, pDsQueryInitParams->pDefaultScope);
FailGracefully(hres, "Failed to cope default scope");
}
// default save location?
if ( (_dwFlags & DSQPF_SAVELOCATION) && pDsQueryInitParams->pDefaultSaveLocation )
{
Trace(TEXT("Default save location:"), W2T(pDsQueryInitParams->pDefaultSaveLocation));
hres = LocalAllocStringW(&_pDefaultSaveLocation, pDsQueryInitParams->pDefaultSaveLocation);
FailGracefully(hres, "Failed to copy save location");
}
// do we have credential information?
if ( _dwFlags & DSQPF_HASCREDENTIALS )
{
TraceMsg("Copying credential/server information from init params");
if ( pDsQueryInitParams->pUserName )
{
hres = LocalAllocStringW(&_pUserName, pDsQueryInitParams->pUserName);
FailGracefully(hres, "Failed to copy user name");
}
if ( pDsQueryInitParams->pPassword )
{
hres = LocalAllocStringW(&_pPassword, pDsQueryInitParams->pPassword);
FailGracefully(hres, "Failed to copy password");
}
if ( pDsQueryInitParams->pServer )
{
hres = LocalAllocStringW(&_pServer, pDsQueryInitParams->pServer);
FailGracefully(hres, "Failed to copy server");
}
Trace(TEXT("_pUserName : %s"), _pUserName ? W2T(_pUserName):TEXT("<not specified>"));
Trace(TEXT("_pPassword : %s"), _pPassword ? W2T(_pPassword):TEXT("<not specified>"));
Trace(TEXT("_pServer : %s"), _pServer ? W2T(_pServer):TEXT("<not specified>"));
}
}
// Finally load the must structures that we are going to use, then modify them
// based on the flags that the caller gave us.
//
// NB: removes the last two items from the file menu assumed to be the
// "save" and its seperator
_hFileMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_FILE));
_hEditMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_EDIT));
_hViewMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_VIEW));
_hHelpMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_MENU_HELP));
if ( !_hFileMenu || !_hEditMenu || !_hViewMenu || !_hHelpMenu )
ExitGracefully(hres, E_FAIL, "Failed to load resources for menus");
if ( _dwFlags & DSQPF_NOSAVE )
{
HMENU hFileMenu = GetSubMenu(_hFileMenu, 0);
INT i = GetMenuItemCount(hFileMenu);
DeleteMenu(hFileMenu, i-1, MF_BYPOSITION);
DeleteMenu(hFileMenu, i-2, MF_BYPOSITION);
}
// Init ComCtl32, including checking to see if we can use the filter control or not,
// the filter control was added to the WC_HEADER32 in IE5, so check the DLL version
// to see which we are using.
InitCommonControls();
hInstanceComCtl32 = GetModuleHandle(TEXT("comctl32"));
TraceAssert(hInstanceComCtl32);
if ( hInstanceComCtl32 )
{
DLLVERSIONINFO dllVersionInfo = { 0 };
DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hInstanceComCtl32, "DllGetVersion");
TraceAssert(pfnDllGetVersion);
dllVersionInfo.cbSize = SIZEOF(dllVersionInfo);
if ( pfnDllGetVersion && SUCCEEDED(pfnDllGetVersion(&dllVersionInfo)) )
{
Trace(TEXT("DllGetVersion succeeded on ComCtl32, dwMajorVersion %08x"), dllVersionInfo.dwMajorVersion);
_fFilterSupported = dllVersionInfo.dwMajorVersion >= 5;
}
}
Trace(TEXT("_fFilterSupported is %d"), _fFilterSupported);
hres = S_OK; // success
exit_gracefully:
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::GetViewInfo(LPCQVIEWINFO pViewInfo)
{
HICON hIcon;
TraceEnter(TRACE_HANDLER, "CDsQuery::GetViewInfo");
pViewInfo->dwFlags = 0;
pViewInfo->hInstance = GLOBAL_HINSTANCE;
pViewInfo->idLargeIcon = IDI_FINDDS;
pViewInfo->idSmallIcon = IDI_FINDDS;
pViewInfo->idTitle = IDS_WINDOWTITLE;
pViewInfo->idAnimation = IDR_DSFINDANIMATION;
TraceLeaveResult(S_OK);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::AddScopes()
{
HRESULT hres;
DWORD dwThreadId;
HANDLE hThread;
LPSCOPETHREADDATA pstd = NULL;
USES_CONVERSION;
TraceEnter(TRACE_HANDLER, "CDsQuery::AddScopes");
// Enumerate the rest of the scopes on a seperate thread to gather the
// scopes we are interested in.
pstd = (LPSCOPETHREADDATA)LocalAlloc(LPTR, SIZEOF(SCOPETHREADDATA));
TraceAssert(pstd);
if ( !pstd )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate scope data structure");
_pqf->GetWindow(&pstd->hwndFrame);
// pstd->pDefaultScope = NULL;
// pstd->pServer = NULL; // no credential stuff currently
// pstd->pUserName = NULL;
// pstd->pPassword = NULL;
if ( _pDefaultScope )
{
hres = LocalAllocStringW(&pstd->pDefaultScope, _pDefaultScope);
FailGracefully(hres, "Failed to copy the default scope");
}
hres = _CopyCredentials(&pstd->pUserName, &pstd->pPassword, &pstd->pServer);
FailGracefully(hres, "Failed to copy credentails");
DllAddRef();
hThread = CreateThread(NULL, 0, AddScopesThread, pstd, 0, &dwThreadId);
TraceAssert(hThread);
if ( !hThread )
{
DllRelease();
ExitGracefully(hres, E_FAIL, "Failed to create background thread to enum scopes - BAD!");
}
CloseHandle(hThread);
hres = S_OK;
exit_gracefully:
if ( FAILED(hres) && pstd )
{
LocalFreeStringW(&pstd->pDefaultScope);
LocalFree((HLOCAL)pstd);
}
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
typedef struct
{
IADsPathname *padp;
WCHAR szGcPath[MAX_PATH];
} BROWSEFORSCOPE;
int CALLBACK CDsQuery::s_BrowseForScopeCB(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
HRESULT hres;
INT iResult = 0;
BROWSEFORSCOPE *pbfs = (BROWSEFORSCOPE*)lpData;
LPTSTR pDirectoryName = NULL;
USES_CONVERSION;
TraceEnter(TRACE_HANDLER, "CDsQuery::s_BrowseForScopeCB");
switch ( uMsg )
{
case DSBM_QUERYINSERT:
{
PDSBITEM pItem = (PDSBITEM)lParam;
TraceAssert(pItem);
// We are interested in modifying the root item of the tree, therefore
// lets check for that being inserted, if it is then we change the
// display name and the icon being shown.
if ( pItem->dwState & DSBS_ROOT )
{
GetModuleFileName(GLOBAL_HINSTANCE, pItem->szIconLocation, ARRAYSIZE(pItem->szIconLocation));
pItem->iIconResID = -IDI_GLOBALCATALOG;
if ( SUCCEEDED(FormatDirectoryName(&pDirectoryName, GLOBAL_HINSTANCE, IDS_GLOBALCATALOG)) )
{
StrCpyN(pItem->szDisplayName, pDirectoryName, DSB_MAX_DISPLAYNAME_CHARS);
LocalFreeString(&pDirectoryName);
}
pItem->dwMask |= DSBF_DISPLAYNAME|DSBF_ICONLOCATION;
iResult = TRUE;
}
break;
}
case BFFM_SELCHANGED:
{
BOOL fEnableOK = TRUE;
LPWSTR pszPath = (LPWSTR)lParam;
LONG nElements = 0;
// The user changes the selection in the browse dialog, therefore
// lets see if we should be enabling the OK button. If the user
// selects GC, but we don't have a GC then we disable it.
if ( SUCCEEDED(pbfs->padp->Set(pszPath, ADS_SETTYPE_FULL)) )
{
pbfs->padp->GetNumElements(&nElements);
Trace(TEXT("nElements on exit from GetNumElements %d"), nElements);
}
if ( !nElements && !pbfs->szGcPath[0] )
{
TraceMsg("'entire directory' selected with NO GC!");
fEnableOK = FALSE;
}
SendMessage(hwnd, BFFM_ENABLEOK, (WPARAM)fEnableOK, 0L);
break;
}
case DSBM_HELP:
{
WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle,
DSQUERY_HELPFILE,
HELP_WM_HELP,
(DWORD_PTR)aBrowseHelpIDs);
break;
}
case DSBM_CONTEXTMENU:
{
WinHelp((HWND)lParam,
DSQUERY_HELPFILE,
HELP_CONTEXTMENU,
(DWORD_PTR)aBrowseHelpIDs);
break;
}
}
TraceLeaveValue(iResult);
}
STDMETHODIMP CDsQuery::BrowseForScope(HWND hwndParent, LPCQSCOPE pCurrentScope, LPCQSCOPE* ppScope)
{
HRESULT hres;
LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pCurrentScope;
BROWSEFORSCOPE bfs = { 0 };
DSBROWSEINFO dsbi = { 0 };
INT iResult;
WCHAR szPath[2048];
WCHAR szRoot[MAX_PATH+10]; // LDAP://
WCHAR szObjectClass[64];
LONG nElements;
USES_CONVERSION;
TraceEnter(TRACE_HANDLER, "CDsQuery::BrowseForScope");
Trace(TEXT("hwndParent %08x, pCurrentScope %08x, ppScope %08x"), hwndParent, pCurrentScope, ppScope);
*ppScope = NULL; // nothing yet!
if ( SUCCEEDED(GetGlobalCatalogPath(_pServer, bfs.szGcPath, ARRAYSIZE(bfs.szGcPath))) )
Trace(TEXT("GC path is: %s"), W2T(bfs.szGcPath));
hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (void **)&bfs.padp);
FailGracefully(hres, "Failed to get the IADsPathname interface");
// Fill out the browse info structure to display the object picker, if we have
// enabled admin features then lets make all objects visible, otherwise
// just the standard features.
dsbi.cbStruct = SIZEOF(dsbi);
dsbi.hwndOwner = hwndParent;
dsbi.pszRoot = szRoot;
dsbi.pszPath = szPath;
dsbi.cchPath = ARRAYSIZE(szPath);
dsbi.dwFlags = (DSBI_RETURNOBJECTCLASS|DSBI_EXPANDONOPEN|DSBI_ENTIREDIRECTORY) & ~DSBI_NOROOT;
dsbi.pfnCallback = s_BrowseForScopeCB;
dsbi.lParam = (LPARAM)&bfs;
dsbi.pszObjectClass = szObjectClass;
dsbi.cchObjectClass = ARRAYSIZE(szObjectClass);
if ( _dwFlags & DSQPF_SHOWHIDDENOBJECTS )
dsbi.dwFlags |= DSBI_INCLUDEHIDDEN;
FormatMsgResource((LPTSTR*)&dsbi.pszTitle, GLOBAL_HINSTANCE, IDS_BROWSEPROMPT);
StrCpyW(szRoot, c_szLDAP);
if ( _pServer )
{
if ( lstrlenW(_pServer) > MAX_PATH )
ExitGracefully(hres, E_INVALIDARG, "_pServer is too big");
StrCatW(szRoot, L"//");
StrCatW(szRoot, _pServer);
}
if ( pDsQueryScope )
{
StrCpyNW(szPath, OBJECT_NAME_FROM_SCOPE(pDsQueryScope), ARRAYSIZE(szPath));
Trace(TEXT("pDsQueryScope: %s"), W2T(szPath));
}
// copy the credential information if needed
if ( _dwFlags & DSQPF_HASCREDENTIALS )
{
TraceMsg("Setting credentails information");
dsbi.pUserName = _pUserName;
dsbi.pPassword = _pPassword;
dsbi.dwFlags |= DSBI_HASCREDENTIALS;
}
iResult = DsBrowseForContainer(&dsbi);
Trace(TEXT("DsBrowseForContainer returns %d"), iResult);
// iResult == IDOK if something was selected (szPath),
// if it is -VE if the call failed and we should error
if ( iResult == IDOK )
{
LPWSTR pszScope = szPath;
LPWSTR pszObjectClass = szObjectClass;
LONG nElements = 0;
Trace(TEXT("Path on exit from DsBrowseForContainer: %s"), W2T(szPath));
// does this look like the GC? If so then default to it, as DsBrowseForContainer
// will return us iffy looking information
if ( SUCCEEDED(bfs.padp->Set(szPath, ADS_SETTYPE_FULL)) )
{
bfs.padp->GetNumElements(&nElements);
Trace(TEXT("nElements on exit from GetNumElements %d"), nElements);
}
if ( !nElements )
{
TraceMsg("nElements = 0, so defaulting to GC");
pszScope = bfs.szGcPath;
pszObjectClass = GC_OBJECTCLASS;
}
Trace(TEXT("Scope selected is: %s, Object class: %s"), W2T(pszScope), W2T(pszObjectClass));
hres = AllocScope(ppScope, 0, pszScope, pszObjectClass);
FailGracefully(hres, "Failed converting the DS path to a scope");
}
else if ( iResult == IDCANCEL )
{
hres = S_FALSE; // nothing selected, returning S_FALSE;
}
else if ( iResult < 0 )
{
ExitGracefully(hres, E_FAIL, "DsBrowseForContainer failed");
}
exit_gracefully:
LocalFreeString((LPTSTR*)&dsbi.pszTitle);
Trace(TEXT("*ppScope == %08x"), *ppScope);
DoRelease(bfs.padp);
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
//
// WndProc for the banner window
//
LRESULT CALLBACK CDsQuery::s_BannerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = 0;
switch ( uMsg )
{
case WM_SIZE:
InvalidateRect(hwnd, NULL, FALSE);
break;
case WM_ERASEBKGND:
break;
case WM_PAINT:
{
TCHAR szBuffer[MAX_PATH];
HFONT hFont, hOldFont;
SIZE szText;
RECT rcClient;
INT len;
PAINTSTRUCT paint;
COLORREF oldFgColor, oldBkColor;
BeginPaint(hwnd, &paint);
hFont = (HFONT)SendMessage(GetParent(hwnd), WM_GETFONT, 0, 0L);
hOldFont = (HFONT)SelectObject(paint.hdc, hFont);
if ( hOldFont )
{
oldFgColor = SetTextColor(paint.hdc, GetSysColor(COLOR_WINDOWTEXT));
oldBkColor = SetBkColor(paint.hdc, ListView_GetBkColor(GetParent(hwnd)));
len = GetWindowText(hwnd, szBuffer, ARRAYSIZE(szBuffer));
GetTextExtentPoint32(paint.hdc, szBuffer, len, &szText);
GetClientRect(GetParent(hwnd), &rcClient);
ExtTextOut(paint.hdc,
(rcClient.right - szText.cx) / 2,
GetSystemMetrics(SM_CYBORDER)*4,
ETO_CLIPPED|ETO_OPAQUE, &rcClient,
szBuffer, len,
NULL);
SetTextColor(paint.hdc, oldFgColor);
SetBkColor(paint.hdc, oldBkColor);
SelectObject(paint.hdc, hOldFont);
}
EndPaint(hwnd, &paint);
break;
}
case WM_SETTEXT:
{
InvalidateRect(hwnd, NULL, FALSE);
//break; // deliberate drop through..
}
default:
lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
return lResult;
}
//
// WndProc for the bg window (lives behind list view, used by rest of the world)
//
LRESULT CALLBACK CDsQuery::s_ResultViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = 0;
CDsQuery* pDsQuery = NULL;
if ( uMsg == WM_CREATE )
{
pDsQuery = (CDsQuery*)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pDsQuery);
}
else
{
pDsQuery = (CDsQuery*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch ( uMsg )
{
case WM_SIZE:
pDsQuery->OnSize(LOWORD(lParam), HIWORD(lParam));
return(0);
case WM_DESTROY:
pDsQuery->_hwndView = NULL; // view is gone!
break;
case WM_NOTIFY:
return(pDsQuery->OnNotify(hwnd, wParam, lParam));
case WM_SETFOCUS:
SetFocus(pDsQuery->_hwndView);
break;
case WM_GETDLGCODE:
return ((LRESULT)(DLGC_WANTARROWS | DLGC_WANTCHARS));
case WM_CONTEXTMENU:
pDsQuery->OnContextMenu(NULL, lParam);
return TRUE;
case DSQVM_ADDRESULTS:
return SUCCEEDED(pDsQuery->OnAddResults((DWORD)wParam, (HDPA)lParam));
case DSQVM_FINISHED:
if ( (DWORD)wParam == pDsQuery->_dwQueryReference )
{
// the references match so lets finish the query, and display
// the "too many results" prompt if the user did a really
// big query and we chopped them off
pDsQuery->StopQuery();
if ( lParam ) // == 0 then we are OK!
{
HWND hwndFrame;
pDsQuery->_pqf->GetWindow(&hwndFrame);
FormatMsgBox(GetParent(hwndFrame),
GLOBAL_HINSTANCE, IDS_WINDOWTITLE, IDS_ERR_MAXRESULT,
MB_OK|MB_ICONERROR);
}
}
SetFocus(pDsQuery->_hwndView);
return(1);
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
STDMETHODIMP CDsQuery::CreateResultView(HWND hwndParent, HWND* phWndView)
{
HRESULT hres;
WNDCLASS wc;
HWND hwndFilter, hwndFilterOld;
HIMAGELIST himlSmall, himlLarge;
DWORD dwLVStyle = LVS_AUTOARRANGE|LVS_SHAREIMAGELISTS|LVS_SHOWSELALWAYS|LVS_REPORT;
RECT rc;
TraceEnter(TRACE_HANDLER, "CDsQuery::CreateResultView");
if ( IsWindow(_hwnd) )
ExitGracefully(hres, E_FAIL, "Can only create one view at a time");
// Create our result viewer, this is the parent window to the ListView
// that we attach when we issue the query.
ZeroMemory(&wc, SIZEOF(wc));
wc.lpfnWndProc = s_ResultViewWndProc;
wc.hInstance = GLOBAL_HINSTANCE;
wc.lpszClassName = VIEW_CLASS;
RegisterClass(&wc);
_hwnd = CreateWindow(VIEW_CLASS,
NULL,
WS_TABSTOP|WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE,
0, 0, 0, 0,
hwndParent,
NULL,
GLOBAL_HINSTANCE,
this);
if ( !_hwnd )
ExitGracefully(hres, E_FAIL, "Failed to create view parent window");
// Now register the window classes we are using.
ZeroMemory(&wc, SIZEOF(wc));
wc.lpfnWndProc = s_BannerWndProc;
wc.hInstance = GLOBAL_HINSTANCE;
wc.lpszClassName = BANNER_CLASS;
RegisterClass(&wc);
if ( _dwOQWFlags & OQWF_SINGLESELECT )
dwLVStyle |= LVS_SINGLESEL;
GetClientRect(_hwnd, &rc);
_hwndView = CreateWindowEx(WS_EX_CLIENTEDGE,
WC_LISTVIEW,
NULL,
WS_TABSTOP|WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE|dwLVStyle,
0, 0,
rc.right, rc.bottom,
_hwnd,
(HMENU)IDC_RESULTS,
GLOBAL_HINSTANCE,
NULL);
if ( !_hwndView )
ExitGracefully(hres, E_FAIL, "Failed to create the view window");
ListView_SetExtendedListViewStyle(_hwndView, LVS_EX_FULLROWSELECT|LVS_EX_LABELTIP);
Shell_GetImageLists(&himlLarge, &himlSmall);
ListView_SetImageList(_hwndView, himlLarge, LVSIL_NORMAL);
ListView_SetImageList(_hwndView, himlSmall, LVSIL_SMALL);
// Create the banner window, this is a child of the ListView, it is used to display
// information about the query being issued
_hwndBanner = CreateWindow(BANNER_CLASS, NULL,
WS_CHILD,
0, 0, 0, 0, // nb: size fixed later
_hwndView,
(HMENU)IDC_STATUS,
GLOBAL_HINSTANCE,
NULL);
if ( !_hwndBanner )
ExitGracefully(hres, E_FAIL, "Failed to create the static banner window");
_SetFilter(_fFilter);
_SetViewMode(_idViewMode);
_ShowBanner(SWP_SHOWWINDOW, IDS_INITALIZING);
hres = S_OK; // success
exit_gracefully:
if ( SUCCEEDED(hres) )
*phWndView = _hwnd;
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
#define MGW_EDIT 2
STDMETHODIMP CDsQuery::ActivateView(UINT uState, WPARAM wParam, LPARAM lParam)
{
HRESULT hres;
HWND hwnd;
INT i;
TraceEnter(TRACE_HANDLER, "CDsQuery::ActivateView");
switch ( uState )
{
case CQRVA_ACTIVATE:
{
HMENU hMenu;
OLEMENUGROUPWIDTHS omgw = { 0, 0, 0, 0, 0, 0 };
// Allow the cframe to merge its menus into our menu bar before we
// add ours to it.
if ( !(hMenu = CreateMenu()) )
ExitGracefully(hres, E_FAIL, "Failed to create a base menu bar to be used");
hres = _pqf->InsertMenus(hMenu, &omgw);
FailGracefully(hres, "Failed when calling CQueryFrame::InsertMenus");
Shell_MergeMenus(GetSubMenu(hMenu, 0), GetSubMenu(_hFileMenu, 0), 0x0, 0x0, 0x7fff, 0);
MergeMenu(hMenu, _hEditMenu, omgw.width[0]);
MergeMenu(hMenu, _hViewMenu, omgw.width[0]+1);
MergeMenu(hMenu, _hHelpMenu, omgw.width[0]+MGW_EDIT+omgw.width[2]+omgw.width[4]);
if ( _dwOQWFlags & OQWF_SINGLESELECT )
{
ENABLE_MENU_ITEM(hMenu, DSQH_EDIT_SELECTALL, FALSE);
ENABLE_MENU_ITEM(hMenu, DSQH_EDIT_INVERTSELECTION, FALSE);
}
hres = _pqf->SetMenu(hMenu, NULL); // set the frames menu bar
FailGracefully(hres, "Failed when calling CQueryFrame::SetMenu");
break;
}
case CQRVA_INITMENUBAR:
{
// we recieve a CQRVA_INITMENUBAR before the popup so that we can store the
// menu bar information, and invalidate an interface pointers we maybe holding
// onto.
Trace(TEXT("Received an CQRVA_INITMENUBAR, hMenu %08x"), wParam);
_hFrameMenuBar = (HMENU)wParam;
DoRelease(_pcm);
break;
}
case CQRVA_INITMENUBARPOPUP:
{
HMENU hFileMenu;
BOOL fDeleteItems = FALSE;
TraceMsg("Received an CQRVA_INITMENUBARPOPUP");
hFileMenu = GetSubMenu(_hFrameMenuBar, 0);
// if we have a view then lets try and collect the selection from it,
// having done that we can merge the verbs for that selection into the
// views "File" menu.
if ( (hFileMenu == (HMENU)wParam) && !_pcm )
{
_fNoSelection = TRUE; // no selection currenlty
if ( IsWindow(_hwndView) )
{
for ( i = GetMenuItemCount(hFileMenu) - 1; i >= 0 ; i-- )
{
if ( !fDeleteItems && (GetMenuItemID(hFileMenu, i) == DSQH_FILE_PROPERTIES) )
{
Trace(TEXT("Setting fDeleteItems true on index %d"), i);
fDeleteItems = TRUE;
}
else
{
if ( fDeleteItems )
DeleteMenu(hFileMenu, i, MF_BYPOSITION);
}
}
// Collect the selection, and using that construct an IContextMenu interface, if that works
// then we can merge in the verbs that relate to this object.
hres = _GetContextMenu();
FailGracefully(hres, "Failed when calling _GetAndViewObject");
if ( ListView_GetSelectedCount(_hwndView) > 0 )
{
_GetContextMenuVerbs(hFileMenu, CMF_VERBSONLY);
_fNoSelection = FALSE;
}
}
ENABLE_MENU_ITEM(hFileMenu, DSQH_FILE_PROPERTIES, !_fNoSelection);
}
// 211991 11/6/00 JonN and DavidDv
// choose columns is disabled if there are no columns to show/the user has marked for disabled
ENABLE_MENU_ITEM(_hFrameMenuBar, DSQH_VIEW_PICKCOLUMNS,
IsWindow(_hwndView) && _hdsaColumns && (!(_dwFlags & DSQPF_NOCHOOSECOLUMNS)));
ENABLE_MENU_ITEM(_hFrameMenuBar, DSQH_VIEW_REFRESH, IsWindow(_hwndView) && _dwThreadId);
_InitViewMenuItems(_hFrameMenuBar);
break;
}
case CQRVA_FORMCHANGED:
{
// we receieve a form change, we store the form name as we will use it
// as the default name for saved queries authored by the user.
Trace(TEXT("Form '%s' selected"), (LPTSTR)lParam);
LocalFreeString(&_pDefaultSaveName);
hres = LocalAllocString(&_pDefaultSaveName, (LPCTSTR)lParam);
FailGracefully(hres, "Failed to set the default save name");
break;
}
case CQRVA_STARTQUERY:
{
Trace(TEXT("Query is: %s"), wParam ? TEXT("starting"):TEXT("stopping"));
break;
}
case CQRVA_HELP:
{
LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
TraceAssert(pHelpInfo)
TraceMsg("Invoking help on the objects in the windows");
WinHelp((HWND)pHelpInfo->hItemHandle, DSQUERY_HELPFILE, HELP_WM_HELP, (DWORD_PTR)aHelpIDs);
break;
}
case CQRVA_CONTEXTMENU:
{
HWND hwndForHelp = (HWND)wParam;
Trace(TEXT("CQRVA_CONTEXTMENU recieved on the bg of the frame %d"), GetDlgCtrlID(hwndForHelp));
WinHelp(hwndForHelp, DSQUERY_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)aHelpIDs);
break;
}
}
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::InvokeCommand(HWND hwndParent, UINT uID)
{
HRESULT hres = S_OK;
HWND hwndFrame;
DECLAREWAITCURSOR;
TraceEnter(TRACE_HANDLER, "CDsQuery::InvokeCommand");
Trace(TEXT("hwndParent %08x, uID %d"), hwndParent, uID);
SetWaitCursor();
switch ( uID )
{
case DSQH_BG_SELECT:
SendMessage(hwndParent, WM_COMMAND, IDOK, 0);
break;
case DSQH_FILE_PROPERTIES:
hres = OnFileProperties();
break;
case DSQH_FILE_SAVEQUERY:
hres = OnFileSaveQuery();
break;
case DSQH_EDIT_SELECTALL:
hres = OnEditSelectAll();
break;
case DSQH_EDIT_INVERTSELECTION:
hres = OnEditInvertSelection();
break;
case DSQH_VIEW_FILTER:
_SetFilter(!_fFilter);
break;
case DSQH_VIEW_LARGEICONS:
case DSQH_VIEW_SMALLICONS:
case DSQH_VIEW_LIST:
case DSQH_VIEW_DETAILS:
_SetViewMode(uID);
break;
case DSQH_VIEW_REFRESH:
{
if ( IsWindow(_hwndView) && _dwThreadId )
{
_InitNewQuery(NULL, FALSE);
PostThreadMessage(_dwThreadId, RVTM_REFRESH, _dwQueryReference, 0L);
}
break;
}
case DSQH_VIEW_PICKCOLUMNS:
{
TraceAssert(_hdsaColumns);
OnPickColumns(hwndParent);
break;
}
case DSQH_HELP_CONTENTS:
{
TraceMsg("Calling for to display help topics");
_pqf->GetWindow(&hwndFrame);
_pqf->CallForm(NULL, DSQPM_HELPTOPICS, 0, (LPARAM)hwndFrame);
break;
}
case DSQH_HELP_WHATISTHIS:
_pqf->GetWindow(&hwndFrame);
SendMessage(hwndFrame, WM_SYSCOMMAND, SC_CONTEXTHELP, MAKELPARAM(0,0));
break;
default:
{
// if it looks like a sort request then lets handle it, otherwise attempt
// to send to the context menu handler we may have at htis poiunt.
if ( (uID >= DSQH_VIEW_ARRANGEFIRST) && (uID < DSQH_VIEW_ARRANGELAST) )
{
TraceAssert(_hdsaColumns);
if ( _hdsaColumns )
{
Trace(TEXT("Calling _SortResults for column %d"), uID - DSQH_VIEW_ARRANGEFIRST);
_SortResults(uID - DSQH_VIEW_ARRANGEFIRST);
}
}
else if ( _pcm )
{
CMINVOKECOMMANDINFO ici;
ici.cbSize = SIZEOF(ici);
ici.fMask = 0;
_pqf->GetWindow(&ici.hwnd);
ici.lpVerb = (LPCSTR)IntToPtr(uID - DSQH_FILE_CONTEXT_FIRST);
ici.lpParameters = NULL;
ici.lpDirectory = NULL;
ici.nShow = SW_NORMAL;
ici.dwHotKey = 0;
ici.hIcon = NULL;
hres = _pcm->InvokeCommand(&ici);
FailGracefully(hres, "Failed when calling IContextMenu::InvokeCommand");
DoRelease(_pcm); // no longer needed
}
break;
}
}
exit_gracefully:
ResetWaitCursor();
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::GetCommandString(UINT uID, DWORD dwFlags, LPTSTR pBuffer, INT cchBuffer)
{
HRESULT hres;
TCHAR szBuffer[MAX_PATH];
TraceEnter(TRACE_HANDLER, "CDsQuery::GetCommandString");
Trace(TEXT("uID %08x, dwFlags %08x, pBuffer %08x, cchBuffer %d"), uID, dwFlags, pBuffer, cchBuffer);
if ( (uID >= DSQH_FILE_CONTEXT_FIRST) && (uID < DSQH_FILE_CONTEXT_LAST) )
{
if ( _pcm )
{
TraceMsg("Trying the IContextMenu::GetCommandString");
hres = _pcm->GetCommandString((uID - DSQH_FILE_CONTEXT_FIRST), GCS_HELPTEXT, NULL, (LPSTR)pBuffer, cchBuffer);
#if UNICODE
// we build UNICODE, therefore if we failed then try and pick up the ANSI string from
// the handler, if that works then we convert the multi-byte string to UNICODE
// and hand that back to the caller.
if ( FAILED(hres) )
{
CHAR szBuffer[MAX_PATH];
hres = _pcm->GetCommandString((uID - DSQH_FILE_CONTEXT_FIRST), GCS_HELPTEXTA, NULL, szBuffer, ARRAYSIZE(szBuffer));
if ( SUCCEEDED(hres) )
{
TraceMsg("Handler provided an ANSI string");
MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, pBuffer, cchBuffer);
}
}
#endif
FailGracefully(hres, "Failed when asking for help text from IContextMenu iface");
}
}
else
{
if ( (uID >= DSQH_VIEW_ARRANGEFIRST) && (uID < DSQH_VIEW_ARRANGELAST) )
{
INT iColumn = uID-DSQH_VIEW_ARRANGEFIRST;
TCHAR szFmt[MAX_PATH];
Trace(TEXT("Get command text for column %d"), iColumn);
if ( _hdsaColumns && (iColumn < DSA_GetItemCount(_hdsaColumns)) )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, iColumn);
TraceAssert(pColumn);
LoadString(GLOBAL_HINSTANCE, IDS_ARRANGEBY_HELP, szFmt, ARRAYSIZE(szFmt));
wsprintf(pBuffer, szFmt, pColumn->pHeading);
Trace(TEXT("Resulting string is: %s"), pBuffer);
}
}
else
{
if ( !LoadString(GLOBAL_HINSTANCE, uID, pBuffer, cchBuffer) )
ExitGracefully(hres, E_FAIL, "Failed to load the command text for this verb");
}
}
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::IssueQuery(LPCQPARAMS pQueryParams)
{
HRESULT hres;
LPTHREADINITDATA ptid = NULL;
LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pQueryParams->pQueryScope;
LPDSQUERYPARAMS pDsQueryParams = (LPDSQUERYPARAMS)pQueryParams->pQueryParameters;
LPTSTR pBuffer = NULL;
MSG msg;
TraceEnter(TRACE_HANDLER, "CDsQuery::IssueQuery");
Trace(TEXT("pQueryParams %08x, pDsQueryScope %08x, pDsQueryParams %08x"), pQueryParams, pDsQueryScope, pDsQueryParams);
// Persist the existing column information if there was some, then
// get the new column table initialized and the columns added to the
// view
if ( _hdsaColumns )
{
if ( _fColumnsModified )
{
_SaveColumnTable(_clsidForm, _hdsaColumns);
_fColumnsModified = FALSE;
}
_SaveColumnTable();
}
// Initialize the view with items
_clsidForm = pQueryParams->clsidForm; // keep the form ID (for persistance)
hres = _InitNewQuery(pDsQueryParams, TRUE);
FailGracefully(hres, "Failed to initialize the new query");
// Now build the thread information needed to get the thread
// up and running.
ptid = (LPTHREADINITDATA)LocalAlloc(LPTR, SIZEOF(THREADINITDATA));
TraceAssert(ptid);
if ( !ptid )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate THREADINITDATA");
ptid->dwReference = _dwQueryReference;
//ptid->hwndView = NULL;
//ptid->pQuery = NULL;
//ptid->pScope = NULL;
//ptid->hdsaColumns = NULL;
//ptid->fShowHidden = FALSE;
//ptid->pServer = NULL;
//ptid->pUserName = NULL;
//ptid->pPassword = NULL;
Trace(TEXT("_dwFlags %08x (& DSQPF_SHOWHIDDENOBJECTS)"), _dwFlags, _dwFlags & DSQPF_SHOWHIDDENOBJECTS);
ptid->fShowHidden = (_dwFlags & DSQPF_SHOWHIDDENOBJECTS) ? 1:0;
ptid->hwndView = _hwndView;
hres = _GetColumnTable(_clsidForm, pDsQueryParams, &ptid->hdsaColumns, FALSE);
FailGracefully(hres, "Failed to create column DSA");
hres = LocalAllocStringW(&ptid->pQuery, (LPWSTR)ByteOffset(pDsQueryParams, pDsQueryParams->offsetQuery));
FailGracefully(hres, "Failed to copy query filter string");
hres = LocalAllocStringW(&ptid->pScope, OBJECT_NAME_FROM_SCOPE(pDsQueryScope));
FailGracefully(hres, "Failed to copy scope to thread init data");
hres = _CopyCredentials(&ptid->pUserName, &ptid->pPassword, &ptid->pServer);
FailGracefully(hres, "Failed to copy credentails");
// now create the thread that is going to perform the query, this includes
// telling the previous one that it needs to close down
if ( _hThread && _dwThreadId )
{
Trace(TEXT("Killing old query thread %08x, ID %d"), _hThread, _dwThreadId);
PostThreadMessage(_dwThreadId, RVTM_STOPQUERY, 0, 0);
PostThreadMessage(_dwThreadId, WM_QUIT, 0, 0);
CloseHandle(_hThread);
_hThread = NULL;
_dwThreadId = 0;
}
DllAddRef();
_hThread = CreateThread(NULL, 0, QueryThread, ptid, 0, &_dwThreadId);
TraceAssert(_hThread);
if ( !_hThread )
{
DllRelease();
ExitGracefully(hres, E_FAIL, "Failed to create background thread - BAD!");
}
hres = S_OK; // success
exit_gracefully:
if ( SUCCEEDED(hres) && IsWindow(_hwndView) )
SetFocus(_hwndView);
if ( FAILED(hres) )
{
QueryThread_FreeThreadInitData(&ptid);
_pqf->StartQuery(FALSE);
}
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::StopQuery()
{
HRESULT hres;
INT cResults = _hdpaResults ? DPA_GetPtrCount(_hdpaResults):0;
LPTSTR pBuffer;
TraceEnter(TRACE_HANDLER, "CDsQuery::StopQuery");
if ( !IsWindow(_hwndView) )
ExitGracefully(hres, E_FAIL, "View not initalized yet");
// we are stopping the query, we are going to tidy up the UI now
// and we just want the thread to closedown cleanly, therefore lets
// do so, increasing our query reference
_pqf->StartQuery(FALSE);
_dwQueryReference++;
_PopulateView(-1, -1); // update status bar etc
if ( _dwThreadId )
PostThreadMessage(_dwThreadId, RVTM_STOPQUERY, 0, 0);
hres = S_OK; // success
exit_gracefully:
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
HRESULT CDsQuery::_SetDataObjectData(IDataObject* pDataObject, UINT cf, LPVOID pData, DWORD cbSize)
{
FORMATETC fmte = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium = { TYMED_NULL, NULL, NULL };
LPVOID pAlloc;
HRESULT hr = AllocStorageMedium(&fmte, &medium, cbSize, &pAlloc);
if (SUCCEEDED(hr))
{
CopyMemory(pAlloc, pData, cbSize);
hr = pDataObject->SetData(&fmte, &medium, TRUE);
ReleaseStgMedium(&medium); // were done, so release the storage medium
}
return hr;
}
HRESULT CDsQuery::_SetDispSpecOptions(IDataObject *pdo)
{
CLIPFORMAT cfDsDispSpecOptions = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSDISPLAYSPECOPTIONS);
FORMATETC fmte = {cfDsDispSpecOptions, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium = { TYMED_NULL, NULL, NULL };
DSDISPLAYSPECOPTIONS *pddso;
LPWSTR pAttribPrefix = DS_PROP_SHELL_PREFIX;
// are we in admin mode? if so then lets fix the property prefix for reading things
// from the display specifiers
if (_dwFlags & DSQPF_ENABLEADMINFEATURES)
pAttribPrefix = DS_PROP_ADMIN_PREFIX;
// include all the string data into the allocation size (these are stored after the
// structure)
DWORD cbStruct = SIZEOF(DSDISPLAYSPECOPTIONS);
cbStruct += StringByteSizeW(pAttribPrefix);
cbStruct += StringByteSizeW(_pUserName);
cbStruct += StringByteSizeW(_pPassword);
cbStruct += StringByteSizeW(_pServer);
// allocate and fill...
HRESULT hr = AllocStorageMedium(&fmte, &medium, cbStruct, (void **)&pddso);
if (SUCCEEDED(hr))
{
DWORD offsetStrings = SIZEOF(DSDISPLAYSPECOPTIONS);
pddso->dwSize = SIZEOF(DSDISPLAYSPECOPTIONS);
pddso->dwFlags = DSDSOF_HASUSERANDSERVERINFO|DSDSOF_DSAVAILABLE;
//pddso->offsetAttribPrefix = 0x0;
//pddso->offsetUserName = 0x0;
//pddso->offsetPassword = 0x0;
//pddso->offsetServer = 0x0;
//pddso->offsetServerConfigPath = 0x0;
pddso->offsetAttribPrefix = offsetStrings;
StringByteCopyW(pddso, offsetStrings, pAttribPrefix);
offsetStrings += StringByteSizeW(pAttribPrefix);
if ( _pUserName )
{
pddso->offsetUserName = offsetStrings;
StringByteCopyW(pddso, offsetStrings, _pUserName);
offsetStrings += StringByteSizeW(_pUserName);
}
if ( _pPassword )
{
pddso->offsetPassword = offsetStrings;
StringByteCopyW(pddso, offsetStrings, _pPassword);
offsetStrings += StringByteSizeW(_pPassword);
}
if ( _pServer )
{
pddso->offsetServer = offsetStrings;
StringByteCopyW(pddso, offsetStrings, _pServer);
offsetStrings += StringByteSizeW(_pServer);
}
// lets set into the IDataObject
hr = pdo->SetData(&fmte, &medium, TRUE);
ReleaseStgMedium(&medium);
}
return hr;
}
STDMETHODIMP CDsQuery::GetViewObject(UINT uScope, REFIID riid, void **ppvOut)
{
HRESULT hres;
IDataObject* pDataObject = NULL;
LPDSQUERYPARAMS pDsQueryParams = NULL;
LPDSQUERYSCOPE pDsQueryScope = NULL;
UINT cfDsQueryParams = RegisterClipboardFormat(CFSTR_DSQUERYPARAMS);
UINT cfDsQueryScope = RegisterClipboardFormat(CFSTR_DSQUERYSCOPE);
BOOL fJustSelection = !(_dwFlags & DSQPF_RETURNALLRESULTS);
TraceEnter(TRACE_HANDLER, "CDsQuery::GetViewObject");
// We only support returning the selection as an IDataObject
DECLAREWAITCURSOR;
SetWaitCursor();
if ( !ppvOut && ((uScope & CQRVS_MASK) != CQRVS_SELECTION) )
ExitGracefully(hres, E_INVALIDARG, "Bad arguments to GetViewObject");
if ( !IsEqualIID(riid, IID_IDataObject) )
ExitGracefully(hres, E_NOINTERFACE, "Object IID supported");
//
// write the extra data we have into the IDataObject:
//
// - query parameters (filter)
// - scope
// - attribute prefix information
//
hres = _GetDataObjectFromSelection(fJustSelection, &pDataObject);
FailGracefully(hres, "Failed to get the IDataObject from the namespace");
if ( SUCCEEDED(_pqf->CallForm(NULL, CQPM_GETPARAMETERS, 0, (LPARAM)&pDsQueryParams)) )
{
if ( pDsQueryParams )
{
hres = _SetDataObjectData(pDataObject, cfDsQueryParams, pDsQueryParams, pDsQueryParams->cbStruct);
FailGracefully(hres, "Failed set the DSQUERYPARAMS into the data object");
}
}
if ( SUCCEEDED(_pqf->GetScope((LPCQSCOPE*)&pDsQueryScope)) )
{
if ( pDsQueryScope )
{
LPWSTR pScope = OBJECT_NAME_FROM_SCOPE(pDsQueryScope);
TraceAssert(pScope);
hres = _SetDataObjectData(pDataObject, cfDsQueryScope, pScope, StringByteSizeW(pScope));
FailGracefully(hres, "Failed set the DSQUERYSCOPE into the data object");
}
}
// success, so lets pass out the IDataObject.
pDataObject->AddRef();
*ppvOut = (LPVOID)pDataObject;
hres = S_OK;
exit_gracefully:
DoRelease(pDataObject);
if ( pDsQueryParams )
CoTaskMemFree(pDsQueryParams);
if ( pDsQueryScope )
CoTaskMemFree(pDsQueryScope);
ResetWaitCursor();
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::LoadQuery(IPersistQuery* pPersistQuery)
{
HRESULT hres;
WCHAR szBuffer[MAX_PATH];
IADs *pDsObject = NULL;
BSTR bstrObjectClass = NULL;
INT iFilter;
LPCQSCOPE pScope = NULL;
INT cbScope;
USES_CONVERSION;
TraceEnter(TRACE_HANDLER, "CDsQuery::LoadQuery");
if ( !pPersistQuery )
ExitGracefully(hres, E_INVALIDARG, "No IPersistQuery object");
if ( SUCCEEDED(pPersistQuery->ReadInt(c_szDsQuery, c_szScopeSize, &cbScope)) &&
(cbScope < SIZEOF(szBuffer)) &&
SUCCEEDED(pPersistQuery->ReadStruct(c_szDsQuery, c_szScope, szBuffer, cbScope)) )
{
Trace(TEXT("Selected scope from file is %s"), W2T(szBuffer));
// get the object class from the file - this should be written to the file
hres = ADsOpenObject(szBuffer, _pUserName, _pPassword, ADS_SECURE_AUTHENTICATION, IID_IADs, (void **)&pDsObject);
FailGracefully(hres, "Failed to bind to the specified object");
hres = pDsObject->get_Class(&bstrObjectClass);
FailGracefully(hres, "Failed to get the object class");
// allocate a new scope
if ( SUCCEEDED(AllocScope(&pScope, 0, szBuffer, bstrObjectClass)) )
{
hres = _pqf->AddScope(pScope, 0x0, TRUE);
FailGracefully(hres, "Failed to add scope to list");
}
}
// Read the remainder of the view state
if ( SUCCEEDED(pPersistQuery->ReadInt(c_szDsQuery, c_szViewMode, &_idViewMode)) )
{
Trace(TEXT("View mode is: %0x8"), _idViewMode);
_SetViewMode(_idViewMode);
}
if ( SUCCEEDED(pPersistQuery->ReadInt(c_szDsQuery, c_szEnableFilter, &iFilter)) )
{
Trace(TEXT("Filter mode set to %d"), _fFilter);
_SetFilter(iFilter);
}
hres = S_OK;
exit_gracefully:
if ( pScope )
CoTaskMemFree(pScope);
DoRelease(pDsObject);
SysFreeString(bstrObjectClass);
TraceLeaveResult(hres);
}
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::SaveQuery(IPersistQuery* pPersistQuery, LPCQSCOPE pScope)
{
HRESULT hres;
LPDSQUERYSCOPE pDsQueryScope = (LPDSQUERYSCOPE)pScope;
LPWSTR pScopePath = OBJECT_NAME_FROM_SCOPE(pDsQueryScope);
WCHAR szGcPath[MAX_PATH];
TraceEnter(TRACE_HANDLER, "CDsQuery::SaveQuery");
if ( !pPersistQuery || !pScope )
ExitGracefully(hres, E_INVALIDARG, "No IPersistQuery/pScope object");
if ( SUCCEEDED(GetGlobalCatalogPath(_pServer, szGcPath, ARRAYSIZE(szGcPath))) && StrCmpW(pScopePath, szGcPath) )
{
// if this is not the GC then persist
TraceMsg("GC path differs from scope, so persisting");
hres = pPersistQuery->WriteInt(c_szDsQuery, c_szScopeSize, StringByteSizeW(pScopePath));
FailGracefully(hres, "Failed to write the scope size");
hres = pPersistQuery->WriteStruct(c_szDsQuery, c_szScope, pScopePath, StringByteSizeW(pScopePath));
FailGracefully(hres, "Failed to write scope");
}
hres = pPersistQuery->WriteInt(c_szDsQuery, c_szViewMode, _idViewMode);
FailGracefully(hres, "Failed to write view mode");
hres = pPersistQuery->WriteInt(c_szDsQuery, c_szEnableFilter, _fFilter);
FailGracefully(hres, "Failed to write filter state");
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
/*----------------------------------------------------------------------------
/ IObjectWithSite
/----------------------------------------------------------------------------*/
STDMETHODIMP CDsQuery::SetSite(IUnknown* punk)
{
HRESULT hres = S_OK;
TraceEnter(TRACE_HANDLER, "CDsQuery::SetSite");
DoRelease(_punkSite);
if ( punk )
{
TraceMsg("QIing for IUnknown from the site object");
hres = punk->QueryInterface(IID_IUnknown, (void **)&_punkSite);
FailGracefully(hres, "Failed to get IUnknown from the site object");
}
exit_gracefully:
TraceLeaveResult(hres);
}
STDMETHODIMP CDsQuery::GetSite(REFIID riid, void **ppv)
{
HRESULT hres;
TraceEnter(TRACE_HANDLER, "CDsQuery::GetSite");
if ( !_punkSite )
ExitGracefully(hres, E_NOINTERFACE, "No site to QI from");
hres = _punkSite->QueryInterface(riid, ppv);
FailGracefully(hres, "QI failed on the site unknown object");
exit_gracefully:
TraceLeaveResult(hres);
}
/*----------------------------------------------------------------------------
/ IDsQueryHandler
/----------------------------------------------------------------------------*/
VOID CDsQuery::_DeleteViewItems(LPDSOBJECTNAMES pdon)
{
INT iResult;
DWORD iItem;
USES_CONVERSION;
TraceEnter(TRACE_HANDLER, "CDsQuery::_DeleteObjectNames");
if ( pdon->cItems )
{
// walk through all the items in the view deleting as required.
for ( iItem = 0 ; iItem != pdon->cItems ; iItem++ )
{
// do we have an item to delete?
if ( pdon->aObjects[iItem].offsetName )
{
LPCWSTR pwszName = (LPCWSTR)ByteOffset(pdon, pdon->aObjects[iItem].offsetName);
Trace(TEXT("pwszName to delete: %s"), W2CT(pwszName));
// walk all the results in the view deleting them as we go.
for ( iResult = 0 ; iResult < DPA_GetPtrCount(_hdpaResults); iResult++ )
{
LPQUERYRESULT pResult = (LPQUERYRESULT)DPA_GetPtr(_hdpaResults, iResult);
TraceAssert(pResult);
// if we match the item we want to delete then remove it, if the view
// is not filtered then remove ite from the list, otherwise leave the
// view update until we have finished deleting
if ( !StrCmpW(pwszName, pResult->pPath) )
{
Trace(TEXT("Item maps to result %d in the list"), iResult);
FreeQueryResult(pResult, DSA_GetItemCount(_hdsaColumns));
DPA_DeletePtr(_hdpaResults, iResult);
if ( !_fFilter )
{
TraceMsg("Deleting the item from the view");
ListView_DeleteItem(_hwndView, iResult);
}
}
}
}
}
// the view was filtered, so lets repopulate with the items
if ( _fFilter )
{
TraceMsg("View is filter, therefore just forcing a refresh");
_FilterView(FALSE);
}
}
TraceLeave();
}
STDMETHODIMP CDsQuery::UpdateView(DWORD dwType, LPDSOBJECTNAMES pdon)
{
HRESULT hres;
TraceEnter(TRACE_HANDLER, "CDsQuery::UpdateView");
switch ( dwType & DSQRVF_OPMASK )
{
case DSQRVF_ITEMSDELETED:
{
if ( !pdon )
ExitGracefully(hres, E_INVALIDARG, "Invlaidate pdon specified for refresh");
_DeleteViewItems(pdon);
break;
}
default:
ExitGracefully(hres, E_INVALIDARG, "Invalidate refresh type speciifed");
}
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ Message/Command Handlers
/----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
/ CDsQuery::OnSize
/ ----------------
/ Result viewer is being sized, so ensure that our children have their
/ sizes correctly addjusted.
/
/ In:
/ cx, cy = new size of the parent window
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
LRESULT CDsQuery::OnSize(INT cx, INT cy)
{
TraceEnter(TRACE_VIEW, "CDsQuery::OnSize");
SetWindowPos(_hwndView, NULL, 0, 0, cx, cy, SWP_NOZORDER|SWP_NOMOVE);
_ShowBanner(0, 0);
TraceLeaveValue(0);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnNotify
/ ------------------
/ Notify message being recieved by the view, so try and handle it as best
/ we can.
/
/ In:
/ hWnd = window handle of the notify
/ wParam, lParam = parameters for the notify event
/
/ Out:
/ LRESULT
/----------------------------------------------------------------------------*/
LRESULT CDsQuery::OnNotify(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
HRESULT hres;
LRESULT lr = 0;
DECLAREWAITCURSOR = GetCursor();
USES_CONVERSION;
TraceEnter(TRACE_VIEW, "CDsQuery::OnNotify");
switch ( ((LPNMHDR)lParam)->code )
{
case HDN_FILTERCHANGE:
_FilterView(TRUE);
break;
case HDN_FILTERBTNCLICK:
{
NMHDFILTERBTNCLICK* pNotify = (NMHDFILTERBTNCLICK*)lParam;
HMENU hMenu;
POINT pt;
HD_ITEM hdi;
UINT uID;
if ( _hdsaColumns && (pNotify->iItem < DSA_GetItemCount(_hdsaColumns)) )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, pNotify->iItem);
if (pColumn)
{
hMenu = LoadMenu(GLOBAL_HINSTANCE, property_type_table[pColumn->iPropertyType].pMenuName);
TraceAssert(hMenu);
if ( hMenu )
{
pt.x = pNotify->rc.right;
pt.y = pNotify->rc.bottom;
MapWindowPoints(pNotify->hdr.hwndFrom, NULL, &pt, 1);
CheckMenuRadioItem(GetSubMenu(hMenu, 0),
FILTER_FIRST, FILTER_LAST, pColumn->idOperator,
MF_BYCOMMAND);
uID = TrackPopupMenu(GetSubMenu(hMenu, 0),
TPM_RIGHTALIGN|TPM_RETURNCMD,
pt.x, pt.y,
0, pNotify->hdr.hwndFrom, NULL);
switch ( uID )
{
case DSQH_CLEARFILTER:
Header_ClearFilter(ListView_GetHeader(_hwndView), pNotify->iItem);
break;
case DSQH_CLEARALLFILTERS:
Header_ClearAllFilters(ListView_GetHeader(_hwndView));
break;
default:
{
if ( uID && (uID != pColumn->idOperator) )
{
// update the filter string based on the new operator
pColumn->idOperator = uID;
_GetFilterValue(pNotify->iItem, NULL);
lr = TRUE;
}
break;
}
}
DestroyMenu(hMenu);
}
}
}
break;
}
case HDN_ITEMCHANGED:
{
HD_NOTIFY* pNotify = (HD_NOTIFY*)lParam;
HD_ITEM* pitem = (HD_ITEM*)pNotify->pitem;
if ( _hdsaColumns && (pNotify->iItem < DSA_GetItemCount(_hdsaColumns)) )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, pNotify->iItem);
TraceAssert(pColumn);
// store the new column width information in the column structure and
// mark the column table as dirty
if ( pitem->mask & HDI_WIDTH )
{
Trace(TEXT("Column %d, cx %d (marking state as dirty)"), pNotify->iItem, pitem->cxy);
pColumn->cx = pitem->cxy;
_fColumnsModified = TRUE;
}
if ( pitem->mask & HDI_FILTER )
{
Trace(TEXT("Filter for column %d has been changed"), pNotify->iItem);
_GetFilterValue(pNotify->iItem, pitem);
}
}
break;
}
case LVN_GETDISPINFO:
{
LV_DISPINFO* pNotify = (LV_DISPINFO*)lParam;
TraceAssert(pNotify);
if ( pNotify && (pNotify->item.mask & LVIF_TEXT) && pNotify->item.lParam )
{
LPQUERYRESULT pResult = (LPQUERYRESULT)pNotify->item.lParam;
INT iColumn = pNotify->item.iSubItem;
pNotify->item.pszText[0] = TEXT('\0'); // nothing to display yet
switch ( pResult->aColumn[iColumn].iPropertyType )
{
case PROPERTY_ISUNDEFINED:
break;
case PROPERTY_ISUNKNOWN:
case PROPERTY_ISSTRING:
{
if ( pResult->aColumn[iColumn].pszText )
StrCpyN(pNotify->item.pszText, pResult->aColumn[iColumn].pszText, pNotify->item.cchTextMax);
break;
}
case PROPERTY_ISNUMBER:
case PROPERTY_ISBOOL:
wsprintf(pNotify->item.pszText, TEXT("%d"), pResult->aColumn[iColumn].iValue);
break;
}
lr = TRUE; // we formatted a value
}
break;
}
case LVN_ITEMACTIVATE:
{
LPNMHDR pNotify = (LPNMHDR)lParam;
DWORD dwFlags = CMF_NORMAL;
HWND hwndFrame;
HMENU hMenu;
UINT uID;
// convert the current selection to IDLITs and an IContextMenu interface
// that we can then get the default verb from.
SetWaitCursor();
hres = _GetContextMenu();
FailGracefully(hres, "Failed when calling _GetContextMenu");
_fNoSelection = !ListView_GetSelectedCount(_hwndView);
if ( !_fNoSelection )
{
// create a popup menu pickup the context menu for the current selection
// and then pass it down to the invoke command handler.
hMenu = CreatePopupMenu();
TraceAssert(hMenu);
if ( hMenu )
{
if ( GetKeyState(VK_SHIFT) < 0 )
dwFlags |= CMF_EXPLORE; // SHIFT + dblclick does a Explore by default
_GetContextMenuVerbs(hMenu, dwFlags);
uID = GetMenuDefaultItem(hMenu, MF_BYCOMMAND, 0);
Trace(TEXT("Default uID after double click %08x"), uID);
if ( uID != -1 )
{
_pqf->GetWindow(&hwndFrame);
InvokeCommand(hwndFrame, uID);
}
DoRelease(_pcm); // no longer needed
DestroyMenu(hMenu);
}
}
break;
}
case LVN_COLUMNCLICK:
{
NM_LISTVIEW* pNotify = (NM_LISTVIEW*)lParam;
TraceAssert(pNotify);
_SortResults(pNotify->iSubItem);
break;
}
default:
lr = DefWindowProc(hWnd, WM_NOTIFY, wParam, lParam);
break;
}
exit_gracefully:
ResetWaitCursor();
TraceLeaveValue(lr);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnAddResults
/ ----------------------
/ The background thread has sent us some results, so lets add them to
/ the DPA of results, discarding the ones we don't add because we cannot
/ grow the DPA.
/
/ dwQueryReference conatins the reference ID for this query, only add
/ results where these match.
/
/ In:
/ dwQueryReference = reference that this block is for
/ hdpaResults = DPA containing the results to add
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::OnAddResults(DWORD dwQueryReference, HDPA hdpaResults)
{
HRESULT hres;
INT i, iPopulateFrom;
TraceEnter(TRACE_VIEW, "CDsQuery::OnAddResults");
if ( (dwQueryReference != _dwQueryReference) || !hdpaResults )
ExitGracefully(hres, E_FAIL, "Failed to add results, bad DPA/reference ID");
// the caller gives us a DPA then we add them to our result DPA, we then
// update the view populating from the first item we added.
iPopulateFrom = DPA_GetPtrCount(_hdpaResults);
for ( i = DPA_GetPtrCount(hdpaResults); --i >= 0 ; )
{
LPQUERYRESULT pResult = (LPQUERYRESULT)DPA_GetPtr(hdpaResults, i);
TraceAssert(pResult);
// add the result to the main DPA, if that fails then ensure we nuke
// this result blob!
if ( -1 == DPA_AppendPtr(_hdpaResults, pResult) )
FreeQueryResult(pResult, DSA_GetItemCount(_hdsaColumns));
DPA_DeletePtr(hdpaResults, i); // remove from result DPA
}
_PopulateView(iPopulateFrom, DPA_GetPtrCount(_hdpaResults));
TraceAssert(DPA_GetPtrCount(hdpaResults) == 0);
DPA_Destroy(hdpaResults);
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnContextMenu
/ -----------------------
/ The user has right clicked in the result view, therefore we must attempt
/ to display the context menu for those objects
/
/ In:
/ hwndMenu = window that that the user menued over
/ pt = point to show the context menu
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
LRESULT CDsQuery::OnContextMenu(HWND hwndMenu, LPARAM lParam)
{
HRESULT hres;
HMENU hMenu = NULL;
POINT pt = { 0, 0 };
INT i;
RECT rc;
HWND hwndFrame;
TraceEnter(TRACE_VIEW, "CDsQuery::OnContextMenu");
// Collect the selection, obtaining a IContextMenu interface pointer, or a HR == S_FALSE
// if there is no selection for us to be using.
hres = _GetContextMenu();
FailGracefully(hres, "Failed when calling _GetContextMenu()");
_fNoSelection = !ListView_GetSelectedCount(_hwndView);
if ( !(hMenu = CreatePopupMenu()) )
ExitGracefully(hres, E_FAIL, "Failed to create the popup menu");
if ( !_fNoSelection )
{
// pick up the context menu that maps tot he current selection, including fixing
// the "select" verb if we need one.
_GetContextMenuVerbs(hMenu, CMF_NORMAL);
}
else
{
// There is no selection so lets pick up the view bg menu, this contains
// some useful helpers for modifying the view state.
HMENU hBgMenu = LoadMenu(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDR_VIEWBACKGROUND));
if ( !hBgMenu )
ExitGracefully(hres, E_FAIL, "Failed to load pop-up menu for the background");
Shell_MergeMenus(hMenu, GetSubMenu(hBgMenu, 0), 0, 0, CQID_MAXHANDLERMENUID, 0x0);
DestroyMenu(hBgMenu);
_InitViewMenuItems(hMenu);
}
// if lParam == -1 then we know that the user hit the "context menu" key
// so lets set the co-ordinates of the item.
if ( lParam == (DWORD)-1 )
{
i = ListView_GetNextItem(_hwndView, -1, LVNI_FOCUSED|LVNI_SELECTED);
Trace(TEXT("Item with focus + selection: %d"), i);
if ( i == -1 )
{
i = ListView_GetNextItem(_hwndView, -1, LVNI_SELECTED);
Trace(TEXT("1st selected item: %D"), i);
}
if ( i != -1 )
{
TraceMsg("We have an item, so getting bounds of the icon for position");
ListView_GetItemRect(_hwndView, i, &rc, LVIR_ICON);
pt.x = (rc.left+rc.right)/2;
pt.y = (rc.top+rc.bottom)/2;
}
MapWindowPoints(_hwndView, HWND_DESKTOP, &pt, 1); // they are in client co-ordinates
}
else
{
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
}
// we have the position so lets use it
_pqf->GetWindow(&hwndFrame);
TrackPopupMenu(hMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwndFrame, NULL);
exit_gracefully:
if ( hMenu )
DestroyMenu(hMenu);
TraceLeaveValue(0);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnFileProperties
/ --------------------------
/ Show properties for the given selection. To do this we CoCreate
/ IDsFolderProperties on the IDsFolder implementation and that
/ we can invoke properties using.
/
/ In:
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::OnFileProperties(VOID)
{
HRESULT hres;
IDataObject* pDataObject = NULL;
IDsFolderProperties* pDsFolderProperties = NULL;
TraceEnter(TRACE_VIEW, "CDsQuery::OnFileProperties");
hres = GetViewObject(CQRVS_SELECTION, IID_IDataObject, (void **)&pDataObject);
FailGracefully(hres, "Failed to get IDataObject for shortcut creation");
hres = CoCreateInstance(CLSID_DsFolderProperties, NULL, CLSCTX_INPROC_SERVER, IID_IDsFolderProperties, (void **)&pDsFolderProperties);
FailGracefully(hres, "Failed to get IDsFolderProperties for the desktop object");
hres = pDsFolderProperties->ShowProperties(_hwnd, pDataObject);
FailGracefully(hres, "Failed to invoke property UI for the given selection");
// hres = S_OK; // success
exit_gracefully:
DoRelease(pDataObject);
DoRelease(pDsFolderProperties);
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnFileSaveQuery
/ -------------------------
/ Allow the user to choose a location to save the query (initial directory
/ is nethood). Having done that we then start the save process by passing
/ the frame object a IQueryIO object that allows them to persist the
/ query into.
/
/ In:
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::OnFileSaveQuery(VOID)
{
HRESULT hres;
OPENFILENAME ofn;
TCHAR szFilename[MAX_PATH];
TCHAR szDirectory[MAX_PATH];
TCHAR szFilter[64];
TCHAR szTitle[64];
LPTSTR pFilter;
IPersistQuery *ppq = NULL;
USES_CONVERSION;
TraceEnter(TRACE_VIEW, "CDsQuery::OnFileSaveQuery");
// Load the default strings and fix up the filter string as it needs
// NULL's seperating the various resource sections.
LoadString(GLOBAL_HINSTANCE, IDS_SAVETITLE, szTitle, ARRAYSIZE(szTitle));
StrCpy(szFilename, _pDefaultSaveName);
LoadString(GLOBAL_HINSTANCE, IDS_SAVEFILTER, szFilter, ARRAYSIZE(szFilter));
for ( pFilter = szFilter ; *pFilter ; pFilter++ )
{
if ( *pFilter == TEXT('\n') )
*pFilter = TEXT('\0');
}
// fix the open filename structure ready to do our save....
ZeroMemory(&ofn, SIZEOF(ofn));
ofn.lStructSize = SIZEOF(ofn);
_pqf->GetWindow(&ofn.hwndOwner);
ofn.hInstance = GLOBAL_HINSTANCE;
ofn.lpstrFilter = szFilter;
ofn.lpstrFile = szFilename;
ofn.nMaxFile = ARRAYSIZE(szFilename);
if ( _pDefaultSaveLocation )
{
Trace(TEXT("Saving into: %s"), W2T(_pDefaultSaveLocation));
StrCpy(szDirectory, W2T(_pDefaultSaveLocation));
ofn.lpstrInitialDir = szDirectory;
}
ofn.lpstrTitle = szTitle;
ofn.Flags = OFN_EXPLORER|OFN_NOCHANGEDIR|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY;
ofn.lpstrDefExt = TEXT("dsq");
// If we get a save filename then lets ensure that we delete the previous
// query saved there (if there is one) and then we can create an IPersistQuery
// object that will save to that location.
if ( GetSaveFileName(&ofn) )
{
Trace(TEXT("Saving query as: %s"), szFilename);
if ( !DeleteFile(szFilename) && (GetLastError() != ERROR_FILE_NOT_FOUND) )
ExitGracefully(hres, E_FAIL, "Failed to delete previous query");
hres = CPersistQuery_CreateInstance(szFilename, &ppq);
FailGracefully(hres, "Failed to create the peristance object");
hres = _pqf->SaveQuery(ppq);
FailGracefully(hres, "Failed when calling IQueryFrame::SaveSearch");
}
hres = S_OK;
exit_gracefully:
DoRelease(ppq);
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnEditSelectAll
/ -------------------------
/ Walk all the items in the view setting their selected state.
/
/ In:
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::OnEditSelectAll(VOID)
{
TraceEnter(TRACE_VIEW, "CDsQuery::OnEditSelectAll");
for ( INT i = ListView_GetItemCount(_hwndView) ; --i >= 0 ; )
{
ListView_SetItemState(_hwndView, i, LVIS_SELECTED, LVIS_SELECTED);
}
TraceLeaveResult(S_OK);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnEditInvertSelection
/ -------------------------------
/ Walk all the items in the view and invert their selected state.
/
/ In:
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::OnEditInvertSelection(VOID)
{
TraceEnter(TRACE_VIEW, "CDsQuery::OnEditInvertSelection");
for ( INT i = ListView_GetItemCount(_hwndView) ; --i >= 0 ; )
{
DWORD dwState = ListView_GetItemState(_hwndView, i, LVIS_SELECTED);
ListView_SetItemState(_hwndView, i, dwState ^ LVIS_SELECTED, LVIS_SELECTED);
}
TraceLeaveResult(S_OK);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_InitNewQuery
/ ----------------------
/ Initialize the view ready for a new query, including setting the frame
/ into a query running state.
/
/ In:
/ pDsQueryParams = DSQUERYPARAMs structure used to issue the query.
/ fRefreshColumnTable = re-read the column table from the params/registry etc
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::_InitNewQuery(LPDSQUERYPARAMS pDsQueryParams, BOOL fRefreshColumnTable)
{
HRESULT hres;
LPTSTR pBuffer;
TraceEnter(TRACE_VIEW, "CDsQuery::_InitNewQuery");
hres = _pqf->StartQuery(TRUE);
TraceAssert(SUCCEEDED(hres));
// Claim the cached DS object for this window
// if refreshing the column table then _GetColumnTable handles this all for us,
// otherwise lets just nuke the result set ourselves.
if ( fRefreshColumnTable )
{
_SaveColumnTable();
hres = _GetColumnTable(_clsidForm, pDsQueryParams, &_hdsaColumns, TRUE);
FailGracefully(hres, "Failed to create column DSA");
}
else
{
_FreeResults();
}
// initialize the view to start the query running, display the prompt banner and
// initialize the result DPA.
_ShowBanner(SWP_SHOWWINDOW, IDS_SEARCHING); // we are now searching
if ( SUCCEEDED(FormatMsgResource(&pBuffer, GLOBAL_HINSTANCE, IDS_SEARCHING)) )
{
_pqf->SetStatusText(pBuffer);
LocalFreeString(&pBuffer);
}
TraceAssert(_hdpaResults==NULL); // should never catch
_hdpaResults = DPA_Create(16);
TraceAssert(_hdpaResults);
if ( !_hdpaResults )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate result DPA");
_dwQueryReference++;
exit_gracefully:
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_GetFilterValue
/ ------------------------
/ Given a column index collect the filter value from it, note that
/ when doing this
/
/ In:
/ i = column to retrieve
/ pitem -> HD_ITEM structure for the current filter / == NULL then read from header
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::_GetFilterValue(INT i, HD_ITEM* pitem)
{
HRESULT hres;
HD_ITEM hdi;
HD_TEXTFILTER textFilter;
TCHAR szBuffer[MAX_PATH];
INT iValue;
UINT cchFilter = 0;
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
if (!pColumn)
ExitGracefully(hres, E_FAIL, "Failed to get the column");
TraceEnter(TRACE_VIEW, "CDsQuery::_GetFilterValue");
// if pitem == NULL then lets pick up the filter value from the
// header control, using the stored property type (the filter one
// has already been nuked) to defined which filter we want.
if ( !pitem )
{
hdi.mask = HDI_FILTER;
switch ( pColumn->iPropertyType )
{
case PROPERTY_ISUNKNOWN:
case PROPERTY_ISSTRING:
{
hdi.type = HDFT_ISSTRING;
hdi.pvFilter = &textFilter;
textFilter.pszText = szBuffer;
textFilter.cchTextMax = ARRAYSIZE(szBuffer);
break;
}
case PROPERTY_ISNUMBER:
case PROPERTY_ISBOOL:
{
hdi.type = HDFT_ISNUMBER;
hdi.pvFilter = &iValue;
break;
}
}
if ( !Header_GetItem(ListView_GetHeader(_hwndView), i, &hdi) )
ExitGracefully(hres, E_FAIL, "Failed to get the filter string");
pitem = &hdi;
}
// discard the previous filter value and lets read from the
// structure the information we need to cache our filter information
FreeColumnValue(&pColumn->filter);
if ( !(pitem->type & HDFT_HASNOVALUE) && pitem->pvFilter )
{
switch ( pitem->type & HDFT_ISMASK )
{
case HDFT_ISSTRING:
{
LPHD_TEXTFILTER ptextFilter = (LPHD_TEXTFILTER)pitem->pvFilter;
TraceAssert(ptextFilter);
pColumn->filter.iPropertyType = PROPERTY_ISSTRING;
// text filters are stored in their wildcarded state, therefore
// filtering doesn't require converting from the text form
// to something more elobrate each pass through. the down
// side is when the operator changes we must rebuild the
// filter string for that column (small price)
GetPatternString(NULL, &cchFilter, pColumn->idOperator, ptextFilter->pszText);
TraceAssert(cchFilter != 0);
if ( cchFilter )
{
hres = LocalAllocStringLen(&pColumn->filter.pszText, cchFilter);
FailGracefully(hres, "Failed to allocate buffer to read string into");
GetPatternString(pColumn->filter.pszText, &cchFilter, pColumn->idOperator, ptextFilter->pszText);
Trace(TEXT("Filter (with pattern info): %s"), pColumn->filter.pszText);
LCMapString(0x0, LCMAP_UPPERCASE, pColumn->filter.pszText, -1, pColumn->filter.pszText, cchFilter+1);
Trace(TEXT("After converting to uppercase (LCMapString): %s"), pColumn->filter.pszText);
}
break;
}
case HDFT_ISNUMBER:
{
INT* piFilter = (INT*)pitem->pvFilter;
TraceAssert(piFilter);
pColumn->filter.iPropertyType = PROPERTY_ISNUMBER;
pColumn->filter.iValue = *piFilter;
Trace(TEXT("Filter: %d"), pColumn->filter.iValue);
break;
}
}
}
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_FilterView
/ --------------------
/ Filter the result set populating the view again with the changes
/
/ In:
/ fCheck
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
UINT _GetFilter(HDSA hdsaColumns, LPTSTR pBuffer, UINT* pcchBuffer)
{
INT i;
TCHAR szBuffer[MAX_PATH];
TraceEnter(TRACE_VIEW, "_GetFilter");
TraceAssert(hdsaColumns && pcchBuffer);
*pcchBuffer = 0;
// form the string containin [operatorID]value pairs for each of the
// filter columns that is defined.
for ( i = 0 ; i < DSA_GetItemCount(hdsaColumns); i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(hdsaColumns, i);
TraceAssert(pColumn);
if ( pColumn->filter.iPropertyType != PROPERTY_ISUNDEFINED )
{
wsprintf(szBuffer, TEXT("[%d]"), pColumn->idOperator);
PutStringElement(pBuffer, pcchBuffer, szBuffer);
switch ( pColumn->filter.iPropertyType )
{
case PROPERTY_ISUNDEFINED:
break;
case PROPERTY_ISUNKNOWN:
case PROPERTY_ISSTRING:
PutStringElement(pBuffer, pcchBuffer, pColumn->filter.pszText);
break;
case PROPERTY_ISNUMBER:
case PROPERTY_ISBOOL:
wsprintf(szBuffer, TEXT("%d"), pColumn->filter.iValue);
PutStringElement(pBuffer, pcchBuffer, szBuffer);
break;
}
}
}
Trace(TEXT("pBuffer contains: %s (%d)"), pBuffer ? pBuffer:TEXT("<NULL>"), *pcchBuffer);
TraceLeaveValue(*pcchBuffer);
}
HRESULT CDsQuery::_FilterView(BOOL fCheck)
{
HRESULT hres = S_OK;
LPTSTR pFilter = NULL;
UINT cchFilter;
BOOL fSetCursor = FALSE;
TraceEnter(TRACE_VIEW, "CDsQuery::_FilterView");
if ( !_hdpaResults )
ExitGracefully(hres, S_OK, "FitlerView bailing, no results");
DECLAREWAITCURSOR;
SetWaitCursor(); // this could take some time
fSetCursor = TRUE;
// get the current filter string, this consists of the filter
// information from all the columns
if ( _GetFilter(_hdsaColumns, NULL, &cchFilter) )
{
hres = LocalAllocStringLen(&pFilter, cchFilter);
FailGracefully(hres, "Failed to allocate filter string");
_GetFilter(_hdsaColumns, pFilter, &cchFilter);
}
// if the filters don't match then re-populate the view,
// as the criteria for the results has changed
if ( !fCheck ||
(!pFilter || !_pFilter) ||
(pFilter && _pFilter && StrCmpI(pFilter, _pFilter)) )
{
LPTSTR pBuffer;
TraceMsg("Filtering the view, filters differ");
ListView_DeleteAllItems(_hwndView);
_ShowBanner(SWP_SHOWWINDOW, IDS_FILTERING);
if ( SUCCEEDED(FormatMsgResource(&pBuffer, GLOBAL_HINSTANCE, IDS_FILTERING)) )
{
_pqf->SetStatusText(pBuffer);
LocalFreeString(&pBuffer);
}
_PopulateView(0, DPA_GetPtrCount(_hdpaResults));
}
// ensure we hang onto the new filter, discarding the previous one
LocalFreeString(&_pFilter);
_pFilter = pFilter;
exit_gracefully:
if (fSetCursor)
ResetWaitCursor();
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_PopulateView
/ ----------------------
/ Add items from the result DPA to the view filtering as required. The
/ caller gives us the start index (0 if all) and we walk the results
/ adding them to the view.
/
/ In:
/ iItem = first item to add / == 0 first / == -1 then add none, just update status
/ iLast = last item to be updated
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::_PopulateView(INT iItem, INT iLast)
{
HRESULT hres;
BOOL fBannerShown = IsWindowVisible(_hwndBanner);
LPTSTR pBuffer = NULL;
LV_ITEM lvi;
INT iColumn, i;
INT iVisible = 0;
INT iHidden = 0;
BOOL fIncludeItem;
MSG msg;
USES_CONVERSION;
TraceEnter(TRACE_VIEW, "CDsQuery::_PopulateView");
Trace(TEXT("Range %d to %d"), iItem, iLast);
if ( iItem > -1 )
{
Trace(TEXT("Adding items %d to %d"), iItem, DPA_GetPtrCount(_hdpaResults));
lvi.mask = LVIF_TEXT|LVIF_PARAM|LVIF_IMAGE;
lvi.iItem = 0x7fffffff;
lvi.iSubItem = 0;
lvi.pszText = LPSTR_TEXTCALLBACK;
// Walk the results in the range we need to add and add them to the view
// applying the filter to remove items we are not interested in.
for ( i = 0; iItem < iLast ; i++, iItem++ )
{
LPQUERYRESULT pResult = (LPQUERYRESULT)DPA_GetPtr(_hdpaResults, iItem);
if (!pResult)
continue;
fIncludeItem = TRUE; // new items always get included
// if the filter is visilbe then lets walk it removing items from the list
// of results. fIncludeItem starts as TRUE and after the filter
// loop should become either TRUE/FALSE. All columns are ANDed together
// therefore the logic is quite simple.
if ( _fFilter )
{
for ( iColumn = 0 ; fIncludeItem && (iColumn < DSA_GetItemCount(_hdsaColumns)); iColumn++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, iColumn);
TraceAssert(pColumn);
// if the column has a filter defined (!PROPERTY_ISUNDEFINED) then
// check that the properties match, if they don't then skip the
// itmer otherwise lets try applying the filter based on
// the type.
if ( pColumn->filter.iPropertyType == PROPERTY_ISUNDEFINED )
continue;
if ( pResult->aColumn[iColumn].iPropertyType == PROPERTY_ISUNDEFINED )
{
// column is undefined therefore lets ignore it, it won't
// match the criteria
fIncludeItem = FALSE;
}
else
{
switch ( pColumn->filter.iPropertyType )
{
case PROPERTY_ISUNDEFINED:
break;
case PROPERTY_ISUNKNOWN:
case PROPERTY_ISSTRING:
{
TCHAR szBuffer[MAX_PATH];
LPTSTR pszBuffer = NULL;
LPTSTR pszValue = pResult->aColumn[iColumn].pszText;
INT cchValue = lstrlen(pszValue);
LPTSTR pszValueUC = szBuffer;
// the filter value is stored in uppercase, so to ensure we are case insensitive
// we must convert the string to uppercase. We have a buffer we will use,
// however, if the value is too large we will allocate a buffer we can use.
if ( cchValue > ARRAYSIZE(szBuffer) )
{
TraceMsg("Value too big for our static buffer, so allocating");
if ( FAILED(LocalAllocStringLen(&pszBuffer, cchValue)) )
{
TraceMsg("Failed to allocate a buffer for the string, so ignoring it!");
fIncludeItem = FALSE;
break;
}
pszValueUC = pszBuffer; // fix the pointer to the new string
}
LCMapString(0x0, LCMAP_UPPERCASE, pszValue, -1, pszValueUC, cchValue+1);
Trace(TEXT("After converting to uppercase (LCMapString): %s"), pszValueUC);
// string properties need to be compared using the match filter
// function, this code is given the filter and the result
// and we must compare, in return we get TRUE/FALSE, therefore
// catch the NOT cases specificly.
switch ( pColumn->idOperator )
{
case FILTER_CONTAINS:
case FILTER_STARTSWITH:
case FILTER_ENDSWITH:
case FILTER_IS:
fIncludeItem = MatchPattern(pszValueUC, pColumn->filter.pszText);
break;
case FILTER_NOTCONTAINS:
case FILTER_ISNOT:
fIncludeItem = !MatchPattern(pszValueUC, pColumn->filter.pszText);
break;
}
LocalFreeString(&pszBuffer); // ensure we don't leak, in thise case it would be costly!
break;
}
case PROPERTY_ISBOOL:
case PROPERTY_ISNUMBER:
{
// numeric properties are handled only as ints, therefore
// lets compare the numeric value we have
switch ( pColumn->idOperator )
{
case FILTER_IS:
fIncludeItem = (pColumn->filter.iValue == pResult->aColumn[iColumn].iValue);
break;
case FILTER_ISNOT:
fIncludeItem = (pColumn->filter.iValue != pResult->aColumn[iColumn].iValue);
break;
case FILTER_GREATEREQUAL:
fIncludeItem = (pColumn->filter.iValue <= pResult->aColumn[iColumn].iValue);
break;
case FILTER_LESSEQUAL:
fIncludeItem = (pColumn->filter.iValue >= pResult->aColumn[iColumn].iValue);
break;
}
break;
}
}
}
}
}
Trace(TEXT("fInclude item is %d"), fIncludeItem);
if ( fIncludeItem )
{
// we are going to add the item to the view, so lets hide the banner
// if it is shown, then add a list view item. The list view
// item has text-callback and the lParam -> pResult structure
// we are using.
//
// also, if the view hasn't had the image list set then lets
// take care of that now
if ( fBannerShown )
{
TraceMsg("Adding an item and banner visible, therefore hiding");
_ShowBanner(SWP_HIDEWINDOW, 0); // hide the banner
fBannerShown = FALSE;
}
lvi.lParam = (LPARAM)pResult;
lvi.iImage = pResult->iImage;
ListView_InsertItem(_hwndView, &lvi);
if ( i % FILTER_UPDATE_COUNT )
UpdateWindow(_hwndView);
}
}
}
// lets update the status bar to reflect the world around us
TraceAssert(_hdpaResults);
if ( _hdpaResults )
{
iVisible = ListView_GetItemCount(_hwndView);
iHidden = DPA_GetPtrCount(_hdpaResults)-iVisible;
}
if ( iVisible <= 0 )
{
_ShowBanner(SWP_SHOWWINDOW, IDS_NOTHINGFOUND);
}
else
{
// ensure that at least one item in the view has focus
if ( -1 == ListView_GetNextItem(_hwndView, -1, LVNI_FOCUSED) )
ListView_SetItemState(_hwndView, 0, LVIS_FOCUSED, LVIS_FOCUSED);
}
if ( SUCCEEDED(FormatMsgResource(&pBuffer,
GLOBAL_HINSTANCE, iHidden ? IDS_FOUNDITEMSHIDDEN:IDS_FOUNDITEMS,
iVisible, iHidden)) )
{
Trace(TEXT("Setting status text to: %s"), pBuffer);
_pqf->SetStatusText(pBuffer);
LocalFreeString(&pBuffer);
}
_iSortColumn = -1; // sort is no longer valid!
TraceLeaveResult(S_OK);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_FreeResults
/ ---------------------
/ Do the tidy up to release the results from the view. This includes
/ destroying the DPA and removing the items from the list view.
/
/ In:
/ Out:
/ -
/----------------------------------------------------------------------------*/
VOID CDsQuery::_FreeResults(VOID)
{
TraceEnter(TRACE_VIEW, "CDsQuery::_FreeResults");
if ( IsWindow(_hwndView) )
ListView_DeleteAllItems(_hwndView);
if ( _hdpaResults )
{
DPA_DestroyCallback(_hdpaResults, FreeQueryResultCB, IntToPtr(DSA_GetItemCount(_hdsaColumns)));
_hdpaResults = NULL;
}
LocalFreeString(&_pFilter);
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_SetViewMode
/ ---------------------
/ Convert the command ID of the view mode into a LVS_ style bit that can
/ then we applied to the view.
/
/ In:
/ uID = view mode to be selected
/
/ Out:
/ DWORD = LVS_ style for this view mode
/----------------------------------------------------------------------------*/
DWORD CDsQuery::_SetViewMode(INT uID)
{
const DWORD dwIdToStyle[] = { LVS_ICON, LVS_SMALLICON, LVS_LIST, LVS_REPORT };
DWORD dwResult = 0;
DWORD dwStyle;
TraceEnter(TRACE_HANDLER|TRACE_VIEW, "CDsQuery::_SetViewMode");
Trace(TEXT("Setting view mode to %08x"), uID);
_idViewMode = uID;
uID -= DSQH_VIEW_LARGEICONS;
if ( uID < ARRAYSIZE(dwIdToStyle ) )
{
dwResult = dwIdToStyle[uID];
if ( IsWindow(_hwndView) )
{
dwStyle = GetWindowLong(_hwndView, GWL_STYLE);
if ( ( dwStyle & LVS_TYPEMASK ) != dwResult )
{
TraceMsg("Changing view style to reflect new mode");
SetWindowLong(_hwndView, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK)|dwResult);
}
}
}
_ShowBanner(0, 0);
TraceLeaveValue(dwResult);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_SetFilter
/ -------------------
/ Sets the visible state of the filter, refreshing the view as required.
/
/ NB: To ensure that the ListView correctly refrehes its contents we first
/ remove the header from the view, then toggle the filter state
/ and re-enable the banner.
/ In:
/ fFilter = flag indicating the filter state
/
/ Out:
/ VOID
/----------------------------------------------------------------------------*/
VOID CDsQuery::_SetFilter(BOOL fFilter)
{
TraceEnter(TRACE_HANDLER|TRACE_VIEW, "CDsQuery::_SetFilter");
_fFilter = fFilter; // store the new filter value
if ( IsWindow(_hwndView) )
{
HWND hwndHeader = ListView_GetHeader(_hwndView);
DWORD dwStyle = GetWindowLong(hwndHeader, GWL_STYLE) & ~(HDS_FILTERBAR|WS_TABSTOP);
SetWindowLong(_hwndView, GWL_STYLE, GetWindowLong(_hwndView, GWL_STYLE) | LVS_NOCOLUMNHEADER);
if ( _fFilter )
dwStyle |= HDS_FILTERBAR|WS_TABSTOP;
SetWindowLong(hwndHeader, GWL_STYLE, dwStyle);
SetWindowLong(_hwndView, GWL_STYLE, GetWindowLong(_hwndView, GWL_STYLE) & ~LVS_NOCOLUMNHEADER);
_ShowBanner(0, 0);
if ( _hdpaResults )
{
if ( (_fFilter && _pFilter) ||
(!_fFilter && (DPA_GetPtrCount(_hdpaResults) != ListView_GetItemCount(_hwndView))) )
{
_FilterView(FALSE);
}
}
}
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_SortResults
/ ---------------------
/ Sort the view given a column, handles either being clicked or
/ invoked on from an API.
/
/ In:
/ iColumn = column to sort on
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
INT _ResultSortCB(LPARAM lParam1, LPARAM lParam2, LPARAM lParam)
{
LPQUERYRESULT pResult1, pResult2;
INT iColumn = LOWORD(lParam);
// if lParam != 0 then we are reverse sorting, therefore swap
// over the object pointers
if ( !HIWORD(lParam) )
{
pResult1 = (LPQUERYRESULT)lParam1;
pResult2 = (LPQUERYRESULT)lParam2;
}
else
{
pResult2 = (LPQUERYRESULT)lParam1;
pResult1 = (LPQUERYRESULT)lParam2;
}
if ( pResult1 && pResult2 )
{
LPCOLUMNVALUE pColumn1 = (LPCOLUMNVALUE)&pResult1->aColumn[iColumn];
LPCOLUMNVALUE pColumn2 = (LPCOLUMNVALUE)&pResult2->aColumn[iColumn];
BOOL fHasColumn1 = pColumn1->iPropertyType != PROPERTY_ISUNDEFINED;
BOOL fHasColumn2 = pColumn2->iPropertyType != PROPERTY_ISUNDEFINED;
// check that both properties are defined, if they are not then return the
// comparison based on that field. then we check that the properties
// are the same type, if that matches then lets compare based on the
// type.
if ( !fHasColumn1 || !fHasColumn2 )
{
return fHasColumn1 ? -1:+1;
}
else
{
TraceAssert(pColumn1->iPropertyType == pColumn2->iPropertyType);
switch ( pColumn1->iPropertyType )
{
case PROPERTY_ISUNDEFINED:
break;
case PROPERTY_ISUNKNOWN:
case PROPERTY_ISSTRING:
return StrCmpI(pColumn1->pszText, pColumn2->pszText);
case PROPERTY_ISBOOL:
case PROPERTY_ISNUMBER:
return pColumn1->iValue - pColumn2->iValue;
}
}
}
return 0;
}
VOID CDsQuery::_SortResults(INT iColumn)
{
DECLAREWAITCURSOR;
TraceEnter(TRACE_VIEW, "CDsQuery::_SortResults");
Trace(TEXT("iColumn %d"), iColumn);
if ( (iColumn >= 0) && (iColumn < DSA_GetItemCount(_hdsaColumns)) )
{
// if we have already hit the column then lets invert the sort order,
// there is no indicator to worry about so this should just work out
// fine. if we haven't used this column before then default oto
// ascending, then do the sort!
//
// ensure that the focused item is visible when the sort has completed
if ( _iSortColumn == iColumn )
_fSortDescending = !_fSortDescending;
else
_fSortDescending = FALSE;
_iSortColumn = iColumn;
Trace(TEXT("Sorting on column %d, %s"),
_iSortColumn, _fSortDescending ? TEXT("(descending)"):TEXT("(ascending)"));
SetWaitCursor();
ListView_SortItems(_hwndView, _ResultSortCB, MAKELPARAM(_iSortColumn, _fSortDescending));
ResetWaitCursor();
}
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_ShowBanner
/ --------------------
/ Show the views banner, including sizing it to obscure only the top section
/ of the window.
/
/ In:
/ uFlags = flags to combine when calling SetWindowPos
/ idPrompt = resource ID of prompt text ot be displayed
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
VOID CDsQuery::_ShowBanner(UINT uFlags, UINT idPrompt)
{
HRESULT hres;
WINDOWPOS wpos;
RECT rcClient;
HD_LAYOUT hdl;
TCHAR szBuffer[MAX_PATH];
TraceEnter(TRACE_VIEW, "CDsQuery::_ShowBanner");
// if we have a resource id then lets load the string and
// set the window text to have it
if ( idPrompt )
{
LoadString(GLOBAL_HINSTANCE, idPrompt, szBuffer, ARRAYSIZE(szBuffer));
SetWindowText(_hwndBanner, szBuffer);
}
// now position the window back to real location, this we need to
// talk to the listview/header control to work out exactly where it
// should be living
GetClientRect(_hwndView, &rcClient);
if ( (GetWindowLong(_hwndView, GWL_STYLE) & LVS_TYPEMASK) == LVS_REPORT )
{
TraceMsg("Calling header for layout information");
wpos.hwnd = ListView_GetHeader(_hwndView);
wpos.hwndInsertAfter = NULL;
wpos.x = 0;
wpos.y = 0;
wpos.cx = rcClient.right;
wpos.cy = rcClient.bottom;
wpos.flags = SWP_NOZORDER;
hdl.prc = &rcClient;
hdl.pwpos = &wpos;
if ( !Header_Layout(wpos.hwnd, &hdl) )
ExitGracefully(hres, E_FAIL, "Failed to get the layout information (HDM_LAYOUT)");
}
SetWindowPos(_hwndBanner,
HWND_TOP,
rcClient.left, rcClient.top,
rcClient.right - rcClient.left, 100,
uFlags);
exit_gracefully:
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_InitViewMenuItems
/ ---------------------------
/ Setup the view menu based on the given view mode and the filter state, enabled
/ disable the items as required.
/
/ In:
/ hMenu = menu to set the menu items on
/
/ Out:
/ -
/----------------------------------------------------------------------------*/
VOID CDsQuery::_InitViewMenuItems(HMENU hMenu)
{
MENUITEMINFO mii;
HMENU hArrangeMenu;
BOOL fHaveView = IsWindow(_hwndView);
INT i;
TraceEnter(TRACE_HANDLER|TRACE_VIEW, "CDsQuery::_InitViewMenuItems");
CheckMenuItem(hMenu, DSQH_VIEW_FILTER, MF_BYCOMMAND| (_fFilter ? MF_CHECKED:0) );
ENABLE_MENU_ITEM(hMenu, DSQH_VIEW_FILTER, fHaveView && _fFilterSupported && (_idViewMode == DSQH_VIEW_DETAILS));
CheckMenuRadioItem(hMenu, DSQH_VIEW_LARGEICONS, DSQH_VIEW_DETAILS, _idViewMode, MF_BYCOMMAND);
// construct the arrange menu, add it to the view menu that we have been given.
hArrangeMenu = CreatePopupMenu();
TraceAssert(hArrangeMenu);
if ( _hdsaColumns && DSA_GetItemCount(_hdsaColumns) )
{
TCHAR szFmt[32];
TCHAR szBuffer[MAX_PATH];
hArrangeMenu = CreatePopupMenu();
TraceAssert(hArrangeMenu);
LoadString(GLOBAL_HINSTANCE, IDS_ARRANGEBY, szFmt, ARRAYSIZE(szFmt));
if ( hArrangeMenu )
{
for ( i = 0 ; i < DSA_GetItemCount(_hdsaColumns); i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
TraceAssert(pColumn);
wsprintf(szBuffer, szFmt, pColumn->pHeading);
InsertMenu(hArrangeMenu, i, MF_STRING|MF_BYPOSITION, DSQH_VIEW_ARRANGEFIRST+i, szBuffer);
}
}
}
// now place the arrange menu into the view and ungrey as required.
ZeroMemory(&mii, SIZEOF(mii));
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_SUBMENU|MIIM_ID;
mii.hSubMenu = hArrangeMenu;
mii.wID = DSQH_VIEW_ARRANGEICONS;
if ( SetMenuItemInfo(hMenu, DSQH_VIEW_ARRANGEICONS, FALSE, &mii) )
{
ENABLE_MENU_ITEM(hMenu, DSQH_VIEW_ARRANGEICONS, fHaveView && GetMenuItemCount(hArrangeMenu));
hArrangeMenu = NULL;
}
if ( hArrangeMenu )
DestroyMenu(hArrangeMenu);
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_GetQueryFormKey
/ --------------------------
/ Given the CLSID for the query form we are interested in, get the
/ forms key for it, note that these settings are stored per-user.
/
/ In:
/ clsidForm = CLSID of form to pick up
/ phKey -> recevies the HKEY of the form we are intersted in.
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::_GetQueryFormKey(REFCLSID clsidForm, HKEY* phKey)
{
HRESULT hres;
TCHAR szGUID[GUIDSTR_MAX];
TCHAR szBuffer[MAX_PATH];
TraceEnter(TRACE_VIEW, "CDsQuery::_GetQueryFormKey");
GetStringFromGUID(clsidForm, szGUID, ARRAYSIZE(szGUID));
wsprintf(szBuffer, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Directory UI\\QueryForms\\%s"), szGUID);
Trace(TEXT("Settings key is: %s"), szBuffer);
if ( ERROR_SUCCESS != RegCreateKey(HKEY_CURRENT_USER, szBuffer, phKey) )
ExitGracefully(hres, E_FAIL, "Failed to open settings key");
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_GetColumnTable
/ ------------------------
/ Build the column table for the view we are about to display. The column
/ table is constructed from either the query parameters or the persisted
/ column settings stored in the registry.
/
/ In:
/ clsidForm = clisd of the form to be used
/ pDsQueryParams -> query parameter structure to be used
/ pHDSA -> DSA to recieve the column table (sorted by visible order)
/ fSetInView = Set the columns into the view
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::_GetColumnTable(REFCLSID clsidForm, LPDSQUERYPARAMS pDsQueryParams, HDSA* pHDSA, BOOL fSetInView)
{
HRESULT hres;
HKEY hKey = NULL;
BOOL fDefaultSettings = TRUE;
LPSAVEDCOLUMN aSavedColumn = NULL;
LPTSTR pSettingsValue = VIEW_SETTINGS_VALUE;
BOOL fHaveSizeInfo = FALSE;
LPWSTR pProperty;
DWORD dwType, cbSize;
LV_COLUMN lvc;
SIZE sz;
INT i, iNewColumn;
HD_ITEM hdi;
IDsDisplaySpecifier *pdds = NULL;
USES_CONVERSION;
TraceEnter(TRACE_VIEW, "CDsQuery::_GetColumnTable");
TraceGUID("clsidForm ", clsidForm);
Trace(TEXT("pDsQueryParams %08x, pHDSA %08x"), pDsQueryParams, pHDSA);
DECLAREWAITCURSOR;
SetWaitCursor();
if ( !pHDSA )
ExitGracefully(hres, E_INVALIDARG, "Bad pDsQueryParams / pHDSA");
// construct the column DSA then attempt to look up the view settings stored in
// the registry for the current form.
*pHDSA = DSA_Create(SIZEOF(COLUMN), 16);
TraceAssert(*pHDSA);
if ( !*pHDSA )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to construct the column DSA");
// If invoked for admin UI then we look at the admin view settings, this way
// the admin can have one set of columns, and the user can have another.
if ( _dwFlags & DSQPF_ENABLEADMINFEATURES )
pSettingsValue = ADMIN_VIEW_SETTINGS_VALUE;
Trace(TEXT("View settings value: %s"), pSettingsValue);
if ( SUCCEEDED(_GetQueryFormKey(clsidForm, &hKey)) )
{
// we have the handle to the forms sub-key table, now lets check and
// see what size the view settings stream is.
if ( (ERROR_SUCCESS == RegQueryValueEx(hKey, pSettingsValue, NULL, &dwType, NULL, &cbSize)) &&
(dwType == REG_BINARY) &&
(cbSize > SIZEOF(SAVEDCOLUMN)) )
{
Trace(TEXT("Reading view settings from registry (size %d)"), cbSize);
aSavedColumn = (LPSAVEDCOLUMN)LocalAlloc(LPTR, cbSize);
TraceAssert(aSavedColumn);
if ( aSavedColumn &&
(ERROR_SUCCESS == RegQueryValueEx(hKey, pSettingsValue, NULL, NULL, (LPBYTE)aSavedColumn, &cbSize)) )
{
// compute the size of the table from the values that we have
// read from the registry and now lets allocate a table for it
for ( i = 0; aSavedColumn[i].cbSize; i++ )
{
COLUMN column = { 0 };
LPCWSTR pProperty = (LPCWSTR)ByteOffset(aSavedColumn, aSavedColumn[i].offsetProperty);
LPCTSTR pHeading = (LPCTSTR)ByteOffset(aSavedColumn, aSavedColumn[i].offsetHeading);
hres = LocalAllocStringW(&column.pProperty, pProperty);
FailGracefully(hres, "Failed to allocate property name");
hres = LocalAllocString(&column.pHeading, pHeading);
FailGracefully(hres, "Failed to allocate column heading");
//column.fHasColumnProvider = FALSE;
column.cx = aSavedColumn[i].cx;
column.fmt = aSavedColumn[i].fmt;
column.iPropertyType = PROPERTY_ISUNKNOWN;
//column.idOperator = 0;
//column.clsidColumnHandler = { 0 };
//column.pColumnHandler = NULL;
ZeroMemory(&column.filter, SIZEOF(column.filter));
column.filter.iPropertyType = PROPERTY_ISUNDEFINED;
Trace(TEXT("pProperty: '%s', pHeading: '%s', cx %d, fmt %08x"),
W2T(column.pProperty), column.pHeading, column.cx, column.fmt);
if ( -1 == DSA_AppendItem(*pHDSA, &column) )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to add column to the DSA");
}
fDefaultSettings = FALSE; // success we have a table
}
}
}
if ( fDefaultSettings )
{
// unable to read the settings from the registy, therefore defaulting to
// those defined in the query parameters block.
if ( !pDsQueryParams )
ExitGracefully(hres, E_INVALIDARG, "No DSQUERYPARAMs to default using");
for ( i = 0 ; i < pDsQueryParams->iColumns; i++ )
{
COLUMN column = { 0 };
switch ( pDsQueryParams->aColumns[i].offsetProperty )
{
case DSCOLUMNPROP_ADSPATH:
pProperty = c_szADsPathCH;
break;
case DSCOLUMNPROP_OBJECTCLASS:
pProperty = c_szObjectClassCH;
break;
default:
pProperty = (LPWSTR)ByteOffset(pDsQueryParams, pDsQueryParams->aColumns[i].offsetProperty);
break;
}
hres = LocalAllocStringW(&column.pProperty, pProperty);
FailGracefully(hres, "Failed to allocate property name");
hres = FormatMsgResource(&column.pHeading, pDsQueryParams->hInstance, pDsQueryParams->aColumns[i].idsName);
FailGracefully(hres, "Failed to allocate column heading");
//column.fHasColumnProvider = FALSE;
column.cx = pDsQueryParams->aColumns[i].cx;
column.fmt = pDsQueryParams->aColumns[i].fmt;
column.iPropertyType = PROPERTY_ISUNKNOWN;
//column.idOperator = 0;
//column.clsidColumnHandler = { 0 };
//column.pColumnHandler = NULL;
ZeroMemory(&column.filter, SIZEOF(column.filter));
column.filter.iPropertyType = PROPERTY_ISUNDEFINED;
// Now fix the width of the column we are about to specify, this
// value has the following meaning:
//
// == 0 => use default width
// > 0 => user 'n' magic characters
// < 0 => pixel width
// FEATURE: -ve should be % of the view
if ( column.cx < 0 )
{
TraceMsg("Column width specified in pixels");
column.cx = -column.cx;
}
else
{
// Default the size if it is == 0, then having done this
// lets grab the font we want to use before moving on
// to create a DC and measure the character we need.
if ( !column.cx )
column.cx = DEFAULT_WIDTH;
sz.cx = 10; // random default value.
if ( !fHaveSizeInfo )
{
HDC hDC;
LOGFONT lf;
HFONT hFont, hOldFont;
SystemParametersInfo(SPI_GETICONTITLELOGFONT, SIZEOF(lf), &lf, FALSE);
hFont = CreateFontIndirect(&lf); // icon title font
if (hFont)
{
hDC = CreateCompatibleDC(NULL); // screen compatible DC
if (hDC)
{
hOldFont = (HFONT)SelectObject(hDC, hFont);
GetTextExtentPoint(hDC, TEXT("0"), 1, &sz);
SelectObject(hDC, hOldFont);
DeleteDC(hDC);
}
DeleteFont(hFont);
}
fHaveSizeInfo = TRUE;
}
column.cx = column.cx*sz.cx; // n chars width
}
Trace(TEXT("pProperty: '%s', pHeading: '%s', cx %d, fmt %08x"),
W2T(column.pProperty), column.pHeading, column.cx, column.fmt);
if ( -1 == DSA_AppendItem(*pHDSA, &column) )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to add column to the DSA");
}
}
// Scan the column list getting both the property name and the CLSID for the
// column handler (if there is one).
hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds);
FailGracefully(hres, "Failed to get the IDsDisplaySpecifier interface");
if ( _dwFlags & DSQPF_HASCREDENTIALS )
{
hres = pdds->SetServer(_pServer, _pUserName, _pPassword, DSSSF_DSAVAILABLE);
FailGracefully(hres, "Failed to server information");
}
for ( i = 0 ; i < DSA_GetItemCount(*pHDSA) ; i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(*pHDSA, i);
TraceAssert(pColumn);
Trace(TEXT("Property for column %d, %s"), i, W2T(pColumn->pProperty));
// lets get the property type, column handler and the default operator for it.
hres = GetColumnHandlerFromProperty(pColumn, NULL);
FailGracefully(hres, "Failed to get the column handler from property string");
if ( pColumn->fHasColumnHandler )
{
TraceMsg("Has a column handler, therefore property is now a string");
pColumn->iPropertyType = PROPERTY_ISSTRING;
}
else
{
pColumn->iPropertyType = PropertyIsFromAttribute(pColumn->pProperty, pdds);
}
pColumn->idOperator = property_type_table[pColumn->iPropertyType].idOperator;
}
// Set the columns up in the view (remove all items first) to allow us
// to add/remove columns as required.
if ( fSetInView )
{
for ( i = Header_GetItemCount(ListView_GetHeader(_hwndView)); --i >= 0 ; )
ListView_DeleteColumn(_hwndView, i);
// add the columns to the view, then having done that set
// the type of the filter to reflect the property being
// shown.
for ( i = 0 ; i < DSA_GetItemCount(_hdsaColumns); i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
TraceAssert(pColumn);
lvc.mask = LVCF_TEXT|LVCF_WIDTH|LVCF_FMT;
lvc.fmt = pColumn->fmt;
lvc.cx = pColumn->cx;
lvc.pszText = pColumn->pHeading;
iNewColumn = ListView_InsertColumn(_hwndView, i, &lvc);
TraceAssert(iNewColumn != -1);
if ( iNewColumn != i )
ExitGracefully(hres, E_FAIL, "Failed to add the column to the view");
hdi.mask = HDI_FILTER;
hdi.type = property_type_table[pColumn->iPropertyType].hdft|HDFT_HASNOVALUE;
hdi.pvFilter = NULL;
Trace(TEXT("iPropertyType %d, hdi.type %08x"), pColumn->iPropertyType, hdi.type);
if ( !Header_SetItem(ListView_GetHeader(_hwndView), iNewColumn, &hdi) )
ExitGracefully(hres, E_FAIL, "Failed to set the filter type into the view");
}
}
hres = S_OK; // success
exit_gracefully:
if ( hKey )
RegCloseKey(hKey);
if ( aSavedColumn )
LocalFree((HLOCAL)aSavedColumn);
DoRelease(pdds);
ResetWaitCursor();
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_SaveColumnTable
/ -------------------------
/ Free the column table stored in a DSA. This code released all the
/ allocated memory stored with the table.
/
/ In:
/ Out:
/ -
/----------------------------------------------------------------------------*/
VOID CDsQuery::_SaveColumnTable(VOID)
{
TraceEnter(TRACE_VIEW, "CDsQuery::_SaveColumnTable");
_FreeResults();
if ( _hdsaColumns )
{
DSA_DestroyCallback(_hdsaColumns, FreeColumnCB, NULL);
_hdsaColumns = NULL;
}
_iSortColumn = -1;
_fSortDescending = FALSE;
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_SaveColumnTable
/ -------------------------
/ Save the current column table from the DPA into the registry so
/ we can restore it the next time the user uses this query form.
/
/ In:
/ clsidForm = form ID to store it under
/ hdsaColumns -> DSA to destory
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::_SaveColumnTable(REFCLSID clsidForm, HDSA hdsaColumns)
{
HRESULT hres;
LPWSTR pProperty;
LPSAVEDCOLUMN aSavedColumn = NULL;
DWORD cbData, offset;
HKEY hKey = NULL;
LPTSTR pSettingsValue = VIEW_SETTINGS_VALUE;
INT i;
USES_CONVERSION;
TraceEnter(TRACE_VIEW, "CDsQuery::_SaveColumnTable");
TraceGUID("clsidForm ", clsidForm);
if ( !hdsaColumns )
ExitGracefully(hres, E_FAIL, "No column data to save");
// first compute the size of the blob we are going to store into
// the registry, whilst doing this compute the offset to
// the start of the string data.
offset = SIZEOF(SAVEDCOLUMN);
cbData = SIZEOF(SAVEDCOLUMN);
for ( i = 0 ; i < DSA_GetItemCount(hdsaColumns); i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(hdsaColumns, i);
TraceAssert(pColumn);
offset += SIZEOF(SAVEDCOLUMN);
cbData += SIZEOF(SAVEDCOLUMN);
cbData += StringByteSizeW(pColumn->pProperty);
// this is a potential problem, must be kept in sync with GetPropertyFromColumn
if ( pColumn->fHasColumnHandler )
cbData += SIZEOF(WCHAR)*(GUIDSTR_MAX + 1); // nb+1 for seperator (,} + string is UNICODE
cbData += StringByteSize(pColumn->pHeading);
}
Trace(TEXT("offset %d, cbData %d"), offset, cbData);
// Allocate the structure and lets place all the data into it,
// again running over the data blocks, append the string data
// to the end of the blob in a single go.
aSavedColumn = (LPSAVEDCOLUMN)LocalAlloc(LPTR, cbData);
TraceAssert(aSavedColumn);
if ( !aSavedColumn )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate settings data");
Trace(TEXT("Building data blob at %08x"), aSavedColumn);
for ( i = 0 ; i < DSA_GetItemCount(hdsaColumns); i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(hdsaColumns, i);
TraceAssert(pColumn);
hres = GetPropertyFromColumn(&pProperty, pColumn);
FailGracefully(hres, "Failed to allocate property from column");
aSavedColumn[i].cbSize = SIZEOF(SAVEDCOLUMN);
aSavedColumn[i].dwFlags = 0;
aSavedColumn[i].offsetProperty = offset;
aSavedColumn[i].offsetHeading = offset + StringByteSizeW(pProperty);
aSavedColumn[i].cx = pColumn->cx;
aSavedColumn[i].fmt = pColumn->fmt;
StringByteCopyW(aSavedColumn, aSavedColumn[i].offsetProperty, pProperty);
offset += StringByteSizeW(pProperty);
StringByteCopy(aSavedColumn, aSavedColumn[i].offsetHeading, pColumn->pHeading);
offset += StringByteSize(pColumn->pHeading);
LocalFreeStringW(&pProperty);
}
aSavedColumn[i].cbSize = 0; // terminate the list of columns with a NULL
Trace(TEXT("offset %d, cbData %d"), offset, cbData);
TraceAssert(offset == cbData);
// now put the data into the registry filed under the key for the query for that
// it maps to.
hres = _GetQueryFormKey(clsidForm, &hKey);
FailGracefully(hres, "Failed to get settings sub-key");
// If invoked for admin UI then we look at the admin view settings, this way
// the admin can have one set of columns, and the user can have another.
if ( _dwFlags & DSQPF_ENABLEADMINFEATURES )
pSettingsValue = ADMIN_VIEW_SETTINGS_VALUE;
Trace(TEXT("View settings value: %s"), pSettingsValue);
if ( ERROR_SUCCESS != RegSetValueEx(hKey, pSettingsValue, 0, REG_BINARY, (LPBYTE)aSavedColumn, cbData) )
ExitGracefully(hres, E_FAIL, "Failed to write setting into the view");
hres = S_OK;
exit_gracefully:
if ( aSavedColumn )
LocalFree((HLOCAL)aSavedColumn);
if ( hKey )
RegCloseKey(hKey);
TraceLeaveResult(hres);
}
// retrieve the context menu object
HRESULT _FolderCFMCallback(LPSHELLFOLDER psf, HWND hwndView, LPDATAOBJECT pDataObject, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Trace(TEXT("uMsg %d, wParam %08x, lParam %08x"), uMsg, wParam, lParam);
switch ( uMsg )
{
case DFM_MERGECONTEXTMENU:
return S_OK;
case DFM_INVOKECOMMAND:
switch (wParam)
{
case (WPARAM)DFM_CMD_PROPERTIES:
TraceMsg("ShowProperties");
return ShowObjectProperties(hwndView, pDataObject);
default:
return S_FALSE;
}
break;
case DFM_GETDEFSTATICID:
*((WPARAM*)lParam) = (WPARAM)DFM_CMD_PROPERTIES;
return NOERROR;
}
return E_NOTIMPL;
}
HRESULT CDsQuery::_GetContextMenu()
{
ITEMIDLIST idl = {0};
LPITEMIDLIST *aidl = NULL;
HKEY aKeys[UIKEY_MAX] = { 0 };
int cItems = ListView_GetSelectedCount(_hwndView);
if (cItems != 0)
{
// try to get the selected item (there is one, so first we try the focused, then the selected)
int i = ListView_GetNextItem(_hwndView, -1, LVNI_FOCUSED|LVNI_SELECTED);
if (i == -1)
i = ListView_GetNextItem(_hwndView, -1, LVNI_SELECTED);
// given the item lets get the result object so that we can create the menu based on
// the right class information
LV_ITEM lvi = { 0 };
lvi.mask = LVIF_PARAM;
lvi.iItem = i;
if (ListView_GetItem(_hwndView, &lvi))
{
LPQUERYRESULT pResult = (LPQUERYRESULT)lvi.lParam;
GetKeysForClass(pResult->pObjectClass, pResult->fIsContainer, ARRAYSIZE(aKeys), aKeys);
}
aidl = (LPITEMIDLIST*)LocalAlloc(LPTR, sizeof(LPITEMIDLIST)*cItems);
}
// given that we probably have the keys we are interested in,
// lets now create the context menu.
DoRelease(_pcm); // release the previous context menu
HRESULT hr = E_OUTOFMEMORY;
if (!cItems|| (cItems && aidl))
{
// fake up the array of IDLISTs so that they can be cloned correctly from
// the view.
for (int i= 0 ; i < cItems ; i++)
aidl[i] = &idl;
hr = CDefFolderMenu_Create2(&idl, _hwnd,
cItems, (LPCITEMIDLIST*)aidl,
this,
_FolderCFMCallback,
ARRAYSIZE(aKeys), aKeys,
&_pcm);
}
if (aidl)
LocalFree(aidl);
if (FAILED(hr))
TidyKeys(ARRAYSIZE(aKeys), aKeys);
return hr;
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_GetContextMenuVerbs
/ -----------------------------
/ Do the query context menu handling the case where we have been invoked
/ modally.
/
/ In:
/ hMenu = menu handle to merge into
/ dwFlags = flags for QueryContextMenu
/
/ Out:
/ VOID
/----------------------------------------------------------------------------*/
VOID CDsQuery::_GetContextMenuVerbs(HMENU hMenu, DWORD dwFlags)
{
TCHAR szBuffer[MAX_PATH];
TraceEnter(TRACE_VIEW, "CDsQuery::_GetContextMenuVerbs");
_pcm->QueryContextMenu(hMenu, 0, DSQH_FILE_CONTEXT_FIRST, DSQH_FILE_CONTEXT_LAST, dwFlags);
Trace(TEXT("Menu item count after QueryContextMenu %d (%08x)"), GetMenuItemCount(hMenu), hMenu);
if ( (_dwOQWFlags & OQWF_OKCANCEL) && LoadString(GLOBAL_HINSTANCE, IDS_SELECT, szBuffer, ARRAYSIZE(szBuffer)) )
{
InsertMenu(hMenu, 0, MF_BYPOSITION|MF_STRING, DSQH_BG_SELECT, szBuffer);
InsertMenu(hMenu, 1, MF_BYPOSITION|MF_SEPARATOR, 0, NULL);
SetMenuDefaultItem(hMenu, DSQH_BG_SELECT, FALSE);
}
TraceLeave();
}
// get the items in the view into a IDataObject, the caller can request either the
// selection or all the items
HRESULT CDsQuery::_AddResultToDataObject(HDSA hdsa, INT i)
{
LV_ITEM lvi = { 0 };
lvi.mask = LVIF_PARAM;
lvi.iItem = i;
HRESULT hr = E_FAIL;
if (ListView_GetItem(_hwndView, &lvi))
{
QUERYRESULT *pResult = (QUERYRESULT*)lvi.lParam;
DATAOBJECTITEM doi = {0};
// copy the strings for the item into the structure
hr = LocalAllocStringW(&doi.pszPath, pResult->pPath);
if (SUCCEEDED(hr) && pResult->pObjectClass)
hr = LocalAllocStringW(&doi.pszObjectClass, pResult->pObjectClass);
// copy the rest of the state
doi.fIsContainer = pResult->fIsContainer;
// append to the DSA.
if (SUCCEEDED(hr))
{
if (-1 == DSA_AppendItem(hdsa, &doi))
{
LocalFreeStringW(&doi.pszPath);
LocalFreeStringW(&doi.pszObjectClass);
hr = E_OUTOFMEMORY;
}
else
{
hr = S_OK;
}
}
}
return hr;
}
HRESULT CDsQuery::_GetDataObjectFromSelection(BOOL fGetAll, IDataObject **ppdo)
{
HRESULT hr = S_OK;
HDSA hdsa = DSA_Create(SIZEOF(DATAOBJECTITEM), 16);
if (hdsa)
{
// focused item always come first (primary selection rulz).
int iFocused = ListView_GetNextItem(_hwndView, -1, LVNI_ALL|LVNI_SELECTED|LVNI_FOCUSED);
if (iFocused > -1)
{
hr = _AddResultToDataObject(hdsa, iFocused);
}
// now walk all the items in the view collecting the selection and adding
// those items to the DPA - we must skip the item which is focused as we
// have already added it.
if (SUCCEEDED(hr))
{
int i = -1;
do
{
i = ListView_GetNextItem(_hwndView, i, LVNI_ALL|LVNI_SELECTED);
if ( (i >= 0) && (i != iFocused))
{
hr = _AddResultToDataObject(hdsa, i);
}
}
while (SUCCEEDED(hr) && (i != -1));
}
// given the DPA lets build an IDataObject using it.
hr = CDataObject_CreateInstance(hdsa,
(_dwFlags & DSQPF_ENABLEADVANCEDFEATURES),
IID_IDataObject, (void**)ppdo);
if (SUCCEEDED(hr))
hr = _SetDispSpecOptions(*ppdo);
}
else
{
hr = E_OUTOFMEMORY;
}
if (FAILED(hr) && hdsa)
FreeDataObjectDSA(hdsa); // data object failed to construct, lets kill the DSA
return hr;
}
/*-----------------------------------------------------------------------------
/ CDsQuery::_CopyCredentials
/ --------------------------
/ Copy the user name, password and server as required.
/
/ In:
/ ppszUserName, psszPassword & ppszServer => destinations
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT CDsQuery::_CopyCredentials(LPWSTR *ppszUserName, LPWSTR *ppszPassword, LPWSTR *ppszServer)
{
HRESULT hres;
TraceEnter(TRACE_VIEW, "CDsQuery::_CopyCredentials");
hres = LocalAllocStringW(ppszUserName, _pUserName);
FailGracefully(hres, "Failed to copy the user name");
hres = LocalAllocStringW(ppszPassword, _pPassword);
FailGracefully(hres, "Failed to copy the password");
hres = LocalAllocStringW(ppszServer, _pServer);
FailGracefully(hres, "Failed to copy the server name");
exit_gracefully:
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnPickColumns
/----------------------------------------------------------------------------*/
typedef struct
{
LPWSTR pProperty;
LPTSTR pHeading;
BOOL fIsColumn; // item is column and added to column list box on init
INT cx; // pixel width of column
INT fmt; // formatting information of column
} PICKERITEM, * LPPICKERITEM;
typedef struct
{
HDPA hdpaItems; // all the items in the view
HDSA hdsaColumns; // column table generated by this dialog
HWND hwndProperties; // hwnd's for the columns/property tables
HWND hwndColumns;
} PICKERSTATE, * LPPICKERSTATE;
VOID _PickerMoveColumn(HWND hwndDest, HWND hwndSrc, BOOL fInsert);
HRESULT _Picker_GetColumnTable(HWND hwndColumns, HDSA* pHDSA);
INT_PTR _PickerDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT _PickerItemFreeCB(LPVOID pData, LPVOID lParam);
INT _PickerItemCmpCB(LPVOID p1, LPVOID p2, LPARAM lParam);
//
// Help ID mappings
//
static DWORD const aPickColumnsHelpIDs[] =
{
IDC_LBPROPERTIES, IDH_COLUMNS_AVAILABLE,
IDC_LBCOLUMNS, IDH_COLUMNS_SHOWN,
IDC_ADD, IDH_ADD_COLUMNS,
IDC_REMOVE, IDH_REMOVE_COLUMNS,
0, 0
};
/*-----------------------------------------------------------------------------
/ _PickerMoveColumn
/ -----------------
/ Move the selected item in one list box to another, we assume that the
/ item data points to a PICKERITEM structure, we transfer the selection
/ and
/
/ In:
/ hwndSrc, hwndState = windows to move selection within
/ fInsert = insert at selection point in destionation, or add the string (sorted)
/
/ Out:
/ VOID
/----------------------------------------------------------------------------*/
VOID _PickerMoveColumn(HWND hwndDest, HWND hwndSrc, BOOL fInsert)
{
INT iSelection, i;
LPPICKERITEM pItem;
USES_CONVERSION;
TraceEnter(TRACE_FIELDCHOOSER, "_PickerMoveColumn");
iSelection = ListBox_GetCurSel(hwndSrc);
TraceAssert(iSelection >= 0 );
if ( iSelection >= 0 )
{
LPPICKERITEM pItem = (LPPICKERITEM)ListBox_GetItemData(hwndSrc, iSelection);
TraceAssert(pItem);
TraceAssert(pItem->pHeading);
// Add the new item to the view (if this it the properties)
// then this will result in the list being sorted and select
// it to allow the user to remove/add another
if ( fInsert )
{
Trace(TEXT("Inserting the item at index %d"), ListBox_GetCurSel(hwndDest)+1);
i = ListBox_InsertString(hwndDest, ListBox_GetCurSel(hwndDest)+1, pItem->pHeading);
}
else
{
TraceMsg("Adding string to listbox");
i = ListBox_AddString(hwndDest, pItem->pHeading);
}
TraceAssert(i != -1);
ListBox_SetItemData(hwndDest, i, (LPARAM)pItem);
ListBox_SetCurSel(hwndDest, i);
// remove the item from the source, ensuring that the
// selection stays visually at the same place (nb
// cope with removing the last item).
ListBox_DeleteString(hwndSrc, iSelection);
if ( iSelection >= ListBox_GetCount(hwndSrc) )
iSelection = ListBox_GetCount(hwndSrc)-1;
if ( iSelection >= 0 )
ListBox_SetCurSel(hwndSrc, iSelection);
}
TraceLeave();
}
/*-----------------------------------------------------------------------------
/ _Picker_GetColumnTable
/ ---------------------
/ The user has hit OK, therefore we must build a new column table, this
/ code walks the items in the columns ListBox and generates the
/ column DSA that we should be using.
/
/ In:
/ hwndColumns -> ListBox containing the columns
/ pHDSA -> DSA to receive the columnt able
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
HRESULT _Picker_GetColumnTable(HWND hwndColumns, HDSA* pHDSA)
{
HRESULT hres;
HDSA hdsaColumns = NULL;
INT i;
INT cxColumn = 0;
TraceEnter(TRACE_FIELDCHOOSER, "_Picker_GetColumnTable");
// Construct a DSA to store the column table in
hdsaColumns = DSA_Create(SIZEOF(COLUMN), 4);
TraceAssert(hdsaColumns);
if ( !hdsaColumns )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to create the column DSA");
// For each entry in the ListBox add an item to the DSA that contains
// the relevant column information
for ( i = 0 ; i < ListBox_GetCount(hwndColumns) ; i++ )
{
COLUMN column = { 0 };
LPPICKERITEM pItem = (LPPICKERITEM)ListBox_GetItemData(hwndColumns, i);
TraceAssert(pItem);
// column.fHasColumnHandler = FALSE;
column.pProperty = NULL;
column.pHeading = NULL;
//column.cx = 0;
//column.fmt = 0;
column.iPropertyType = PROPERTY_ISUNKNOWN;
column.idOperator = 0;
// column.clsidColumnHandler = { 0 };
// column.pColumnHandler = NULL;
// fIsColumn indicates that the entry was originally a column therefore
// has extra state information, ensure that we copy this over, otherwise
// just pick sensible defaults. Having done that we can allocate the
// strings, add the item and continue...
if ( pItem->fIsColumn )
{
column.cx = pItem->cx;
column.fmt = pItem->fmt;
}
else
{
// Have we cached the column width yet? If not then lets do
// so and apply it to all the columns which are using the
// default width (as they have not yet been sized)
if ( !cxColumn )
{
HDC hDC;
LOGFONT lf;
HFONT hFont, hOldFont;
SIZE sz;
sz.cx = 10; // random default value.
SystemParametersInfo(SPI_GETICONTITLELOGFONT, SIZEOF(lf), &lf, FALSE);
hFont = CreateFontIndirect(&lf); // icon title font
if (hFont)
{
hDC = CreateCompatibleDC(NULL); // screen compatible DC
if (hDC)
{
hOldFont = (HFONT)SelectObject(hDC, hFont);
GetTextExtentPoint(hDC, TEXT("0"), 1, &sz);
SelectObject(hDC, hOldFont);
DeleteDC(hDC);
}
DeleteObject(hFont);
}
cxColumn = DEFAULT_WIDTH * sz.cx;
}
column.cx = cxColumn;
column.fmt = 0;
}
if ( FAILED(GetColumnHandlerFromProperty(&column, pItem->pProperty)) ||
FAILED(LocalAllocString(&column.pHeading, pItem->pHeading)) ||
(-1 == DSA_AppendItem(hdsaColumns, &column)) )
{
LocalFreeStringW(&column.pProperty);
LocalFreeString(&column.pHeading);
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to construct the column entry");
}
}
Trace(TEXT("New column table contains %d items"), DSA_GetItemCount(hdsaColumns));
hres = S_OK;
exit_gracefully:
if ( FAILED(hres) && hdsaColumns )
{
DSA_DestroyCallback(hdsaColumns, FreeColumnCB, NULL);
hdsaColumns = NULL;
}
*pHDSA = hdsaColumns;
TraceLeaveResult(hres);
}
/*-----------------------------------------------------------------------------
/ _PickerDlgProc
/ --------------
/ Dialog proc for handling the dialog messages (there is a suprise)
/
/ In:
/ hwnd, uMsg, wParam, lParam = message information
/ DWL_USER => LPPICKERSTATE structure
/
/ Out:
/ INT_PTR
/----------------------------------------------------------------------------*/
#define SET_BTN_STYLE(hwnd, idc, style) \
SendDlgItemMessage(hwnd, idc, BM_SETSTYLE, MAKEWPARAM(style, 0), MAKELPARAM(TRUE, 0));
INT_PTR _PickerDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
USES_CONVERSION;
INT_PTR fResult = FALSE;
LPPICKERSTATE pState = NULL;
BOOL fUpdateButtonState = TRUE;
PICKERITEM item;
INT i, j;
if ( uMsg == WM_INITDIALOG )
{
pState = (LPPICKERSTATE)lParam;
TraceAssert(pState);
SetWindowLongPtr(hwnd, DWLP_USER, lParam);
pState->hwndProperties = GetDlgItem(hwnd, IDC_LBPROPERTIES);
pState->hwndColumns = GetDlgItem(hwnd, IDC_LBCOLUMNS);
// pState->hdsaColumns contains the currently visible column table, this is
// the table being used by the current view, therefore we must not modify
// it, just treat it as read only. Add the columns to the ListBox
// marking the visible items in the properties DPA, then add those
// items not already shown to the properties list box.
for ( i = 0 ; i < DSA_GetItemCount(pState->hdsaColumns) ; i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(pState->hdsaColumns, i);
TraceAssert(pColumn);
if ( SUCCEEDED(GetPropertyFromColumn(&item.pProperty, pColumn)) )
{
j = DPA_Search(pState->hdpaItems, &item, 0, _PickerItemCmpCB, NULL, DPAS_SORTED);
Trace(TEXT("Searching for %s yielded %d"), W2T(item.pProperty), j);
if ( j >= 0 )
{
LPPICKERITEM pItem = (LPPICKERITEM)DPA_GetPtr(pState->hdpaItems, j);
TraceAssert(pItem);
ListBox_SetItemData(pState->hwndColumns,
ListBox_AddString(pState->hwndColumns, pItem->pHeading),
(LPARAM)pItem);
}
LocalFreeStringW(&item.pProperty);
}
}
for ( i = 0 ; i < DPA_GetPtrCount(pState->hdpaItems) ; i++ )
{
LPPICKERITEM pItem = (LPPICKERITEM)DPA_GetPtr(pState->hdpaItems, i);
TraceAssert(pItem && pItem->pHeading);
if ( !pItem->fIsColumn )
{
ListBox_SetItemData(pState->hwndProperties,
ListBox_AddString(pState->hwndProperties, pItem->pHeading),
(LPARAM)pItem);
}
}
// Ensure we default select the top items of each list
ListBox_SetCurSel(pState->hwndProperties, 0);
ListBox_SetCurSel(pState->hwndColumns, 0);
}
else
{
pState = (LPPICKERSTATE)GetWindowLongPtr(hwnd, DWLP_USER);
TraceAssert(pState);
switch ( uMsg )
{
case WM_HELP:
{
LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
WinHelp((HWND)pHelpInfo->hItemHandle,
DSQUERY_HELPFILE,
HELP_WM_HELP,
(DWORD_PTR)aPickColumnsHelpIDs);
break;
}
case WM_CONTEXTMENU:
WinHelp((HWND)wParam, DSQUERY_HELPFILE, HELP_CONTEXTMENU, (DWORD_PTR)aPickColumnsHelpIDs);
fResult = TRUE;
break;
case WM_COMMAND:
{
switch ( LOWORD(wParam) )
{
case IDOK:
{
_Picker_GetColumnTable(pState->hwndColumns, &pState->hdsaColumns);
EndDialog(hwnd, IDOK);
break;
}
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
case IDC_ADD:
_PickerMoveColumn(pState->hwndColumns, pState->hwndProperties, TRUE);
break;
case IDC_REMOVE:
_PickerMoveColumn(pState->hwndProperties, pState->hwndColumns, FALSE);
break;
case IDC_LBPROPERTIES:
{
if ( ListBox_GetCount(pState->hwndProperties) > 0 )
{
if ( HIWORD(wParam) == LBN_DBLCLK )
_PickerMoveColumn(pState->hwndColumns, pState->hwndProperties, TRUE);
}
break;
}
case IDC_LBCOLUMNS:
{
if ( ListBox_GetCount(pState->hwndColumns) > 1 )
{
if ( HIWORD(wParam) == LBN_DBLCLK )
_PickerMoveColumn(pState->hwndProperties, pState->hwndColumns, FALSE);
}
break;
}
default:
fUpdateButtonState = FALSE;
break;
}
break;
}
default:
fUpdateButtonState = FALSE;
break;
}
}
// if the selections state change, or something which would cause
// us to refresh the add/remove buttons state then lets do it.
// each button is enabled only if there is a selection and the
// number of items is > the min value.
if ( pState && fUpdateButtonState )
{
BOOL fEnableAdd = FALSE;
BOOL fEnableRemove = FALSE;
DWORD dwButtonStyle;
if ( (ListBox_GetCount(pState->hwndProperties) > 0) )
{
fEnableAdd = TRUE;
}
if ( (ListBox_GetCount(pState->hwndColumns) > 1) )
{
fEnableRemove = TRUE;
}
// Make sure the DefButton is an enabled button
// FEATURE: Need to add an SHSetDefID() export to shlwapi
// which is simply a SetDefID that "does the right thing"
// wrt disabled buttons.
if ( (!fEnableRemove) && (!fEnableAdd) )
{
SET_BTN_STYLE(hwnd, IDC_ADD, BS_PUSHBUTTON);
SET_BTN_STYLE(hwnd, IDC_REMOVE, BS_PUSHBUTTON);
SendMessage(hwnd, DM_SETDEFID, IDOK, 0);
SET_BTN_STYLE(hwnd, IDOK, BS_DEFPUSHBUTTON);
SetFocus(GetDlgItem(hwnd, IDOK));
}
else if ( !fEnableAdd )
{
dwButtonStyle = (DWORD)GetWindowLong(GetDlgItem(hwnd, IDC_ADD), GWL_STYLE);
if ( dwButtonStyle & BS_DEFPUSHBUTTON )
{
SET_BTN_STYLE(hwnd, IDC_ADD, BS_PUSHBUTTON);
SendMessage(hwnd, DM_SETDEFID, IDC_REMOVE, 0);
SET_BTN_STYLE(hwnd, IDC_REMOVE, BS_DEFPUSHBUTTON);
SetFocus(GetDlgItem(hwnd, IDC_REMOVE));
}
}
else if ( !fEnableRemove )
{
dwButtonStyle = (DWORD) GetWindowLong(GetDlgItem(hwnd, IDC_REMOVE), GWL_STYLE);
if ( dwButtonStyle & BS_DEFPUSHBUTTON )
{
SET_BTN_STYLE(hwnd, IDC_REMOVE, BS_PUSHBUTTON);
SendMessage(hwnd, DM_SETDEFID, IDC_ADD, 0);
SET_BTN_STYLE(hwnd, IDC_ADD, BS_DEFPUSHBUTTON);
SetFocus(GetDlgItem(hwnd, IDC_ADD));
}
}
Button_Enable(GetDlgItem(hwnd, IDC_ADD), fEnableAdd);
Button_Enable(GetDlgItem(hwnd, IDC_REMOVE), fEnableRemove);
}
return fResult;
}
/*-----------------------------------------------------------------------------
/ CDsQuery::OnPickColumns
/ -----------------------
/ Handle picking the columns that should be displayed in the result view,
/ if the user selects new columns and hits OK then we refresh the
/ view and the internal table of visible columns.
/
/ In:
/ hwndParent = parent for the dialog
/
/ Out:
/ HRESULT
/----------------------------------------------------------------------------*/
INT _PickerItemFreeCB(LPVOID pData, LPVOID lParam)
{
LPPICKERITEM pItem = (LPPICKERITEM)pData;
LocalFreeStringW(&pItem->pProperty);
LocalFreeString(&pItem->pHeading);
LocalFree(pItem);
return 1;
}
INT _PickerItemCmpCB(LPVOID p1, LPVOID p2, LPARAM lParam)
{
USES_CONVERSION;
LPPICKERITEM pEntry1 = (LPPICKERITEM)p1;
LPPICKERITEM pEntry2 = (LPPICKERITEM)p2;
INT nResult = -1;
if ( pEntry1 && pEntry2 )
nResult = StrCmpW(pEntry1->pProperty, pEntry2->pProperty);
return nResult;
}
typedef struct
{
PICKERSTATE *pps;
HDPA hdpaProperties; // attributes to be appeneded.
} PICKERENUMATTRIB;
HRESULT CALLBACK _PickerEnumAttribCB(LPARAM lParam, LPCWSTR pAttributeName, LPCWSTR pDisplayName, DWORD dwFlags)
{
HRESULT hres;
PICKERENUMATTRIB *ppea = (PICKERENUMATTRIB*)lParam;
PICKERITEM item;
INT j;
USES_CONVERSION;
TraceEnter(TRACE_FIELDCHOOSER, "_PickerEnumAttribCB");
// fix casting
item.pProperty = (LPWSTR)pAttributeName;
j = DPA_Search(ppea->pps->hdpaItems, &item, 0, _PickerItemCmpCB, NULL, DPAS_SORTED);
if ( j == -1 )
{
Trace(TEXT("Property not already in list: %s"), W2CT(pAttributeName));
hres = StringDPA_AppendStringW(ppea->hdpaProperties, pAttributeName, NULL);
FailGracefully(hres, "Failed to add unique property to DPA");
}
hres = S_OK;
exit_gracefully:
TraceLeaveResult(hres);
}
HRESULT CDsQuery::OnPickColumns(HWND hwndParent)
{
USES_CONVERSION;
HRESULT hres;
HDPA hdpaProperties = NULL;
PICKERSTATE state;
PICKERENUMATTRIB pea = { 0 };
INT i, j, iProperty, iColumn;
LPDSQUERYCLASSLIST pDsQueryClassList = NULL;
IDsDisplaySpecifier* pdds = NULL;
TraceEnter(TRACE_FIELDCHOOSER, "CDsQuery::OnPickColumns");
Trace(TEXT("Column count %d"), DSA_GetItemCount(_hdsaColumns));
state.hdpaItems = NULL;
state.hdsaColumns = _hdsaColumns;
state.hwndProperties = NULL;
state.hwndColumns = NULL;
// Build a list of unique properties sorted and remove the ones
// which match the currently displayed property set.
state.hdpaItems = DPA_Create(16);
TraceAssert(state.hdpaItems);
if ( !state.hdpaItems )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to create property DPA");
//
// attempt to get the IDsDisplaySpecifier object
//
hres = CoCreateInstance(CLSID_DsDisplaySpecifier, NULL, CLSCTX_INPROC_SERVER, IID_IDsDisplaySpecifier, (void **)&pdds);
FailGracefully(hres, "Failed to get the IDsDisplaySpecifier interface");
hres = pdds->SetServer(_pServer, _pUserName, _pPassword, DSSSF_DSAVAILABLE);
FailGracefully(hres, "Failed to server information");
// add the columns properties to the the list, marking them as active columns
// storing their size and other information.
for ( i = 0 ; i < DPA_GetPtrCount(_hdsaColumns); i++ )
{
LPCOLUMN pColumn = (LPCOLUMN)DSA_GetItemPtr(_hdsaColumns, i);
TraceAssert(pColumn);
LPPICKERITEM pItem = (LPPICKERITEM)LocalAlloc(LPTR, SIZEOF(PICKERITEM));
TraceAssert(pItem);
if ( pItem )
{
pItem->pProperty = NULL;
pItem->pHeading = NULL;
pItem->fIsColumn = TRUE;
pItem->cx = pColumn->cx;
pItem->fmt = pColumn->fmt;
hres = GetPropertyFromColumn(&pItem->pProperty, pColumn);
TraceAssert(SUCCEEDED(hres));
if ( SUCCEEDED(hres) )
{
hres = LocalAllocString(&pItem->pHeading, pColumn->pHeading);
TraceAssert(SUCCEEDED(hres));
}
Trace(TEXT("Adding column %d, with property %s"), i, W2T(pItem->pProperty));
if ( FAILED(hres) || (-1 == DPA_AppendPtr(state.hdpaItems, pItem)) )
{
TraceMsg("Failed to add property to the DPA");
hres = E_FAIL;
}
if ( FAILED(hres) )
_PickerItemFreeCB(pItem, NULL);
}
}
DPA_Sort(state.hdpaItems, _PickerItemCmpCB, NULL); // sort the DPA now we have all the elements
// for all the classes we have loop through and build the unique
// list, sorted as we go.
hres = _pqf->CallForm(&_clsidForm, DSQPM_GETCLASSLIST, 0, (LPARAM)&pDsQueryClassList);
FailGracefully(hres, "Failed to get the class list");
if ( !pDsQueryClassList )
ExitGracefully(hres, E_FAIL, "Failed to get the class list");
Trace(TEXT("Classes returned from DSQPM_GETCLASSLIST %d"), pDsQueryClassList->cClasses);
for ( i = 0 ; i < pDsQueryClassList->cClasses ; i++ )
{
LPWSTR pObjectClass = (LPWSTR)ByteOffset(pDsQueryClassList, pDsQueryClassList->offsetClass[i]);
TraceAssert(pObjectClass);
Trace(TEXT("Adding class '%s' to the property DPA"), W2T(pObjectClass));
// allocate a DPA to be filed with items
StringDPA_Destroy(&pea.hdpaProperties);
pea.pps = &state;
pea.hdpaProperties = DPA_Create(16);
if ( !pea.hdpaProperties )
ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate string DPA");
if ( SUCCEEDED(EnumClassAttributes(pdds, pObjectClass, _PickerEnumAttribCB, (LPARAM)&pea)) )
{
Trace(TEXT("Unique property list has %d entries"), DPA_GetPtrCount(pea.hdpaProperties));
// Having constructed the unique list of properties for this class lets now
// add them to the item data list and allocate real structures for them.
for ( iProperty = 0 ; iProperty < DPA_GetPtrCount(pea.hdpaProperties); iProperty++ )
{
LPWSTR pProperty = StringDPA_GetStringW(pea.hdpaProperties, iProperty);
TraceAssert(pProperty != NULL);
LPPICKERITEM pItem = (LPPICKERITEM)LocalAlloc(LPTR, SIZEOF(PICKERITEM));
TraceAssert(pItem);
if ( pItem )
{
WCHAR szBuffer[MAX_PATH];
GetFriendlyAttributeName(pdds, pObjectClass, pProperty, szBuffer, ARRAYSIZE(szBuffer));
pItem->pProperty = NULL;
pItem->pHeading = NULL;
pItem->fIsColumn = FALSE;
pItem->cx = 0;
pItem->fmt = 0;
hres = LocalAllocStringW(&pItem->pProperty, pProperty);
TraceAssert(SUCCEEDED(hres));
if ( SUCCEEDED(hres) )
{
hres = LocalAllocStringW2T(&pItem->pHeading, szBuffer);
TraceAssert(SUCCEEDED(hres));
}
if ( FAILED(hres) || (-1 == DPA_AppendPtr(state.hdpaItems, pItem)) )
{
TraceMsg("Failed to add property to the DPA");
hres = E_FAIL;
}
if ( FAILED(hres) )
_PickerItemFreeCB(pItem, NULL);
}
DPA_Sort(state.hdpaItems, _PickerItemCmpCB, NULL); // sort the DPA now we have all the elements
}
}
}
Trace(TEXT("Property table is %d items in size"), DPA_GetPtrCount(state.hdpaItems));
// If the user selects OK then the DlgProc will have generated a new column
// table stored in the PICKERSTATE structure, we should persist this, then
// load it ready to refresh the result viewer.
i = (int)DialogBoxParam(GLOBAL_HINSTANCE, MAKEINTRESOURCE(IDD_PICKCOLUMNS),
hwndParent,
_PickerDlgProc, (LPARAM)&state);
if ( i == IDOK )
{
hres = _SaveColumnTable(_clsidForm, state.hdsaColumns);
FailGracefully(hres, "Failed to write column table");
hres = _InitNewQuery(NULL, TRUE); // initialize the view
FailGracefully(hres, "Failed when starting new query");
TraceAssert(_dwThreadId);
PostThreadMessage(_dwThreadId, RVTM_SETCOLUMNTABLE, _dwQueryReference, (LPARAM)state.hdsaColumns);
_fColumnsModified = FALSE;
}
hres = S_OK;
exit_gracefully:
StringDPA_Destroy(&pea.hdpaProperties);
if ( state.hdpaItems )
DPA_DestroyCallback(state.hdpaItems, _PickerItemFreeCB, NULL);
if ( pDsQueryClassList )
CoTaskMemFree(pDsQueryClassList);
DoRelease(pdds);
TraceLeaveValue(hres);
}
// persistance object
class CDsPersistQuery : public IPersistQuery
{
private:
LONG _cRef;
TCHAR m_szFilename[MAX_PATH];
public:
CDsPersistQuery(LPCTSTR pFilename);;
~CDsPersistQuery();
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
// IPersist
STDMETHOD(GetClassID)(CLSID* pClassID)
{ return E_NOTIMPL; }
// IPersistQuery
STDMETHOD(WriteString)(LPCTSTR pSection, LPCTSTR pValueName, LPCTSTR pValue);
STDMETHOD(ReadString)(LPCTSTR pSection, LPCTSTR pValueName, LPTSTR pBuffer, INT cchBuffer);
STDMETHOD(WriteInt)(LPCTSTR pSection, LPCTSTR pValueName, INT value);
STDMETHOD(ReadInt)(LPCTSTR pSection, LPCTSTR pValueName, LPINT pValue);
STDMETHOD(WriteStruct)(LPCTSTR pSection, LPCTSTR pValueName, LPVOID pStruct, DWORD cbStruct);
STDMETHOD(ReadStruct)(LPCTSTR pSection, LPCTSTR pValueName, LPVOID pStruct, DWORD cbStruct);
STDMETHOD(Clear)()
{ return S_OK; }
};
#define STRING_SIZE TEXT("%sLength")
#define STRING_VALUE TEXT("%sValue")
CDsPersistQuery::CDsPersistQuery(LPCTSTR pFilename) :
_cRef(1)
{
StrCpy(m_szFilename, pFilename); // copy the filename
DllAddRef();
}
CDsPersistQuery::~CDsPersistQuery()
{
DllRelease();
}
// IUnknown
ULONG CDsPersistQuery::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CDsPersistQuery::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
HRESULT CDsPersistQuery::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENTMULTI(CDsPersistQuery, IPersist, IPersistQuery), // IID_IPersist
QITABENT(CDsPersistQuery, IPersistQuery), // IID_IPersistQuery
{0, 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDAPI CPersistQuery_CreateInstance(LPTSTR pszPath, IPersistQuery **pppq)
{
CDsPersistQuery *ppq = new CDsPersistQuery(pszPath);
if (!ppq)
return E_OUTOFMEMORY;
HRESULT hr = ppq->QueryInterface(IID_IPersistQuery, (void **)pppq);
ppq->Release();
return hr;
}
// IPersistQuery
STDMETHODIMP CDsPersistQuery::WriteString(LPCTSTR pSection, LPCTSTR pKey, LPCTSTR pValue)
{
HRESULT hr = S_OK;
TCHAR szBuffer[MAX_PATH];
if ( !pSection || !pKey || !pValue )
return E_INVALIDARG;
// Write the string into the stream as a UNICODE string. If we are built for UNICODE
// then we can simply WriteProfileStruct, otherwise we must attempt to convert it
// from a multi-byte string.
// Write the string size
wsprintf(szBuffer, STRING_SIZE, pKey);
INT cchValue = 1+lstrlen(pValue);
if ( !WritePrivateProfileStruct(pSection, szBuffer, &cchValue, SIZEOF(cchValue), m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to write string size to stream");
wsprintf(szBuffer, STRING_VALUE, pKey);
if ( !WritePrivateProfileStruct(pSection, szBuffer, (LPVOID)pValue, SIZEOF(WCHAR)*cchValue, m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to write string to stream");
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
STDMETHODIMP CDsPersistQuery::ReadString(LPCTSTR pSection, LPCTSTR pKey, LPTSTR pBuffer, INT cchBuffer)
{
HRESULT hr;
TCHAR szBuffer[MAX_PATH];
INT cchValue;
TraceEnter(TRACE_IO, "CDsPersistQuery::ReadString");
if ( !pSection || !pKey || !pBuffer )
ExitGracefully(hr, E_INVALIDARG, "Nothing to read (or into)");
pBuffer[0] = TEXT('\0'); // terminate the buffer
Trace(TEXT("pSection: %s, pKey: %s"), pSection, pKey);
// Read the length of the string, checking to see if its fits in the the buffer
// we have, if it does then read and convert as requried.
wsprintf(szBuffer, STRING_SIZE, pKey); // <key name>Length
Trace(TEXT("Opening key: %s"), szBuffer);
if ( !GetPrivateProfileStruct(pSection, szBuffer, &cchValue, SIZEOF(cchValue), m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to read string size");
Trace(TEXT("cchValue %d"), cchValue);
if ( cchValue > cchBuffer )
ExitGracefully(hr, E_FAIL, "Buffer too small for string in stream");
if ( cchValue > 0 )
{
wsprintf(szBuffer, STRING_VALUE, pKey);
if ( !GetPrivateProfileStruct(pSection, szBuffer, pBuffer, SIZEOF(WCHAR)*cchValue, m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to read string data");
}
Trace(TEXT("Value is: %s"), pBuffer);
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
STDMETHODIMP CDsPersistQuery::WriteInt(LPCTSTR pSection, LPCTSTR pKey, INT value)
{
HRESULT hr;
TraceEnter(TRACE_IO, "CDsPersistQuery::WriteInt");
if ( !pSection || !pKey )
ExitGracefully(hr, E_INVALIDARG, "Nothing to write");
Trace(TEXT("pSection: %s, pKey: %s, value: %d"), pSection, pKey, value);
if ( !WritePrivateProfileStruct(pSection, pKey, &value, SIZEOF(value), m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to write value");
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
STDMETHODIMP CDsPersistQuery::ReadInt(LPCTSTR pSection, LPCTSTR pKey, LPINT pValue)
{
HRESULT hr;
TraceEnter(TRACE_IO, "CDsPersistQuery::ReadInt");
if ( !pSection || !pKey || !pValue )
ExitGracefully(hr, E_INVALIDARG, "Nothing to read");
Trace(TEXT("pSection: %s, pKey: %s, pValue: %08x"), pSection, pKey, pValue);
if ( !GetPrivateProfileStruct(pSection, pKey, pValue, SIZEOF(*pValue), m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to read value");
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
STDMETHODIMP CDsPersistQuery::WriteStruct(LPCTSTR pSection, LPCTSTR pKey, LPVOID pStruct, DWORD cbStruct)
{
HRESULT hr;
TraceEnter(TRACE_IO, "CDsPersistQuery::WriteStruct");
if ( !pSection || !pKey || !pStruct )
ExitGracefully(hr, E_INVALIDARG, "Nothing to write");
Trace(TEXT("pSection: %s, pKey: %s, pStruct: %08x, cbStruct: %d"), pSection, pKey, pStruct, cbStruct);
if ( !WritePrivateProfileStruct(pSection, pKey, pStruct, cbStruct, m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to write struct");
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}
STDMETHODIMP CDsPersistQuery::ReadStruct(LPCTSTR pSection, LPCTSTR pKey, LPVOID pStruct, DWORD cbStruct)
{
HRESULT hr;
TraceEnter(TRACE_IO, "CDsPersistQuery::ReadStruct");
if ( !pSection || !pKey || !pStruct )
ExitGracefully(hr, E_INVALIDARG, "Nothing to read");
Trace(TEXT("pSection: %s, pKey: %s, pStruct: %08x, cbStruct: %d"), pSection, pKey, pStruct, cbStruct);
if ( !GetPrivateProfileStruct(pSection, pKey, pStruct, cbStruct, m_szFilename) )
ExitGracefully(hr, E_FAIL, "Failed to read struct");
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr);
}