Gewusst wie: Erstellen eines IFilters für die SharePoint 2010-Suche und die Windows-Suche mithilfe von C++, der ATL und der MFC

Zusammenfassung: Schritt-für-Schritt-Anleitung zum Erstellen eines IFilters zum Indizieren von Dateiinhalten mithilfe von C++, der MFC-Bibliothek (Microsoft Foundation Class) und der ATL (Active Template Library). Dieser Artikel richtet sich an Entwickler, die SharePoint 2010 und .NET Framework verwenden und wenig mit C++ arbeiten.

Letzte Änderung: Montag, 9. März 2015

Gilt für: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Inhalt dieses Artikels
Einführung in IFilter für Windows-Desktopsuche und SharePoint Server 2010-Suche
So funktioniert ein IFilter
Schnittstellen, die zur Erstellung eines IFilters vom Code implementiert werden müssen
Einrichten des Projekts vor dem Erstellen eines IFilters
Implementieren des IFilters
Registrieren des IFilters
Testen des IFilters mit dem Hilfsprogramm "IFiltTst"
Packen und Bereitstellen eines IFilters
Schlussbemerkung
Weitere Ressourcen
Informationen zum Autor

**Bereitgestellt von:**Alex Culp, RBA Consulting (in englischer Sprache)

Inhalt

  • Einführung in IFilter für Windows-Desktopsuche und SharePoint Server 2010-Suche

  • So funktioniert ein IFilter

  • Schnittstellen, die zur Erstellung eines IFilters vom Code implementiert werden müssen

  • Einrichten des Projekts vor dem Erstellen eines IFilters

  • Implementieren des IFilters

  • Registrieren des IFilters

  • Testen des IFilters mit dem Hilfsprogramm "IFiltTst"

  • Packen und Bereitstellen eines IFilters

  • Schlussbemerkung

  • Weitere Ressourcen

  • Informationen zum Autor

Beispielcode herunterladen (in englischer Sprache)

Einführung in IFilter für Windows-Desktopsuche und SharePoint Server 2010-Suche

Ein IFilter ist eine Schnittstelle, über die die Windows-Desktopsuche und Microsoft SharePoint Server 2010-Suche die Inhalte von Dateien indizieren können. Zwar funktioniert die standardmäßige Volltextsuche für Dokumente in den meisten Situationen gut, doch ist sie nicht immer die geeignete Methode, etwa beim Indizieren einer Datei in einem binären Format oder beim Indizieren einer Textdatei, in der Sie ganz bestimmte Informationen finden müssen. Windows bietet integrierte IFilter für Microsoft Office 2010-Produkte und Filterpakete, die Sie herunterladen können (siehe Microsoft Office 2010 Filter Packs). Von Adobe gibt es einen IFilter für PDF-Dateien. Technisch gesehen ist ein IFilter eine Schnittstelle, aber auch die Implementierungen dieser Schnittstelle werden als IFilter bezeichnet, was ein wenig für Verwirrung sorgen kann. Zur Klarstellung wird deshalb darauf hingewiesen, dass wir in diesem Artikel, wenn wir die Schnittstelle meinen, immer den Begriff IFilter-Schnittstelle verwenden.

Ab Windows 7 kann zum Implementieren eines IFilters kein verwalteter Code mehr verwendet werden, da für einen gegebenen Prozess zu einer Zeit immer nur eine Version der .NET Framework-Runtime geladen werden kann. Das bedeutet Folgendes: Wenn ein IFilter-Entwickler die Version 2.0 von .NET Framework verwendet und ein anderer Entwickler die Version 4.0, dann sind die zwei IFilter nicht miteinander kompatibel. In Windows 7 und höheren Versionen werden Filter, die in verwaltetem Code geschrieben sind, explizit gesperrt. Wegen der potenziellen Probleme bezüglich der CLR-Versionsverwaltung (Common Language Runtime) mit dem Prozess, in dem mehrere Add-Ins ausgeführt werden, müssen Filter in systemeigenem Code geschrieben werden. Es ist zwar möglich, einen IFilter in Microsoft Visual Basic 6.0 zu schreiben, doch ist dies wahrscheinlich angesichts des hohen Durchsatzes, der zum Indizieren von Tausenden oder womöglich Millionen von Dateien (z. B. in SharePoint) nötig ist, eine äußerst ungünstige Vorgehensweise. Die beste Wahl zum Entwickeln eines IFilters ist somit die Implementierung mithilfe von C++.

Um einen IFilter zu schreiben, müssen Sie mehrere COM-Schnittstellen implementieren (IFilter, IPersistFile, IPersistStream und IUnknown). Sie können COM-Objekte auch ohne Verwendung der ATL (Active Template Library) schreiben, allerdings erleichtert die ATL die Entwicklung erheblich, weil sie die COM-Infrastruktur bereitstellt (Erstellen und Zerstören von Objekten, Zuordnen der Schnittstelle zur konkreten Implementierung usw.).

Dementsprechend wird in diesem Artikel für die Implementierung eines IFilters die ATL verwendet. Außerdem wird die MFC-Bibliothek (Microsoft Foundation Class) zum Implementieren der Zeichenfolgenmanipulation verwendet. Diese ist ein gängiges Feature von IFiltern.

So funktioniert ein IFilter

Ein IFilter verarbeitet eine Datei oder einen Datenstrom und teilt diese(n) in zu verarbeitende Abschnitte auf. Bei SharePoint (sowohl Office SharePoint Server 2007 als auch SharePoint 2010) verwendet der Indexdienst die IPersistStream-Schnittstelle, um einen Datenstrom an eine bestimmte IFilter-Implementierung zu senden. Danach ruft der aufrufende Prozess (der SharePoint-Suchprozess oder die Desktopsuche) die Init-Methode der IFilter-Schnittstelle auf, damit die betreffende Implementierung des IFilters etwaige vorbereitende Setupaktionen ausführen kann. Die eigentliche Arbeit leistet jedoch die GetChunk-Methode. Damit Sie die Funktionsweise der GetChunk-Methode verstehen, müssen Sie das Konzept des Abschnitts verstehen. Ein Abschnitt ist ein einzelnes Datenelement. Der Hostprozess ruft immer wieder GetChunk auf, um das nächste Datenelement abzurufen, so lange, bis FILTER_E_END_OF_CHUNKS zurückgegeben wird.

Abbildung 1 zeigt eine typische Abfolge von Aufrufen aus dem Testprogramm ifilttst.exe an eine IFilter-Implementierung. Beachten Sie, dass die Abfolge von einer Implementierung ausgeht, in der die Load-Methode in IPersistFile die Load-Methode in IPersistStream aufruft.

Abbildung 1. Abfolge von Aufrufen aus einem Testprogramm an eine IFilter-Implementierung

Folge der Aufrufe aus einem Testprogramm

Wenn Ihr IFilter von SharePoint Server 2007 oder von SharePoint 2010 aufgerufen wird, dann wird nicht die IPersistFile-Schnittstelle verwendet, sondern die Load-Methode stellt einen IStream-Parameter bereit. Abbildung 2 zeigt die Abfolge der Aufrufe aus SharePoint.

Abbildung 2. Abfolge der Aufrufe aus SharePoint

Folge der Aufrufe aus SharePoint

Schnittstellen, die zur Erstellung eines IFilters vom Code implementiert werden müssen

Damit Sie einen IFilter erstellen können, muss der Code die IFilter- und die IUnknown-Schnittstelle implementieren sowie entweder die IPersistStream-Schnittstelle oder die IPersistFile-Schnittstelle. Grundsätzlich sollten Sie alle folgenden Schnittstellen implementieren.

"IFilter"-Schnittstelle

In der IFilter-Schnittstelle wird die Hauptarbeit zur Implementierung des IFilters geleistet. Sie müssen die folgenden Methoden in der IFilter-Schnittstelle implementieren:

  • GetChunk Diese Methode positioniert den Filter am Anfang des ersten Datenabschnitts oder am nächsten Abschnitt und gibt einen Deskriptor zurück. Diese Methode enthält die Logik für die Dateianalyse.

  • GetText Diese Methode ruft den Text aus dem aktuellen Abschnitt ab.

  • GetValue Diese Methode ruft Werte aus dem aktuellen Abschnitt ab, die nicht string sind. Zu diesen Werten gehören datetime, integer, long und bool.

  • Init Diese Methode initialisiert die Filtersitzung. Sämtliche Initialisierungslogik sollte in dieser Methode ausgeführt werden, z. B. das Einrichten von internen Variablen.

Die BindRegion-Methode wird derzeit nicht verwendet. Sie kann daher E_NOTIMPL zurückgeben.

"IPersistStream"-Schnittstelle

Zum Implementieren eines IFilters für SharePoint müssen Sie die IPersistStream-Schnittstelle implementieren. Wenn Sie einen IFilter für SharePoint implementieren, haben Sie außer dem Dateiinhalt keine anderen Informationen über die Datei. Der Grund hierfür ist, dass der Inhalt als Datenstrom übergeben wird, und zwar über ein IStream-Objekt als Parameter der Load-Methode und nicht als Dateiinformationen. Es gibt keine Möglichkeit, zusätzliche Informationen aus dem IFilter-Code zu gewinnen, z. B. unter welchem Verzeichnis oder welcher URL das Dokument gehostet wird. Die einzige Methode, die von einem IFilter verwendet wird, ist die Load-Methode. Die anderen Methoden in dieser Schnittstelle können E_NOTIMPL zurückgeben.

"IPersistFile"-Schnittstelle

In der Desktopsuche und in SharePoint wird die IPersistFile-Schnittstelle nicht verwendet, da sie den Sandkasten der Windows-Suche durchbricht. Dennoch ist es nützlich, diese Schnittstelle zu implementieren, um den IFilter testen zu können. Die einzige Methode, die Sie implementieren müssen, ist Load. Die anderen Methoden in dieser Schnittstelle können E_NOTIMPL zurückgeben. Vom Testtool ifilttst.exe wird diese Schnittstelle zum Übergeben von Informationen über eine Datei an den IFilter verwendet. Das Testen des IFilters wird weiter unten in diesem Artikel behandelt. Sie können die Load-Methode implementieren, indem Sie einen auf den Dateiinformationen basierenden Datenstrom laden und dann die Load-Methode in der IPersistStream-Schnittstelle aufrufen.

"IUnknown"-Schnittstelle

Die IUnknown-Schnittstelle ermöglicht Clients die Verwendung der QueryInstance-Methode zum Abrufen von Zeigern auf andere Schnittstellen für ein gegebenes Objekt und zum Verwalten der Existenz des Objekts über die AddRef- und die Release-Methode. Alle anderen COM-Schnittstellen werden direkt oder indirekt von IUnknown geerbt. Daher sind die drei Methoden in IUnknown die ersten Einträge in der V-Tabelle für jede Schnittstelle. Wenn Sie die ATL verwenden, führt diese die gesamte Arbeit für die Implementierung von IUnknown aus. Weitere Informationen zur IUnknown-Schnittstelle finden Sie unter IUnknown (Schnittstelle).

Einrichten des Projekts vor dem Erstellen eines IFilters

Bevor Sie einen IFilter erstellen, müssen Sie das Windows SDK herunterladen und installieren. Das Microsoft Windows SDK für Windows 7 und .NET Framework 4 (in englischer Sprache) enthält die Headerdateien, die Sie zum Implementieren des IFilters benötigen. Darüber hinaus müssen Sie das Microsoft Visual Studio-Entwicklungssystem als Administrator ausführen, um die DLL registrieren zu können.

So erstellen Sie das Projekt

  1. Öffnen Sie Visual Studio 2010.

  2. Wählen Sie die richtige Projektvorlage aus. Wählen Sie unter Visual C++ die Option ATL aus, und geben Sie dann einen Namen für das Projekt ein, wie in Abbildung 3 gezeigt.

    Abbildung 3. Auswählen der ATL und Benennen des Projekts

    Wählen Sie "ATL" aus, und geben Sie dem Projekt einen Namen

  3. Klicken Sie auf OK, um die Lösung zu erstellen, und klicken Sie dann im nächsten Dialogfeld auf Weiter, wie in Abbildung 4 gezeigt.

    Abbildung 4. Startseite des ATL-Projekt-Assistenten

    Startseite des ATL-Projekt-Assistenten

  4. Klicken Sie im Dialogfeld Anwendungseinstellungen auf MFC unterstützen. Damit können Sie auf die CString-Klasse zugreifen. Dies ist sehr nützlich, weil IFilter in der Regel in großem Umfang Zeichenfolgenmanipulationen ausführen.

  5. Klicken Sie auf Fertig stellen, um das Projekt zu erstellen, wie in Abbildung 5 gezeigt.

    Abbildung 5. Auswählen von "MFC unterstützen" im Dialogfeld "Anwendungseinstellungen"

    Wählen Sie "MFC unterstützen" im Dialogfeld aus

Verwenden der 64-Bit-Version von IFiltern, nicht der 32-Bit-Version

Aufgrund der Speicherbeschränkungen bei 32-Bit-Betriebssystemen sind die meisten SharePoint-Instanzen 64-Bit-Versionen. SharePoint 2010 wird sogar nur als 64-Bit-Edition ausgeliefert. Demzufolge müssen Sie Ihre IFilter in der 64-Bit-Version erstellen, wenn SharePoint 2010 unterstützt werden soll. Dies gilt auch, wenn Sie einen IFilter für die Desktopsuche unter einem 64-Bit-Betriebssystem implementieren möchten. Zum Unterstützen der 64-Bit-Version Ihrer IFilter müssen Sie eine Win64-Lösungsplattform erstellen.

So erstellen Sie eine Win64-Lösungsplattform

  1. Klicken Sie in Visual Studio im Menü Erstellen auf Konfigurations-Manager, um das Dialogfeld Konfigurations-Manager zu öffnen, wie in Abbildung 6 gezeigt.

    Abbildung 6. Dialogfeld "Konfigurations-Manager"

    Konfigurations-Manager-Dialogfeld

  2. Wählen Sie unter Aktive Projektmappenplattform in der Dropdownliste die Option Neu aus. Wählen Sie x64 oder Itanium aus. Falls Sie sich bezüglich des Prozessors auf dem Zielserver nicht sicher sind, wählen Sie x64 aus, wie in Abbildung 7 gezeigt.

    Abbildung 7. Auswählen von "x64" oder "Itanium" im Dialogfeld "Neue Projektmappenplattform"

    Wählen Sie "x642 oder "Itanium" im Dialogfeld aus

  3. Klicken Sie auf OK, und schließen Sie das Dialogfeld Konfigurations-Manager.

Zusätzliche Includeverzeichnisse für den Zugriff auf alle benötigten Headerdateien und Bibliotheken

Für den Zugriff auf alle benötigten Headerdateien und Bibliotheken fügen Sie den SDK-Include-Ordner zu Ihren zusätzlichen Includeverzeichnissen hinzu. Wenn Sie ein 64-Bit-Betriebssystem verwenden, lautet der Standardpfad für das Windows SDK C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include. Bei einem 32-Bit-Betriebssystem sollte das SDK in C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include installiert sein.

So legen Sie den Includepfad fest

  1. Klicken Sie mit der rechten Maustaste auf die Projektdatei, klicken Sie auf Eigenschaften und dann auf C/C++.

  2. Wählen Sie in der Dropdownliste Konfiguration die Option Alle Konfigurationen aus.

  3. Wählen Sie in der Dropdownliste Plattform die Option Alle Plattformen aus. Diese Optionen sind notwendig, da der Includepfad für alle Konfigurationen und Plattformen gilt. Wenn Sie den Includepfad nur für eine Konfiguration festlegen, z. B. Win32, wird der IFilter unter der anderen Konfiguration nicht kompiliert, wie in Abbildung 8 gezeigt.

    Abbildung 8. Auswählen der Optionen "Alle Konfigurationen" und "Alle Plattformen"

    Wählen Sie "Alle Konfigurationen" und "Alle Plattformen" aus

  4. Navigieren Sie in der Dropdownliste Zusätzliche Includeverzeichnisse zu Ihrem SDK-Ordner, wie in Abbildung 9 gezeigt. Ist dieser Ordner nicht vorhanden, laden Sie das Microsoft Windows SDK für Windows 7 und .NET Framework 4 (ISO) (in englischer Sprache) herunter.

    Abbildung 9. Navigieren zum SDK-Include-Ordner

    Navigieren Sie zum SDK-Ordner "Include"

  5. Klicken Sie auf OK, um zum Dialogfeld Eigenschaftenseiten zurückzukehren.

So richten Sie das Debuggen für das IFilter-Beispielprojekt ein

  1. Klicken Sie mit der rechten Maustaste auf die Projektdatei, und klicken Sie dann auf Eigenschaften, um das Dialogfeld Eigenschaftenseiten zu öffnen.

  2. Klicken Sie unter Konfigurationseigenschaften auf Debuggen.

    Übernehmen Sie in der Dropdownliste Plattform die Option Alle Plattformen.

  3. Klicken Sie auf C/C++ und dann auf Allgemein.

    Wählen Sie in der Dropdownliste Debuginformationsformat die Option Programm für Bearbeiten und Fortfahren (/ZI) aus.

So ändern Sie die Laufzeitbibliothek

  1. Im Anschluss an das obige Verfahren klicken Sie unter C/C++ auf Codegenerierung.

  2. Wählen Sie in der Dropdownliste Laufzeitbibliothek die Option Multithreaded-DLL (/MD) aus. Der Code kann nur kompiliert werden, wenn Sie diese Einstellung ausgewählt haben.

    Wenden Sie diese Einstellung auf Alle Konfigurationen und Alle Plattformen an, wie in Abbildung 10 gezeigt.

    Abbildung 10. Auswählen von "Multithreaded-DLL (/MD)" in der Liste "Laufzeitbibliothek"

    Wählen Sie "Multithreaded DLL (/MD)" in der Liste aus

    Der Kompilierungsfehler ist in afx_ver.h vordefiniert.

    #if defined(_AFXDLL) && !defined(_DLL)
    #error Please use the /MD switch for _AFXDLL builds
    #endif
    

Implementieren des IFilters

Im verbleibenden Teil dieses Artikels erhalten Sie Schritt-für-Schritt-Anleitungen zum Implementieren des IFilters. Wenn Sie möchten, können Sie den Code für den Beispiel-IFilter herunterladen (siehe Beispiel-IFilter (in englischer Sprache)).

HinweisHinweis

In Bezug auf die Speicherverwaltung verwendet der gesamte Code in diesem Beispiel lokale Variablen, die keine Bereinigung erfordern. Vermeiden Sie, wenn möglich, Schlüsselwörter wie new, malloc oder calloc, wenn Sie den IFilter implementieren. Andernfalls müssen Sie nach der Ausführung den Speicher bereinigen. Selbst ein kleiner Speicherverlust kann erhebliche Probleme verursachen, wenn er in Tausenden oder Millionen von Dateien aufläuft.

Hinsichtlich CString- und String-Konvertierungen gibt es zahlreiche Typen von Zeichenfolgen in C++, z. B. char*, LPCSTR und LPCWSTR. Dabei dient jeder Zeichenfolgentyp einem anderen Zweck. Um die Entwicklung von Bereichen im Code zu vereinfachen, die je nach IFilter-Implementierung variieren, kann ein Entwickler, der nach dieser Strategie vorgeht, die CString-Klasse für den Großteil der Arbeit verwenden. Die CString-Klasse entspricht grob gesehen der string-Klasse in verwaltetem Code. Viele der Routinen, die in der verwalteten string-Klasse enthalten sind, finden sich auch in der CString-Klasse. Da die CString-Klasse Teil der MFC-Bibliothek ist, umfasst die Implementierung eines IFilters in diesem Artikel die MFC.

Einführung in die Beispiel-Zieldatei für den IFilter

Bevor Sie die Details der Implementierung untersuchen können, benötigen Sie eine Beispiel-Zieldatei. Die folgende Datei ist zwar einfach strukturiert, veranschaulicht aber die Verwendung der durchforsteten Eigenschaften string, integer und datetime.

Beispieldatei "JohnDoe.my"

Customer Name: John Doe

DOB: 5/5/1973

Favorite Sport: Football

Height (Inches): 73

Erstellen der Implementierungsklasse

In diesem Abschnitt erstellen Sie die Klasse, die die IFilter-Schnittstellen implementiert.

So erstellen Sie die Implementierungsklasse

  1. Klicken Sie mit der rechten Maustaste auf das Projekt, und wählen Sie dann Klasse hinzufügen aus, um das Dialogfeld Klasse hinzufügen zu öffnen.

  2. Wählen Sie Einfaches ATL-Objekt aus, und klicken Sie dann auf Weiter, um den ATL-Assistenten für einfache Objekte zu öffnen, wie in Abbildung 11 gezeigt.

    Abbildung 11. Auswählen von "Einfaches ATL-Objekt" im Dialogfeld "Klasse hinzufügen"

    Wählen Sie "Einfaches ATL-Objekt" im Dialogfeld aus

  3. Geben Sie im Feld Kurzname den Namen des IFilters ein, und klicken Sie dann auf Weiter, wie in Abbildung 12 gezeigt.

    Abbildung 12. Angeben des IFilter-Namens im Feld "Kurzname"

    Geben Sie den Namen des IFilters an

    Hat die Implementierungsklasse den gleichen Namen wie das Projekt, erhalten Sie eine Warnung, wonach die CPP-Datei bereits vorhanden ist. Der Grund hierfür ist, dass die Datei bereits vorhanden war, als Sie das Projekt erstellten, und einige globale Funktionen für COM enthält. Klicken Sie auf Ja, wie in Abbildung 13 gezeigt.

    Abbildung 13. Warnung über die bereits vorhandene CPP-Datei

    Warnung wegen bereits vorhandener CPP-Datei

  4. Das Dialogfeld Dateityp-Handleroptionen wird geöffnet. Klicken Sie auf Weiter, um das Dialogfeld Optionen zu öffnen, wie in Abbildung 14 gezeigt.

    Abbildung 14. Dialogfeld "Dateityp-Handleroptionen"

    Das Dialogfeld "Dateityp-Handleroptionen"

  5. Wählen Sie unter Threadmodell die Option Beide aus. Dies ist notwendig, weil IFilter sowohl einfache als auch Apartment-Threadingmodelle unterstützen. Klicken Sie auf Fertig stellen, um die Erstellung der Implementierungsklasse abzuschließen, wie in Abbildung 15 gezeigt.

    Abbildung 15. Auswählen der Option "Beide" unter "Threadmodell"

    Wählen Sie unter "Threadingmodell" die Option "Beide" aus

Aktualisieren der IDL-Datei (Interface Definition Language)

Die IDL-Datei definiert die COM-Schnittstellen und -Vorgänge, die die verwendete DLL unterstützt. Sie müssen die Datei aktualisieren, damit die IFilter-, IPersistFile- und IPersistStream-Schnittstelle unterstützt wird.

So aktualisieren Sie die IDL-Datei

  1. Öffnen Sie in Visual Studio im Projektmappen-Explorer die Datei MyIFilter.idl, die sich im Ordner Quelldateien befindet, wie in Abbildung 16 gezeigt.

    Abbildung 16. Datei "MyIFilter.idl" im Ordner "Quelldateien"

    "MyIFilter.idl" befindet sich im Ordner "Quelldateien"

  2. Sie müssen zwei import-Anweisungen hinzufügen. Fügen Sie am Anfang der IDL-Datei die folgenden Codezeilen ein.

    Import "filter.idl";
    Import "propsys.idl";
    
  3. Fügen Sie in der coclass-Deklaration die folgenden Codezeilen ein.

    Interface IFilter;
    Interface IPersistFile;
    Interface IPersistStream;
    

Die IDL-Datei sollte nun etwa wie das folgende Beispiel aussehen.

import "filter.idl";
import "propsys.idl";
import "oaidl.idl";
import "ocidl.idl";
 
[
    object,
    uuid(E18A08A4-0667-474F-B8B8-74523292BB75),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IMyIFilter : IDispatch{
};
[
    uuid(A622AC67-C33B-4886-8C2A-10BF8A7C016D),
    version(1.0),
]
library MyIFilterLib
{
    importlib("stdole2.tlb");
    [
        uuid(A204ECE7-61DD-4F9F-AC89-DD9B7EFB2076)
    ]
    coclass MyIFilter
    {
        [default] interface IMyIFilter;
        interface IFilter;
        interface IPersistFile;
        interface IPersistStream;
    };
};
HinweisHinweis

Die GUIDs ändern sich bei jeder Erstellung eines neuen Projekts.

Erben der benötigten Schnittstellen

Sie müssen die erforderlichen Schnittstellen einschließen, damit die Implementierungsklasse von diesen erben kann. Öffnen Sie die Headerdatei MyIFilter.h, und fügen Sie der Klassendeklaration die folgenden Codezeilen hinzu.

public IFilter,
public IPersistStream,
public IPersistFile

Außerdem müssen Sie die folgenden Schnittstellen in die COM-Zuordnung einfügen.

COM_INTERFACE_ENTRY(IFilter)

COM_INTERFACE_ENTRY(IPersistStream)

COM_INTERFACE_ENTRY(IPersistFile)

Die Headerdatei sollte nun etwa wie das folgende Beispiel aussehen.

class ATL_NO_VTABLE CMyIFilter :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CMyIFilter, &CLSID_MyIFilter>,
    public IDispatchImpl<IMyIFilter, &IID_IMyIFilter, &LIBID_MyIFilterLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IFilter,
    public IPersistStream,
    public IPersistFile
{
public:
    CMyIFilter()
    {
    }
 
DECLARE_REGISTRY_RESOURCEID(IDR_MYIFILTER1)
 
BEGIN_COM_MAP(CMyIFilter)
    COM_INTERFACE_ENTRY(IMyIFilter)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IFilter)
    COM_INTERFACE_ENTRY(IPersistStream)
    COM_INTERFACE_ENTRY(IPersistFile)
END_COM_MAP()

Hilfsprogrammklassen zum Festlegen einer durchforsteten Eigenschaft für die IFilter

Der tatsächliche Code für das Festlegen einer durchforsteten Eigenschaft ist relativ kompliziert. Deshalb wird in diesem Artikel eine sehr nützliche Klasse verwendet, die die Abschnitts- und Eigenschaftslogik vereinfacht.

Fügen Sie Ihrem Projekt die im Folgenden beschriebenen Dateien ChunkValue.h und ChunkValue.cpp hinzu.

"ChunkValue.h"

#pragma once
 
#include <strsafe.h>
#include <shlwapi.h>
#include <propkey.h>
#include <propsys.h>
#include <filter.h>
#include <filterr.h>
 
// This class simplifies both chunk and property value pair logic.
// To use, create a ChunkValue class as follows.
// Example:
//      CChunkValue chunk;
//      hr = chunk.SetBoolValue(PKEY_IsAttachment, true);
//      or
//      hr = chunk.SetFileTimeValue(PKEY_ItemDate, ftLastModified);
class CChunkValue
{
public:
    CChunkValue();
 
    ~CChunkValue();
 
    // Clear the ChunkValue.
    void Clear();
 
    // Is this propvalue valid?
    BOOL IsValid();
 
 
    // Get the value as an allocated PROPVARIANT.
    HRESULT GetValue(PROPVARIANT **ppPropVariant);
 
    // Get the string value.
    PWSTR GetString();
 
    // Copy the chunk.
    HRESULT CopyChunk(STAT_CHUNK *pStatChunk);
 
    // Get the type of chunk.
    CHUNKSTATE GetChunkType();
 
    // Set the property by key to a unicode string.
    HRESULT SetTextValue(REFPROPERTYKEY pkey, PCWSTR pszValue, CHUNKSTATE chunkType = CHUNK_VALUE,
                         LCID locale = 0, DWORD cwcLenSource = 0, DWORD cwcStartSource = 0,
                         CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
    // Set the property by key to a bool.
    HRESULT SetBoolValue(REFPROPERTYKEY pkey, BOOL bVal, CHUNKSTATE chunkType = CHUNK_VALUE, LCID locale = 0,
                         DWORD cwcLenSource = 0, DWORD cwcStartSource = 0, CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
    // Set the property by key to a variant bool.
    HRESULT SetBoolValue(REFPROPERTYKEY pkey, VARIANT_BOOL bVal, CHUNKSTATE chunkType = CHUNK_VALUE, LCID locale = 0,
                         DWORD cwcLenSource = 0, DWORD cwcStartSource = 0, CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
    // Set the property by key to an int.
    HRESULT SetIntValue(REFPROPERTYKEY pkey, int nVal, CHUNKSTATE chunkType = CHUNK_VALUE,
                        LCID locale = 0, DWORD cwcLenSource = 0, DWORD cwcStartSource = 0,
                        CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
    // Set the property by key to a long.
    HRESULT SetLongValue(REFPROPERTYKEY pkey, long lVal, CHUNKSTATE chunkType = CHUNK_VALUE, LCID locale = 0,
                         DWORD cwcLenSource = 0, DWORD cwcStartSource = 0, CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
    // Set the property by key to a dword.
    HRESULT SetDwordValue(REFPROPERTYKEY pkey, DWORD dwVal, CHUNKSTATE chunkType = CHUNK_VALUE, LCID locale = 0,
                          DWORD cwcLenSource = 0, DWORD cwcStartSource = 0, CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
    // Set the property by key to an int64.
    HRESULT SetInt64Value(REFPROPERTYKEY pkey, __int64 nVal, CHUNKSTATE chunkType = CHUNK_VALUE, LCID locale = 0,
                          DWORD cwcLenSource = 0, DWORD cwcStartSource = 0, CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
    // Set the property by key to a filetime.
    HRESULT SetFileTimeValue(REFPROPERTYKEY pkey, FILETIME dtVal, CHUNKSTATE chunkType = CHUNK_VALUE,
                             LCID locale = 0, DWORD cwcLenSource = 0, DWORD cwcStartSource = 0,
                             CHUNK_BREAKTYPE chunkBreakType = CHUNK_NO_BREAK);
 
protected:
    // Set the locale for this chunk.
    HRESULT SetChunk(REFPROPERTYKEY pkey, CHUNKSTATE chunkType=CHUNK_VALUE, LCID locale=0, DWORD cwcLenSource=0, DWORD cwcStartSource=0, CHUNK_BREAKTYPE chunkBreakType=CHUNK_NO_BREAK);
 
    // Member variables.
private:
    bool m_fIsValid;
    STAT_CHUNK  m_chunk;
    PROPVARIANT m_propVariant;
    PWSTR m_pszValue;
 
};

"ChunkValue.cpp"

#include "stdafx.h"
#include "atlbase.h"
#include "atlconv.h"
#include <string>
CChunkValue::CChunkValue() : m_fIsValid(false), m_pszValue(NULL)
{
          PropVariantInit(&m_propVariant);
        Clear();
}
 
CChunkValue::~CChunkValue()
{
    Clear();
};
 
// Clear the ChunkValue.
void CChunkValue::Clear()
{
    m_fIsValid = false;
    ZeroMemory(&m_chunk, sizeof(m_chunk));
    PropVariantClear(&m_propVariant);
    CoTaskMemFree(m_pszValue);
    m_pszValue = NULL;
}
 
// Is this propvalue valid?
BOOL CChunkValue::IsValid()
{
    return m_fIsValid;
}
 
 
// Get the value as an allocated PROPVARIANT.
HRESULT CChunkValue::GetValue(PROPVARIANT **ppPropVariant)
{
    HRESULT hr = S_OK;
    if (ppPropVariant == NULL)
    {
        return E_INVALIDARG;
    }
 
    *ppPropVariant = NULL;
 
    PROPVARIANT *pPropVariant = static_cast<PROPVARIANT*>(CoTaskMemAlloc(sizeof(PROPVARIANT)));
 
    if (pPropVariant)
    {
        hr = PropVariantCopy(pPropVariant, &m_propVariant);
        if (SUCCEEDED(hr))
        {
            // Detach and return this as the value.
            *ppPropVariant = pPropVariant;
        }
        else
        {
            CoTaskMemFree(pPropVariant);
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
 
    return hr;
}
 
// Get the string value.
PWSTR CChunkValue::GetString()
{
    return m_pszValue;
};
 
// Copy the chunk.
HRESULT CChunkValue::CopyChunk(STAT_CHUNK *pStatChunk)
{
    if (pStatChunk == NULL)
    {
        return E_INVALIDARG;
    }
 
    *pStatChunk = m_chunk;
    return S_OK;
}
 
// Get the type of chunk
CHUNKSTATE CChunkValue::GetChunkType()
{
    return m_chunk.flags;
}
 
// Set the property by key to a unicode string.
HRESULT CChunkValue::SetTextValue(REFPROPERTYKEY pkey, PCWSTR pszValue, CHUNKSTATE chunkType,
                     LCID locale, DWORD cwcLenSource, DWORD cwcStartSource,
                     CHUNK_BREAKTYPE chunkBreakType)
{
    if (pszValue == NULL)
    {
        return E_INVALIDARG;
    }
 
 
    HRESULT hr = SetChunk(pkey, chunkType, locale, cwcLenSource, cwcStartSource, chunkBreakType);
    if (SUCCEEDED(hr))
    {
        size_t cch = wcslen(pszValue) + 1;
        PWSTR pszCoTaskValue = static_cast<PWSTR>(CoTaskMemAlloc(cch * sizeof(WCHAR)));
        if (pszCoTaskValue)
        {
                  StringCchCopyW(pszCoTaskValue, cch, pszValue);
            // StringCchCopy(pszCoTaskValue, cch, pszValue);
            m_fIsValid = true;
            if (chunkType == CHUNK_VALUE)
            {
                hr = InitPropVariantFromString(pszCoTaskValue,&m_propVariant);
            }
            else
            {
                m_pszValue = pszCoTaskValue;
            }
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    return hr;
};
 
// Set the property by key to a bool.
HRESULT CChunkValue::SetBoolValue(REFPROPERTYKEY pkey, BOOL bVal, CHUNKSTATE chunkType, LCID locale,
                     DWORD cwcLenSource, DWORD cwcStartSource, CHUNK_BREAKTYPE chunkBreakType)
{
    return SetBoolValue(pkey, bVal ? VARIANT_TRUE : VARIANT_FALSE, chunkType, locale, cwcLenSource,
                          cwcStartSource, chunkBreakType);
};
 
// Set the property by key to a variant bool.
HRESULT CChunkValue::SetBoolValue(REFPROPERTYKEY pkey, VARIANT_BOOL bVal, CHUNKSTATE chunkType, LCID locale,
                     DWORD cwcLenSource, DWORD cwcStartSource, CHUNK_BREAKTYPE chunkBreakType)
{
    HRESULT hr = SetChunk(pkey, chunkType, locale, cwcLenSource, cwcStartSource, chunkBreakType);
    if (SUCCEEDED(hr))
    {
        hr = InitPropVariantFromBoolean(bVal,&m_propVariant);
        m_fIsValid = true;
    }
    return hr;
};
 
// Set the property by key to an int.
HRESULT CChunkValue::SetIntValue(REFPROPERTYKEY pkey, int nVal, CHUNKSTATE chunkType,
                    LCID locale, DWORD cwcLenSource, DWORD cwcStartSource,
                    CHUNK_BREAKTYPE chunkBreakType)
{
    HRESULT hr = SetChunk(pkey, chunkType, locale, cwcLenSource, cwcStartSource, chunkBreakType);
    if (SUCCEEDED(hr))
    {
        hr = InitPropVariantFromInt32(nVal,&m_propVariant);
        m_fIsValid = true;
    }
    return hr;
};
 
// Set the property by key to a long
HRESULT CChunkValue::SetLongValue(REFPROPERTYKEY pkey, long lVal, CHUNKSTATE chunkType, LCID locale,
                     DWORD cwcLenSource, DWORD cwcStartSource, CHUNK_BREAKTYPE chunkBreakType)
{
    HRESULT hr = SetChunk(pkey, chunkType, locale, cwcLenSource, cwcStartSource, chunkBreakType);
    if (SUCCEEDED(hr))
    {
        hr = InitPropVariantFromInt64(lVal,&m_propVariant);
        m_fIsValid = true;
    }
    return hr;
};
 
// Set the property by key to a dword
HRESULT CChunkValue::SetDwordValue(REFPROPERTYKEY pkey, DWORD dwVal, CHUNKSTATE chunkType, LCID locale,
                      DWORD cwcLenSource, DWORD cwcStartSource, CHUNK_BREAKTYPE chunkBreakType)
{
    HRESULT hr = SetChunk(pkey, chunkType, locale, cwcLenSource, cwcStartSource, chunkBreakType);
    if (SUCCEEDED(hr))
    {
        InitPropVariantFromUInt64(dwVal,&m_propVariant);
        m_fIsValid = true;
    }
    return hr;
};
 
// Set property by key to an int64
HRESULT CChunkValue::SetInt64Value(REFPROPERTYKEY pkey, __int64 nVal, CHUNKSTATE chunkType, LCID locale,
                      DWORD cwcLenSource, DWORD cwcStartSource, CHUNK_BREAKTYPE chunkBreakType)
{
    HRESULT hr = SetChunk(pkey, chunkType, locale, cwcLenSource, cwcStartSource, chunkBreakType);
    if (SUCCEEDED(hr))
    {
        hr = InitPropVariantFromInt64(nVal,&m_propVariant);
        m_fIsValid = true;
    }
    return hr;
};
 
 
// Set Property by key to a filetime
HRESULT CChunkValue::SetFileTimeValue(REFPROPERTYKEY pkey, FILETIME dtVal, CHUNKSTATE chunkType,
                         LCID locale, DWORD cwcLenSource, DWORD cwcStartSource,
                         CHUNK_BREAKTYPE chunkBreakType)
{
    HRESULT hr = SetChunk(pkey, chunkType, locale, cwcLenSource, cwcStartSource, chunkBreakType);
    if (SUCCEEDED(hr))
    {
        hr = InitPropVariantFromFileTime(&dtVal,&m_propVariant);
        m_fIsValid = true;
    }
    return hr;
};
 
 
 
// Initialize the STAT_CHUNK.
inline HRESULT CChunkValue::SetChunk(REFPROPERTYKEY pkey,
                                     CHUNKSTATE chunkType/*=CHUNK_VALUE*/,
                                     LCID locale /*=0*/,
                                     DWORD cwcLenSource /*=0*/,
                                     DWORD cwcStartSource /*=0*/,
                                     CHUNK_BREAKTYPE chunkBreakType /*= CHUNK_NO_BREAK */)
{
    Clear();
 
    // Initialize the chunk.
    m_chunk.attribute.psProperty.ulKind = PRSPEC_PROPID;
    m_chunk.attribute.psProperty.propid = pkey.pid;
    m_chunk.attribute.guidPropSet = pkey.fmtid;
    m_chunk.flags = chunkType;
    m_chunk.locale = locale;
    m_chunk.cwcLenSource = cwcLenSource;
    m_chunk.cwcStartSource = cwcStartSource;
    m_chunk.breakType = chunkBreakType;
 
    return S_OK;
}

Definieren von durchforsteten Eigenschaften für den IFilter

Es gibt mehrere Optionen, durchforstete Eigenschaften zu definieren. Eine Möglichkeit besteht darin, die Eigenschaften zu verwenden, die bereits in der Headerdatei propkey.h definiert sind. Zu diesen vordefinierten Eigenschaften gehören beispielsweise Title, Trademarks und Size. Diese Methode eignet sich gut, wenn Ihr Dateiformat keine speziellen Eigenschaften enthält, die erfasst werden sollen. Wenn Sie jedoch einzigartige Eigenschaften verwenden, raten wir von der Verwendung der vordefinierten Eigenschaften ab, weil sonst Verwirrung entstehen könnte. Erstellen Sie dann stattdessen eigene Eigenschaften mithilfe des gleichen Makros, mit dem auch die vordefinierten Eigenschaften erstellt wurden, DEFINE_PROPERTYKEY. Zur Vermeidung von Konflikten mit einem anderen IFilter sollten Sie eine höhere Nummer für den Eigenschaftsschlüssel verwenden.

Im folgenden Beispiel wird das Erstellen von eigenen durchforsteten Eigenschaften veranschaulicht. Fügen Sie eine neue Headerdatei mit dem Namen CrawledProperties.h hinzu, und fügen Sie dieser dann die folgenden Codezeielen hinzu.

"CrawledProperties.h"

#include <propkeydef.h>
/**** Customer Name ***
Property Name:  200
 
Category: Basic
Property Set ID: 0b63e343-9ccc-11d0-bcdb-00805fccce04
Variant Type: 31
Data Type: Text
Multi-valued: No 
*/
DEFINE_PROPERTYKEY(PKEY_BASIC_200_CUSTOMER_NAME, 0xb63e343, 0x9ccc, 0x11d0, 0xbc, 0xdb, 0x00, 0x80, 0x5f, 0xcc, 0xce, 0x04, 200);
 
/* *** DOB ***
Property Name:  201
 
Category: Basic
Property Set ID: 0b63e343-9ccc-11d0-bcdb-00805fccce04
Variant Type: 64
Data Type: Text
Multi-valued: No 
*/
DEFINE_PROPERTYKEY(PKEY_BASIC_201_DOB, 0xb63e343, 0x9ccc, 0x11d0, 0xbc, 0xdb, 0x00, 0x80, 0x5f, 0xcc, 0xce, 0x04, 201);
 
/* *** Favorite Sport ***
Property Name:  202
 
Category: Basic
Property Set ID: 0b63e343-9ccc-11d0-bcdb-00805fccce04
Variant Type: 31
Data Type: Text
Multi-valued: No 
*/
DEFINE_PROPERTYKEY(PKEY_BASIC_202_FAV_SPORT, 0x0b63e343, 0x9ccc, 0x11d0, 0xbc, 0xdb, 0x00, 0x80, 0x5f, 0xcc, 0xce, 0x04, 202);
 
/* *** Height ***
Property Name:  203
 
Category: Basic
Property Set ID: 0b63e343-9ccc-11d0-bcdb-00805fccce04
Variant Type: 31
Data Type: Text
Multi-valued: No 
*/
DEFINE_PROPERTYKEY(PKEY_BASIC_203_HEIGHT, 0x0b63e343, 0x9ccc, 0x11d0, 0xbc, 0xdb, 0x00, 0x80, 0x5f, 0xcc, 0xce, 0x04, 203);

Implementieren der abstrakten Methoden in der "MyIFilter"-Klasse

Im nächsten Schritt fügen Sie Code hinzu, der die abstrakten Methoden in der MyIFilter-Klasse implementiert.

Aktualisierungen der Headerdatei

Zum Implementieren aller Methoden in den Schnittstellen, von denen die Implementierungsklasse erbt, deklarieren Sie diese Methoden zuerst als konkret. Um die Deklarationen der abstrakten Methoden zu finden, klicken Sie mit der rechten Maustaste auf eine der Schnittstellen (IFilter, IPersistFile und IPersistStream) und wählen dann Gehe zu Deklaration aus. Kopieren Sie diese Methoden aus den betreffenden Headerdateien, und fügen Sie sie dann in der Datei MyIFilter.h in die Klassendeklaration für die MyIFilter-Klasse ein. Entfernen Sie das "= 0" am Ende der Methodendeklarationen. Sie stellen eine konkrete Implementierung aller dieser Methoden bereit, deshalb möchten Sie nicht, dass diese als abstract deklariert sind.

Außerdem müssen Sie der Datei MyIFilter.h einige Membervariablen und eine Methode zur Behandlung von Fehlern hinzufügen. Der Zweck der jeweiligen Membervariablen wird weiter unten in diesem Artikel erklärt.

Deklaration der Membervariablen und der "HandleError"-Methode

private:
    IStream*                    m_pStream;         // Stream of this document
    long _uRefs;
    DWORD                       m_dwChunkId;        // Current chunk id
    DWORD                       m_iText;            // index into ChunkValue
    CChunkValue                 m_currentChunk;     // the current chunk value
    void HandleError(LPCTSTR message, HRESULT hr);

Die vollständige Headerdatei sollte nun etwa wie der folgende Code aussehen.

"MyIFilter.h"

// MyIFilter.h : Declaration of the CMyIFilter
 
#pragma once
#include "resource.h"       // main symbols
#include "Filter.h"
#include "ObjIdl.h"
#include "Initguid.h"
#include "ChunkValue.h"

#include "MyIFilter_i.h"
#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif

using namespace ATL;
 
// CMyIFilter
 
class ATL_NO_VTABLE CMyIFilter :
      public CComObjectRootEx<CComMultiThreadModel>,
      public CComCoClass<CMyIFilter, &CLSID_MyIFilter>,
      public IDispatchImpl<IMyIFilter, &IID_IMyIFilter, &LIBID_MyIFilterLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
      public IFilter,
      public IPersistStream,
      public IPersistFile
{
public:
      CMyIFilter()
      {
      }
 
DECLARE_REGISTRY_RESOURCEID(IDR_MYIFILTER1)
 
BEGIN_COM_MAP(CMyIFilter)
      COM_INTERFACE_ENTRY(IMyIFilter)
      COM_INTERFACE_ENTRY(IDispatch)
      COM_INTERFACE_ENTRY(IFilter)
      COM_INTERFACE_ENTRY(IPersistStream)
      COM_INTERFACE_ENTRY(IPersistFile)
END_COM_MAP()

      DECLARE_PROTECT_FINAL_CONSTRUCT()
 
      HRESULT FinalConstruct()
      {
            return S_OK;
   }
 
   void FinalRelease()
   {
   }
 
   virtual  SCODE STDMETHODCALLTYPE  Init( ULONG grfFlags,
                                            ULONG cAttributes,
                                            FULLPROPSPEC const * aAttributes,
                                            ULONG * pFlags );
 
    virtual  SCODE STDMETHODCALLTYPE  GetChunk( STAT_CHUNK * pStat);
 
    virtual  SCODE STDMETHODCALLTYPE  GetText( ULONG * pcwcBuffer,
                                               WCHAR * awcBuffer );
 
    virtual  SCODE STDMETHODCALLTYPE  GetValue( PROPVARIANT * * ppPropValue );
 
    virtual  SCODE STDMETHODCALLTYPE  BindRegion( FILTERREGION origPos,
                                                  REFIID riid,
                                                  void ** ppunk);
 
    virtual  SCODE STDMETHODCALLTYPE  GetClassID( CLSID * pClassID );
 
    virtual  SCODE STDMETHODCALLTYPE  IsDirty();
 
    virtual  SCODE STDMETHODCALLTYPE  Load( LPCWSTR pszFileName,
                                            DWORD dwMode);
 
    virtual  SCODE STDMETHODCALLTYPE  Save( LPCWSTR pszFileName,
                                            BOOL fRemember );
 
    virtual  SCODE STDMETHODCALLTYPE  SaveCompleted( LPCWSTR pszFileName );
 
    virtual  SCODE STDMETHODCALLTYPE  GetCurFile( LPWSTR  * ppszFileName );
    
    virtual HRESULT STDMETHODCALLTYPE Load( 
        /* [unique][in] */ __RPC__in_opt IStream *pStm);
    
    virtual HRESULT STDMETHODCALLTYPE Save( 
        /* [unique][in] */ __RPC__in_opt IStream *pStm,
        /* [in] */ BOOL fClearDirty);
    
    virtual HRESULT STDMETHODCALLTYPE GetSizeMax( 
        /* [out] */ __RPC__out ULARGE_INTEGER *pcbSize);
 
   virtual HRESULT GetNextChunkValue(CChunkValue &chunkValue);
 
 
private:
    IStream*                    m_pStream;         // Stream of this document
    long _uRefs;
    DWORD                       m_dwChunkId;        // Current chunk id
    DWORD                       m_iText;            // index into ChunkValue
    CChunkValue                 m_currentChunk;     // the current chunk value
    void HandleError(LPCTSTR message, HRESULT hr);
 
};
 
OBJECT_ENTRY_AUTO(__uuidof(MyIFilter), CMyIFilter)

Headerdateien, die in das IFilter-Projekt eingeschlossen werden müssen

Zum Implementieren der übrigen Methoden für die IFilter-Implementierung müssen Sie mehrere Headerdateien einschließen. Öffnen Sie die Datei MyIFilter.cpp, und stellen Sie sicher, dass die folgenden Headerdateien eingeschlossen sind.

#include "stdafx.h"
#include "resource.h"
#include "MyIFilter_i.h"
#include "dllmain.h"
#include "Filter.h"
#include "ChunkValue.h"
#include "ObjIdl.h"
#include "Initguid.h"
#include "XEventLog.h"
#include "CrawledProperties.h"
#include "MyIFilter.h"

Implementieren von nicht verwendeten Methoden im IFilter-Projekt

Bestimmte Methoden werden zwar nie aufgerufen, aber Sie müssen dennoch eine Implementierung für sie bereitstellen, weil sie in den Headerdateien als abstract deklariert sind. Da diese Methoden niemals aufgerufen werden, können Sie sie implementieren, indem Sie E_NOTIMPL zurückgeben.

HinweisHinweis

C++ implementiert Schnittstellen als reine abstrakte Klassen.

Öffnen Sie die Datei MyIFilter.cpp, und fügen Sie die folgenden Methodenimplementierungen hinzu.

Nicht implementierte Methoden

SCODE STDMETHODCALLTYPE CMyIFilter::BindRegion( FILTERREGION origPos,
  REFIID riid,
  void ** ppunk)
{ 
    return E_NOTIMPL;
}
 
 
SCODE STDMETHODCALLTYPE CMyIFilter::GetClassID( CLSID * pClassID )
{
    return E_NOTIMPL;
}
 
SCODE STDMETHODCALLTYPE CMyIFilter::IsDirty()
{
    return E_NOTIMPL;
}
 
SCODE STDMETHODCALLTYPE CMyIFilter::Save( LPCWSTR pszFileName,
  BOOL fRemember )
{
    return E_NOTIMPL;
}
 
SCODE STDMETHODCALLTYPE CMyIFilter::SaveCompleted( LPCWSTR pszFileName )
{
    return E_NOTIMPL;
}
 
SCODE STDMETHODCALLTYPE CMyIFilter::GetCurFile( LPWSTR  * ppszFileName )
{ 
    return E_NOTIMPL;
}
 
HRESULT STDMETHODCALLTYPE CMyIFilter::Save(__RPC__in_opt IStream *pStm, BOOL fClearDirty)
{ 
    return E_NOTIMPL;
}
 
HRESULT STDMETHODCALLTYPE CMyIFilter::GetSizeMax(__RPC__out ULARGE_INTEGER *pcbSize)
{
    return E_NOTIMPL;
}

"Load"-Methoden, die im IFilter-Projekt implementiert werden müssen

Sie müssen zwei Load-Methoden implementieren: eine in der IPersistFile-Schnittstelle und eine in der IPersistStream-Schnittstelle.

Zum Implementieren der Load-Methode für IPersistStream fügen Sie die folgenden Codezeilen hinzu.

"IPersistFile"-"Load"-Methode

SCODE STDMETHODCALLTYPE CMyIFilter::Load( LPCWSTR pszFileName,
  DWORD dwMode)
{ 
   CString msg;
   
    IStream *stream;
    USES_CONVERSION;
    HRESULT hResult = SHCreateStreamOnFile(pszFileName, STGM_READ, &stream);
    if (FAILED (hResult))
    {
        msg.FormatMessage(_T("SHCreateStreamOnFile failed for file %1."),pszFileName);
        HandleError(msg,hResult);
        return hResult;
    }
    // Use the load method for the stream.
    return Load(stream); 
}
HinweisHinweis

Die Implementierung für die IPersistFile.Load-Methode lädt einen Datenstrom und ruft die IPersistStream.Load-Methode auf.

"IPersistFile.Load"

HRESULT STDMETHODCALLTYPE CMyIFilter::Load( __RPC__in_opt IStream *pStm)
{ 
    if (m_pStream)
    {
        m_pStream->Release();
    }
    m_pStream = pStm;
    m_pStream->AddRef();
    return S_OK;
}
HinweisHinweis

Die Implementierung von IPersistStream.Load behält eine Membervariable für den Datenstrom bei und stellt sicher, dass die Verweisanzahl korrekt ist. Nachfolgende Aufrufe von GetChunk benötigen diesen Datenstrom, um die Informationen zu finden. Diese Methode ist die einzige Möglichkeit, eine Kopie des Datenstroms abzurufen.

"GetChunk", "GetText", "GetValue" und "Init" im IFilter-Beispielprojekt

Drei dieser vier Methoden sind relativ komplex, da sie mit umfangreicher Typkonvertierung, Zeigerlogik, Pufferverwendung und Zeichenfolgenmanipulation verbunden sind. Glücklicherweise können Sie, wenn Sie einen eigenen IFilter schreiben, die hier gezeigte Implementierung ohne jegliche Abänderung übernehmen. So können Sie sich darauf konzentrieren, wie Sie die GetNextChunkValue-Methode implementieren. Eine Beschreibung dazu finden Sie weiter unten in diesem Artikel. Fügen Sie der Datei MyIFilter.cpp die folgenden Methoden hinzu.

Implementierung von "GetText"

SCODE STDMETHODCALLTYPE CMyIFilter::GetText( ULONG * pcwcBuffer,
    WCHAR * awcBuffer )
{ 
    HRESULT hr = S_OK;
 
    if ((pcwcBuffer == NULL) || (*pcwcBuffer == 0))
    {
        return E_INVALIDARG;
    }
 
    if (!m_currentChunk.IsValid())
    {
        return FILTER_E_NO_MORE_TEXT;
    }
 
    if (m_currentChunk.GetChunkType() != CHUNK_TEXT)
    {
        return FILTER_E_NO_TEXT;
    }
 
    ULONG cchTotal = static_cast<ULONG>(wcslen(m_currentChunk.GetString()));
    ULONG cchLeft = cchTotal - m_iText;
    ULONG cchToCopy = min(*pcwcBuffer - 1, cchLeft);
 
    if (cchToCopy > 0)
    {
        PCWSTR psz = m_currentChunk.GetString() + m_iText;
 
        // Copy the chars.
        StringCchCopyNW(awcBuffer, *pcwcBuffer, psz, cchToCopy);
 
        // Null terminate it.
        awcBuffer[cchToCopy] = '\0';
 
        // Set how much data is copied.
        *pcwcBuffer = cchToCopy;
 
        // Remember we copied it.
        m_iText += cchToCopy;
        cchLeft -= cchToCopy;
 
        if (cchLeft == 0)
        {
            hr = FILTER_S_LAST_TEXT;
        }
    }
    else
    {
        hr = FILTER_E_NO_MORE_TEXT;
    }
 
    return hr;
 
}

Implementierung von "GetChunk"

SCODE STDMETHODCALLTYPE CMyIFilter::GetChunk( STAT_CHUNK * pStat)
{ 
    HRESULT hr = S_OK;
 
    // A return of S_FALSE indicates that the chunk should be skipped and that
    // we should try to get the next chunk.
 
    int cIterations = 0;
    hr = S_FALSE;
 
    while ((S_FALSE == hr) && (~cIterations & 0x0100))  // Limit to 256 iterations for safety
    {
        pStat->idChunk = m_dwChunkId;
        m_iText = 0;
        hr = GetNextChunkValue(m_currentChunk);
        ++cIterations;
    }
 
    if (hr == S_OK)
    {
        if (m_currentChunk.IsValid())
        {
            // Copy out the STAT_CHUNK
            m_currentChunk.CopyChunk(pStat);
 
            // and set the id to be the sequential chunk.
            pStat->idChunk = ++m_dwChunkId;
        }
        else
        {
            HandleError(_T("Current chunk is invalid"),E_INVALIDARG);
            hr = E_INVALIDARG;
        }
    }
 
    return hr;
}

Implementierung von "GetValue"

SCODE STDMETHODCALLTYPE CMyIFilter::GetValue( PROPVARIANT * * ppPropValue )
{ 
    HRESULT hr = S_OK;
 
    // If this is not a value chunk they should not be calling this.
    if (m_currentChunk.GetChunkType() != CHUNK_VALUE)
    {
        return FILTER_E_NO_MORE_VALUES;
    }
 
    if (ppPropValue == NULL)
    {
        return E_INVALIDARG;
    }
 
    if (m_currentChunk.IsValid())
    {
        // Return the value of this chunk as a PROPVARIANT ( they own freeing it properly ).
        hr = m_currentChunk.GetValue(ppPropValue);
        m_currentChunk.Clear();
    }
    else
    {
        // We have already returned the value for this chunk, so go away.
        hr = FILTER_E_NO_MORE_VALUES;
    }
 
    return hr;
}

Implementierung von "Init"

SCODE STDMETHODCALLTYPE CMyIFilter::Init( ULONG grfFlags,
  ULONG cAttributes,
  FULLPROPSPEC const * aAttributes,
  ULONG * pFlags )
{
    //This pointer is not set to any value. If you do not set it to 0
    //the IFilter will not work.
    *pFlags = 0;
 
    // Common initialization.
    m_dwChunkId = 0;
    m_iText = 0;
    m_currentChunk.Clear();
   
    return S_OK;
}

Implementieren der Fehlerbehandlung ("HandleError"-Methode) im IFilter-Beispielprojekt

Es ist wichtig, eine Fehlerbehandlung zu implementieren, damit etwaige Fehler, die bei der Ausführung des Codes auftreten, behoben werden können. ATL-COM-Klassen lösen normalerweise keine Ausnahmen aus, sondern geben HRESULTs zurück, daher besteht beim Gebrauch dieser Klassen kein Risiko. Wenn Sie jedoch eine Klasse verwenden, die eine Ausnahme auslösen kann, muss jede Methode eine try…catch-Logik aufweisen, mit der die Ausnahme behandelt und HRESULT zurückgegeben wird. Eine ausgelöste Ausnahme (z. B. wegen nicht ausreichenden Speicherplatzes) versetzt die searchFilterHost.exe und den Windows-Indexer in den Status fehlerhaft, deshalb müssen Sie sicherstellen, dass von dem IFilter keine Ausnahme ausgeht. Die meisten API-Aufrufe in C++ geben ein HRESULT zurück. Ein HRESULT ist ein Handle zu einem Ergebnis und ist tatsächlich eine ganze Zahl. 0 oder ein positiver Wert bedeutet, dass keine Fehler aufgetreten sind. Ein negativer Wert gibt an, dass ein Problem vorliegt. Gehen wir den Code noch einmal durch, den wir zum Laden einer Datei in einen Datenstrom geschrieben haben.

if (FAILED (hResult))
{
   msg.FormatMessage(_T("SHCreateStreamOnFile failed for file %1."),pszFileName);
   HandleError(msg,hResult);
   return hResult;
}
HinweisHinweis

In diesem Code wurde das FAILED-Makro verwendet. Der Wert 0 oder eine positive Zahl gibt zwar eine erfolgreiche Ausführung an, doch empfiehlt sich aus Gründen der besseren Dokumentation die Verwendung dieses Makros bzw. seiner Entsprechung SUCCEEDED, weil der Code dadurch selbsterklärend wird.

Eine der gängigsten Vorgehensweisen zum Implementieren der Fehlerbehandlung in Windows besteht darin, den Fehler in das Ereignisprotokoll zu schreiben. Das Beispiel in diesem Artikel enthält die HandleError-Methode. Diese schreibt in das Windows-Anwendungsprotokoll, wenn ein Fehler auftritt. Soll Ihr IFilter Fehler anders behandeln, dann müssen Sie eine eigene Implementierung dieser Methode schreiben.

Laden Sie vor dem Implementieren der HandleError-Methode den Beispielcode aus Ereignisprotokollierung, Teil I: XEventLog – Einfache NT-Ereignisprotokollierung (in englischer Sprache) herunter, und fügen Sie dem MyIFilter-Projekt die Dateien XEventLog.h und XEventLog.cpp hinzu. Die XEventLog-Klasse übernimmt den gesamten komplexen Vorgang des Schreibens in das Ereignisprotokoll. Um die XEventLog-Klasse zu verwenden, fügen Sie zuerst der Headerdatei MyIFilter.h einen Verweis hinzu. Fügen Sie den restlichen include-Anweisungen für die CMyIFilter-Klasse die folgende Codezeile hinzu:

#include "XEventLog.h"

Implementieren Sie die HandleError-Methode mithilfe des folgenden Codes.

void CMyIFilter::HandleError(LPCTSTR message, HRESULT hr)
{
   //Change this to whatever app name you want
   const LPCTSTR AppName = _T("My IFilter");
 
   CString msg;
   msg.FormatMessage(_T("%1 HResult = %2!d!"),message,hr);
   CXEventLog eventLog(AppName);
   eventLog.Write(EVENTLOG_ERROR_TYPE,msg);
}

Implementieren von "ReadLine"

Da dieses Beispielprojekt hier eine Textdatei verwendet, ist es nützlich, eine Routine zu nutzen, mit der eine einzelne Zeile aus der Beispieldatei gelesen und dann zur Verarbeitung in ein CString-Objekt konvertiert wird. Diese Zeile ist auch die Abschnittsgröße. Wie Sie sich erinnern, ist ein Abschnitt als jegliches Datenelement definiert, das einen einzelnen Wert enthält. In diesem Fall hat die Beispieldatei einen Wert pro Zeile.

Fügen Sie der Datei MyIFilter.cpp den folgenden Code hinzu, um die ReadLine-Methode zu implementieren.

Implementation of ReadLine
//Reads a line from the buffer
CString CMyIFilter::ReadLine()
{
    BYTE buffer[10001];
    BOOL eof = false;
    ULONG bytesRead;
    HRESULT hr;
    int i;
 
    for (i=0;!eof && i < 10000;i++)
    {
        hr = m_pStream->Read(buffer + i,1,&bytesRead);
        if (FAILED (hr) || 0 == bytesRead)
        {
            eof = TRUE;
        }
        else if (i > 0 && (char)buffer[i] == '\n')
        {
           eof = TRUE;
        }
    }
    buffer[i] = 0;
    CString ret((char*)buffer);
    return ret;
}

Implementieren von "GetNextChunkValue" im IFilter-Beispielprojekt

Die GetNextChunkValue-Methode ist nicht Teil der IFilter-Schnittstelle, sondern sie wurde in der Headerdatei MyIFilter.h deklariert und wird von der GetChunk-Methode aufgerufen. Es handelt sich um eine vereinfachte Methode, die es dem IFilter-Entwickler erlaubt, sich auf die Implementierung der Logik für die Analyse der Werte aus der Datei zu konzentrieren, anstatt sich mit unwesentlicheren Tüfteleien beschäftigen zu müssen. Ein Entwickler sollte den gesamten bis hierher präsentierten Code ohne Änderungen nutzen können.

Der folgende Code bezieht durchforstete Eigenschaften aus der Beispieldatei und schließt die Demonstration des Erstellens eines komplett neu gestalteten IFilters ab.

Implementierung von "GetNextChunkValue"

HRESULT CMyIFilter::GetNextChunkValue(CChunkValue &chunkValue)
{
    const int MAX_LINES = 128;
    chunkValue.Clear();
    CString line;
    CString propertyValue;
    int ndx = 0;
 

    for (int lines=0;lines < MAX_LINES;lines++)
    {
        line = ReadLine();

        if (line.Find(_T("Customer Name:")) >= 0)
        {
            //Found customer, lets save the value.
            ndx = line.Find(_T(":"));
            propertyValue = line.Mid(ndx+1,1000); //arbitrary long integer, it reads to the end of the line
            propertyValue = propertyValue.Trim();
            chunkValue.SetTextValue(PKEY_BASIC_200_CUSTOMER_NAME,propertyValue);
            return S_OK;
        }
        if (line.Find(_T("Favorite Sport:")) >= 0)
        {
            //Found the favorite sport.
            ndx = line.Find(_T(":"));
            propertyValue = line.Mid(ndx+1,1000); //arbitrary long integer, it reads to the end of the line
            propertyValue = propertyValue.Trim();
            chunkValue.SetTextValue(PKEY_BASIC_202_FAV_SPORT,propertyValue);
            return S_OK;
        }
        if (line.Find(_T("Height (Inches):")) >= 0)
        {
            //Found height.
            ndx = line.Find(_T(":"));
            propertyValue = line.Mid(ndx+1,1000); //arbitrary long integer, it reads to the end of the line
            propertyValue = propertyValue.Trim();
            int height = _ttoi(propertyValue);
            chunkValue.SetIntValue(PKEY_BASIC_203_HEIGHT,height);
            return S_OK;
        }
        if (line.Find(_T("DOB:")) >= 0)
        {
            //Found date of birth.
            ndx = line.Find(_T(":"));
            propertyValue = line.Mid(ndx+1,1000); //arbitrary long integer, it reads to the end of the line
            propertyValue = propertyValue.Trim();
 
            // Now, convert the date string to a systime.
            FILETIME filetime;
            SYSTEMTIME systime;
            int month=0;
            int day=0;
            int year=0;
            ZeroMemory(&systime,sizeof(systime));
            USES_CONVERSION;
            char* aString = T2A(propertyValue.GetBuffer());
            if (sscanf(aString,"%d/%d/%d",&day,&month,&year) == 3)
            {
                systime.wMonth = month;
                systime.wDay = day;
                systime.wYear = year;
 
                SystemTimeToFileTime(&systime,&filetime);
            }
 
            // Add the date to the chunk value and return.
            chunkValue.SetFileTimeValue(PKEY_BASIC_201_DOB,filetime);
            return S_OK;
        }
        // Did not find anything interesting, so we must be out of chunks.
        return FILTER_E_END_OF_CHUNKS;
    }
}

Registrieren des IFilters

Wenn Sie einen 64-Bit-IFilter erstellen (was notwendig ist, wenn Sie den IFilter mit SharePoint 2010 oder der 64-Bit-Edition von SharePoint Server 2007 verwenden möchten), müssen Sie sicherstellen, dass alle Registrierungseinstellungen in die 64-Bit-Registrierung eingetragen sind. Soll die Erstellung oder Aktualisierung dieser Registrierungsschlüssel automatisiert werden, müssen Sie dies berücksichtigen. Zum Erstellen der 64-Bit-Schlüssel muss der Prozess, der die Registrierungseinträge einrichtet, als 64-Bit-Prozess ausgeführt werden. Sie können kein 32-Bit-Installationsprogramm verwenden, um die Registrierungsschlüssel einzurichten, sonst werden die Schlüssel alle falsch platziert. Leider kann das Erstellen eines 64-Bit-Installationsprogramms recht schwierig sein. Sie haben jedoch die Möglichkeit, eine 64-Bit-Konsolenanwendung zum Einrichten der Registrierungsschlüssel zu erstellen. Wenn Sie den IFilter in für den Produktivbetrieb geeigneter Qualität schreiben möchten, müssen Sie ein 64-Bit-Installationsprogramm erstellen. Weitere Informationen dazu finden Sie unter Verwenden von 64-Bit-Windows-Installer-Paketen.

Registrieren der COM-DLL für den Beispiel-IFilter

Da es sich um eine COM-DLL handelt, können Sie sie registrieren, indem Sie an der Befehlszeile regsvr32 [name of your DLL] eingeben. Oder, falls Sie ein Setuppaket erstellen, können Sie die DLL durch das Setuppaket registrieren lassen.

HinweisHinweis

Visual Studio registriert die DLL jedes Mal, wenn sie auf dem lokalen Computer kompiliert wird.

Registrieren des persistenten Handlers

Im nächsten Schritt erstellen Sie den persistenten Handler. Dies ist der Registrierungsschlüssel, der der IFilter-Implementierung, die diese Erweiterung durchforstet, die Dateinamenserweiterung zuordnet. Erstellen Sie die neue GUID mithilfe von Visual Studio. Klicken Sie dazu im Menü Extras auf GUID erstellen.

Die GUID für den persistenten Handler ist eine beliebige GUID, wie in Abbildung 17 gezeigt.

Abbildung 17. Auswählen des Befehls "GUID erstellen" im Menü "Extras"

Klicken Sie im Menü "Extras" auf "GUID erstellen"

Weitere Informationen zu den Registrierungsschlüsseln, die zum Registrieren des persistenten Handlers verwendet werden, finden Sie unter Registrieren von Filterhandlern (in englischer Sprache). Die Klassen-ID für Ihre Klasse finden Sie in der Datei MyIFilter.idl direkt oberhalb der coclass-Deklaration. Der folgende Code ist ein Beispiel hierfür.

    importlib("stdole2.tlb");
    [
        uuid(A204ECE7-61DD-4F9F-AC89-DD9B7EFB2076)
    ]
    coclass MyIFilter
    {
        [default] interface IMyIFilter;
        interface IFilter;
        interface IPersistFile;
        interface IPersistStream;
    };

Tabelle 1 zeigt die Registrierungsschlüssel für den persistenten Filterhandler.

Tabelle 1. Registrierungsschlüssel für den persistenten Handler

Registrierungsschlüssel

Name des Werts

Werttyp

Wert

Hinweise

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.{Extension}

(Standard)

REG_SZ

{Extension}File Format

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.{Extension}

Inhaltstyp

REG_SZ

application/{Extension}

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.{Extension}

Erkannter Typ

REG_SZ

text

Je nach der IFilter-Implementierung ist das Beispiel eine Textdatei.

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.{Extension}\PersistentHandler

(Standard)

REG_SZ

{Persistent Handler Guid}

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{Persistent Handler Guid}

(Standard)

REG_SZ

{Extension} File Persistent Handler

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{Persistent Handler Guid}\PersistentAddinsRegistered

(Standard)

REG_SZ

(Wert nicht festgelegt)

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{Persistent Handler Guid}\PersistentAddinsRegistered\{89BCB740-6119-101A-BCB7-00DD010655AF}

(Standard)

REG_SZ

{Class ID Guid}

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{Persistent Handler Guid}\PersistentHandler

(Standard)

REG_SZ

{Persistent Handler Guid}

Registrierungsschlüssel für SharePoint Server 2007

Zusätzlich zu den Registrierungsschlüsseln für den persistenten Handler werden in Tabelle 2 alle Registrierungsschlüssel aufgelistet, die benötigt werden, damit ein IFilter in SharePoint Server 2007 funktioniert.

Tabelle 2. Registrierungsschlüssel für IFilter in SharePoint Server 2007

Registrierungsschlüssel

Name des Werts

Werttyp

Wert

Hinweise

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Applications\{Site Guid}Gather\Portal_Content\Extensions\ExtensionList

##

REG_SZ

{Extension}

## ist die nächste verfügbare Nummer. Sie können diesen Registrierungsschlüssel in der Verwaltung der SharePoint-Suche erstellen.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Setup\Filters\{Extension}

(Standard)

REG_SZ

(Wert nicht festgelegt)

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Setup\Filters\{Extension}

Extension

REG_SZ

{Extension}

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Setup\Filters\{Extension}

FileTypeBucket

REG_DWORD

5

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Setup\Filters\{Extension}

MimeTypes

REG_SZ

application/{Extension}

Wenn Sie dies als application/text festlegen, verwendet SharePoint statt Ihres IFilters die Volltextsuche.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Setup\Filters\{Extension}

ThreadingModel

REG_SZ

Beide

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Setup\ContentIndexCommon\Filters\Extension\{Extension}

(Standard)

REG_MULTI_SZ

{Class ID Guid}

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office Server\12\Search\Global\Gathering Manager

(Standard)

REG_MULTI_SZ

{Class ID Guid}

Dieser Schlüssel betrifft die maximale Größe der zu analysierenden Datei. Wenn Sie einen IFilter entwerfen, der sehr große Dateien analysiert, müssen Sie für diese Einstellung die maximale Größe einer Datei angeben. Auch wenn der IFilter nur die ersten Kilobytes der Datei analysiert, wird der IFilter dann nicht aufgerufen, wenn die Datei zu groß ist.

Registrierungsschlüssel für SharePoint 2010

Abgesehen vom Einrichten des persistenten Handlers brauchen Sie nur einen weiteren Registrierungsschlüssel für SharePoint 2010, wie in Tabelle 3 gezeigt.

Tabelle 3. Registrierungsschlüssel für IFilter in SharePoint 2010

Registrierungsschlüssel

Name des Werts

Werttyp

Wert

Hinweise

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\14\Search\Setup\ContentIndexCommon\Filters\Extension\.{Extension}

(Standard)

REG_SZ

{Class ID Guid}

Testen des IFilters mit dem Hilfsprogramm "IFiltTst"

Am einfachsten können Sie Ihre IFilter-Implementierung mit dem Hilfsprogramm IFiltTst testen. Weitere Informationen hierzu finden Sie unter "IFiltTst". Sie können dieses Hilfsprogramm so konfigurieren, dass es beim Starten des Debugvorgangs automatisch ausgeführt wird, sodass Sie in den Code einsteigen können.

So konfigurieren Sie die automatische Ausführung von IFiltTst

  1. Klicken Sie mit der rechten Maustaste auf das MyIFilter-Projekt, und klicken Sie dann auf Eigenschaften.

  2. Legen Sie die Textzeile Befehl für die x64-Version auf C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\x64\ifilttst.exe fest.

  3. Fügen Sie die Befehlszeile -I "[Path to the Sample File]" hinzu. Der Pfad zu der Beispieldatei muss von Anführungszeichen eingeschlossen sein, wenn er Leerzeichen enthält, wie in Abbildung 18 gezeigt.

    Abbildung 18. Festlegen der Befehlstextzeile auf den Pfad

    Festlegen der Befehlstextzeile auf den Pfad

  4. Für 32-Bit-Versionen verwenden Sie die ausführbare Datei ifilttst.exe unter C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\.

  5. Klicken Sie auf OK.

Sie können jetzt durch Drücken der Taste F5 oder Auswählen des Befehls Debugging starten im Menü Debuggen in den Code einsteigen.

Packen und Bereitstellen eines IFilters

Auch wenn Sie zum Erstellen der Registrierungseinträge eine Befehlszeile verwenden, sollten Sie ein Setuppaket für den IFilter erstellen, um den Filter auf Servern installieren zu können. Wenn Sie ein Setuppaket erstellen, brauchen Sie sich keine Gedanken mehr darüber zu machen, ob die richtige Version der ATL- und der MFC-DLLs bereitgestellt wird. Falls Sie sich entschließen, ein Setuppaket für den IFilter zu erstellen, und der IFilter ist eine 64-Bit-Implementierung, müssen Sie zum Konfigurieren der Registrierungsschlüssel einen 64-Bit-Installer erstellen. Weitere Informationen zur Verwendung von 64-Bit-Windows-Installer-Paketen finden Sie unter Verwenden von 64-Bit-Windows-Installer-Paketen.

Schlussbemerkung

Ein IFilter ist eine Schnittstelle, über die die Windows-Desktopsuche und Microsoft SharePoint Server 2010-Suche die Inhalte von Dateien indizieren können. Die beste Vorgehensweise zum Entwickeln eines IFilters besteht darin, den IFilter mithilfe von C++ zu implementieren.

Um einen IFilter zu schreiben, müssen Sie mehrere COM-Schnittstellen implementieren (IFilter, IPersistFile, IPersistStream und IUnknown). Sie können COM-Objekte auch ohne Verwendung der ATL (Active Template Library) schreiben, allerdings erleichtert die ATL die Entwicklung erheblich, weil sie die COM-Infrastruktur bereitstellt.

Entwickler, die SharePoint 2010 und .NET Framework verwenden und wenig mit C++ arbeiten, können anhand des Beispiels in diesem Artikel mit der Entwicklung eines benutzerdefinierten IFilters beginnen.

Weitere Ressourcen

Weitere Informationen finden Sie in den folgenden Ressourcen:

Informationen zum Autor

Alex Culp entwirft und entwickelt seit 1997 eine breite Vielfalt von Anwendungen für die Microsoft-Plattform. Er hat an der University of Texas in Austin seinen Bachelor of Science in Informatik erworben. Seine Freizeit verbringt Alex am liebsten mit seiner Frau Terah und seinem Sohn Nicholas. Neben seiner Begeisterung für Technologie pflegt er Hobbys wie Laufen und alle Arten von Abenteuersportarten. Er engagiert sich außerdem im Studentenverein seiner Hochschule und übt eine ehrenamtliche Tätigkeit als Seelsorger in einem örtlichen Krankenhaus aus.