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

使用文本文件和 fileSystemWatcher 的状态报告器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2012年10月18日

CPOL

4分钟阅读

viewsIcon

23704

downloadIcon

682

报告另一个程序的缓冲区消息发送到文本文件的工具

引言

该程序旨在方便地报告另一个程序的活动进度或状态。
它基于以下前提:被监视的程序将把编号的状态消息写入文本文件。
它使用 FileSystemWatcher 检测文件中的更改,读取消息并在对话框中显示它们。 我首先尝试了该程序,主程序一次只向消息文本文件写入一条消息。 我很快发现这很脆弱,因为真正快速的操作无法以足够快的速度写入文件。 所以我尝试让主程序保留最后 20 条消息的列表,并尝试每次写入文件
生成新消息时。 如果几次写入失败,则不成问题,因为下一次写入将包含错过的消息。 监视程序在每次更改时都会读取文件,并根据消息编号过滤掉过去的消息,然后显示新的消息。 报告对话框在一段时间延迟后关闭,或者如果消息文本文件被删除。 这些细节使 这个工具更有价值。 它可以让您使用用任何其他语言编写的程序,也许是一个不容易支持多线程的程序,并且可靠地看到某些活动的进度报告。

背景

我编写此程序是为了帮助使用为 Autodesk 的 AutoCad 程序编写的程序。 在 Windows XP 上很好地显示进度条的工具在 Windows 7 上的行为有所不同,因此我必须做一些事情来处理长时间运行的进程中发生的 UI "阻塞"。 AutoCad 工具是用 AutoLisp 编写的,我特别希望有一个外部进程来执行报告,因此没有任何阻塞的机会。

Using the Code

要使用状态监视器,您将运行 StatusByFileWatch.exe 作为一个外部进程。 它接受两个命令行参数,按此顺序

  • 完整的文本文件名
  • 对话框在没有活动发生时自动关闭的延迟时间,以微秒为单位

演示程序是用 C# 编写的,但这里的原则适用于任何语言。 在任何需要监视其消息的程序中,设置以下变量:

  • 完整的文本文件名
  • StatusByFileWatch.exe 的完整文件名
  • 消息缓冲区列表
  • 缓冲区长度
string _exeLoc = 
  @"C:\+Storage\Programming\DotNet\StatusByFileWatch\bin\Release\StatusByFileWatch.exe"; //modify 
                                                                                         //as needed
string _txtFilename = Environment.GetEnvironmentVariable("TEMP") + @"\Logfile for Demo.txt";
List<string> _msgs = new List<string>();
int _bufflen = 20;    

然后,无论您有哪个需要监视消息的进程,您都将

  • 删除 textfile
  • 清除消息列表变量
  • 使用 writeStatusMsg 函数将新消息写入文本文件
  • 使用所需的参数启动 StatusByFileWatch.exe
  • 使用 WriteStatusMsg 在某个进程进行时添加消息
  • 完成后删除文本文件(或者什么都不做,让对话框根据关闭延迟参数自行关闭)
private void button1_Click(object sender, EventArgs e) {
   try {
    File.Delete(_txtFilename);
   }
   catch { }
   _msgs = new List<string>();
   writeStatusMsg("Starting Loop, be patient....");
   Thread.Sleep(2000);
   //run the watcher exe
   try {
    System.Diagnostics.Process.Start(_exeLoc, "\"" + _txtFilename + "\" 7000");
   }
   catch { }
   //run a loop that does enough work to block the UI from updating
   for (int i = 1; i < 30; i++) {
    //write message to the text file
    writeStatusMsg("Doing Loop " + i.ToString() + " be patient....");
    Thread.Sleep(500);
   }
   //delete message file to close watcher dialog
   try {
    File.Delete(_txtFilename);
   }
   catch { }
  }  

请注意,如果消息列表长度超出限制,这会自动截断第一条消息。

private void writeStatusMsg (string msg) {
   //add to message list, but do not exceed buffer
   if (_msgs.Count >= _bufflen)
    _msgs.RemoveAt(0);
   //add message
   _msgs.Add(_msgIndex.ToString() + "," + msg);
   _msgIndex++;
   //try to write to file
   try {
    TextToFile(_txtFilename, _msgs.ToArray());
   }
   catch { }
  }

TextToFile 函数在演示代码中。

每种语言都将有用于启动外部 EXE、写入和删除文本文件的工具,因此此模式可以应用于任何语言。

关注点

一个非常重要的一点是,监视器必须以不阻止被监视程序向文件写入的方式读取文本文件。 此函数可以很好地做到这一点

public static bool TextFiletoList(string fName, out List<string> fileLines) {
   fileLines = new List<string>();
   if (File.Exists(fName)) {
    FileStream fs = new FileStream(fName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    using (StreamReader sReader = new StreamReader(fs)) {
     string text = sReader.ReadLine();
     while (text != null) {
      fileLines.Add(text);
      text = sReader.ReadLine();
     }

    }
   }
   if (fileLines.Count > 0) { return true; }
   else { return false; }
  }

创建 FileStream 的行至关重要。

可以进行许多改进和自定义,例如在消息对话框中显示进度条。 如果您在消息中建立了某些关键字,除了仅仅显示消息之外,您还可以做各种事情。 将其推向极致,您本质上是在序列化您希望对话框处于的状态,并且对话框会在每次发布状态时更新。

关于这方面棘手的部分是将其与其他可能没有等待使用的反序列化函数的语言混合使用。 我不得不为 AutoLisp 编写自己的函数,因为 AutoCad 和 .NET 之间默认的通信方式(称为 resultbuffer...)在数据来回传递时会对您的数据做一些不友好的事情。

尽管如此,这个程序并没有变得复杂,只是在文本文件中使用了简单的编号消息。

历史

  • 2012 年 10 月 18 日:首次发布
© . All rights reserved.