Windows Search Indexer:開発者向けの情報とカスタマイズ
Windows Search Indexer(以下、Indexer)は、Windowsオペレーティングシステムの中核を担うコンポーネントであり、ローカルドライブ、ネットワークドライブ、メール、Webブラウザの履歴など、様々なデータソースから情報を収集し、インデックスを作成することで、高速かつ効率的なファイル検索を可能にします。開発者にとって、Indexerはアプリケーションに組み込むことで、ユーザーに優れた検索エクスペリエンスを提供するための強力なツールとなります。
本稿では、Indexerの内部構造、API、カスタマイズ方法、トラブルシューティングなど、開発者向けの情報を網羅的に解説します。
1. Indexerの概要
Indexerは、バックグラウンドサービスとして動作し、システムリソースを最適化しながら、以下の主要な機能を提供します。
- コンテンツのクロール: 設定されたデータソース(場所)を定期的にまたはイベントトリガーによってクロールし、ファイルシステム、メール、その他のデータソースからコンテンツを収集します。
- コンテンツの抽出: ファイルやドキュメントの内容を解析し、テキスト、メタデータ(作成者、日付、タイトルなど)、その他の属性を抽出します。
- インデックスの作成: 抽出された情報に基づき、検索可能なインデックスを構築します。インデックスは、効率的な検索のために最適化されたデータ構造です。
- クエリの処理: ユーザーからの検索クエリを受け付け、インデックスを用いて高速に検索を実行し、結果を返します。
- インデックスの維持: ファイルの追加、削除、変更を監視し、インデックスを最新の状態に保ちます。
2. Indexerのアーキテクチャ
Indexerは、複数のコンポーネントが連携して動作する複雑なシステムです。主要なコンポーネントは以下の通りです。
- Indexer Service (SearchIndexer.exe): Indexerの中心的なサービスであり、クロール、コンテンツ抽出、インデックス作成、クエリ処理などの主要な機能を実行します。
- Filter Host (FilterHost.exe): ファイル形式ごとに特化したフィルターをホストするプロセスです。フィルターは、ファイルの内容を解析し、テキストとメタデータを抽出するために使用されます。
- Protocol Handler: ファイルシステム、メール、Webブラウザなど、様々なデータソースにアクセスするためのインターフェースを提供します。
- Indexer Catalog: 作成されたインデックスを格納するデータベースです。Windows Search サービスは、複数のカタログをサポートしており、それぞれ異なるデータソースを対象とすることができます。
- Notification Subsystem: ファイルシステムの変更を監視し、Indexerに通知します。これにより、インデックスは常に最新の状態に保たれます。
- API (Application Programming Interface): 開発者がIndexerの機能を利用するためのインターフェースを提供します。
3. 開発者向けのAPI:
Indexerは、COM (Component Object Model) ベースのAPIを提供しており、C++、C#、VB.NETなどの様々なプログラミング言語から利用できます。主要なAPIインターフェースは以下の通りです。
- ISearchManager: Indexerの構成と制御を行うためのメインインターフェースです。カタログの取得、オプションの設定、インデックスの再構築などの操作が可能です。
- ISearchCatalogManager: インデックスカタログの管理を行うためのインターフェースです。カタログの作成、削除、プロパティの設定などの操作が可能です。
- ISearchQueryHelper: 検索クエリの構築と実行を支援するインターフェースです。クエリ構文の解析、条件の追加、並べ替えの指定などの操作が可能です。
- ISearchResult: 検索結果を表すインターフェースです。ファイルのパス、タイトル、スニペットなどの情報を取得できます。
- ISearchPersistentItemsChangedSink: インデックスの変更通知を受け取るためのインターフェースです。ファイルの追加、削除、変更をリアルタイムに検知できます。
3.1 ISearchManagerインターフェース
ISearchManager
インターフェースは、Indexerの最上位のインターフェースであり、以下のメソッドを提供します。
- GetCatalog(LPWSTR pszCatalogName, REFIID riid, void ppCatalogManager):** 指定された名前のカタログの
ISearchCatalogManager
インターフェースを取得します。 - GetConnectedDataSourceManager(REFIID riid, void ppConnectedDataSourceManager):** 接続されたデータソースの管理を行う
IConnectedDataSourceManager
インターフェースを取得します。 - GetSearchAssist(REFIID riid, void ppSearchAssist):** 検索アシスト機能を提供する
ISearchAssist
インターフェースを取得します。 - ReindexAll(BOOL fForce): 全てのカタログを再インデックスします。
fForce
パラメータは、強制的に再インデックスを行うかどうかを指定します。 - SetOption(SEARCH_OPTION option, PROPVARIANT *pValue): Indexerのオプションを設定します。
option
パラメータは、設定するオプションの種類を指定し、pValue
パラメータは、オプションの値を指定します。 - GetOption(SEARCH_OPTION option, PROPVARIANT *pValue): Indexerのオプションを取得します。
option
パラメータは、取得するオプションの種類を指定し、pValue
パラメータは、オプションの値を格納するPROPVARIANT
構造体へのポインタを指定します。
3.2 ISearchCatalogManagerインターフェース
ISearchCatalogManager
インターフェースは、インデックスカタログの管理を行うためのインターフェースであり、以下のメソッドを提供します。
- AddPersistentItems(DWORD dwItemCount, LPCWSTR *rgpszItemPaths, BOOL fForceReindex): インデックスに追加するアイテム(ファイルまたはディレクトリ)のパスを指定します。
dwItemCount
パラメータは、アイテムの数を示し、rgpszItemPaths
パラメータは、アイテムのパスの配列へのポインタを示します。fForceReindex
パラメータは、アイテムを強制的に再インデックスするかどうかを指定します。 - RemovePersistentItems(DWORD dwItemCount, LPCWSTR *rgpszItemPaths): インデックスから削除するアイテムのパスを指定します。
dwItemCount
パラメータは、アイテムの数を示し、rgpszItemPaths
パラメータは、アイテムのパスの配列へのポインタを示します。 - GetPersistentItems(DWORD *pdwItemCount, LPWSTR prgpszItemPaths):** インデックスに登録されているアイテムのパスを取得します。
pdwItemCount
パラメータは、アイテムの数を格納する変数へのポインタを示し、prgpszItemPaths
パラメータは、アイテムのパスの配列へのポインタを格納する変数へのポインタを示します。 - ReindexItems(DWORD dwItemCount, LPCWSTR *rgpszItemPaths, BOOL fForceReindex): 指定されたアイテムを再インデックスします。
dwItemCount
パラメータは、アイテムの数を示し、rgpszItemPaths
パラメータは、アイテムのパスの配列へのポインタを示します。fForceReindex
パラメータは、アイテムを強制的に再インデックスするかどうかを指定します。 - GetStatus(SEARCH_INDEXING_STATUS *pStatus): カタログのインデックス作成の状態を取得します。
pStatus
パラメータは、状態を格納するSEARCH_INDEXING_STATUS
構造体へのポインタを示します。 - Pause(): インデックス作成を一時停止します。
- Resume(): 一時停止されたインデックス作成を再開します。
- Reset(): カタログをリセットし、インデックスを再構築します。
- SetOption(CATALOG_OPTION option, PROPVARIANT *pValue): カタログのオプションを設定します。
option
パラメータは、設定するオプションの種類を指定し、pValue
パラメータは、オプションの値を指定します。 - GetOption(CATALOG_OPTION option, PROPVARIANT *pValue): カタログのオプションを取得します。
option
パラメータは、取得するオプションの種類を指定し、pValue
パラメータは、オプションの値を格納するPROPVARIANT
構造体へのポインタを指定します。
3.3 ISearchQueryHelperインターフェース
ISearchQueryHelper
インターフェースは、検索クエリの構築と実行を支援するインターフェースであり、以下のメソッドを提供します。
- CreateQuery(LPCWSTR pszQueryText, REFIID riid, void ppQuery):** 指定されたクエリ文字列に基づいて検索クエリを作成します。
- GenerateSQLFromUserQuery(LPCWSTR pszUserQuery, LPWSTR *ppszSQLQuery): ユーザーが入力したクエリ文字列をSQLクエリに変換します。
- EscapeLikeValue(LPCWSTR pszValue, LPWSTR *ppszEscapedValue): SQLのLIKE句で使用するために、値をエスケープします。
- SetLocale(LCID lcid): クエリの実行に使用するロケールを設定します。
- SetMaxResults(ULONG cMaxResults): 検索結果の最大数を設定します。
- SetSortColumns(DWORD cSortColumns, SORTCOLUMN *rgSortColumns): 検索結果の並べ替え順を設定します。
- SetRestriction(LPCWSTR pszRestriction): 検索結果を絞り込むための制限を設定します。
- SetParameter(LPCWSTR pszName, PROPVARIANT *pPropVar): クエリのパラメータを設定します。
3.4 ISearchResultインターフェース
ISearchResult
インターフェースは、検索結果を表すインターフェースであり、以下のメソッドを提供します。
- GetValue(LPCWSTR pszColumnName, PROPVARIANT *pPropVar): 指定された列の値を
PROPVARIANT
構造体として取得します。 - GetColumnNames(DWORD *pcColumnNames, LPWSTR prgpszColumnNames):** 検索結果に含まれる列の名前を取得します。
3.5 ISearchPersistentItemsChangedSinkインターフェース
ISearchPersistentItemsChangedSink
インターフェースは、インデックスの変更通知を受け取るためのインターフェースであり、以下のメソッドを提供します。
- ItemsChanged(DWORD dwChangeType, DWORD dwItemCount, LPCWSTR *rgpszItemPaths): インデックスに加えられた変更を通知します。
dwChangeType
パラメータは、変更の種類(追加、削除、変更など)を示し、dwItemCount
パラメータは、影響を受けたアイテムの数を示し、rgpszItemPaths
パラメータは、影響を受けたアイテムのパスの配列へのポインタを示します。
4. カスタマイズ
Indexerは、様々な方法でカスタマイズできます。以下に、主なカスタマイズ方法を説明します。
- インデックス対象の場所の指定: コントロールパネルの「インデックスのオプション」から、インデックスを作成する場所(ドライブ、フォルダなど)を指定できます。
- ファイルの種類別の処理の指定: ファイルの種類ごとに、インデックスを作成するかどうか、どのような情報を抽出するかなどを設定できます。
- カスタムプロパティの追加: ファイルのカスタムプロパティをIndexerに認識させ、検索対象に含めることができます。
- カスタムフィルターの開発: 新しいファイル形式に対応するために、独自のフィルターを開発できます。
- プロトコルハンドラの開発: 新しいデータソースに対応するために、独自のプロトコルハンドラを開発できます。
4.1 インデックス対象の場所の指定
Indexerがインデックスを作成する場所は、コントロールパネルの「インデックスのオプション」から変更できます。「変更」ボタンをクリックすると、インデックスに追加する場所を選択できるダイアログが表示されます。
4.2 ファイルの種類別の処理の指定
コントロールパネルの「インデックスのオプション」の「詳細設定」ボタンをクリックすると、ファイルの種類ごとにインデックスを作成するかどうか、どのような情報を抽出するかなどを設定できるダイアログが表示されます。
4.3 カスタムプロパティの追加
ファイルのカスタムプロパティをIndexerに認識させるには、次の手順を実行します。
- カスタムプロパティを定義するスキーマを作成します。スキーマは、XML形式で記述され、プロパティの名前、型、説明などを定義します。
- 作成したスキーマをIndexerに登録します。
- ファイルを再インデックスします。
4.4 カスタムフィルターの開発
新しいファイル形式に対応するために、独自のフィルターを開発できます。フィルターは、COMコンポーネントとして実装され、IFilter
インターフェースを実装する必要があります。
IFilter
インターフェースの主なメソッドは以下の通りです。
- Init(DWORD grfFlags, BOOL fReadOnly, DWORD dwPreferredSection): フィルターを初期化します。
- GetChunk(STAT_CHUNK *pStat): ファイルの内容をチャンクに分割し、チャンクの情報を取得します。
- GetText(ULONG pcwcBuffer, WCHAR awcBuffer): チャンクからテキストを取得します。
- GetValue(PROPVARIANT ppPropValue):** チャンクのプロパティ値を取得します。
4.5 プロトコルハンドラの開発
新しいデータソースに対応するために、独自のプロトコルハンドラを開発できます。プロトコルハンドラは、COMコンポーネントとして実装され、IProtocolHandler
インターフェースを実装する必要があります。
IProtocolHandler
インターフェースの主なメソッドは以下の通りです。
- Initialize(IUri pUri, IInternetSession pSession, IBindCtx *pBindCtx): プロトコルハンドラを初期化します。
- StartBinding(IBindStatusCallback pBSC, IBindCtx pBindCtx, DWORD grfBINDF, BINDINFO *pBindInfo): データソースへのバインドを開始します。
- ContinueBinding(IBindStatusCallback pBSC, IBindCtx pBindCtx): バインド処理を続行します。
- AbortBinding(HRESULT hrError, LPCWSTR pszErrorUri): バインド処理を中止します。
5. トラブルシューティング
Indexerに問題が発生した場合、以下の手順でトラブルシューティングを行うことができます。
- Indexerの状態を確認: タスクマネージャーで、
SearchIndexer.exe
が実行されているか確認します。 - イベントログを確認: アプリケーションログとシステムログに、Indexerに関連するエラーや警告がないか確認します。
- インデックスの再構築: コントロールパネルの「インデックスのオプション」から、インデックスを再構築します。
- Windows Searchサービスの再起動: Windows Searchサービスを再起動します。
- システムファイルのチェック: システムファイルチェッカーツール (
sfc /scannow
) を実行して、システムファイルを修復します。
6. パフォーマンス
Indexerは、システムリソースを消費するため、パフォーマンスに影響を与える可能性があります。パフォーマンスを最適化するために、以下の点に注意してください。
- インデックス対象の場所を絞り込む: 不要な場所をインデックスから除外します。
- インデックスを作成するファイルの種類を絞り込む: 不要なファイルの種類をインデックスから除外します。
- システムの空き容量を確保する: インデックスを作成するための十分な空き容量を確保します。
- 高速なストレージを使用する: SSDなどの高速なストレージを使用することで、インデックス作成速度を向上させることができます。
- インデックス作成のスケジュールを調整する: システムが使用されていない時間帯にインデックス作成を行うようにスケジュールを設定します。
7. まとめ
Windows Search Indexerは、開発者がアプリケーションに組み込むことで、ユーザーに優れた検索エクスペリエンスを提供するための強力なツールです。本稿では、Indexerの内部構造、API、カスタマイズ方法、トラブルシューティングなど、開発者向けの情報を網羅的に解説しました。Indexerを効果的に活用することで、アプリケーションの利便性を向上させ、ユーザー満足度を高めることができるでしょう。
8. コード例 (C#)
以下は、C#でIndexerのAPIを利用する簡単な例です。
“`csharp
using System;
using System.Runtime.InteropServices;
namespace SearchIndexerExample
{
class Program
{
// COM interfaces
[ComImport, Guid(“5e941d8f-7447-4226-8e1b-68656a53094d”), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ISearchManager
{
void GetCatalog(
[MarshalAs(UnmanagedType.LPWStr)] string pszCatalogName,
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[Out, MarshalAs(UnmanagedType.Interface)] out object ppCatalogManager);
void GetConnectedDataSourceManager(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[Out, MarshalAs(UnmanagedType.Interface)] out object ppConnectedDataSourceManager);
void GetSearchAssist(
[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
[Out, MarshalAs(UnmanagedType.Interface)] out object ppSearchAssist);
void ReindexAll(
[MarshalAs(UnmanagedType.Bool)] bool fForce);
void SetOption(
int option,
[In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pValue);
void GetOption(
int option,
[Out, MarshalAs(UnmanagedType.Struct)] out PROPVARIANT pValue);
}
[ComImport, Guid("a956c6e2-3415-4251-ba4e-1e22eff559cb"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ISearchCatalogManager
{
void AddPersistentItems(
uint dwItemCount,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] string[] rgpszItemPaths,
[MarshalAs(UnmanagedType.Bool)] bool fForceReindex);
void RemovePersistentItems(
uint dwItemCount,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] string[] rgpszItemPaths);
void GetPersistentItems(
out uint pdwItemCount,
[Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] out string[] prgpszItemPaths);
void ReindexItems(
uint dwItemCount,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] string[] rgpszItemPaths,
[MarshalAs(UnmanagedType.Bool)] bool fForceReindex);
void GetStatus(
out int pStatus); // Replace int with actual SEARCH_INDEXING_STATUS struct
void Pause();
void Resume();
void Reset();
void SetOption(
int option,
[In, MarshalAs(UnmanagedType.Struct)] PROPVARIANT pValue);
void GetOption(
int option,
[Out, MarshalAs(UnmanagedType.Struct)] out PROPVARIANT pValue);
}
[StructLayout(LayoutKind.Explicit, Size = 16)]
struct PROPVARIANT
{
[FieldOffset(0)]
public ushort vt;
[FieldOffset(2)]
public ushort wReserved1;
[FieldOffset(4)]
public ushort wReserved2;
[FieldOffset(6)]
public ushort wReserved3;
[FieldOffset(8)]
public long llVal;
[FieldOffset(8)]
public double dblVal;
[FieldOffset(8)]
public short boolVal;
[FieldOffset(8)]
public IntPtr ppszVal;
// Add other types as needed
}
[ComImport, Guid("cc8756cb-28d8-4faa-a399-e290d811ca13"), ClassInterface(ClassInterfaceType.None)]
class SearchManagerClass
{
}
static void Main(string[] args)
{
try
{
// Get the Search Manager
ISearchManager searchManager = (ISearchManager)new SearchManagerClass();
// Get the Catalog Manager for the "SystemIndex"
Guid catalogManagerGuid = new Guid("a956c6e2-3415-4251-ba4e-1e22eff559cb");
ISearchCatalogManager catalogManager;
searchManager.GetCatalog("SystemIndex", catalogManagerGuid, out object catalogManagerObject);
catalogManager = (ISearchCatalogManager)catalogManagerObject;
// Add a file to the index
string filePath = @"C:\MyDocuments\MyFile.txt"; // Replace with your file path
string[] filePaths = new string[] { filePath };
catalogManager.AddPersistentItems(1, filePaths, true);
Console.WriteLine("File added to the index. Reindexing...");
// Optionally, force a reindex
// searchManager.ReindexAll(true); // Reindexes the entire catalog. Use with caution.
catalogManager.ReindexItems(1, filePaths, true); // More specific reindexing.
Console.WriteLine("Reindexing complete. Check if the file is searchable.");
// Example: Get Catalog Status (replace with actual logic to interpret status)
int status;
catalogManager.GetStatus(out status);
Console.WriteLine($"Catalog Status: {status}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Console.ReadKey();
}
}
}
“`
重要な注意点:
- COM interop: このコードは、COM interopを使用してIndexer APIにアクセスします。プロジェクトに
System.Runtime.InteropServices
への参照を追加する必要があります。 - アクセス許可: Indexer APIを使用するには、適切なアクセス許可が必要です。管理者権限が必要になる場合があります。
- エラー処理: コード例には基本的なエラー処理が含まれていますが、実稼働環境では、より堅牢なエラー処理が必要です。
- PROPVARIANT:
PROPVARIANT
構造体は、様々なデータ型を扱うために使用されます。例では基本的な構造体定義のみを示しています。必要なデータ型に合わせて拡張する必要があります。 - SEARCH_INDEXING_STATUS:
GetStatus
メソッドの戻り値の型SEARCH_INDEXING_STATUS
は例では int でプレースホルダーとして表現されています。この構造体を適切に定義し、取得したステータスコードを解釈する必要があります。 Microsoft のドキュメントを参照して、適切な構造体定義を見つけてください。 - パスの変更:
filePath
変数を、実際に存在するファイルへの正しいパスに変更してください。 - 再インデックス:
ReindexAll
は、カタログ全体を再インデックスするため、時間がかかる可能性があります。特定のファイルのみを再インデックスする場合は、ReindexItems
メソッドを使用してください。 - NuGet パッケージ: 検索機能とのより高度な統合には、NuGet パッケージである
WindowsAPICodePack
を使用することを検討してください。これには、COM interopを直接操作するよりも使いやすい、管理されたインターフェイスが用意されています。
このコード例は、Indexer APIの基本的な使用方法を示すものです。より複雑なシナリオでは、APIをより深く理解し、適切なエラー処理とアクセス許可管理を実装する必要があります。
9. 今後の展望
Windows Search Indexerは、常に進化し続けています。Microsoftは、より高速で正確な検索結果を提供するために、Indexerのアルゴリズムと機能を継続的に改善しています。また、クラウドストレージやその他の新しいデータソースとの統合も進められています。開発者は、最新のドキュメントとリソースを常に参照し、Indexerの最新の機能と改善を活用することで、アプリケーションの検索エクスペリエンスをさらに向上させることができます。
このドキュメントが、Windows Search Indexerを理解し、アプリケーションで効果的に活用するための出発点となることを願っています。