C# 程序,实现二进制文件十六进制查看器,支持按行定位

主窗体代码 (Form1.cs)

cs 复制代码
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace HexViewer
{
    public partial class Form1 : Form
    {
        private byte[] fileData;
        private int bytesPerLine = 16;
        private int currentPosition = 0;
        private int totalLines = 0;

        // 控件声明
        private MenuStrip menuStrip;
        private ToolStripMenuItem fileMenu;
        private ToolStripMenuItem openMenuItem;
        private ToolStripMenuItem exitMenuItem;
        private ToolStripMenuItem viewMenu;
        private ToolStripMenuItem bytesPerLineMenu;
        private ToolStripMenuItem bytes8MenuItem;
        private ToolStripMenuItem bytes16MenuItem;
        private ToolStripMenuItem bytes32MenuItem;
        private RichTextBox hexRichTextBox;
        private TextBox lineNumberTextBox;
        private Button goToButton;
        private Label statusLabel;
        private Panel topPanel;
        private Label lineLabel;
        private OpenFileDialog openFileDialog;

        public Form1()
        {
            InitializeComponent();
            InitializeCustomComponents();
        }

        private void InitializeCustomComponents()
        {
            // 窗体设置
            this.Text = "十六进制文件查看器";
            this.Size = new System.Drawing.Size(1000, 700);
            this.StartPosition = FormStartPosition.CenterScreen;

            // 菜单栏
            menuStrip = new MenuStrip();
            
            fileMenu = new ToolStripMenuItem("文件");
            openMenuItem = new ToolStripMenuItem("打开", null, OpenFile_Click);
            openMenuItem.ShortcutKeys = Keys.Control | Keys.O;
            exitMenuItem = new ToolStripMenuItem("退出", null, (s, e) => Application.Exit());
            fileMenu.DropDownItems.Add(openMenuItem);
            fileMenu.DropDownItems.Add(new ToolStripSeparator());
            fileMenu.DropDownItems.Add(exitMenuItem);
            
            viewMenu = new ToolStripMenuItem("查看");
            bytesPerLineMenu = new ToolStripMenuItem("每行字节数");
            bytes8MenuItem = new ToolStripMenuItem("8 字节", null, (s, e) => SetBytesPerLine(8));
            bytes16MenuItem = new ToolStripMenuItem("16 字节", null, (s, e) => SetBytesPerLine(16));
            bytes32MenuItem = new ToolStripMenuItem("32 字节", null, (s, e) => SetBytesPerLine(32));
            bytes16MenuItem.Checked = true;
            bytesPerLineMenu.DropDownItems.Add(bytes8MenuItem);
            bytesPerLineMenu.DropDownItems.Add(bytes16MenuItem);
            bytesPerLineMenu.DropDownItems.Add(bytes32MenuItem);
            viewMenu.DropDownItems.Add(bytesPerLineMenu);
            
            menuStrip.Items.Add(fileMenu);
            menuStrip.Items.Add(viewMenu);
            
            // 顶部面板
            topPanel = new Panel();
            topPanel.Dock = DockStyle.Top;
            topPanel.Height = 40;
            topPanel.Padding = new Padding(5);
            
            lineLabel = new Label();
            lineLabel.Text = "跳转到行:";
            lineLabel.Location = new System.Drawing.Point(5, 10);
            lineLabel.Size = new System.Drawing.Size(70, 25);
            
            lineNumberTextBox = new TextBox();
            lineNumberTextBox.Location = new System.Drawing.Point(80, 8);
            lineNumberTextBox.Size = new System.Drawing.Size(100, 25);
            lineNumberTextBox.KeyPress += LineNumberTextBox_KeyPress;
            
            goToButton = new Button();
            goToButton.Text = "跳转";
            goToButton.Location = new System.Drawing.Point(190, 7);
            goToButton.Size = new System.Drawing.Size(75, 28);
            goToButton.Click += GoToButton_Click;
            
            topPanel.Controls.Add(lineLabel);
            topPanel.Controls.Add(lineNumberTextBox);
            topPanel.Controls.Add(goToButton);
            
            // 十六进制显示区域
            hexRichTextBox = new RichTextBox();
            hexRichTextBox.Dock = DockStyle.Fill;
            hexRichTextBox.Font = new System.Drawing.Font("Consolas", 10f);
            hexRichTextBox.WordWrap = false;
            hexRichTextBox.KeyDown += HexRichTextBox_KeyDown;
            
            // 状态栏
            statusLabel = new Label();
            statusLabel.Dock = DockStyle.Bottom;
            statusLabel.Height = 25;
            statusLabel.Text = "就绪 - 使用 Ctrl+O 打开文件";
            statusLabel.Padding = new Padding(5, 0, 0, 0);
            
            // 添加控件
            this.Controls.Add(hexRichTextBox);
            this.Controls.Add(topPanel);
            this.Controls.Add(statusLabel);
            this.Controls.Add(menuStrip);
            
            this.MainMenuStrip = menuStrip;
            
            // 文件对话框
            openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "选择二进制文件";
            openFileDialog.Filter = "所有文件 (*.*)|*.*";
        }

        private void SetBytesPerLine(int bytes)
        {
            bytesPerLine = bytes;
            bytes8MenuItem.Checked = (bytes == 8);
            bytes16MenuItem.Checked = (bytes == 16);
            bytes32MenuItem.Checked = (bytes == 32);
            
            if (fileData != null)
            {
                DisplayHexData(currentPosition);
            }
        }

        private void OpenFile_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    fileData = File.ReadAllBytes(openFileDialog.FileName);
                    currentPosition = 0;
                    totalLines = (fileData.Length + bytesPerLine - 1) / bytesPerLine;
                    DisplayHexData(0);
                    statusLabel.Text = $"已加载: {openFileDialog.FileName} | 大小: {fileData.Length:N0} 字节 | 总行数: {totalLines:N0}";
                    this.Text = $"十六进制文件查看器 - {Path.GetFileName(openFileDialog.FileName)}";
                    lineNumberTextBox.Text = "1";
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"打开文件失败: {ex.Message}", "错误", 
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void DisplayHexData(int startPosition)
        {
            if (fileData == null || fileData.Length == 0)
            {
                hexRichTextBox.Text = "";
                return;
            }

            // 确保起始位置有效
            if (startPosition < 0) startPosition = 0;
            if (startPosition >= fileData.Length) startPosition = fileData.Length - 1;
            
            // 计算从哪一行开始显示
            int startLine = startPosition / bytesPerLine;
            int startOffset = startLine * bytesPerLine;
            
            // 计算显示的行数(基于窗口高度)
            int linesToShow = (hexRichTextBox.Height / 20) + 2; // 估算每行高度
            if (linesToShow < 20) linesToShow = 30;
            
            StringBuilder sb = new StringBuilder();
            
            for (int i = 0; i < linesToShow; i++)
            {
                int offset = startOffset + i * bytesPerLine;
                if (offset >= fileData.Length) break;
                
                // 行号 (8位十六进制)
                sb.Append(offset.ToString("X8"));
                sb.Append("  ");
                
                // 十六进制部分
                int bytesInThisLine = Math.Min(bytesPerLine, fileData.Length - offset);
                for (int j = 0; j < bytesPerLine; j++)
                {
                    if (j < bytesInThisLine)
                    {
                        sb.Append(fileData[offset + j].ToString("X2"));
                    }
                    else
                    {
                        sb.Append("  ");
                    }
                    
                    if (j == bytesPerLine / 2 - 1)
                        sb.Append("  "); // 中间分隔
                    else
                        sb.Append(" ");
                }
                
                sb.Append(" ");
                
                // ASCII 部分
                for (int j = 0; j < bytesInThisLine; j++)
                {
                    byte b = fileData[offset + j];
                    char c = (b >= 32 && b <= 126) ? (char)b : '.';
                    sb.Append(c);
                }
                
                sb.AppendLine();
            }
            
            hexRichTextBox.Text = sb.ToString();
            
            // 高亮当前光标所在行
            HighlightCurrentLine();
            
            currentPosition = startOffset;
            UpdateStatus();
        }

        private void HighlightCurrentLine()
        {
            if (hexRichTextBox.Text.Length == 0) return;
            
            int currentLine = currentPosition / bytesPerLine;
            
            // 简单的行高亮:通过选择行文本
            int startIndex = 0;
            for (int i = 0; i < currentLine; i++)
            {
                startIndex = hexRichTextBox.Text.IndexOf('\n', startIndex) + 1;
                if (startIndex <= 0) return;
            }
            
            int endIndex = hexRichTextBox.Text.IndexOf('\n', startIndex);
            if (endIndex == -1) endIndex = hexRichTextBox.Text.Length;
            
            hexRichTextBox.SelectionStart = startIndex;
            hexRichTextBox.SelectionLength = endIndex - startIndex;
            hexRichTextBox.SelectionBackColor = System.Drawing.Color.LightYellow;
            
            // 滚动到可见位置
            hexRichTextBox.ScrollToCaret();
        }

        private void GoToLine(int lineNumber)
        {
            if (fileData == null)
            {
                MessageBox.Show("请先打开文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            
            if (lineNumber < 1 || lineNumber > totalLines)
            {
                MessageBox.Show($"行号必须在 1 到 {totalLines} 之间", "无效行号", 
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            
            int newPosition = (lineNumber - 1) * bytesPerLine;
            if (newPosition >= fileData.Length)
                newPosition = fileData.Length - 1;
            
            currentPosition = newPosition;
            DisplayHexData(currentPosition);
            lineNumberTextBox.Text = lineNumber.ToString();
        }

        private void GoToOffset(int offset)
        {
            if (fileData == null) return;
            
            if (offset < 0) offset = 0;
            if (offset >= fileData.Length) offset = fileData.Length - 1;
            
            currentPosition = offset;
            DisplayHexData(currentPosition);
            lineNumberTextBox.Text = ((currentPosition / bytesPerLine) + 1).ToString();
        }

        private void UpdateStatus()
        {
            if (fileData != null)
            {
                int currentLine = currentPosition / bytesPerLine + 1;
                int currentByte = currentPosition;
                statusLabel.Text = $"行: {currentLine:N0} / {totalLines:N0} | " +
                                  $"偏移: 0x{currentPosition:X8} ({currentPosition:N0}) | " +
                                  $"字节: {currentByte + 1} / {fileData.Length:N0}";
            }
        }

        private void GoToButton_Click(object sender, EventArgs e)
        {
            if (int.TryParse(lineNumberTextBox.Text, out int lineNumber))
            {
                GoToLine(lineNumber);
            }
            else
            {
                // 尝试解析为十六进制偏移
                string input = lineNumberTextBox.Text.Trim();
                if (input.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
                {
                    if (int.TryParse(input.Substring(2), System.Globalization.NumberStyles.HexNumber, null, out int offset))
                    {
                        GoToOffset(offset);
                        return;
                    }
                }
                else if (int.TryParse(input, System.Globalization.NumberStyles.HexNumber, null, out int hexOffset))
                {
                    GoToOffset(hexOffset);
                    return;
                }
                
                MessageBox.Show("请输入有效的行号 (1-" + totalLines + ") 或十六进制偏移 (如 0x100)", 
                    "无效输入", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        private void LineNumberTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Enter)
            {
                GoToButton_Click(sender, e);
                e.Handled = true;
            }
        }

        private void HexRichTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (fileData == null) return;
            
            int currentLine = currentPosition / bytesPerLine;
            
            switch (e.KeyCode)
            {
                case Keys.Up:
                    if (currentLine > 0)
                    {
                        GoToLine(currentLine);
                    }
                    e.Handled = true;
                    break;
                    
                case Keys.Down:
                    if (currentLine < totalLines - 1)
                    {
                        GoToLine(currentLine + 2);
                    }
                    e.Handled = true;
                    break;
                    
                case Keys.PageUp:
                    int pageLines = (hexRichTextBox.Height / 20);
                    int newLine = Math.Max(0, currentLine - pageLines);
                    GoToLine(newLine + 1);
                    e.Handled = true;
                    break;
                    
                case Keys.PageDown:
                    pageLines = (hexRichTextBox.Height / 20);
                    newLine = Math.Min(totalLines - 1, currentLine + pageLines);
                    GoToLine(newLine + 1);
                    e.Handled = true;
                    break;
                    
                case Keys.Home:
                    GoToLine(1);
                    e.Handled = true;
                    break;
                    
                case Keys.End:
                    GoToLine(totalLines);
                    e.Handled = true;
                    break;
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (fileData != null)
            {
                DisplayHexData(currentPosition);
            }
        }
    }
}

入口点代码 (Program.cs)

cs 复制代码
using System;
using System.Windows.Forms;

namespace HexViewer
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

功能说明

功能 说明
打开文件 Ctrl+O 或菜单"文件→打开"
十六进制显示 每行固定字节数,左右对齐
ASCII 显示 右侧显示可打印字符,不可打印显示为点号
行号显示 左侧显示文件偏移地址(十六进制)
跳转到行 输入行号或十六进制偏移(如 0x100)后跳转
键盘导航 ↑↓ 上一行/下一行,PgUp/PgDn 翻页,Home/End 首尾
当前行高亮 当前所在行背景高亮显示
每行字节数 支持 8/16/32 字节每行(查看菜单)

运行界面

相关推荐
咕噜企业签名分发-淼淼1 小时前
浅谈云服务器在后端托管与签名分发场景中的应用价值
开发语言·php
大卡片1 小时前
IDE软件实现注意事项
单片机
在繁华处1 小时前
Java从零到熟练(八):泛型与注解
java·开发语言·python
SilentSamsara1 小时前
命令行工具开发:Click/Typer + 打包为独立二进制
linux·服务器·开发语言·前端·python·青少年编程·fastapi
m0_377108141 小时前
stm32-mpu6050
stm32·单片机·嵌入式硬件
Ulyanov1 小时前
深入QML滑块与进度控制:构建动态数据可视化界面:QML+PySide6现代开发入门(六)
开发语言·python·算法·ui·信息可视化·雷达电子对抗仿真
zyl837211 小时前
Python 函数、模块、异常处理 超详细入门教程
开发语言·windows·python
苏州IT威翰德1 小时前
苏州IT基础架构IQ/OQ/PQ确认服务 | 服务器网络验证
开发语言·php
析木不会编程1 小时前
单片机|嵌入式硬件设计--电阻选型
单片机·嵌入式硬件