Click here to Skip to main content
15,868,016 members
Articles / Desktop Programming / MFC

MFC PowerShells Easily

Rate me:
Please Sign up or sign in to vote.
3.50/5 (4 votes)
24 Feb 2015CPOL8 min read 27.8K   683   16   2
A very easy way to drive PowerShell console application through MFC

Introduction

This article integrates PowerShell with MFC, making available to MFC applications, about 90% of PowerShell powerful processing capability.

Background

Beginner's MFC background knowledge is required. Of course, PowerShell knowledge is very desirable.

Using the code

The idea is simple: I design a MFC class called CPowerShell, and using one instance of it, I call a c++ virtual property called Run, specifying the PowerShell script to run. For instance:

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

or

PowerShell
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");

That's all! It's very simple indeed!

Please, notice that the dir command used above is an alias for the PowerShell cmdlet Get-ChildItem, and not the homonym from the command line commands.

Yes, I created a new verb to write the title: I PowerShell, he/she/it and MFC PowerShells (easily). Very soon, everyone will be PowerShelling. It is very powerful and very useful for non IT people too.

Therefore, I decided to create a MFC class, as simple as possible, to drive PowerShell, using registered PowerShell COM objects. This was the first problem I met - I did not find any PowerShell COM object registered in my computer! So, I went back to read about_powershell.exe, the documentation for the PowerShell executable, and I found what I need: a parameter (-command) that accepts as argument a script (a string too). It's all I needed.

Conclusion: Can't I use COM technology? No problem! I see I can use CROM technology!

Using CROM technology is almost like using COM, and we can still borrow the CROM/COM objects implemented by others. In the case of CROM/PowerShell, it's possible to run the executable with specific argument to have PowerShell executing some task, and possibly, returning some results. The task can be either simple, complex or very complex, with remoting or not.

Writing this article, I got very creative. After creating a new verb (to PowerShell), I created a new acronym: CROM - Creatable (through CreateProcess API) and Re-creatable (again, through CreateProcess API) Object Model. Initially, I considered calling it Smart Object (SOB), but its acronym has already a specific meaning... So, CROM is my choice, and it's a homonym for a fictional deity (I watched a movied called CROM, the barbarian or something similar...).

Before we continue, I must tell you that this article is about PowerShell, which, currently, means version 4. If you use PowerShell version 3, you may find some inconsistencies; just upgrade to version 4. Any version less than 3, is not PowerShell anymore; it is rubbishell, and probably most PowerShell codes/scripts in this article will fail.

I must tell you need this: Windows 8.1 (fully updated) and Visual Studio Community 2013 or equivalent (fully updated) are required.

After PowerShell runs the script (the one above or any other), the results can be automatically written into either a CListBox or a CEdit object (or into both) by the CPowerShell object. The results can be read from an array held by the CPowerShell object, anytime, until a new script is run.

By the example above, anyone can notice that I rather write down the alias instead of the cmdlet names, just to type much less keys. dir (3 keys) is much shorter than Get-ChildItem (13 keys plus shift key thrice). Nonetheless, in my opinion, no one should ever use an alias without remembering the respective cmdlet name, every time an alias is used. Moreover, when documenting a PowerShell code (script) the cmdlet name must always be used.

If you want to know the cmdlet corresponding to the alias dir, in the attached application window, type Ctrl+A, "gal dir" and Alt+R+R (or F5).

By now, you must be quite curious about the meaning of "c++ virtual property" expression I used some lines above. The reason, I have already explained: writing this article, I got very creative. In my conception, there are 2 types of "c++ properties": virtual and real. The "c++ virtual property" (without memory support), and the "c++ property" (which has memory support).

If you look in the file named PowerShell.h, you'll see macros related to these conceptions.

The CPowerShell MFC class is very simple, nevertheless, it's very powerful, just because PowerShell is very (really very) powerful, and I borrow all that power through the CROM (LOL) technology.

The CPowerShell MFC class implementation is basically instantiating powershell.exe executable with an argument specifying the -command parameter and the script to be run. Instead of using any IPC mechanism to send results back to the MFC desktop application, I use the simplest method: user's local TMP folder. That's it.

It's important to understanda key point about the CPowerShell MFC class: The script specified must make PowerShell return to its command prompt only after the task is fully executed and the output is fully created ( this makes Start-Job of no or very limited use ). The exception is for registered tasks and jobs, which open a new world of possibilities.

Where are the errors?

The CPowerShell is a simple MFC class, and it doesn't catch the error stream, automatically. Thus, if the user wants to see any errors, he/she must code the script to redirect the error stream to the success stream. For instance:

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

The script above will cause PowerShell to write error messages (I hope you don't have folders with names like the first 2) that won't be caught by the MFC CPowerShell object. But it's possible to change the code a little bit and redirect the error messages along with the success output:

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

 

The attached project brings 15 examples. Right after the apllication is run, the edit view window shows information about 3 PowerShell automatic variables: $PSVersionTable, $Host and $MyInvocation.<font color="#111111" face="Segoe UI" size="3"> HEY! so there are 16 examples!</font>

Notice that, at any time, you can enter your own PowerShell script, by pressing Ctrl+A (to clear the window) and type your script. To run it, press Alt+R+R, or just F5.

For instance, rigt now, type Ctrl+A and type gcm; now press Alt+R+R (or just F5) to see all commands (alias, functions and cmdlets) available in the PowerShell session.

Example #1: a very puzzling example to anyone not used to PowerShell.

This PowerShell script shows all xml items in 3 specific folders.

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

Example #2: your documents folder

PowerShell
dir ~/documents

Example #3: what's your IP

And some more info:

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

Example #4: grouping items in a folder, by extension

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

Example #5: full help, please

It's easy to show the local documentation for the Compare-Object cmdlet (diff):

PowerShell
help -full diff

Notice example 6.

Example #6: the example 6, from documentation mentioned above.

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

Example #7: just the names, nothing more

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

Example #8: what can you tell me about this powershell.exe

PowerShell
gi $pshome/powershell.exe|
    fl *

Example #9: Renaming files and COM objects

For this example, you need to have JS, VBS and BAT files in C:\Temp folder. The PowerShell script will rename all of them, by appending .TXT to each of them. Before renaming the files a popup window will ask you if you really want to rename them. If you press NO (or the popup windows times out), the files won't be renamed.

Notice, the popup window is "borrowed" from the COM object Wscript.Shell.

PowerShell
$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.'
}

Example #A: a very short script and a huge list of classes in the default CIM NameSpace (ROOT/cimv2)

PowerShell
gcls -class *

Example #B: some information from CIM_OperatingSystem class

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


Example #C: time to read the TOP STORIES from CNN (invoking REST method)

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

Example #D: more news, this time in a window "borrowed" from PowerShell, through CROM (LOL) technology

Thanks to CROM technology, we can use the Out-GridView ( help -full ogv ) to show the stories from Daily Mirror.

PowerShell
$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

Example #E: JSON Objects and Earthquakes - how are they related

The default browser has the answer.

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

I could have used Invoke-RestMethod (help -full irm) instead of Invoke-WebRequest (help -full wget), but using wget makes clear that JSON objects were downloaded and then, converted to psobjects.

Example #F: Thanks to CROM technology, all these are "borrowed/used" - YouTube videos API, PowerShell window through Out-GridView and .NET WebControl to play a video

CROM (LOL) technology, is really powerfull, thanks to the PowerShell objects (and others) it is able to access.

This last example, will query YouTube about "timelapse" videos and show 15 (at most) in an Out-GridView, for you, the user select one (if you cancel the selection, the script ends here). The video selected, will then, be played in a .NET WebBrowser control.

If you want to finish, you have no other way but to press Alt+F4.

PowerShell
$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()
    }

Points of Interest

Due to the fact that the MFC CPowerShell class is unable to trap the outputs to the host, some cmdlets (notably those that contain 'host' in the noun) and features cannot be used with this MFC class.

Through PowerShell and this MFC class, any MFC application can aggregate a huge number of features without any code, thanks to CROM technology, using PowerShell features not yet mentioned, like registered tasks and jobs.

History

just posted
4/March/2015: included a new "C++ virtual property", RunAsync, in the CPowerShell MFC class.
5/March/2015: included function Delete() inside BOOL RunScript(CString&) function's every return statement, for cleaning up.
11/March/2015: I just changed the article section to MFC
 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
QuestionI missed the "gal" (how could I?) Pin
aljodav26-Feb-15 14:16
aljodav26-Feb-15 14:16 
AnswerRe: I missed the "gal" (how could I?) Pin
aljodav3-Mar-15 17:10
aljodav3-Mar-15 17:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.