Introduction
You can leverage Microsoft Indexing Server to provide powerful search capabilities within your application. Indexing Server can be used to index files on your local or network file system as well as files of your local web site. You can then query the Indexing catalog using a standard SQL syntax. The following article describes in detail how you can create and configure your own Indexing catalog and then search it from within your application. This article concentrates on how you can programmatically administrate Indexing Server, for example create a new Indexing catalog and then add folders to be indexed by this catalog.
The Indexing Service administration API
Current versions of the .NET Framework do not provide any types to programmatically administrate the Indexing Server. But Indexing Server provides a COM based API that you can use. Add a reference to the �Indexing Service Administration Type Library 1.0� which you can find under the COM tab of the "Add Reference" dialog. This references the �ciodm.dll� in the windows\system32 folder. Next import the namespace �CIODMLib
� which will give you access to the three types:
- AdminIndexServerClass- This type is used to administrate the Indexing Server itself. It allows to create or remove catalogs, start or stop the Indexing service, etc.
- CatAdmClass - This type provides access to an existing Indexing catalog. You can add or remove search scopes, start or stop the catalog, etc.
- ScopeAdm - This type provides access to a search scope. You can set logon information for the scope, start a full or incremental scan, etc.
How to retrieve the list of Indexing catalogs?
Indexing Server can have many Indexing catalogs and your application always tells the Indexing Server which catalog to search for. You can use the AdminIndexServerClass
type to enumerate all the defined Indexing catalogs. First you need to create an instance of the type AdminIndexServerClass
. Then you can call FindFirstCatalog()
to find the first catalog in the list. It returns true
if it finds a catalog otherwise false
. To find the next catalog you can call FindNextCatalog()
which again returns true
if it finds another catalog otherwise false
. As the example below shows you first call FindFirstCatalog()
and then in a loop FindNextCatalog()
till one of them returns false
. Each time you then call GetCatalog()
to get a reference to the current catalog in the list. This returns an object of the type object
and you need to query for the appropriate interface on that object. Simple type casting will not work as you are working with COM objects. You need to use the �as
� keyword followed by the interface you are looking for. Underneath, this will query for the appropriate interface on that COM object and return null
if not present or a reference to the interface. The interface you are looking for is the ICatAdm
interface which provides you access to the catalog itself. The CatAdm
type itself implements the ICatAdm
interface.
protected void FillCatalogList(ListView ListOfCatalogs)
{
AdminIndexServerClass Admin = new AdminIndexServerClass();
ListOfCatalogs.Items.Clear();
bool FoundCatalog = Admin.FindFirstCatalog();
while (FoundCatalog)
{
ICatAdm Catalog = Admin.GetCatalog() as ICatAdm;
ListViewItem Item =
ListOfCatalogs.Items.Add(Catalog.CatalogName);
Item.SubItems.Add(Catalog.CatalogLocation);
Item.SubItems.Add(Catalog.IsUpToDate.ToString());
Item.SubItems.Add(Catalog.DocumentsToFilter.ToString());
Item.SubItems.Add(Catalog.FilteredDocumentCount.ToString());
Item.SubItems.Add(Catalog.DelayedFilterCount.ToString());
Item.SubItems.Add(Catalog.FreshTestCount.ToString());
Item.SubItems.Add(Catalog.IndexSize.ToString());
Item.SubItems.Add(Catalog.PctMergeComplete.ToString());
Item.SubItems.Add(Catalog.PendingScanCount.ToString());
Item.SubItems.Add(Catalog.PersistentIndexCount.ToString());
Item.SubItems.Add(Catalog.QueryCount.ToString());
Item.SubItems.Add(Catalog.StateInfo.ToString());
Item.SubItems.Add(Catalog.TotalDocumentCount.ToString());
Item.SubItems.Add(Catalog.UniqueKeyCount.ToString());
Item.SubItems.Add(Catalog.WordListCount.ToString());
FoundCatalog = Admin.FindNextCatalog();
}
if (ListOfCatalogs.Items.Count > 0)
ListOfCatalogs.SelectedIndices.Add(0);
}
As the example above shows the ICatAdm
interface provides a number of properties about the catalog. You can obtain the name, the location, if the catalog is up to date or not, etc. The example above adds each found catalog to a list view.
How to start, stop, pause or continue Indexing Server?
The AdminIndexServerClass
provides four methods to start, stop, pause and continue the Indexing service. It also provides methods to find out if the service is running or paused:
Start()
- starts the Indexing service.
Stop()
- stops the Indexing service.
Pause()
- pauses the Indexing service.
Continue()
- continues a paused Indexing service.
IsRunning()
- returns true
if the Indexing service is running.
IsPaused()
- returns true
if the Indexing service is paused.
How to add or remove an Indexing catalog?
You can add a new catalog with the method AddCatalog()
. You need to pass along the name of the new catalog as well as the folder location where the catalog will be created. You need to restart the Indexing service before you can use a newly created catalog. The following example shows a dialog so the user can enter the name and location of a new catalog. Next it calls AddCatalog
and then asks the user whether to restart the Indexing service so the catalog becomes usable. Finally it calls the method shown above to re-query the list of defined catalogs - FillCatalogList()
.
private void AddCatalog_Click(object sender, EventArgs e)
{
AddNewCatalog AddNew = new AddNewCatalog();
if (AddNew.ShowDialog(this) == DialogResult.OK)
{
AdminIndexServerClass Admin =
new AdminIndexServerClass();
Admin.AddCatalog(AddNew.NewCatalogName,
AddNew.NewCatalogLocation);
if (MessageBox.Show(this, RestartService, this.Text,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
Admin.Stop();
Admin.Start();
}
FillCatalogList(ListOfCatalogs);
}
}
You can call RemoveCatalog()
to remove an existing catalog. You need to pass along the name of the catalog. You also need to first stop the Indexing service to be able to remove any catalog. The example below asks if the user wants to delete the currently selected catalog. If the user confirms it stops the Indexing services, deletes the catalog and then starts the Indexing service again. Afterwards it again calls FillCatalogList()
to refresh the list of catalogs in the list view.
private void RemoveCatalog_Click(object sender, EventArgs e)
{
string CatalogName = ListOfCatalogs.SelectedItems[0].Text;
if (MessageBox.Show(this, ConfirmDeleteCatalog, CatalogName,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
AdminIndexServerClass Admin = new AdminIndexServerClass();
Admin.Stop();
Admin.RemoveCatalog(CatalogName, true);
Admin.Start();
FillCatalogList(ListOfCatalogs);
}
}
How to retrieve the list of search scopes of a catalog?
Each catalog has search scopes, the folders which are included in the catalog. A search scope can be inclusive (by default) or exclusive, meaning this folder will not be included in the catalog. With exclusive you can define sub-folders which should be excluded by this catalog. Listing all the search scopes of the catalog works very similar to the listing of all catalogs of the Indexing Server. First obtain a reference to the catalog by calling the method GetCatalogByName()
on an instance of type AdminIndexServerClass
. This returns an object of type object
and you need to query for the ICatAdm
interface using the �as
� keyword.
private ICatAdm GetCatalog(string CatalogName)
{
AdminIndexServerClass Admin = new AdminIndexServerClass();
return Admin.GetCatalogByName(CatalogName) as ICatAdm;
}
Next you call FindFirstScope()
on ICatAdm
interface to get the first search scope which returns true
if one is found otherwise false
. You call FindNextScope()
on ICatAdm
interface to find subsequent search scopes which again returns true
when one is found otherwise false
. You do this again in a loop till it returns false
. Each time you call GetScope()
to obtain a reference to the current search scope. This again returns an object of the type object
and you query for the IScopeAdm
interface via the �as
� keyword. That interface then provides a list of properties about the search scope itself:
private void FillScopeList(string CatalogName)
{
ListOfScopes.Items.Clear();
ICatAdm Catalog = GetCatalog(CatalogName);
bool FoundScope = Catalog.FindFirstScope();
while (FoundScope)
{
IScopeAdm Scope = Catalog.GetScope() as IScopeAdm;
ListViewItem Item = ListOfScopes.Items.Add(Scope.Alias);
Item.SubItems.Add(Scope.ExcludeScope.ToString());
Item.SubItems.Add(Scope.Logon);
Item.SubItems.Add(Scope.Path);
Item.SubItems.Add(Scope.VirtualScope.ToString());
FoundScope = Catalog.FindNextScope();
}
if (ListOfScopes.Items.Count > 0)
ListOfScopes.SelectedIndices.Add(0);
}
The example above adds all search scopes of a catalog to a list view. This includes the path of the search scope and whether it is exclusive. The VirtualScope
is only set if this catalog indexes a web site.
How to start, stop, pause or continue a catalog?
You can start, stop, pause or continue an individual catalog. First you need to get a reference to the catalog by calling GetCatalogByName
on an instance of the type AdminIndexServerClass
. Then you query again for the ICatAdm
interface. The ICatAdm
interface also provides methods to find out if the catalog is running or paused:
StartCatalog()
- starts the Indexing catalog.
StopCatalog()
- stops the Indexing catalog.
PauseCatalog()
- pauses the Indexing catalog.
ContinueCatlog()
- continues a paused Indexing catalog.
IsCatalogRunning()
- returns true
if the Indexing catalog is running.
IsCatalogPaused()
- returns true
if the Indexing catalog is paused.
How to add or remove a search scope to a catalog?
First you need to obtain a reference to the catalog and then call the method AddScope()
. You pass along the folder to index and true
if the search scope is exclusive or false
if it is inclusive. The example below shows a dialog so the user can enter the folder of the search scope and select if it is exclusive or inclusive. Next it gets the name of the currently selected catalog, obtains the catalog object for it and then adds the search scope by calling AddScope()
on the Catalog
object. It then asks the user if a full scan for it should be performed. In that case it gets a reference to the newly created search scope by calling GetScopeByPath()
on the Catalog
object and then calls Rescan()
on it passing along true
for a full scan. Finally it calls the method FillScopeList()
to refresh the list of search scopes in the list view.
private void AddScope_Click(object sender, EventArgs e)
{
string CatalogName = ListOfCatalogs.SelectedItems[0].Text;
AddNewScope AddNew = new AddNewScope();
if (AddNew.ShowDialog(this) == DialogResult.OK)
{
ICatAdm Catalog = GetCatalog(CatalogName);
Catalog.AddScope(AddNew.Path, AddNew.ExcludeFolder,
null, null);
if (MessageBox.Show(this, PerformFullScan, this.Text,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
IScopeAdm Scope =
Catalog.GetScopeByPath(AddNew.Path) as IScopeAdm;
Scope.Rescan(true);
}
FillScopeList(ListOfCatalogs.SelectedItems[0].Text);
}
}
Adding or removing search scopes does not require stopping or restarting the catalog service. To remove a search scope you call the method RemoveScope()
on the Catalog
object and pass along the path of the search scope. The example below asks if the user wants to delete the currently selected search scope. If the user confirms it deletes the search scope and afterwards it again calls FillScopeList()
to refresh the list of search scopes in the list view.
private void RemoveScope_Click(object sender, EventArgs e)
{
string CatalogName = ListOfCatalogs.SelectedItems[0].Text;
string ScopePath =
ListOfScopes.SelectedItems[0].SubItems[3].Text
if (MessageBox.Show(this, ConfirmDeleteScope, ScopePath,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
ICatAdm Catalog = GetCatalog(CatalogName);
Catalog.RemoveScope(ScopePath);
FillScopeList(ListOfCatalogs.SelectedItems[0].Text);
}
}
Summary
The administrative API of Indexing Server is very simple. The AdminIndexServerClass
type gives you full control over the Indexing Server itself. The ICatAdm
interface and CatAdm
type provide full control over each catalog. The IScopeAdm
interface and ScopeAdm
type provide full control over each search scope. This makes it very easy for you to add new catalogs and search scopes or remove existing catalogs and search scopes programmatically. You also have the ability to force a full or partial scan of search scopes so that additions or changes are available to your application instantly. Refer to the MSDN help for more details about the Indexing Server API. The enclosed sample application provides a sample administrative interface to Indexing Server using the very same API. If you have comments on this article or this topic, please contact me @ klaus_salchner@hotmail.com. I want to hear if you have learned something new. Contact me if you have questions about this topic or article.