Visual Studio给Powershell 程序创建可视化界面

豆子经常写些Powershell的小脚本用来管理Windows服务器。有的时候我希望能够把这些小工具分享给同事,不过有些同事对于Powershell一窍不通,对于脚本控制台界面更是深恶痛绝,如果有些傻瓜化的图像界面便于操作就好了。豆子在网上做了些小研究,写了个简单的测试界面,下面分享一下心得。

网上有一些第三方的付费工具可以直接进行GUI的开发,穷人舍不得花钱,豆子用的是微软的Visual Studio 2015的社区版。这个是版本是免费下载的,下载安装很费时间,大概花了2个小时。这个是下载链接  https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx

简单的说,我需要在visual studio 上设计图形界面,生成对应的XAMl文件。XAML是基于XML的语言,一般用来定义Windows的图像界面。如果能想办法在Powershell里面识别出XAML文件的元素,Powershell就可以直接生成对应的图像界面。每一个元素对象都有自己的属性和方法,因此我可以直接对他们进行操作。

这篇博文微软官方提供了一个很好的脚本对XAML文件进行翻译,并进行了详细解释

http://blogs.technet.com/b/heyscriptingguy/archive/2014/08/01/i-39-ve-got-a-powershell-secret-adding-a-gui-to-scripts.aspx

下面这个博文提供了一个模板,他基于上一篇的脚本进行了加工,操作变得更简单。我们需要的就是生成XAMI文件,然后拷贝到其对应的字符串里,对于这个字符串的解析就不用自己操心了。

http://foxdeploy.com/2015/04/10/part-i-creating-powershell-guis-in-minutes-using-visual-studio-a-new-hope/

下面来个实际的例子

打开visual studio,然后新建一个项目,选择WPF

然后根据自己的需求,打开工具栏,拖曳成自己想要的界面。

比如说,下面是豆子的练习之作,我希望查询近期AD账户被锁的记录,同时可以一键解锁所有被锁账户。下面用到的几个工具是label, button, image 和listViewer,如果用过任何开发工具的人应该都很熟悉这些。

注意当我在主界面上拿鼠标拖曳这些图像控件的时候,他会自动生成对应的XAML文件

复制这些内容,然后粘贴到下面的Powershell 模板文件的@@符号之间里面。 如下所示

#ERASE ALL THIS AND PUT XAML BELOW between the @" "@ 
$inputXML = @"
<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication3"
        mc:Ignorable="d"
        Title="UnlockTool" Height="350" Width="525" Background="#FFCDDFEC">
    <Grid>
        <Label x:Name="label" Content="Click the Button to query LockOut history" HorizontalAlignment="Left" Margin="18,10,0,0" VerticalAlignment="Top" Width="292"/>
        <Button x:Name="button" Content="Query" HorizontalAlignment="Left" Margin="27,56,0,0" VerticalAlignment="Top" Width="75"/>
        <Button x:Name="button1" Content="Unlock" HorizontalAlignment="Left" Margin="27,260,0,0" VerticalAlignment="Top" Width="75"/>
        <ListView x:Name="listView" HorizontalAlignment="Left" Height="140" Margin="27,99,0,0" VerticalAlignment="Top" Width="454">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="UserName" DisplayMemberBinding ="{Binding ‘username‘}" Width="160"/>
                    <GridViewColumn Header="LockOut Computer" DisplayMemberBinding ="{Binding ‘computer‘}" Width="160"/>
                    <GridViewColumn Header="LockOut Time" DisplayMemberBinding ="{Binding ‘time‘}" Width="160"/>
                </GridView>
            </ListView.View>
        </ListView>
        <Image x:Name="image" HorizontalAlignment="Left" Height="75" Margin="372,10,0,0" VerticalAlignment="Top" Width="95" Source="c:\temp\unlock.png"/>
    </Grid>
</Window>
"@        
$inputXML = $inputXML -replace ‘mc:Ignorable="d"‘,‘‘ -replace "x:N",‘N‘  -replace ‘^<Win.*‘, ‘<Window‘
[void][System.Reflection.Assembly]::LoadWithPartialName(‘presentationframework‘)
[xml]$XAML = $inputXML
#Read XAML
    $reader=(New-Object System.Xml.XmlNodeReader $xaml) 
  try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Actually make the objects work
#===========================================================================

#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan
function Show-Form{
$Form.ShowDialog() | out-null
}
Show-Form

如果直接执行上面的代码的话,Powershell ISE 会生成对应的图像界面,并且在控制台输出当前的元素对象,这些解析出来的元素对象是可以直接在powershell里面操作的。

界面有了,接下来我需要添加真正的操作代码。

首先自己写一个方法从PDC上获取4740日志,这个需要事先在GPO上打开审计功能。对其XML的内容格式进行分析之后,进行输出,获取自己需要的内容。这里我需要知道用户名,从哪里锁住的,以及锁住的时间。

关于Windows 日志处理,可以参考我的另外一篇博文

http://beanxyz.blog.51cto.com/5570417/1695288

function get-lockout{
$eventcritea = @{logname=‘security‘;id=4740}
$Events =get-winevent -ComputerName (Get-ADDomain).pdcemulator -FilterHashtable $eventcritea 
#$Events = Get-WinEvent -ComputerName syddc01 -Filterxml $xmlfilter        
            
# Parse out the event message data            
ForEach ($Event in $Events) {    
      
    # Convert the event to XML            
    $eventXML = [xml]$Event.ToXml()    
          
    # Iterate through each one of the XML message properties            
    For ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) { 
     
            
        # Append these as object properties            
        Add-Member -InputObject $Event -MemberType NoteProperty -Force -Name  $eventXML.Event.EventData.Data[$i].name -Value $eventXML.Event.EventData.Data[$i].‘#text‘            
    }            
}            
$events | select  @{n=‘UserName‘;e={$_.targetusername}},@{n=‘Computer‘;e={$_.targetdomainname}},@{n=‘time‘;e={$_.timecreated}}
}

接下来写事件的函数,比如单击第一个按钮,自动调用get-lockout方法,通过管道进行输出;第二个按钮直接解锁所有的用户。

$WPFbutton.add_click({
get-lockout | %{$WPFlistView.AddChild($_)}
}
  )
    
$WPFbutton1.add_click(
{
Search-ADAccount -LockedOut | ForEach-Object {Unlock-ADAccount -Identity $_.distinguishedname -PassThru }
})

注意第一个按钮的事件,我这里试图一行一行的在GridView里面添加数据,默认情况下他并不知道哪里数据需要添加,而是会把所有的数据都加在每一格子的数据里面,因此我们需要在前面的Grid定义里面手动绑定一下,注意DisplayMemberBinding 里面绑定的是我自定义的输出字段的名字

<ListView.View>
                <GridView>
                    <GridViewColumn Header="UserName" DisplayMemberBinding ="{Binding ‘username‘}" Width="160"/>
                    <GridViewColumn Header="LockOut Computer" DisplayMemberBinding ="{Binding ‘computer‘}" Width="160"/>
                    <GridViewColumn Header="LockOut Time" DisplayMemberBinding ="{Binding ‘time‘}" Width="160"/>
                </GridView>
            </ListView.View>

运行结果如下所示

下面是完整的脚本:

#ERASE ALL THIS AND PUT XAML BELOW between the @" "@ 
$inputXML = @"
<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication3"
        mc:Ignorable="d"
        Title="UnlockTool" Height="350" Width="525" Background="#FFCDDFEC">
    <Grid>
        <Label x:Name="label" Content="Click the Button to query LockOut history" HorizontalAlignment="Left" Margin="18,10,0,0" VerticalAlignment="Top" Width="292"/>
        <Button x:Name="button" Content="Query" HorizontalAlignment="Left" Margin="27,56,0,0" VerticalAlignment="Top" Width="75"/>
        <Button x:Name="button1" Content="Unlock" HorizontalAlignment="Left" Margin="27,260,0,0" VerticalAlignment="Top" Width="75"/>
        <ListView x:Name="listView" HorizontalAlignment="Left" Height="140" Margin="27,99,0,0" VerticalAlignment="Top" Width="454">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="UserName" DisplayMemberBinding ="{Binding ‘username‘}" Width="160"/>
                    <GridViewColumn Header="LockOut Computer" DisplayMemberBinding ="{Binding ‘computer‘}" Width="160"/>
                    <GridViewColumn Header="LockOut Time" DisplayMemberBinding ="{Binding ‘time‘}" Width="160"/>
                </GridView>
            </ListView.View>
        </ListView>
        <Image x:Name="image" HorizontalAlignment="Left" Height="75" Margin="372,10,0,0" VerticalAlignment="Top" Width="95" Source="c:\temp\unlock.png"/>
    </Grid>
</Window>
"@        
$inputXML = $inputXML -replace ‘mc:Ignorable="d"‘,‘‘ -replace "x:N",‘N‘  -replace ‘^<Win.*‘, ‘<Window‘
[void][System.Reflection.Assembly]::LoadWithPartialName(‘presentationframework‘)
[xml]$XAML = $inputXML
#Read XAML
    $reader=(New-Object System.Xml.XmlNodeReader $xaml) 
  try{$Form=[Windows.Markup.XamlReader]::Load( $reader )}
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."}
#===========================================================================
# Store Form Objects In PowerShell
#===========================================================================
$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name)}
Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following interactable elements from our form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables
#===========================================================================
# Actually make the objects work
#===========================================================================
function get-lockout{
$eventcritea = @{logname=‘security‘;id=4740}
$Events =get-winevent -ComputerName (Get-ADDomain).pdcemulator -FilterHashtable $eventcritea 
#$Events = Get-WinEvent -ComputerName syddc01 -Filterxml $xmlfilter        
            
# Parse out the event message data            
ForEach ($Event in $Events) {    
      
    # Convert the event to XML            
    $eventXML = [xml]$Event.ToXml()    
          
    # Iterate through each one of the XML message properties            
    For ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) { 
     
            
        # Append these as object properties            
        Add-Member -InputObject $Event -MemberType NoteProperty -Force -Name  $eventXML.Event.EventData.Data[$i].name -Value $eventXML.Event.EventData.Data[$i].‘#text‘            
    }            
}            
$events | select  @{n=‘UserName‘;e={$_.targetusername}},@{n=‘Computer‘;e={$_.targetdomainname}},@{n=‘time‘;e={$_.timecreated}}
}
$WPFbutton.add_click({
get-lockout | %{$WPFlistView.AddChild($_)}
}
  )
    
$WPFbutton1.add_click(
{
Search-ADAccount -LockedOut | ForEach-Object {Unlock-ADAccount -Identity $_.distinguishedname -PassThru }
})
#Reference Sample entry of how to add data to a field
    #$vmpicklistView.items.Add([pscustomobject]@{‘VMName‘=($_).Name;Status=$_.Status;Other="Yes"})
    #$WPFtextBox.Text = $env:COMPUTERNAME
    #$WPFbutton.Add_Click({$WPFlistView.Items.Clear();start-sleep -Milliseconds 840;Get-DiskInfo -computername $WPFtextBox.Text | % {$WPFlistView.AddChild($_)}  })
#===========================================================================
# Shows the form
#===========================================================================
write-host "To show the form, run the following" -ForegroundColor Cyan
function Show-Form{
$Form.ShowDialog() | out-null
}
Show-Form

最后,豆子不希望别个看见源代码的话,可以考虑转换成EXE文件。从本质上说,powershell没法编译成可执行文件,但是可以“包装”在一个exe文件里面。一个免费的工具是PowerGUI,可以从Dell的网站上下载。

每次执行EXE其实仍然会执行一个解压的操作,然后自动加载PowerShell,这样的等待时间会稍长,因此豆子更宁愿直接运行ps1文件。

时间: 2024-10-12 07:46:18

Visual Studio给Powershell 程序创建可视化界面的相关文章

visual studio 2010 winform程序不能添加对system.web的引用

visual studio 2010 winform程序不能添加对system.web的引用[转载] 需要引用到System.Web.发现没有“System.Web”.在通过“浏览”方式,找到该DLL引用后,发现它出现一个叹号. 折腾了老半天,注意到错误提示中说了当前所用的框架是“.Net Framework 4 Client Profile”,看来这是VS 2010里新出来的划分了(在之前的版本,可是没有“Client”这东东的),于是打开项目的属性页,将“应用程序”下的“目标框架”选为“.N

使用WPF为Powershell程序制作GUI界面

1. 使用Xaml创建应用界面 打开visual studio,创建一个新的项目,在已安装模板中选择Visual C# →Wpf应用. 完成创建后,我们得到如下图所示的应用界面. wpf界面是基于xaml语言设计的,但是无需学习xaml,也可以用工具箱中的各种控件拖拽出不错的布局(就像拖拽winform的控件一样).再配合属性面板,修改各个控件的属性,很容易就得到一个还看得过去的GUI界面. 保存项目.在项目的目录下找到MainWindows.xaml,将其复制一份,这个xaml文件将会成为po

抛开visual studio,纯手工创建asp.net mvc遇到的问题

脱离Visual Studio,只用文本编辑器..NET Framework.IIS Express创建ASP.NET MVC应用时,需要精简~/View目录下web.config文件内容,之前创建的asp.net mvc hellow world应用一切正常,在清理web.config文件后,出现如下错误: The view must derive from WebViewPage, or WebViewPage<TModel>. (The view at ‘~/Views/home/ind

visual studio 开发linux程序

VisualGDB支持Linux的原理是,通过ssh连接到Linux系统上通过ssh给linux下达命令 . (类似visualgdb的有windgb  ,这2个都是商业软件.) Visual GDB把GDB调试工具集成到visual studio 开发环境中,在调试过程中,可以使用visual studio原有的单步执行.设定断定等快捷键,还可以在visual GDB session的窗口中输入GDB的调试命令,集成了visualGDB之后还可以在程序执行的过程中用鼠标悬停的方式查看和修改变量

利用Visual Studio寻找C#程序必要的运行库文件

在工程打包中,有时候很头痛的就是运行所需要的库文件不能够全面的包含进来,特别是有时候调用了一系列外部扩展.对于这些问题,我们可以借用Visual Studio的打包功能帮助我们寻找软件运行必须的库文件. 首先,我们通过“文件---新建---项目---其他项目类型---安装与部署---Visual Studio Installer”新建一个“安装向导”工程. 默认下一步即可,然后我们选择,在右侧空白区域,添加程序运行所需的环境文件,这里需要特别添加程序工程文件中的引用,包括DLL引用,如下图所示

【VBS】使用Visual Studio调试VBS程序

首先要确保机器上安装了Visual Stuido, 然后打开命令行窗口执行如下命令,会弹出是否使用Visual Studio进行调试的确认窗口. 点[是]进行调试. WScript.exe [vbs文件全路径] [参数1] [参数2] [...] [参数n] //D //X WScript.exe命令说明: WScript.exe C:\Test.vbs par1 par2 par3 //D //X 参数说明: //D 打开调试程序. //X 在调试程序中启动该程序. //? 可以使用该参数查看

一步步教你从VC 6.0 转到 Visual Studio 编写C程序

之前一直习惯用VC 6.0编写C程序,但是换到win8或者win10后,VC的各种不兼容,各种闪退实在让人无语.所以转到Visual Studio进行C编程,用过之后发现效果非常好.本文专门为哪些刚接触VS的人员写点自己的心得,同时对初次接触VS可能遇到的问题进行简要分析. 1.怎样用VS配置C工程 对于VS的安装,网上很多安装包,可以根据需要安装.我安装的是VS2010,建议安装VS2013及后序版本.这里不在介绍安装过程,主要以图片形式说明怎么建立C工程: 2.关于自动补全 可以使用快捷键

如何使用Visual Studio进行C++项目创建

打开Visual Studio 2015,点击程序左上角的"文件"--"新建"--"项目". 在弹出窗口选择模板"visual C++",在新建项目栏里选择"win32控制台应用程序",直接点击确定.(如果没有"Win32控制台程序",需要安装,直接在此页面根据引导安装即可) 显示如下页面,点击下一步即可. 勾选"空项目",直接点击"完成". 鼠标

使用visual studio 调试android 程序 ,真机调试

1 使用visual studio 2015 新建 blank android APP , 2 安卓手机调整到开发者模式 3 通过USB链接到PC 4 自动检测 设备(这一步貌似没有立即检测到真机设备,打开两分钟真机设备才出来), 5 选择你的设备 调试正式开始,之后VS会自动部署你的hello world app 到你真机设备 并自动启动