65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET Setup API 封装器(解析 INF、OEM 文件)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.42/5 (9投票s)

2010年5月13日

MIT

5分钟阅读

viewsIcon

28860

downloadIcon

699

用于使用 Setup API 解析 driver.INF 和 txtsetup.OEM 的 .NET 类

目录

引言

Setup API 是解析driver.INFtxtsetup.OEM文件的推荐方法。不幸的是,Setup API 是一系列 C 风格函数,.NET Framework 没有等效的接口。本文提供了一些简单的 .NET (C#) 类,它们封装了 Setup API 中用于解析 INF 文件的常用函数。

背景

我曾需要解析由数十家第三方公司和个人编写的上百个driver.INFtxtsetup.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

下载包含两个您将直接使用的类:InfFileInfLine

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 LastMessageLastError 是上次调用的方法的 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 日:初始版本
© . All rights reserved.