mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5363 lines
170 KiB
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);
|
|
}
|