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

二进制检查器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (14投票s)

2016年1月27日

CPOL

4分钟阅读

viewsIcon

42579

downloadIcon

1862

一个可以以简单方式打开、查看和编辑二进制文件的 Windows 窗体应用程序

引言

Binary Inspector 是一个简单的 Windows 窗体应用程序,可以打开二进制文件并以清晰的方式显示其内容。它还允许编辑二进制文件。由于它不是为大文件设计的,因此它仅限于读取大小不超过 10KB 的文件。我见过类似的工具通过查找文本并提供翻译旧电子游戏模拟器 ROM 文件的方法来实现这一点。我认为这个应用程序可能会成为这样一个工具。

下载它! 或获取 源代码
要求:Windows Vista*、Seven、Eight、10。
* 必须安装 .NET Framework 4.5。

背景

五年前,我写了一篇关于 多基数编辑器组件 的文章。我很快就开发了这个应用程序。当时,我被要求打开和读取一个二进制文件,但我找不到一个免费的工具能够如此简单地做到这一点。我还想要我想要检查的每个字节的地址。最后,如果我能以不同的视图打开和检查文件,为什么不允许编辑它呢?我可以在其中添加我的多基数编辑器并使其发挥作用。

我很遗憾没有早点写这个。最近,我决定恢复我的旧代码和项目,并让它们复活。该项目最初使用 Visual Studio 2010 Express、Windows Forms 和 .NET Framework 3.5 开发,现在我正在使用 Visual Studio 2015 Community 将其更新到 .NET Framework 4.0。我想重点介绍我使用的一些有趣的片段。完整的源代码可供下载。下图 1 展示了它的外观

Figure 1: Main window

图 1

最初的想法是保持简单易用。现在我看到有其他工具可以做到这一点,我会提到 HexEdit。我认为它是一个功能齐全的工具。

Using the Code

核心功能是将字节列表显示为数字(十进制、十六进制、二进制或八进制)。要做到这一点,首先要做的是打开一个文件。任何类型的文件都可以,它可以是图像文件或文本文件。下面的代码片段描述了文件是如何打开的。文件内容存储在我称之为 bytes 的数组变量中,然后调用 displayBytes() 方法。每当需要将二进制数据渲染到文本框时,就会调用 displayBytes()

	if (dialogResult == DialogResult.OK) {
	  if (File.Exists(openFileDialog1.FileName)) {
		fileStream = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read);
		// Limits the size of files this application can open and display.
		if (fileStream.Length < 10000) {
			bytes = ReadFully(fileStream);

			displayBytes();
			// ...
		}
		else
		  txtBinary.Text = "File is too large";
	  }
	}

通过一些数学和逻辑,我在文本框中显示二进制数据。用户可以设置一些选项,包括:以十六进制显示字节、显示提示、使用换行符、尝试转换为 ASCII 字符、鼠标移过时显示信息以及鼠标移过时自动选择。为了进行渲染,下面的代码完成了所有魔法。

    private void displayBytes() {
      char validChar;
      int padlef = cbHexadecimal.Checked ? 2 : 3;

      txtBinary.Clear();

      if (bytes == null)
        return;

      StringBuilder text = new StringBuilder();
      for (int i = 0; i < bytes.Length; i++) {

        if (UseBytesPerLine && i != 0 && (i % options.BytesPerLine == 0))
          text.AppendLine();

        if (options.ConvertCharacter)
          if (tryGetChar(bytes[i], out validChar)) {
            text.Append(validChar.ToString().PadLeft(padlef));
            text.Append(" ");
            continue;
          }

        if (cbHexadecimal.Checked) {
          text.Append(bytes[i].ToString("X").PadLeft(2, '0'));
        }
        else
          text.Append(bytes[i].ToString().PadLeft(3, ' '));

        text.Append(" ");
      }
      txtBinary.Text = text.ToString();

      updateAddress();
    }

之后,调用 updateAddress() 来填充每行的第一个字节地址位置。这一切都是根据用户设置的选项计算的。查看下面的代码是如何完成的。

    private void updateAddress() {
		txtAddress.Clear();      
		int height;
		height = 0;
		int charIndex = txtBinary.GetCharIndexFromPosition(new Point(0, height));
		int charIndexFromLastLine = txtBinary.GetCharIndexFromPosition
						(new Point(0, txtBinary.Height - 2));
		int lastLine = txtBinary.GetLineFromCharIndex(charIndexFromLastLine);
        int breaklineCount = Environment.NewLine.Length;
		StringBuilder addresses = new StringBuilder();
		for(int i = 0; i < lastLine - 1; i++) {
		  height = txtBinary.Font.Height * i + 1;
		  charIndex = txtBinary.GetCharIndexFromPosition(new Point(1, height));
          if(options.UseBreakLine)
            charIndex -= breaklineCount * i; // Do not count the breakline characters
		  int calculatedByteIndex = -1;
		  if (options.DisplayHexadecimal) {
			// When displaying hexadecimal every byte occupy up to 2 characters 0 - FF.
			// Between every byte there is a space. Based on this information,
                        // the index on the byte array for the char index from the text area will be:
			calculatedByteIndex = charIndex / 3;
		  }
		  else {
			calculatedByteIndex = charIndex / 4;
		  }
			addresses.AppendLine((calculatedByteIndex).ToString("X"));
		  
		} // for        
        txtAddress.Text = addresses.ToString();
    }

文本框的 GetCharIndexFromPosition 方法用于给定屏幕相对点获取当前字符位置。请注意,GetCharIndexFromPosition 方法将换行符视为字符,因此需要调整 charIndex 值。文本框的字体高度用于定义每行的高度。地址仅针对可见文本计算。如果查看器显示十六进制值,它会计算每个字节使用 3 个字符,否则使用 4 个字符。然后可以计算字节数,如果我获取每行的第一个字节的位置(charIndex)并除以 34(取决于它显示的是十进制还是十六进制值),我就可以获得每行的地址。

除此之外,我还使用了一些资源来添加改进 GUI 的功能。我认为在这里详细介绍所有这些会过于冗长。我只在下面指出它们。如果你是一名学生,并且正在尝试学习如何做这些事情,请查看源代码以了解其实现方式。如果你有任何疑问,请随时与我联系。

关注点

  • 我添加了 model.Options 以便使用用户控件的 DataBindings。这些选项定义了应用程序的行为和数据呈现方式。用户可以在名为 OptionsView 的窗体中更改这些选项。
  • 这些选项被加载并存储到位于特殊文件夹 Application.LocalUserAppDataPath 的用户设置文件中。
  • 为大多数 GUI 组件添加了工具提示,用户可以打开或关闭它们。
  • 有一个选项可以在鼠标指针悬停的字节的 StatusTrip 中显示当前字节信息。
  • 双击字节位置会显示 EditByte 窗体,该窗体使用多基数编辑器组件,并允许用户更改所选字节。
  • 使用 Doxygen 工具生成了文档。

未来工作

  • 升级到 .NET Framework 4.6.1
  • 添加用户帮助工具(问号)
  • 使用资源文件添加多语言支持
  • 为 Web 应用程序创建类似的控件

历史

  • 2010 - 该代码首次编写
  • 2016 - 我决定更新、发布并撰写有关它的文章
© . All rights reserved.