.NET Setup API 封装器(解析 INF、OEM 文件)
用于使用 Setup API 解析 driver.INF 和 txtsetup.OEM 的 .NET 类
目录
引言
Setup
API 是解析driver.INF和txtsetup.OEM文件的推荐方法。不幸的是,Setup
API 是一系列 C 风格函数,.NET Framework 没有等效的接口。本文提供了一些简单的 .NET (C#) 类,它们封装了 Setup
API 中用于解析 INF 文件的常用函数。
背景
我曾需要解析由数十家第三方公司和个人编写的上百个driver.INF、txtsetup.OEM以及其他各种.INF文件。我很快发现这些文件的语法比我最初想的要复杂得多。它们起初看起来很简单,例如
[Section 1]
key1 = field1,field2,fieldN
key2 = int,string,or,binary data
no,key,on,this,line
[Section2]
key1 = field1,field2
[Section 1]
keyN = field1,field2,fieldN
可以有很多节,每节有许多“键
”行。键不一定唯一,即使在同一节内也是如此。键甚至不是必需的(一行可以只是值的列表)。请注意,[Section 1]
出现了两次。这是 Setup
API 可以透明处理的有效语法,就好像该节没有物理上被分割一样。
给定的一行可以包含许多字段。字段可以包含整数、字符串、多字符串和二进制数据。有关注释、包含空格/逗号/引号的字符串字段、十进制与十六进制整数格式、行连接以及本地化字符串替换等各种规则。
.INF文件的文本编码不是 ASCII,也不是 UTF-8。它是“Windows 西欧语言”,也称为代码页 1252。这是一种单字节编码,与 ASCII 非常相似,但又不完全相同。某些字符,例如“注册”或“商标”符号(我不记得是哪个),在代码页 1252 中的编码与 ASCII 不同(或者根本不存在于 ASCII 中)。
记住我的警告,学徒:您不想手动解析此类文件。您想使用 Setup
API,因为它能够处理您可能遇到的所有语法细微差别。
直接使用 Setup
API 一段时间后,我发现自己一遍又一遍地重复相同的代码模式和错误检查。许多函数使用文件句柄或 C 结构,它们似乎渴望被封装在类中,所以我决定编写一些 C# 封装类。使用这些类使我的代码更加清晰,编写起来也更容易。
Using the Code
下载包含两个您将直接使用的类:InfFile
和 InfLine
。
InfFile
InfFile
构造函数接受一个文件路径。它仅捕获路径,因此不可能出错。InfFile.Open()
打开文件。如果成功,则返回 0,如果失败,则返回 Win32 错误代码。稍后会详细介绍错误检查。InfFile.EnumSection()
可用于枚举文件中的节。InfFile.FindFirstLine()
返回一个InfLine
,代表指定节中的第一行。InfFile.FindFirstKey()
返回一个InfLine
,代表指定节中具有指定键的第一行。- 完成文件操作后,请务必调用
InfFile.Close()
或Dispose()
。InfFile 实现IDisposable
。
InfLine
InfLine
类表示文件节中的一行。它具有执行以下操作的方法
- 获取行中的字段数。
- 获取第 N 个字段的字符串、多字符串(数组)、整数或字节
- 获取顺序下一行或具有给定键的下一行的
InfLine
。
调用 InfLine.GetString(int fieldNum)
时,将 0
传递给获取行的键(“=
”左侧的值)。传递 1
获取“=
”右侧的第一个逗号分隔值,依此类推。
通常,您需要对要查找的节和键、每个字段中的数据类型等有所了解。解析真正的driver.inf文件时,许多行是指向其他节的引用。例如,从一行中读取的 string
可能是节的名称(或名称的一部分)。
错误检查和报告
这两个类都包含两个与错误相关的属性:LastError
和 LastMessage
。LastError
是上次调用的方法的 Win32
错误代码。调用任一类中的任何方法后,您可以检查 LastError
是否为 0
。如果不是 0
,则发生错误,LastMessage
包含与 LastError
中的错误代码对应的错误消息。调用任何方法时都会重置这些属性。
此外,这两个类中的大多数方法,例如 InfLine.GetInteger()
,都有两个重载。在这种情况下,两个重载都接受一个 int
,指定应包含整数的字段编号。如果字段不包含整数或不存在该字段,则会发生错误。第一个重载(如下所示)返回一个 Win32
错误代码(错误代码始终为 uint
)。如果未发生错误,它将返回 0
,并且从文件中读取的 int
值将作为 out
参数传回。第二个重载不返回任何错误信息。
public uint GetInteger(int fieldNum, out int intVal);
public int GetInteger(int fieldNum);
在任一情况下,您都可以在调用方法后检查 LastError
,但有时检查方法直接返回的错误代码会更方便。
示例代码
考虑以下简单的 INF 文件(来自下载中的test.inf)。
[Section 1]
key1 = "hello, world", 42
key2 = "written in", 2010
[No Keys Section]
val1, val2
val3, val4
第一个节包含两行,每行都有一个键、一个字符串和一个整数。整数字段可以作为字符串解析,但我们将直接将它们解析为 int
变量。第二个节包含没有键的行,只有逗号分隔的字符串值。
以下代码打开并解析test.inf文件。此代码也在下载中。
static void Main(string[] args)
{
using (InfFile file = new InfFile(".\\test.inf"))
{
if (file.Open() == 0)
{
ReadSection1(file);
ReadSection2(file);
}
else
{
Console.WriteLine("An error occurred opening the INF file: " +
file.LastMessage);
}
}
}
static void ReadSection1(InfFile file)
{
// Get the InfLine for "key1" in [Section 1].
InfLine line = file.FindFirstKey("Section 1", "key1");
if (line == null)
{
Console.WriteLine("Error getting first key: " + file.LastMessage);
}
else
{
// Parse all lines in the section.
// Expect "key = string, int" on each line.
do
{
string keyVal;
string strVal;
int intVal;
if (line.GetString(0, out keyVal) == 0 &&
line.GetString(1, out strVal) == 0 &&
line.GetInteger(2, out intVal) == 0)
{
Console.WriteLine("{0} = {1}, {2}", keyVal, strVal, intVal);
}
} while (line.FindNextLine() == 0);
// ERROR_LINE_NOT_FOUND is expected when we run out of lines.
if (line.LastError != InfError.ERROR_LINE_NOT_FOUND)
{
Console.WriteLine("Unexpected error: " + line.LastMessage);
}
}
}
static void ReadSection2(InfFile file)
{
InfLine line = file.FindFirstLine("No Keys Section");
if (line == null)
{
Console.WriteLine("Error getting first line: " + file.LastMessage);
}
else
{
// Parse all lines in the section.
// Expect "string1, string2" on each line.
do
{
string str1, str2;
if (line.GetString(1, out str1) == 0 &&
line.GetString(2, out str2) == 0)
{
Console.WriteLine("Strings are \"{0}\" and \"{1}\"", str1, str2);
}
} while (line.FindNextLine() == 0);
// ERROR_LINE_NOT_FOUND is expected when we run out of lines.
if (line.LastError != InfError.ERROR_LINE_NOT_FOUND)
{
Console.WriteLine("Unexpected error: " + line.LastMessage);
}
}
}
最后,这是输出。
key1 = hello, world, 42
key2 = written in, 2010
Strings are "val1" and "val2"
Strings are "val3" and "val4"
历史
- 2010 年 5 月 12 日:初始版本