2025-11-28 00:35:46 +09:00

525 lines
18 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// This sample requires ATL (installed as part of Visual Studio)
#include <crtdbg.h>
#include <msdasc.h>
#include <atlbase.h>
#include <atlcom.h>
#include <searchapi.h>
#include <propkey.h>
#include <ntquery.h>
#include <strsafe.h>
#include <wininet.h>
CComModule Module;
CComModule &_Module = Module;
template <class T> inline HRESULT CreateComObject(T ** ppOutPtr)
{
*ppOutPtr = NULL;
CComObject<T> * pObj = NULL;
HRESULT hr = CComObject<T>::CreateInstance( &pObj );
if (SUCCEEDED(hr))
{
*ppOutPtr = pObj;
pObj->GetUnknown()->AddRef();
}
return hr;
}
//*****************************************************************************
// Open a database session...
HRESULT OpenSession(IDBCreateCommand **ppCreateCommand)
{
*ppCreateCommand = NULL;
CComPtr<IDataInitialize> spDataInit;
CComPtr<IUnknown> spUnknownDBInitialize;
CComPtr<IDBInitialize> spDBInitialize;
CComPtr<IDBCreateSession> spDBCreateSession;
HRESULT hr = CoCreateInstance(CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spDataInit));
if (SUCCEEDED(hr))
{
hr = spDataInit->GetDataSource( NULL, CLSCTX_INPROC_SERVER, L"provider=Search.CollatorDSO.1", __uuidof(IDBInitialize), &spUnknownDBInitialize);
}
if (SUCCEEDED(hr))
{
hr = spUnknownDBInitialize->QueryInterface(&spDBInitialize);
}
if (SUCCEEDED(hr))
{
hr = spDBInitialize->Initialize();
}
if (SUCCEEDED(hr))
{
hr = spDBInitialize->QueryInterface(&spDBCreateSession);
}
if (SUCCEEDED(hr))
{
CComPtr<IUnknown> spUnknownCreateCommand;
hr = spDBCreateSession->CreateSession(0, __uuidof(IDBCreateCommand), &spUnknownCreateCommand);
if (SUCCEEDED(hr))
{
hr = spUnknownCreateCommand->QueryInterface(ppCreateCommand);
}
}
return hr;
}
//*****************************************************************************
// Run a query against the database, optionally enabling eventing...
HRESULT ExecuteQuery(IDBCreateCommand * pDBCreateCommand, PCWSTR pwszQuerySQL, bool fEnableEventing, REFIID riid, void **ppv)
{
CComPtr<IUnknown> spUnknownCommand;
CComPtr<ICommandProperties> spCommandProperties;
CComPtr<ICommandText> spCommandText;
HRESULT hr = pDBCreateCommand->CreateCommand(0, __uuidof(ICommand), &spUnknownCommand );
if (SUCCEEDED(hr))
{
hr = spUnknownCommand->QueryInterface(&spCommandProperties);
}
if (SUCCEEDED(hr))
{
DBPROP rgProps[2] = {};
DBPROPSET propSet = {};
rgProps[propSet.cProperties].dwPropertyID = DBPROP_USEEXTENDEDDBTYPES;
rgProps[propSet.cProperties].dwOptions = DBPROPOPTIONS_OPTIONAL;
rgProps[propSet.cProperties].vValue.vt = VT_BOOL;
rgProps[propSet.cProperties].vValue.boolVal = VARIANT_TRUE;
propSet.cProperties++;
if (fEnableEventing)
{
rgProps[propSet.cProperties].dwPropertyID = DBPROP_ENABLEROWSETEVENTS;
rgProps[propSet.cProperties].dwOptions = DBPROPOPTIONS_OPTIONAL;
rgProps[propSet.cProperties].vValue.vt = VT_BOOL;
rgProps[propSet.cProperties].vValue.boolVal = VARIANT_TRUE;
propSet.cProperties++;
}
propSet.rgProperties = rgProps;
static const GUID guidQueryExt = DBPROPSET_QUERYEXT;
propSet.guidPropertySet = guidQueryExt;
hr = spCommandProperties->SetProperties(1, &propSet);
}
if (SUCCEEDED(hr))
{
hr = spUnknownCommand->QueryInterface(&spCommandText);
}
if (SUCCEEDED(hr))
{
hr = spCommandText->SetCommandText(DBGUID_DEFAULT, pwszQuerySQL);
}
if (SUCCEEDED(hr))
{
DBROWCOUNT cRows;
hr = spCommandText->Execute(NULL, riid, NULL, &cRows, reinterpret_cast<IUnknown **>(ppv));
}
return hr;
}
//*****************************************************************************
// Retrieves the URL from a given workid
HRESULT RetrieveURL(IDBCreateCommand *pDBCreateCommand, REFPROPVARIANT itemID, PWSTR pwszURL, int cchURL)
{
WCHAR wszQuery[512];
CComPtr<IRowset> spRowset;
HRESULT hr = (itemID.vt == VT_UI4) ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hr))
{
hr = StringCchPrintf( wszQuery, ARRAYSIZE(wszQuery), L"SELECT TOP 1 System.ItemUrl FROM SystemIndex WHERE workid=%u", itemID.ulVal );
}
if (SUCCEEDED(hr))
{
hr = ExecuteQuery( pDBCreateCommand, wszQuery, false, IID_PPV_ARGS(&spRowset) );
}
if (SUCCEEDED(hr))
{
CComPtr<IGetRow> spGetRow;
DBCOUNTITEM ciRowsRetrieved = 0;
HROW hRow = NULL;
HROW * phRow = &hRow;
CComPtr<IPropertyStore> spPropertyStore;
hr = spRowset->GetNextRows( DB_NULL_HCHAPTER, 0, 1, &ciRowsRetrieved, &phRow );
if (SUCCEEDED(hr))
{
hr = spRowset->QueryInterface(&spGetRow);
if (SUCCEEDED(hr))
{
CComPtr<IUnknown> spUnknownPropertyStore;
hr = spGetRow->GetRowFromHROW( NULL, hRow, __uuidof(IPropertyStore), &spUnknownPropertyStore );
if (SUCCEEDED(hr))
{
hr = spUnknownPropertyStore->QueryInterface(&spPropertyStore);
}
}
if (SUCCEEDED(hr))
{
PROPVARIANT var = {};
hr = spPropertyStore->GetValue( PKEY_ItemUrl, &var );
if (SUCCEEDED(hr))
{
if (var.vt == VT_LPWSTR)
{
hr = StringCchCopy( pwszURL, cchURL, var.pwszVal );
}
else
{
hr = E_INVALIDARG;
}
::PropVariantClear( &var );
}
}
spRowset->ReleaseRows( ciRowsRetrieved, phRow, NULL, NULL, NULL );
}
}
return hr;
}
//*****************************************************************************
PCWSTR ItemStateToString( ROWSETEVENT_ITEMSTATE itemState )
{
switch (itemState)
{
case ROWSETEVENT_ITEMSTATE_NOTINROWSET:
return L"NotInRowset";
case ROWSETEVENT_ITEMSTATE_INROWSET:
return L"InRowset";
case ROWSETEVENT_ITEMSTATE_UNKNOWN:
return L"Unknown";
}
return L"";
}
//*****************************************************************************
PCWSTR PriorityLevelToString( PRIORITY_LEVEL priority )
{
switch (priority)
{
case PRIORITY_LEVEL_FOREGROUND:
return L"Foreground";
case PRIORITY_LEVEL_HIGH:
return L"High";
case PRIORITY_LEVEL_LOW:
return L"Low";
case PRIORITY_LEVEL_DEFAULT:
return L"Default";
}
return L"";
}
//*****************************************************************************
// An event listener class that listens to indexer events and logs them
// as they arrive...
class ATL_NO_VTABLE CRowsetEventListener :
public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IRowsetEvents
{
public:
BEGIN_COM_MAP(CRowsetEventListener)
COM_INTERFACE_ENTRY(IRowsetEvents)
END_COM_MAP()
// IRowsetEvents
STDMETHOD(OnNewItem)(REFPROPVARIANT itemID, ROWSETEVENT_ITEMSTATE newItemState);
STDMETHOD(OnChangedItem)(REFPROPVARIANT itemID, ROWSETEVENT_ITEMSTATE rowsetItemState, ROWSETEVENT_ITEMSTATE changedItemState);
STDMETHOD(OnDeletedItem)(REFPROPVARIANT itemID, ROWSETEVENT_ITEMSTATE deletedItemState);
STDMETHOD(OnRowsetEvent)(ROWSETEVENT_TYPE eventType, REFPROPVARIANT eventData);
CComPtr<IDBCreateCommand> spDBCreateCommand;
private:
HRESULT PrintURL( REFPROPVARIANT itemID );
};
HRESULT CRowsetEventListener::PrintURL( REFPROPVARIANT itemID )
{
WCHAR wszURL[INTERNET_MAX_URL_LENGTH];
HRESULT hr = (itemID.vt == VT_UI4) ? S_OK : E_INVALIDARG;
if (SUCCEEDED(hr))
{
hr = RetrieveURL( spDBCreateCommand, itemID, wszURL, ARRAYSIZE(wszURL) );
if (FAILED(hr))
{
// It's possible that for some items we won't be able to retrieve the URL.
// This can happen when our application doesn't have sufficient priveledges to read the URL
// or if the URL has been deleted from the system.
hr = StringCchCopy( wszURL, ARRAYSIZE(wszURL), L"URL-Lookup-NotFound" );
}
}
if (SUCCEEDED(hr))
{
printf( "workid: %u; URL: %S\n", itemID.ulVal, wszURL );
}
return hr;
}
STDMETHODIMP CRowsetEventListener::OnNewItem(REFPROPVARIANT itemID, ROWSETEVENT_ITEMSTATE newItemState)
{
// This event is received when the indexer has completed indexing of a NEW item that falls within the
// scope of your query. If your query is for C:\users, then only newly indexed items within C:\users
// will be given.
printf( "OnNewItem( newItemState: %S )\n\t\t", ItemStateToString(newItemState) );
return PrintURL( itemID );
}
STDMETHODIMP CRowsetEventListener::OnChangedItem(REFPROPVARIANT itemID, ROWSETEVENT_ITEMSTATE rowsetItemState, ROWSETEVENT_ITEMSTATE changedItemState)
{
// This event is received when the indexer has completed re-indexing of an item that was already in
// the index that falls within the scope of your query. The rowsetItemState parameter indicates the
// state of the item regarding your query when it was initially executed. The changedItemState
// represents the state of the item following reindexing.
printf( "OnChangedItem( rowsetItemState: %S changedItemState: %S )\n\t\t", ItemStateToString(rowsetItemState), ItemStateToString(changedItemState) );
return PrintURL( itemID );
}
STDMETHODIMP CRowsetEventListener::OnDeletedItem(REFPROPVARIANT itemID, ROWSETEVENT_ITEMSTATE deletedItemState)
{
// This event is received when the indexer has completed deletion of an item that was already in
// the index that falls within the scope of your query. Note that the item may not have been in your
// original query even if the original query was solely scope-based if the item was added following
// your query.
printf( "OnDeletedItem( deletedItemState: %S )\n\t\t", ItemStateToString(deletedItemState) );
return PrintURL( itemID );
}
STDMETHODIMP CRowsetEventListener::OnRowsetEvent(ROWSETEVENT_TYPE eventType, REFPROPVARIANT eventData)
{
switch (eventType)
{
case ROWSETEVENT_TYPE_DATAEXPIRED:
// This event signals that your rowset is no longer valid, so further calls made to the rowset
// will fail. This can happen if the client (your application) loses its connection to the
// indexer. Indexer restarts or network problems with remote queries could cause this.
printf( "OnRowsetEvent( ROWSETEVENT_TYPE_DATAEXPIRED )\n\t\tData backing the rowset has expired. Requerying is needed.\n" );
break;
case ROWSETEVENT_TYPE_FOREGROUNDLOST:
// This event signals that a previous request for PRIORITY_LEVEL_FOREGROUND made on this rowset
// has been downgraded to PRIORITY_LEVEL_HIGH. The most likely cause of this is another query
// having requested foreground prioritization. The indexer treats prioritization requests as a
// stack where only the top request on the stack may have foreground priority.
printf( "OnRowsetEvent( ROWSETEVENT_TYPE_FOREGROUNDLOST )\n\t\tForeground priority has been downgraded to high priority.\n" );
break;
case ROWSETEVENT_TYPE_SCOPESTATISTICS:
// This informational event is sent periodically when there has been a prioritization request for
// any value other than PRIORITY_LEVEL_DEFAULT. This event allows tracking indexing progress in
// response to a prioritization reqeust.
printf( "OnRowsetEvent( ROWSETEVENT_TYPE_SCOPESTATISTICS )\n\t\tStatistics( indexedDocs:%u docsToAddCount:%u docsToReindexCount: %u )\n", eventData.caul.pElems[0], eventData.caul.pElems[1], eventData.caul.pElems[2] );
break;
}
return S_OK;
}
//*****************************************************************************
// Watches events on the given query with the given priority for a period of
// time. If dwTimeout == 0, then it will monitor until all items are indexed
// within the query. Otherwise, it monitors for dwTimeout MS.
void WatchEvents( PCWSTR pwszQuerySQL, PRIORITY_LEVEL priority, DWORD dwTimeout )
{
CComPtr<IDBCreateCommand> spDBCreateCommand;
CComPtr<IRowset> spRowset;
CComPtr<IRowsetPrioritization> spRowsetPrioritization;
CComPtr<CRowsetEventListener> spListener;
HRESULT hr = OpenSession( &spDBCreateCommand );
if (SUCCEEDED(hr))
{
hr = ExecuteQuery( spDBCreateCommand, pwszQuerySQL, true, IID_PPV_ARGS(&spRowset) );
}
if (SUCCEEDED(hr))
{
hr = spRowset->QueryInterface(&spRowsetPrioritization);
}
if (SUCCEEDED(hr))
{
hr = CreateComObject( &spListener );
}
if (SUCCEEDED(hr))
{
spListener->spDBCreateCommand = spDBCreateCommand;
DWORD dwAdviseID;
hr = ConnectToConnectionPoint( spListener->GetUnknown(), __uuidof(IRowsetEvents), TRUE, spRowset, &dwAdviseID, NULL );
if (SUCCEEDED(hr))
{
DWORD indexedDocumentCount = 0;
DWORD oustandingAddCount = 0;
DWORD oustandingModifyCount = 0;
spRowsetPrioritization->GetScopeStatistics( &indexedDocumentCount, &oustandingAddCount, &oustandingModifyCount );
printf( "Prioritization and Eventing Demo\n\n" );
printf( "Query: %S\n\n", pwszQuerySQL );
printf( "Indexed Docs: %u\n", indexedDocumentCount );
printf( "Oustanding Adds: %u\n", oustandingAddCount );
printf( "Oustanding Modifies: %u\n\n", oustandingModifyCount );
printf( "Setting Priority: %S\n\n", PriorityLevelToString(priority) );
printf( "Now monitoring events for this query...\n\n" );
spRowsetPrioritization->SetScopePriority( priority, 1000 );
if (dwTimeout == 0)
{
while (SUCCEEDED(hr) && ((oustandingAddCount > 0) || (oustandingModifyCount > 0)))
{
Sleep( 1000 );
hr = spRowsetPrioritization->GetScopeStatistics( &indexedDocumentCount, &oustandingAddCount, &oustandingModifyCount );
}
}
else
{
Sleep( dwTimeout );
}
ConnectToConnectionPoint( spListener->GetUnknown(), __uuidof(IRowsetEvents), FALSE, spRowset, &dwAdviseID, NULL );
}
}
if (FAILED(hr))
{
printf( "Failure: %08X\n", hr );
}
}
int wmain(int argc, wchar_t *argv[])
{
if ( (argc <= 1) ||
(argc == 2 && ((0 == wcscmp(argv[1], L"-?")) || (0 == wcscmp(argv[1], L"/?")) )) )
{
printf( "Allows monitoring and prioritization of indexer URLs.\n\n" );
printf( "Eventing [drive:][path] [/p[:]priority] [/t[:]duration]\n\n" );
printf( " [drive:][path]\n" );
printf( " Specifies drive and directory of location to watch\n" );
printf( " /p Prioritizes indexing at the given speed\n" );
printf( " priority F Foreground H High\n" );
printf( " L Low D Default\n" );
printf( " /t Specifies how long in MS to monitor query\n" );
printf( " duration 0 Until all content is indexed\n" );
printf( " NNNN Monitor for NNNN milliseconds\n\n" );
}
else if (SUCCEEDED(CoInitializeEx(0, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
{
HRESULT hr = S_OK;
bool fHandled = true;
DWORD dwTimeout = (300 * 1000); // default 5 mins
PRIORITY_LEVEL priority = PRIORITY_LEVEL_DEFAULT;
WCHAR wszURL[INTERNET_MAX_URL_LENGTH];
*wszURL = L'\0';
for (int nArg = 1; ((nArg < argc) && SUCCEEDED(hr)); nArg++)
{
if (argv[nArg][0] == '/')
{
if (towlower(argv[nArg][1]) == 'p')
{
PCWSTR pwsz = argv[nArg] + 2;
if (*pwsz == L':')
{
pwsz++;
}
switch (towlower(*pwsz))
{
case 'f':
priority = PRIORITY_LEVEL_FOREGROUND;
break;
case 'h':
priority = PRIORITY_LEVEL_HIGH;
break;
case 'l':
priority = PRIORITY_LEVEL_LOW;
break;
case 'd':
priority = PRIORITY_LEVEL_DEFAULT;
break;
default:
hr = E_INVALIDARG;
}
}
else if (towlower(argv[nArg][1]) == 't')
{
PCWSTR pwsz = argv[nArg] + 2;
if (*pwsz == L':')
{
pwsz++;
}
dwTimeout = static_cast<DWORD>(_wtol(pwsz));
fHandled = true;
}
else
{
hr = E_INVALIDARG;
}
}
else
{
hr = StringCchCopy( wszURL, ARRAYSIZE(wszURL), argv[nArg] );
}
}
if (SUCCEEDED(hr))
{
WCHAR wszQuerySQL[INTERNET_MAX_URL_LENGTH];
if (*wszURL)
{
hr = StringCchPrintf( wszQuerySQL, ARRAYSIZE(wszQuerySQL), L"SELECT workid FROM SystemIndex WHERE SCOPE='%s'", wszURL );
}
else
{
hr = StringCchCopy( wszQuerySQL, ARRAYSIZE(wszQuerySQL), L"SELECT workid FROM SystemIndex" );
}
if (SUCCEEDED(hr))
{
WatchEvents( wszQuerySQL, priority, dwTimeout );
}
}
CoUninitialize();
}
return 0;
}