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

MFC PowerShell 轻松实现

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (4投票s)

2015 年 2 月 25 日

CPOL

8分钟阅读

viewsIcon

29597

downloadIcon

731

一种非常简单的方法,可以通过 MFC 来驱动 PowerShell 控制台应用程序

引言

本文将 PowerShell 与 MFC 集成,使得 MFC 应用程序能够利用 PowerShell 约 90% 的强大处理能力。

背景

需要具备 MFC 新手背景知识。当然,PowerShell 知识也非常可取。

使用代码

思路很简单:我设计了一个名为 CPowerShell 的 MFC 类,并使用它的一个实例,调用一个名为 Run 的 C++ 虚拟属性,指定要运行的 PowerShell 脚本。例如:

CPowerShell posh;
posh.Run = _T("dir");

CPowerShell posh;
posh.AssociatedListBox=&m_listBox; // if you want the listbox control represented by that variable be automatically filled in
posh.AssociatedEdit=&m_editBox; // if you want the editbox control represented by that variable be automatically filled in
posh.Run = _T("dir");

就是这样!它确实非常简单!

请注意,上面使用的 dir 命令是 PowerShell cmdlet Get-ChildItem 的别名,而不是命令行命令中的同名命令。

是的,我创造了一个新的动词来写标题:我 PowerShell,他/她/它和 MFC PowerShells(轻松)。很快,每个人都会 PowerShell。它非常强大,对非 IT 人员也很有用。

因此,我决定创建一个尽可能简单的 MFC 类来驱动 PowerShell,使用注册的 PowerShell COM 对象。这是我遇到的第一个问题——我没有在我的电脑上找到任何注册的 PowerShell COM 对象!于是,我回过头来阅读 about_powershell.exe,即 PowerShell 可执行文件的文档,找到了我需要的东西:一个参数(-command),它接受一个脚本(字符串形式)作为参数。这就是我所需要的一切。

结论:我不能使用 **COM** 技术吗?没问题!我看到我可以使用 **CROM** 技术!

使用 **CROM** 技术几乎与使用 **COM** 相同,我们仍然可以借用他人实现的 **CROM/COM** 对象。在 **CROM**/PowerShell 的情况下,可以通过运行可执行文件并带有特定参数来让 PowerShell 执行某些任务,并可能返回某些结果。任务可以是简单的、复杂的或非常复杂的,是否包含远程操作。

写这篇文章时,我变得非常有创意。在创造了一个新动词(to PowerShell)之后,我创造了一个新缩写:**CROM** - **C**reatable (通过 CreateProcess API) and **R**e-creatable (再次通过 CreateProcess API) **O**bject **M**odel。最初,我考虑称之为 Smart Object (SOB),但它的缩写已经有特定含义……所以,**CROM** 是我的选择,它与一个虚构的神的名字同音(我看了一部名为 CROM, the barbarian 或类似的电影……)。

在继续之前,我必须告诉你,这篇文章是关于 PowerShell 的,目前是指版本 4。如果你使用 PowerShell 版本 3,你可能会遇到一些不一致之处;只需升级到版本 4。任何低于 3 的版本,都不再是 PowerShell 了,它就是垃圾(rubbishell),而且本文中的大多数 PowerShell 代码/脚本都可能会失败。

我必须告诉你,你需要这个:Windows 8.1(已完全更新)和 Visual Studio Community 2013 或同等版本(已完全更新)是必需的。

在 PowerShell 运行脚本(上面或任何其他脚本)后,CPowerShell 对象可以将结果自动写入 CListBox 或 CEdit 对象(或两者)。结果可以随时从 CPowerShell 对象持有的数组中读取,直到运行新脚本为止。

通过上面的例子,任何人都可以注意到我宁愿写别名而不是 cmdlet 名称,只是为了少打几个字。dir(3 个字符)比 Get-ChildItem(13 个字符加上三个 Shift 键)短得多。尽管如此,在我看来,任何人都不应该在不记住相应 cmdlet 名称的情况下使用别名,每次使用别名时都应该如此。此外,在记录 PowerShell 代码(脚本)时,必须始终使用 cmdlet 名称。

如果你想知道与别名 dir 对应的 cmdlet,在附加的应用程序窗口中,输入 Ctrl+A,然后输入 "gal dir",最后按 Alt+R+R(或 F5)。

到目前为止,你一定很好奇我前面提到的“c++ 虚拟属性”表达式的含义。原因我已解释过:写这篇文章时,我变得非常有创意。在我看来,有 2 种“c++ 属性”:虚拟和真实的。“c++ 虚拟属性”(无内存支持),以及“c++ 属性”(有内存支持)。

如果你查看名为 PowerShell.h 的文件,你会看到与这些概念相关的宏。

CPowerShell MFC 类非常简单,但它非常强大,仅仅是因为 PowerShell 非常(真的非常)强大,而我通过 **CROM**(哈哈)技术借用了所有这些力量。

CPowerShell MFC 类的实现基本上是实例化 powershell.exe 可执行文件,并带有一个指定 -command 参数和要运行的脚本的参数。我没有使用任何 IPC 机制将结果发送回 MFC 桌面应用程序,而是使用了最简单的方法:用户本地的 TMP 文件夹。就这样。

理解 CPowerShell MFC 类的关键点很重要:指定的脚本必须让 PowerShell 在任务完全执行和输出完全创建后才返回到其命令提示符(这使得 Start-Job 的作用很小或没有作用)。例外情况是注册的任务和作业,它们开辟了新的可能性。

错误在哪里?

CPowerShell 是一个简单的 MFC 类,它不会自动捕获错误流。因此,如果用户想看到任何错误,他/她必须编写脚本将错误流重定向到成功流。例如:

function f{
    dir c:\Qwertytrewq
    dir c:\Asdfgfdsa
    dir c:\Windows *ini
}f

上面的脚本将导致 PowerShell 写入错误消息(我希望你没有像前两个那样的文件夹名),而这些错误消息不会被 MFC CPowerShell 对象捕获。但可以通过稍微修改代码,将错误消息与成功输出一起重定向。

function f{
    dir c:\Qwertytrewq
    dir c:\Asdfgfdsa
    dir c:\Windows *ini
}f 2>&1

 

附加项目带来了 15 个示例。应用程序运行后,编辑视图窗口会显示有关 3 个 PowerShell 自动变量的信息:$PSVersionTable$Host$MyInvocation.嘿!所以有 16 个示例!

请注意,在任何时候,你都可以通过按 Ctrl+A(清空窗口)并输入你的 PowerShell 脚本来输入自己的脚本。要运行它,请按 Alt+R+R,或仅按 F5。

例如,现在,输入 Ctrl+A 并输入 gcm;然后按 Alt+R+R(或仅 F5)来查看 PowerShell 会话中所有可用的命令(别名、函数和 cmdlet)。

示例 #1:对不熟悉 PowerShell 的人来说,这是一个非常令人费解的示例。

这个 PowerShell 脚本显示了 3 个特定文件夹中的所有 xml 项。

function ={
    dir $args[1..(-1+$args.count)] $args[0]
}= *xml $pshome c:/windows c:/windows/system32

示例 #2:你的文档文件夹

dir ~/documents

示例 #3:你的 IP 地址是什么?

以及更多信息

[Net.Dns]::gethostbyname($env:computername)|
    tee -var var|
    % addresslist
$var|
    ft -a

示例 #4:按扩展名对文件夹中的项进行分组

dir $env:windir/* -include *dll,*exe,*ini|
    sort extension|
    ft -groupby extension

示例 #5:完整的帮助,请

显示 Compare-Object cmdlet(diff)的本地文档很容易

help -full diff

注意示例 6。

示例 #6:上面文档中提到的示例 6。

$procsBefore=ps
notepad.exe
$procsAfter=ps
diff -referenceobject $procsBefore -differenceobject $procsAfter

示例 #7:只有名称,没有其他

dir $env:windir/* -include *dll,*exe,*ini|
    fw -column 1

示例 #8:你能告诉我关于这个 powershell.exe 的什么信息?

gi $pshome/powershell.exe|
    fl *

示例 #9:重命名文件和 COM 对象

对于这个示例,你需要在 C:\Temp 文件夹中拥有 JS、VBS 和 BAT 文件。PowerShell 脚本将重命名所有这些文件,方法是在每个文件后面附加 .TXT。在重命名文件之前,会弹出一个窗口询问你是否真的要重命名它们。如果你按 NO(或弹出窗口超时),文件将不会被重命名。

注意,弹出窗口是从 COM 对象 Wscript.Shell “借用”的。

$obj=new-object -com wscript.shell
$r=$obj.popup('Do you want to rename the files? This dialog box will auto extinguish in 10s... ...so, be prepared.', 10, 'let me know:', 0x04 + 0x20)
$null=[system.runtime.interopservices.marshal]::releasecomobject($obj)
if($r-eq6){
    write 'the files were renamed.'
    dir c:/temp/* -inc *.JS,*.VBS,*.BAT|ren -new{"$_.txt"}
}else{
    write 'the files were NOT renamed.'
}

示例 #A:一个非常简短的脚本以及默认 CIM 命名空间(ROOT/cimv2)中大量类的列表

gcls -class *

示例 #B:来自 CIM_OperatingSystem 类的一些信息

$props='caption','status','installdate','lastbootuptime','muilanguages'
gcim -class CIM_OperatingSystem|
    ft $props -a|
    out-string -width 120


示例 #C:阅读 CNN 头条新闻的时间(调用 REST 方法)

write 'from CNN:' ''
$rss='http://rss.cnn.com/rss/edition.rss'
irm $rss|
    ft tit*,desc* -a -wr|
    out-string -width 140

示例 #D:更多新闻,这次是在一个“借用”自 PowerShell 的窗口中,通过 **CROM**(哈哈)技术

得益于 **CROM** 技术,我们可以使用 Out-GridView(help -full ogv)来显示《每日镜报》的故事。

$u='www.mirror.co.uk/news/rss.xml'
irm $u|
    select title,@{l='description';e={$_.description.innertext}}|
    ogv -title 'from Daily Mirror:' -output single|
    fl

示例 #E:JSON 对象和地震——它们之间有什么关系?

默认浏览器有答案。

(wget http://www.seismi.org/api/eqs|ConvertFrom-Json).earthquakes|
    select -first 20|
    ConvertTo-Html -title EARTHQUAKES >($htm=$env:TMP+'/=~=quakes.htm')
ii $htm

我本可以使用 Invoke-RestMethod(help -full irm)而不是 Invoke-WebRequest(help -full wget),但使用 wget 可以清楚地表明 JSON 对象已被下载然后转换为 psobjects。

示例 #F:得益于 **CROM** 技术,所有这些都被“借用/使用”——YouTube 视频 API、通过 Out-GridView 的 PowerShell 窗口以及用于播放视频的 .NET WebControl

**CROM**(哈哈)技术非常强大,因为它能够访问 PowerShell 对象(以及其他对象)。

最后一个示例将查询 YouTube 的“延时摄影”视频,并显示最多 15 个视频在一个 Out-GridView 中,供你选择(如果你取消选择,脚本将在此结束)。然后,所选的视频将在 .NET WebBrowser 控件中播放。

如果你想结束,别无选择,只能按 Alt+F4。

$qry=@'
http://gdata.youtube.com/feeds/api/videos?
    q=timelapse
    &orderby=published
    &max-results=15
    &v=2
'@

irm ($qry-replace'\s')|
    select title,@{
        l='author'
        e={$_.author.name}
    },@{
        l='date published'
        e={$_.published -as [datetime]}
    },@{
        l='video id'
        e={($_.id-split':')[-1]}
    }|
    ogv -title 'Timelapse videos' -output single|%{
#       start iexplore.exe "https://www.youtube.com/watch?v=$($_.'video id')"
        $wb=[Windows.Controls.WebBrowser]@{
            cliptobounds=$true
        }
        $wnd=[Windows.Window]@{
            topmost=$true
            windowstate='maximized'
            windowstyle='none'
            content=$wb
        }
        $wb.navigate("https://www.youtube.com/watch?v=$($_.'video id')")
        $null=$wnd.showdialog()
    }

关注点

由于 MFC CPowerShell 类无法捕获到主机的输出,某些 cmdlet(特别是那些名词中包含“host”的)和功能无法与此 MFC 类一起使用。

通过 PowerShell 和这个 MFC 类,任何 MFC 应用程序都可以聚合大量功能,而无需编写任何代码,这得益于 **CROM** 技术,并使用了尚未提及的 PowerShell 功能,如注册任务和作业。

历史

刚刚发布
2015 年 3 月 4 日:在 CPowerShell MFC 类中包含了一个新的“C++ 虚拟属性”RunAsync
2015 年 3 月 5 日:在 BOOL RunScript(CString&) 函数的每个 return 语句中包含了函数 Delete(),用于清理。
2015 年 3 月 11 日:我刚刚将文章部分更改为 MFC。
 

© . All rights reserved.