IFilter 测试套件验证筛选器处理程序。 测试套件通过:调用 IFilter 方法并检查返回的值是否符合 IFilter 接口规范;并检查区块标识符是否唯一且增加, IFilter 接口在重新初始化后的行为一致,并且任何具有无效参数的 IFilter 方法调用都返回预期的错误代码。 测试套件程序还会转储由筛选器处理程序筛选的文件的输出,并检查注册表中的 IFilter 注册信息。
本主题按如下所示进行组织:
注释
如果将一个新的文件类型的筛选器处理程序安装为替代现有筛选器注册的项目,则安装程序应保存当前注册,并在卸载新的筛选器处理程序时恢复原先的注册。 没有链式筛选器的机制。 因此,新的筛选器处理程序负责复制旧筛选器的任何必要功能。
命令行调用
IFilter 测试套件由三个命令行应用程序( ifilttst.exe、 filtdump.exe和 filtreg.exe 和初始化文件)组成, ifilttst.ini。
重要
在 Windows 7 及更高版本中,以托管代码编写的筛选器将被显式阻止。 由于运行多个外接程序的进程存在潜在的公共语言运行时(CLR)版本控制问题,筛选器必须使用本机代码编写。
Ifilttst.exe
ifilttst.exe 程序运行多个测试来验证筛选器处理程序。 以下示例演示如何从命令行调用 ifilttst.exe 程序:
ifilttst /i test.htm /l /d /v 1
该示例执行以下任务:
- 指示程序筛选该文件 test.htm
- 将日志消息重定向到 test.htm.log
- 将转储消息重定向到 test.htm.dmp
- 将详细程度设置为 1
若要使上述命令正常工作,必须有三个文件位于当前工作目录中: test.htmifilttst.exe和 ifilttst.ini。 下表中列出了命令行开关。
| 切换和可能的变量 | Description |
|---|---|
| /i 文件名 | 要筛选的输入文件或目录。 文件名可以包含通配符 * 和 ?。 |
| /l | 日志消息将定向到文件而不是屏幕。 日志消息描述所执行的各个测试以及测试的通过/失败结果。 日志文件名与输入文件名相同,但扩展名为 .log。 |
| /d | 转储消息将定向到文件而不是屏幕。 转储消息描述数据块的内容。 当详细级别为 3 时,将输出分块结构。 转储文件名与输入文件名相同,但扩展名为 .dmp。 |
| /-l | 禁用日志记录。 此标志替代 /l 开关。 |
| /-d | 禁用转储。 此标志替代 /d 开关。 |
| /v 整数 | 冗长级别。 默认值是3。
|
| /t 整数 | 要启动的线程数。 默认值为 1。 |
| /r 整数] | 以递归方式筛选子目录。 可选整数参数指定要对其执行递归的深度。 如果未指定整数,或者如果整数为 0,则假定完全递归。 默认情况下,递归深度为 1。 |
| /c 整数 | 要循环的次数。 如果整数为 0,则测试将无限循环。 默认情况下,测试仅循环一次。 |
注释
必须在命令行开关和值之间包含一个空格。
Filtdump.exe
filtdump.exe 程序加载指定文档的筛选器处理程序,并打印 IFilter DLL 生成的输出。 以下示例演示如何调用 filtdump.exe 程序。
filtdump filename.ext
Filtdump.exe 使用 ILoadFilter::LoadIFilter 方法加载适用于指定文件扩展名的 IFilter DLL 并输出结果。 例如,以下命令指示 filtdump.exe 加载扩展名 .smp 的 smpfilt.dll 筛选器处理程序,从文件 myfile.smp 中提取所有文本和属性,然后打印结果。
filtdump myfile.smp
Filtreg.exe
filtreg.exe 程序检查注册表中的 IFilter 安装信息。 通过键入 filtreg.exe 程序的名称从命令行调用,如以下示例所示。
filtreg
Filtreg.exe 通过打印文件扩展名和扩展的 IFilter DLL 的名称来枚举具有与之关联的筛选器处理程序的所有文件扩展名。 这是验证 IFilter 正确安装的简单方法。
Ifilttst.ini
通过调用 IFilter::Init 方法初始化 IFilter 接口。 IFilter::Init 方法采用以下四个参数:
- grfFlags
- cAttributes
- aAttributes
- pdwFlags
IFilter 测试套件的 ifilttst.exe 程序的用户可以在名为 ifilttst.ini的文件中指定这些参数的值。 下表描述了指定前三个参数(输入参数)的 ifilttst.ini 文件中的条目。 有关示例文件,请参阅 示例 ifilttst.ini 文件。
注释
pdwFlags 参数没有表项,因为它是输出参数;在调用 IFilter::Init 方法之前,它不需要有任何特殊值。
| 项 | Description | |
|---|---|---|
| Flags | OR 运算符要联接的IFILTER_INIT标志的名称,以形成 IFilter::Init 方法的 grfFlags 参数。 标志名称必须全部为大写,并且在同一行中。 | |
| cAttributes | 一个表示 cAttributes 参数值的十进制整数。 | |
| aAttributes | 此条目必须以 aAttributes 开头,并且必须与节中的其他 aAttributes 条目不同。
aAttributes 条目的合法名称包括:aAttributes、aAttributes1、aAttributes2 等。 第一个令牌必须是 GUID。 GUID 的格式必须完全如[Test3]部分中所示。 第二个标记可以是由十六进制表示法组成的数字的属性标识符(PID),也可以是指向宽字符串(lpwstr)的指针。 可以通过将字符串括在双引号中来指定 lpwstr,如示例 ifilttst.ini 文件的 [Test6] 节所示。 |
如果未指定 Flag 和 cAttributes 条目,则它们默认为 0。 如果将 cAttributes 设置为等于 2,则应指定两 个 aAttributes 名称。 在 [Test5] 示例中, cAttributes 为 1,但没有指定 aAttributes 。 然后,测试调用 iFilter::Init 方法,cAttributes 等于 1,aAttributes 等于 NULL。 这是一个有用的测试用例,因为它可能会导致 IFilter::Init 方法中的访问冲突。
如果 ifilttst.exe 在工作目录中找不到名为 ifilttst.ini 的文件,则使用默认配置来初始化 IFilter::Init 对象。 下面的示例演示了默认配置。
[default]
grfFlags = IFILTER_INIT_APPLY_INDEX_ATTRIBUTES
cAttributes = 0
示例 ifilttst.ini 文件
ifilttst.ini 文件按节进行组织,节名称括在方括号中。 在此示例中,这些节被命名为[Test1]、[Test2]等。 所有节名称必须是唯一的。 该测试从第一部分读取值,并使用这些值初始化 IFilter 。 然后,所有测试都使用此 IFilter 配置运行。 然后,使用上面列出的参数释放并重新初始化 IFilter 。 此过程将重复,直到测试所有配置。
; Only extract text from the object
[Test1]
Flags =
cAttributes = 0
// Get all attributes (text-type and internal value-type properties.
[Test2]
Flags = IFILTER_INIT_APPLY_INDEX_ATTRIBUTES
cAttributes = 0
// This also extracts just text from the object (the GUID is PSGUID_STORAGE, and the propid is
// PID_STG_CONTENTS).
[Test3]
Flags = IFILTER_INIT_CANON_PARAGRAPHS IFILTER_INIT_HARD_LINE_BREAKS
cAttributes = 1
aAttributes1 = b725f130-47ef-101a-a5f1-02608c9eebac 13
// Only extract requested attribute from the html object (the GUID corresponds to the HTML IFilter.
[Test4]
Flags = IFILTER_INIT_CANON_HYPHENS IFILTER_INIT_CANON_SPACES
cAttributes = 1
aAttributes1 = 70eb7a10-55d9-11cf-b75b-00aa0051fe20 2
// Question: what happens if cAttributes is nonzero, but aAttributes is empty?
[Test5]
Flags = IFILTER_INIT_CANON_SPACES IFILTER_INIT_APPLY_INDEX_ATTRIBUTES IFILTER_INIT_APPLY_OTHER_ATTRIBUTES
cAttributes = 1
// Here is an attribute with a lpwstr instead of a propid (the lpwstr is enclosed in quotes).
// The GUID corresponds to the meta tag clsid for the HTML IFilter.
[Test6]
Flags =
cAttributes = 1
aAttributes1 = D1B5D3F0-C0B3-11CF-9A92-00A0C908DBF1 "GENERATOR"
IFilter 测试过程
初始化 IFilter 后,ifilttst.exe 程序对 IFilter 执行一系列测试。 除了遵循 IFilter 测试过程之外,请确保 IFilter 实现采用安全代码做法。 请参阅在 Windows 搜索中实现筛选器处理程序中的“Windows 搜索安全代码做法”。
验证测试
验证测试逐块检查对象,每个区块都经过验证,并检查所有返回代码。 验证测试将所有返回 STAT_CHUNK 结构保存在列表中。
验证测试验证以下条件:
- STAT_CHUNK。idChunk 区块 ID 必须是唯一且增加的。
- STAT_CHUNK。flags 参数是已识别的区块状态,例如 CHUNKSTATE、CHUNK_TEXT 或 CenabledHUNK_VALUE 常量。
- STAT_CHUNK。breakType 参数是已识别的中断类型(0、1、2、3、4)。
- 如果 IFilter 初始化属性指定 IFilter 应仅返回包含内部值类型属性的区块,则 idChunkSource 必须等于 0。
- 如果区块不是派生的,即它不是内部值类型属性,那么STAT_CHUNK。idChunkSource 必须等于STAT_CHUNK。idChunk。
- IFilter::GetChunk 返回S_OK或其他可接受的返回值,例如FILTER_E_END_OF_CHUNKS、FILTER_E_LINK_UNAVAILABLE等。
- 如果区块包含文本, 则 IFilter::GetText 返回S_OK、FILTER_S_LAST_TEXT或FILTER_E_NO_MORE_TEXT。
- 如果 IFilter::GetText 返回FILTER_S_LAST_TEXT,则对 IFilter::GetText 的下一次调用将返回FILTER_E_NO_MORE_TEXT。
- 如果区块包含值, 则 IFilter::GetValue 返回S_OK或FILTER_E_NO_MORE_VALUES。
一致性测试
ifilttxt.exe 程序使用与验证测试中相同的参数重新初始化 IFilter 接口,并执行一致性测试。 如果 IFilter 实现已使用 IFILTER_INIT IFILTER_INIT_INDEXING_ONLY标志进行初始化,则测试会释放 IFilter 接口,并在对 IFilter::Init 方法进行另一次调用之前重新绑定它。
一致性测试验证以下条件:
- IFilter::GetChunk 方法返回的每个STAT_CHUNK结构与验证测试中返回的相应STAT_CHUNK相同。
- IFilter::GetChunk 返回S_OK或其他可接受的返回值,例如FILTER_E_END_OF_CHUNKS、FILTER_E_LINK_UNAVAILABLE等。
输入测试无效
ifilttst.exe 程序使用相同的参数重新初始化 IFilter 接口,并执行无效的输入测试。 此测试一次处理一个块,使得函数调用出错,例如在当前块包含文本时调用 IFilter::GetValue 方法。 测试检查所有返回代码是否符合 IFilter 规范。
无效的输入测试验证以下条件:
- 如果当前区块包含文本, 则 IFilter::GetValue 返回FILTER_E_NO_VALUES,对 IFilter::GetText 的调用成功。
- 如果当前区块包含值, 则 IFilter::GetText 返回FILTER_E_NO_TEXT,对 IFilter::GetValue 的调用成功。
- 如果上一次调用 IFilter::GetText 返回FILTER_E_NO_MORE_TEXT,则对 IFilter::GetText 的连续调用FILTER_E_NO_MORE_TEXT返回。
- 如果上一次调用 IFilter::GetValue 返回FILTER_E_NO_MORE_VALUES,则对 IFilter::GetValue 的连续调用将返回FILTER_E_NO_MORE_VALUES。
- 如果上一次调用 IFilter::GetChunk 返回FILTER_E_END_OF_CHUNKS,则对 IFilter::GetChunk 的连续调用返回FILTER_E_END_OF_CHUNKS。
注释
无效的输入测试将当前区块结构与验证测试中返回的结构进行比较,以确保它们相同。
针对不同的 IFilter 配置进行测试
ifilttst.exe 程序释放 IFilter 接口并重新绑定,这次会用下一组参数初始化它。 测试重复执行循环:验证测试、一致性测试和无效输入测试,直到文件ifilttst.ini中指定的所有所需IFilter配置都已被测试。
确保已注册的项目被索引
IFilter 的最终测试可确保已正确注册 IFilter,并调用它来为已注册使用它的项编制索引。 可以使用目录管理器启动重新编制索引,或使用爬网范围管理器(CSM)设置默认规则,指示希望索引器爬网的 URL。 索引编制完成后,使用 Windows 搜索 UI 在项的内容或属性中搜索字符串。 如果项已编制索引,它们将显示在搜索结果中。
有关重新编制索引的详细信息,请参阅 “使用目录管理器 ”和 “使用爬网范围管理器”。 ReindexMatchingUrls 代码示例演示了如何指定要重新编制索引的文件和方法。 CrawlScopeCommandLine 代码示例演示如何为爬网范围管理器(CSM)索引定义命令行选项。 GitHub 上提供了这两个代码示例。
示例日志文件
请求后,Ifilttst.exe 程序可以生成一个日志,其中包含在执行过程中所执行的步骤的说明。 以下示例摘自日志文件,详细程度设置为最高可能值 3。
1. INFO----**** New configuration ****
2.
3. Section name : Test2
4. grfFlags : 63
5. cAttributes : 0
6. aAttributes : NONE
7. pdwFlags : 0
8.
9. INFO----Successfully bound filter.
10.
11. PASS----Init() returned a valid value for pdwFlags.
12.
13. INFO----Successfully initialized filter.
14.
15. INFO----Performing validation test. In this part of the test, the chunks structures
16. returned by the IFilter are checked for correctness, and the return values
17. of the IFilter calls are checked.
18.
19. PASS----GetChunk() succeeded.
20.
21. PASS----The current chunk has a legal value for the flags field.
第一行是一条信息性消息,指示已从 ifilttst.ini 文件加载新配置。 行(3) 表示当前配置是从 ifilttst.ini 文件中的哪个节读取的。 行 (4) 到 (7) 列出 IFilter::Init 的参数。 以 INFO 开头的行是关于 IFilter 绑定以及验证测试开始的提示性信息。 以 PASS 开头的行表示某些特定测试已通过的消息。
以下日志示例中的行是警告。 警告会引起人们对 IFilter 行为的关注,尽管这是合法的。 此警告指示 IFilter::GetChunk 方法已返回不包含文本的文本区块。
WARNING-First call to GetText() returned FILTER_E_NO_MORE_TEXT.
以下示例错误消息指示 IFilter 发出了未请求的区块。
ERROR---The IFilter has emitted a chunk which it was not requested to emit.
Check the initialization parameters in section Test1 of the initialization file.
INFO----Current chunk propid : 0x5
对于此示例错误消息, IFilter 发出了一个包含 PID 的 0x5区块。 检查 ifilttst.ini 中的节 [Test1] 将显示 IFilter 配置为不发出具有此 PID 的区块。 例如,如果在 Flags 条目中既未指定IFILTER_INIT_APPLY_INDEX_ATTRIBUTES也没有IFILTER_INIT_APPLY_OTHER_ATTRIBUTES,并且 cAttributes 为 0, 则 IFilter 将只发出 PID 且 0x13 对应于PID_STG_CONTENTS的区块。
示例转储文件
请求后,Ifilttst.exe 可以生成一个文件转储,其中包含它找到的区块及其内容。 以下示例是此类转储文件的片段。
1. Chunk ID: ........... 2
2. Chunk Break Type: ... END OF SENTENCE
3. Chunk State: ........ TEXT
4. Chunk Locale: ....... 0x411
5. Chunk Source ID: .... 2
6. Chunk Start Source .. 0x0
7. Chunk Length Source . 0x0
8. GUID ................ b725f130-47ef-101a-a5f1-02608c9eebac
9. Property ID ......... 0x13
10. This is a HTML IFilter test page
11. Chunk ID: ........... 3
12. Chunk Break Type: ... END OF SENTENCE
13. Chunk State: ........ TEXT
14. Chunk Locale: ....... 0x411
15. Chunk Source ID: .... 2
16. Chunk Start Source .. 0x0
17. Chunk Length Source . 0x0
18. GUID ................ f29f85e0-4ff9-1068-ab91-08002b27b3d9
19. Property ID ......... 0x2
20. This is a HTML IFilter test page
21. Chunk ID: ........... 4
22. Chunk Break Type: ... END OF SENTENCE
23. Chunk State: ........ VALUE
24. Chunk Locale: ....... 0x411
25. Chunk Source ID: .... 2
26. Chunk Start Source .. 0x0
27. Chunk Length Source . 0x0
28. GUID ................ f29f85e0-4ff9-1068-ab91-08002b27b3d9
29. Property ID ......... 0x2
30. This is an HTML IFilter test page
前九行描述当前区块结构。 GUID 和 PID 对应于 PSGUID_STORAGE/PID_STG_CONTENTS。 这是包含纯文本的区块。 文本位于以下区块结构中:
10. This is an HTML IFilter test page
下一个区块(从第 11 行开始)具有不同的 GUID,对应于 HTML IFilter,以及不同的 PID,对应于 HTML HREF。 这是 HTML IFilter 导出的内部值类型属性。
下一个区块(从第 21 行开始)具有相同的 GUID 和 PID,但其区块状态 VALUE 不是 TEXT。 请注意,这两个区块中的文本与第一个区块中的文本相同。 但是,由于 IFilter 是为应用于此短语的三个属性(纯文本、HTML HREF 作为文本和 HTML HREF 作为值)而设计的,因此结果以三个单独的区块形式发出。
其他资源
- GitHub 上提供的 IFilterSample 代码示例演示如何创建用于实现 IFilter 接口的 IFilter 基类。
- 有关索引过程的概述,请参阅 “索引过程”。
- 有关文件类型的概述,请参阅 文件类型。
- 若要查询文件类型的文件关联属性,请参阅 PerceivedTypes、SystemFileAssociations 和应用程序注册。