Powershell文本框自动更新文件内容

z3yyvxxp  于 2023-03-08  发布在  Shell
关注(0)|答案(2)|浏览(156)

任何人都可以帮助我修复下面的代码。我在文本框中显示了一个日志文件,但当日志文件更新时,文本框不会自动更新。我尝试使用计时器,但它不会工作,因为它会刷新整个表单,输入文件名也会重置,所以它会返回空值。请转到“viewComo”函数。

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()

#2. Instantiate a Form Object
#-----------------------------------------------------------------------------------------
$Form_MAIN = New-Object system.Windows.Forms.Form;
$Form_MAIN.ClientSize  = New-Object System.Drawing.Point(1024,1000);
$Form_MAIN.StartPosition = "manual";
$Form_MAIN.Location = New-Object System.Drawing.Size(600,300);
$Form_MAIN.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#6699CC");
$Form_MAIN.text = "DataCenter Operation Applications";
$Form_MAIN.TopMost = $false;
$Form_MAIN.AutoSize = $false;
$Form_MAIN.AutoScale = $false;
$Form_MAIN.MaximizeBox = $false;

#3. Build the Form Components
#-----------------------------------------------------------------------------------------
$MainMenu = New-Object System.Windows.Forms.MenuStrip;

$Menu_File = New-Object System.Windows.Forms.ToolStripMenuItem("File");
$SubMenu_Open = New-Object System.Windows.Forms.ToolStripMenuItem("Open");
$SubMenu_Save = New-Object System.Windows.Forms.ToolStripMenuItem("Save");
$SubMenu_Exit = New-Object System.Windows.Forms.ToolStripMenuItem("Exit");

$Menu_DCApp = New-Object System.Windows.Forms.ToolStripMenuItem("DCApp");
$SubMenu_Reports_Checker = New-Object System.Windows.Forms.ToolStripMenuItem("Reports_Checker");
$SubMenu_Reports_Transfer = New-Object System.Windows.Forms.ToolStripMenuItem("Reports_Transfer");
$SubMenu_COB_Monitoring = New-Object System.Windows.Forms.ToolStripMenuItem("COB_Monitoring");
$SubMenu_AD_AccountLock_Checker = New-Object      System.Windows.Forms.ToolStripMenuItem("AD_AccountLock_Checker");

$Menu_Help = New-Object System.Windows.Forms.ToolStripMenuItem("Help");
$Menu_About = New-Object System.Windows.Forms.ToolStripMenuItem("About");

$GB_Top = New-Object system.Windows.Forms.Groupbox;
$GB_Top.height = 330;
$GB_Top.width = 990;
$GB_Top.text = "Como Checker, Please input the Service name below";
$GB_Top.location = New-Object System.Drawing.Point(15,30);
$GB_Top.Font = New-Object System.Drawing.Font('Calibri',12);

$TB_Top_Input = New-Object system.Windows.Forms.TextBox;
$TB_Top_Input.multiline = $true;
$TB_Top_Input.width = 840;
$TB_Top_Input.height = 30;
$TB_Top_Input.location = New-Object System.Drawing.Point(10,25);
$TB_Top_Input.Font = New-Object System.Drawing.Font('Calibri',10);

$TB_Top_Button = New-Object system.Windows.Forms.Button;
$TB_Top_Button.Text = "Search COMO";
$TB_Top_Button.width = 124;
$TB_Top_Button.height = 30;
$TB_Top_Button.location = New-Object System.Drawing.Point(850,25);
$TB_Top_Button.Add_Click({checkComoFiles})

$RB_LabelQuery = New-Object System.Windows.Forms.Label;
$RB_LabelQuery.Text = "Choose the Range to query:";
$RB_LabelQuery.AutoSize  = $true;
$RB_LabelQuery.width = 104;
$RB_LabelQuery.height = 10;
$RB_LabelQuery.location = New-Object System.Drawing.Point(10,55);
$RB_LabelQuery.Font = New-Object System.Drawing.Font('Callibri',9);

$RB_Option1 = New-Object system.Windows.Forms.RadioButton;
$RB_Option1.Text = "Last 1 Hour";
$RB_Option1.AutoSize  = $true;
$RB_Option1.width = 104;
$RB_Option1.height = 10;
$RB_Option1.location = New-Object System.Drawing.Point(170,55);
$RB_Option1.Font = New-Object System.Drawing.Font('Callibri',9);


$RB_Option2 = New-Object system.Windows.Forms.RadioButton;
$RB_Option2.Text = "Last 3 Hours";
$RB_Option2.AutoSize = $true;
$RB_Option2.width = 104;
$RB_Option2.height = 10;
$RB_Option2.location = New-Object System.Drawing.Point(270,55);
$RB_Option2.Font = New-Object System.Drawing.Font('Callibri',9);

$RB_Option3 = New-Object system.Windows.Forms.RadioButton;
$RB_Option3.Text = "Last 24 Hours";
$RB_Option3.AutoSize = $true;
$RB_Option3.width = 104;
$RB_Option3.height = 10;
$RB_Option3.location = New-Object System.Drawing.Point(360,55);
$RB_Option3.Font = New-Object System.Drawing.Font('Callibri',9);

$RB_LabelOutput = New-Object System.Windows.Forms.Label;
$RB_LabelOutput.Text = "Result:";
$RB_LabelOutput.AutoSize  = $true;
$RB_LabelOutput.width = 104;
$RB_LabelOutput.height = 10;
$RB_LabelOutput.location = New-Object System.Drawing.Point(10,85);
$RB_LabelOutput.Font = New-Object System.Drawing.Font('Calibri',12);

#$TB_Top_Output = New-Object system.Windows.Forms.TextBox;
#$TB_Top_Output.multiline = $true;
#$TB_Top_Output.width = 970;
#$TB_Top_Output.height = 200;
#$TB_Top_Output.location = New-Object System.Drawing.Point(10,110);

$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size=New-Object System.Drawing.Size(970,200)
$dataGridView.Location = New-Object System.Drawing.Point(10,110); 
$dataGridView.BackgroundColor = "White";

#$form.Controls.Add($dataGridView)
$dataGridView.ColumnCount = 1
$dataGridView.ColumnHeadersVisible = $true
$dataGridView.Columns[0].Name = "Como Name"
$datagridview.Columns[0].Width = 300;
$datagridview.Font = New-Object System.Drawing.Font('Courier New',9);
$datagridview.Add_CellMouseDoubleClick({viewComoGrid})

$GB_Bottom = New-Object system.Windows.Forms.Groupbox;
$GB_Bottom.height = 600;
$GB_Bottom.width = 990;
$GB_Bottom.text = "View COMO";
$GB_Bottom.location = New-Object System.Drawing.Point(15,380);
$GB_Bottom.Font = New-Object System.Drawing.Font('Calibri',12);

$TB_Bottom_Input = New-Object system.Windows.Forms.TextBox;
$TB_Bottom_Input.multiline = $true;
$TB_Bottom_Input.width = 840;
$TB_Bottom_Input.height = 30;
$TB_Bottom_Input.location = New-Object System.Drawing.Point(10,20);
$TB_Bottom_Input.Font = New-Object System.Drawing.Font('Calibri',10);

$TB_Bottom_Button = New-Object system.Windows.Forms.Button;
$TB_Bottom_Button.Text = "View COMO";
$TB_Bottom_Button.width = 124;
$TB_Bottom_Button.height = 30;
$TB_Bottom_Button.location = New-Object System.Drawing.Point(850,20);
$TB_Bottom_Button.Font = New-Object System.Drawing.Font('Calibri',10);
$TB_Bottom_Button.Add_Click({viewComo})

$RB_LabelOutput_Bottom = New-Object System.Windows.Forms.Label;
$RB_LabelOutput_Bottom.Text = "Result:";
$RB_LabelOutput_Bottom.AutoSize  = $true;
$RB_LabelOutput_Bottom.width = 104;
$RB_LabelOutput_Bottom.height = 10;
$RB_LabelOutput_Bottom.location = New-Object System.Drawing.Point(10,55);
$RB_LabelOutput_Bottom.Font = New-Object System.Drawing.Font('Calibri',12);

$TB_Top_Output_Bottom = New-Object system.Windows.Forms.TextBox;
$TB_Top_Output_Bottom.multiline = $true;
$TB_Top_Output_Bottom.size = New-Object System.Drawing.Size(970,510) 
$TB_Top_Output_Bottom.ReadOnly = $True
#$TB_Top_Output_Bottom.width = 970;
#$TB_Top_Output_Bottom.height = 510;
$TB_Top_Output_Bottom.location = New-Object System.Drawing.Size(10,80);
$TB_Top_Output_Bottom.ScrollBars = 'Both'
$TB_Top_Output_Bottom.Font = New-Object System.Drawing.Font('Courier New',9);

#4. Add the Components to the Group Boxes
#-----------------------------------------------------------------------------------------
$GB_Top.controls.AddRange(@($RB_LabelQuery, $RB_Option1,$RB_Option2,$RB_Option3,$TB_Top_Input,     $TB_Top_Button, $RB_LabelOutput, $dataGridView));
$GB_Bottom.controls.AddRange(@($L_Bottom_Output,$B_Enter, $TB_Bottom_Input, $TB_Bottom_Button,   $RB_LabelOutput_Bottom, $TB_Top_Output_Bottom  ));

#4. Add the Components to the Form
#-----------------------------------------------------------------------------------------
$Menu_File.DropDownItems.Add($SubMenu_Open);
$Menu_File.DropDownItems.Add($SubMenu_Save);
$Menu_File.DropDownItems.Add($SubMenu_Exit);

$Menu_DCApp.DropDownItems.Add($SubMenu_Reports_Checker);
$Menu_DCApp.DropDownItems.Add($SubMenu_Reports_Transfer);
$Menu_DCApp.DropDownItems.Add($SubMenu_COB_Monitoring);
$Menu_DCApp.DropDownItems.Add($SubMenu_AD_AccountLock_Checker);

$MainMenu.Items.Add($Menu_File);
$MainMenu.Items.Add($Menu_DCApp);  
$MainMenu.Items.Add($Menu_Help);
$MainMenu.Items.Add($Menu_About);

$Form_MAIN.controls.AddRange(@($MainMenu,$TB_Output));

#4.1 Add the Group Boxes to the Form
#-----------------------------------------------------------------------------------------
$Form_MAIN.controls.AddRange(@($GB_Top,$GB_Bottom));

#5. Code the Event Handlers
#-----------------------------------------------------------------------------------------
$SubMenu_Open.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  Open File."; })
$SubMenu_Save.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  Save File."; })
$SubMenu_Exit.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  Exit Program."; })

$SubMenu_Reports_Checker.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  Reports_Checker"; })
$SubMenu_Reports_Transfer.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  Reports_Transfer"; })
$SubMenu_COB_Monitoring.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  COB_Monitoring"; })
$SubMenu_AD_AccountLock_Checker.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  AD_AccountLock_Checker"; })

$Menu_DCApp.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  Options!"; })
$Menu_Help.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  Help me!"; })
$Menu_About.Add_Click({ $TB_Top_Output_Bottom.Text = "`r`n  All about ME."; })

function checkComoFiles {

$servicename= $TB_Top_Input.Text
$dataGridView.Rows.Clear()
$TB_Top_Input.Clear()

if ($RB_Option1.Checked) {
  $hours = 1

$ResultQuery = Get-ChildItem -Path C:\Users\user\Downloads\ -File | Where-Object {($_.LastWriteTime - gt (Get-Date (Get-Date).AddHours(-$hours))) } |
ForEach-Object { $_ | Select-String -List -Pattern $servicename  -SimpleMatch  |
        Select-Object -first 1 -ExpandProperty FileName}

 }

if ($RB_Option2.Checked) {
 $hours = 3

$ResultQuery = Get-ChildItem -Path C:\Users\user\Downloads\ -File | Where-Object {($_.LastWriteTime -gt (Get-Date (Get-Date).AddHours(-$hours))) } |
ForEach-Object { $_ | Select-String -List -Pattern $servicename  -SimpleMatch  |
        Select-Object -first 1 -ExpandProperty FileName}

}

if ($RB_Option3.Checked) {
 $hours = 24

$ResultQuery = Get-ChildItem -Path C:\Users\user\Downloads\ -File | Where-Object {($_.LastWriteTime -gt (Get-Date (Get-Date).AddHours(-$hours))) } |
ForEach-Object { $_ | Select-String -List -Pattern $servicename  -SimpleMatch  |
        Select-Object -first 1 -ExpandProperty FileName}

}

$dataGridView.Columns[0].Name = $servicename

$rows = @($ResultQuery)
foreach ($row in $rows)
{    
$dataGridView.Rows.Add($row)
}

}

Function viewComo { 

$comoName = $TB_Bottom_Input.Text
$openComolive = Get-Content C:\Users\user\Downloads\$comoName  
$TB_Top_Output_Bottom.Lines = $openComolive
$TB_Bottom_Input.Clear()

}

Function viewComoGrid {

$rowIndex = $datagridview.CurrentRow.Index
$columnIndex = $datagridview.CurrentCell.ColumnIndex

#Write-Host $rowIndex
#Write-Host $columnIndex 
#Write-Host $datagridview.Rows[$rowIndex].Cells[0].value
#Write-Host $datagridview.Rows[$rowIndex].Cells[$columnIndex].value

$comoNameGrid = $datagridview.Rows[$rowIndex].Cells[0].value
$openComoliveGrid = Get-Content C:\Users\user\Downloads\$comoNameGrid  

  $TB_Top_Output_Bottom.Lines = $openComoliveGrid
  $TB_Bottom_Input.Clear()

  }
#6. Display Form
#-----------------------------------------------------------------------------------------
$Form_Main.ShowDialog();

我试图使用下面的计时器在我上面的代码,但它不会工作,因为整个形式将被刷新

$timer = new-object Windows.Forms.Timer
$timer.Interval=10000
$timer.add_Tick({$TB_Top_Output_Bottom.Lines = Get-Content C:\Users\user\Downloads\$logname | Out-String;     $TB_Top_Output_Bottom.Refresh()})
$timer.Start()
x4shl7ld

x4shl7ld1#

原始版本:

这不是我想要的答案

我认为mklement 0的答案可能是一种更安全的方法,但我记得几年前阅读过关于FileSystemWatcher的文章,以前从未使用过,所以想给予一下。
找到了这个C# FileSystemWatcher answer,并想出了如何在PowerShell中重新创建工作。
发现SynchronizingObject for System.Timers.Timer的这种有趣用法似乎允许Timer的Elapsed事件在与控件或窗体相同的线程中运行,并发现FileSystemWatcher也有SynchronizingObject属性。
当使用NotePad.exe编辑和保存MyLogFile.TXT时,这似乎可以完美地工作,但当我在VSCode中加载MyLogFile.TXT时,脚本要么崩溃,要么停止工作。我认为VSCode锁定了文件,阻止脚本阅读它,但我不确定。
我想强调的是,这是一个实验,这超出了我的经验,谨慎使用

设置用于测试FileSystemWatcher的窗体的基本代码:

using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
[Application]::EnableVisualStyles()

$FilePathToWatch = $PSScriptRoot
$FileNameToWatch = "MyLogFile.TXT"
$FilePathNameToWatch = Join-Path -Path $FilePathToWatch -ChildPath $FileNameToWatch

$Form_MAIN = [Form]@{
    AutoSize = $false
    AutoScale = $false
    BackColor = '0x6699CC'
    ClientSize  = "1024,1000"
    Location = "600,300"
    MaximizeBox = $false
    StartPosition = "manual"
    Text            = "DataCenter Operation Applications"
    Topmost         = $true
}

$TextBox_Output = [TextBox]@{
    Anchor = 'Top, Left, Bottom, Right'
    Location = '12, 12'
    Multiline = $true
    Name = 'TextBox_Output'
    Size = "$($Form_MAIN.ClientSize.Width - 24), $($Form_MAIN.ClientSize.Height - 24)"
    Text = Get-Content -Raw $FilePathNameToWatch
}
$Form_Main.Controls.Add($TextBox_Output)

用于设置FileSystemWatcher的代码,以ShowDialog()结尾以打开窗体,以$watcher.Dispose()结尾以(如mklement 0所指出的)阻止$watcher在窗体关闭后继续激发。:

[IO.FileSystemWatcher]$watcher = [IO.FileSystemWatcher]@{
    Path = $FilePathToWatch
    NotifyFilter = [IO.NotifyFilters]::LastWrite
    Filter = $FileNameToWatch
    SynchronizingObject = $TextBox_Output
}
$watcher.Add_Changed({
    $TextBox_Output.Text = Get-Content -Raw $FilePathNameToWatch
})
$watcher.EnableRaisingEvents = $true

$null = $Form_Main.ShowDialog()
$watcher.Dispose()

更新版本:

浏览上述代码的变更,完整代码按顺序列于以下各节:
添加GetLogFileContent函数,用于安全阅读文件,或者在文件不存在时返回空字符串。

using namespace System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms
[Application]::EnableVisualStyles()

$FilePathToWatch = $PSScriptRoot
$FileNameToWatch = "MyLogFile.TXT"
$FilePathNameToWatch = Join-Path -Path $FilePathToWatch -ChildPath $FileNameToWatch

function GetLogFileContent {
    param ( [Parameter(Mandatory = $true, Position = 0)][string]$FilePathName )
    if(Test-Path -PathType Leaf -LiteralPath $FilePathName) { Get-Content -Raw $FilePathName } else { '' }
}

添加了窗体的FormClosing事件的代码,以关闭$watcher,使其不再激发事件并正确处置。

$Form_MAIN = [Form]@{
    AutoSize = $false
    AutoScale = $false
    BackColor = '0x6699CC'
    ClientSize  = "1024,1000"
    Location = "600,300"
    MaximizeBox = $false
    StartPosition = "manual"
    Text            = "DataCenter Operation Applications"
    Topmost         = $true
}
$Form_MAIN.Add_FormClosing({
    $watcher.Dispose()
})

TextBox Text属性被分配给GetLogFileContent函数的调用结果。

$TextBox_Output = [TextBox]@{
    Anchor = 'Top, Left, Bottom, Right'
    Location = '12, 12'
    Multiline = $true
    Name = 'TextBox_Output'
    Size = "$($Form_MAIN.ClientSize.Width - 24), $($Form_MAIN.ClientSize.Height - 24)"
    Text = GetLogFileContent $FilePathNameToWatch
}
$Form_Main.Controls.Add($TextBox_Output)

$watcher的NotifyFilter属性现在设置为检查FileNameLastWrite。提醒一下,SynchronizingObject设置为TextBox,以便可以在同一线程上更新它。

[IO.FileSystemWatcher]$watcher = [IO.FileSystemWatcher]@{
    Path = $FilePathToWatch
    NotifyFilter = [IO.NotifyFilters]::FileName -bor [IO.NotifyFilters]::LastWrite
    Filter = $FileNameToWatch
    SynchronizingObject = $TextBox_Output
}

将删除和重命名事件添加到$watcher以捕获删除和重命名,并使用GetLogFileContent函数填充文本框Text属性。

$watcher.Add_Changed({
    $TextBox_Output.Text = GetLogFileContent $FilePathNameToWatch
})
$watcher.Add_Deleted({
    $TextBox_Output.Text = ''
})
$watcher.Add_Renamed({
    $TextBox_Output.Text = if($_.Name -eq $FileNameToWatch) { GetLogFileContent $FilePathNameToWatch } else { '' }
})

Watcher.Dispose()已从代码末尾删除(在FormClosing事件中处理)。

$watcher.EnableRaisingEvents = $true
$null = $Form_Main.ShowDialog()
cgyqldqp

cgyqldqp2#

注:

  • 此答案回答了问题 * 的提问 *。
  • Darin's helpful answer展示了一种 * 替代 * 方法,该方法不会 * 无条件地、定期地 * 重新读取日志文件(基于计时器),而是使用基于事件的文件系统监视器来仅更新重新读取文件 *(如果文件发生更改 *)。
  • 这种方法更优雅,而且通常更可取,* 除非 * 日志文件更新非常频繁,在这种情况下,您需要一种节流机制(基于计时器的方法隐式提供了这种机制,尽管也可以使用文件监视器方法实现)。

您的方法 * 没有 * 明显的问题:

  • System.Windows.Forms.Timer类型在GUI线程上触发其事件,因此允许修改表单状态。
  • 虽然在.add_<eventName>()调用中作为事件委托传递的脚本块({ ... })在脚本的"子"作用域中运行,但由于PowerShell的动态作用域,您仍然可以从脚本作用域中"读取"变量。
  • 当您使用.ShowDialog()模式显示窗体时,由于WinForms控制事件循环,因此将新内容赋给文本框控件的.Lines属性就足够了:控件应该 * 自动 * 刷新(甚至显式的.Refresh()调用也 * 不 * 刷新 * 整个窗体 *)。

以下是一个独立的概念验证:

  • 启动一个后台作业,每秒将当前时间戳写入给定文件。
  • WinForms代码使用一个计时器事件读取该文件并使用它更新其多行文本框控件。
using namespace System.Windows.Forms
using namespace System.Drawing

Add-Type -AssemblyName System.Windows.Forms

# Create the form.
$form = [Form] @{
  Text = "Textbox Timer-Based Refresh Demo"
  Size = [Size]::new(380,200)
  StartPosition = "CenterScreen"
}

# Create the textbox and add it to the form.
$form.Controls.AddRange(@(
  ($textBox = [TextBox] @{
    Location = [Point]::new(10, 10)
    Size = [Size]::new(320, 90)
    MultiLine = $true
  })
))

# Create a timer that fires every second, and
# reads the then-current file content.
$timer = [Timer]::new()
$timer.InterVal = 1000
$timer.add_Tick({
  $textBox.Lines = Get-Content -Raw $logName
})
$timer.Start()

# The log file to read.
$logName = 't.txt'

# Create a background job that updates the log file every second.
$jb = Start-Job {
  while ($true) {
    Get-Date > "$using:PWD/$using:logName"
    Start-Sleep 1
  }
}

# Show the form modally.
$null = $form.ShowDialog()

# Clean up
$timer.Dispose()
$jb | Remove-Job -Force

相关问题