Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

PowerShell internals and PowerShell threading

4.90/5 (39 votes)
28 Jul 2015LGPL37 min read 142.7K   1K  
This article presents you some capabilities of PowerShell like the usage of .NET Framework 4.0 and .NET threading.

Introduction  

PowerShell is a great scripting language providing access to the complete .NET Framework. Most people out there forget that PowerShell is not a programming language and seems to be limited in some ways. But what should one do if she or he is not allowed to use a real programming language? What if you are only allowed to provide a script in contrast to a binary that is precompiled to e.g., IL, ByteCode, PCode, or even compiled as it is the case of C, C++? The answer ... one option is to use PowerShell. I'd like to demonstrate to you the dynamicity and flexibility of PowerShell.

My next article will show you some details about desired state configurations in PowerShell 4.0.

But before introducing my thoughts, I want to provide you with some PowerShell internals.

PowerShell 1.0 and 2.0 internals   

PowerShell has been written in C++ 6.0. This information can be retrieved by using Microsoft's dumpbin tool and looking for the "msvcrt.dll" binary. MSVCRT is the C++ runtime for Visual C++ version 4.2 to 6.0. The versions before and after have used differently named DLLs for each version (e.g., msvcr70.dll, msvcr100.dll, etc.). 

PowerShell imports

That information gives us the great knowledge that PowerShell.exe itself hasn't been written in .NET. But how does PowerShell cooperate with .NET? 

The answer is simple. PowerShell uses the CorBindToRuntimeEx function of mscoree.dll.

CorBindToRuntimeEx in PowerShell

C++
HRESULT CorBindToRuntimeEx (
    [in]  LPWSTR    pwszVersion, 
    [in]  LPWSTR    pwszBuildFlavor, 
    [in]  DWORD     flags, 
    [in]  REFCLSID  rclsid, 
    [in]  REFIID    riid, 
    [out] LPVOID*   ppv
);  

There is nearly no difference between .NET 4.0 and 2.0 in this point except that:

  • MSCorEE.idl has been replaced by MSCorEE.h,
  • flags has been renamed to startupFlags, and
  • the function has been marked as deprecated in .NET 4.0. 

More information can be found here: http://msdn.microsoft.com/en-us/library/99sz37yh(v=VS.100).aspx.

That's the reason why one has more problems using .NET 4.0 code in PowerShell than .NET 2.0.

By the way, using .NET 4.0 is possible in PowerShell, but you have to define your own host or change some registry settings or config files, because PowerShell.exe and the ISE have been compiled against .NET 2.0. e.g.:

reg add hklm\software\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1
reg add hklm\software\wow6432node\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1

The above registry changes will allow PowerShell to use .NET 4.0.

Attention

Attention! Modifying the .NET Framework registry settings may affect the whole system. Please consider modifying the configuration files instead. 

The same applies to the ISE by changing powershell_ise.exe.config:

XML
<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <startup>
  <supportedRuntime version="v4.0.30319" />
 </startup>
</configuration>

You can even change powershell.exe.config ($pshome\) as follows:

XML
<?xml version="1.0"?> 
<configuration> 
    <startup useLegacyV2RuntimeActivationPolicy="true"> 
        <supportedRuntime version="v4.0.30319"/> 
        <supportedRuntime version="v2.0.50727"/> 
    </startup> 
</configuration>

Or you can write your own PowerShell host:

C#
using System;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;
namespace PowerShell40
{
    class Program
    {
        static int Main(string[] args)
        {
            return ConsoleShell.Start
            (
                RunspaceConfiguration.Create(),
                "Windows PowerShell .NET 4.0 Enabled",
                string.Empty,
                args
            );
        }
    }
}

In order to compile your .NET 4.0 console application, you need to add a reference to the Microsoft.PowerShell.ConsoleHost and System.Management.Automation assemblies, which can be found under %programfiles%\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0.

PowerShell 3.0 and 4.0 internals

Nearly the same with some exceptions as shown below applies to Microsoft's PowerShell 3.0 and 4.0:

VC++ 6.0 has been used again for programming the PowerShell 3.0 release. The attentive reader might has noticed that PowerShell is now containing an SHELL32.dll import in addition to version 2.0:

HRESULT SHGetKnownFolderPath(
  _In_      REFKNOWNFOLDERID rfid,
  _In_      DWORD dwFlags,
  _In_opt_  HANDLE hToken,
  _Out_     PWSTR *ppszPath
); 

The function uses PowerShell internally to retrieve the full path of a "know folder", more about it here:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx

Moreover Microsoft is no longer using the following imports:

KERNEL32
  Image 4  VirtualProtect
  Image 5  RtlCaptureContext
  Image 6  RtlLookupFunctionEntry
  Image 7  RtlVirtualUnwind

Instead the following have been added:

KERNEL32
  Image 8  InterlockedDecrement
  Image 9  GetFileAttributesW
  Image 10  GetCurrentDirectoryW
  Image 11  SetConsoleTitleW
  Image 12  GetModuleHandleA
  Image 13  InterlockedCompareExchange
  Image 14  InterlockedExchange

SHELL32
  Image 15  SHGetKnownFolderPath

ADVAPI32
  Image 16  RegGetValueW

Image 17

PowerShell 3.0 addresses .NET Framework 4.0

Windows PowerShell 3.0 can be downloaded here (it's part of Windows Management Framework 3.0):

http://www.microsoft.com/en-us/download/details.aspx?id=34595 

Image 19

Image 20

The WMF 4.0 download link is listed below:

http://www.microsoft.com/en-us/download/details.aspx?id=39347

One thing surprised me:

powershell.exe is now a x86 application (though installing the x64 version) and no longer a x64 bit application.

Some PowerShell 4.0 PE header details:

FILE HEADER VALUES
             14C machine (x86)
               5 number of sections
        51B89908 time date stamp Wed Jun 12 17:51:36 2013
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
             102 characteristics
                   Executable
                   32 bit word machine

OPTIONAL HEADER VALUES
             10B magic # (PE32)

Image 21

and

some PowerShell 3.0 PE header details:

FILE HEADER VALUES
             14C machine (x86)
               5 number of sections
        50338287 time date stamp Tue Aug 21 14:43:51 2012
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
             102 characteristics
                   Executable
                   32 bit word machine

OPTIONAL HEADER VALUES
             10B magic # (PE32)

PowerShell 3.0 Property Details

vs. version 2.0 of powershell

FILE HEADER VALUES
            8664 machine (x64)
               5 number of sections
        4A5BC7F3 time date stamp Tue Jul 14 01:49:07 2009
               0 file pointer to symbol table
               0 number of symbols
              F0 size of optional header
              22 characteristics
                   Executable
                   Application can handle large (>2GB) addresses

OPTIONAL HEADER VALUES
             20B magic # (PE32+)

PowerShell 2.0 Property Details

What's new in PowerShell 3.0?  

The help system has been improved.

You can now get a GUI list of powershell's commands by using the "Show-Command" CMDLet:

Image 24

The PS help system has now the possibility to update the help files by invoking the CMDLet "Update-Help" or by searching for commands like the following via "Get-Help":

Image 25

Image 26

Opening the online help is still possible as it has been in PS 2.0 times:

 Image 27

Active directory CMDLets have been added too.

For example it is now possible to install AD via PowerShell:

The new commands located in the ADDSDeployment module are:

  • Install-ADDSDomainController 
  • Install-ADDSDomain
  • Install-ADDSForest 
  • UnInstall-ADDSDomainController
  • Test-ADDSDomainInstallation
  • Test-ADDSForestInstallation 
  • ADDSDomainControllerUnInstallation
  • Test-ADDSReadOnlyDomainControllerUnInstallation  and
  • Test-ADDSDomainControllerInstallation

The AD administrative center has been extended to include a PowerShell history window:

Image 28

 

The beta program of Microsoft (Microsoft Connect) allows you to use Microsoft Online Backup together with PowerShell 3.0.

http://connect.microsoft.com/onlinebackup

The module to be loaded (use the CMDLet Import-Module) in PowerShell is called: MSOnlineBackup

There are more new features like "PowerShell Web Access", in order to have a quick overview please be so kind to refer to the nice technet article:

http://technet.microsoft.com/en-us/library/hh831611.aspx

What's new in PowerShell 4.0?  

The read about the news and about the accomplishments regarding PowerShell 4.0 please be so kind to have a look at the article "PowerShell 4.0 - What's New?".

Switching between PS version

It is possible to change the coding rules and it is possible to change between the PowerShell versions. Use the command: 

Set-StrictMode -Version <Version> 

Possible values are:

1.0

2.0    (PS 2.0 is required)

3.0    (PS 3.0 is required)

4.0    (PS 4.0 is required) or 

Latest (latest switches to the highest available powershell version)

Diving deeper - Threading    

As shown in the previous sections, neither you are limited to any runtime version in PowerShell nor you are limited in threading capabilities, as I'll show you now.

PowerShell 2.0 and 4.0 provide some kind or some sort of jobs, which enable you to run background jobs. This might be useful, for example, if you have a number of computers and want to get some information for each via PowerShell. There are millions of scenarios where threading is useful.

Even for such a simple command, PowerShell will create a new process, a new runspace, and communicate via IPC via the "host" process: 

start-job { gwmi win32_process }   

Another example in detail

start-job { get-process }

will use an IPC channel with WinRM to run, and doesn't require administrative permissions.

The second one

invoke-command -scriptblock {get-process} -computer localhost -asjob

uses both HTTP and WinRM and needs administrative rights.

PowerShell in the task manager

This might be considered as overkill. Moreover, you do not have the ability to control the jobs via .NET mechanisms.

For that reason, I've provided a simple library that makes it possible to start threads over .NET, no second process will be created, the communication effort needed is much lower, and the CPU doesn't explode if you have a complex task.

Method #1 

VB
function CreateThreadStart ([ScriptBlock]$scriptBlock)
{
    $ps_s=$scriptBlock.GetPowerShell()
    $smi=($ps_s.gettype().getmethods()| ? {$_.name -eq "Invoke"})[0]$fp = 
                                           $smi.methodhandle.getfunctionpointer
    $ts=new-object threading.threadstart $ps_s,$fp
    return $ts
}

function CreateThread ([ScriptBlock]$scriptBlock)
{
    return new-object threading.thread (CreateThreadStart $scriptBlock)
}

This method is quite simple and does nothing else to get a pointer to a function and creates an object of type System.Threading.ThreadStart.

But you are limited because PowerShell (i.e. the GetPowerShell() method) will only allow to pass one argument in the script block.

This will work:

[System.Threading.Thread] $thread = (CreateThread {Get-Service})
$thread.Start()

This won't:

# Error: "Can only convert a script block that contains exactly one pipeline 
  or command. Expressions or control structures aren't permitted. Make sure t
  he script block contains exactly one pipeline or command."
[System.Threading.Thread] $thread = (CreateThread {Get-Service;Get-Service})
$thread.Start()

Method #2 

Creating your own PSHost and PSHostUserInterface.

This can be done by creating your own .NET DLL. If you have problems with possible audits etc., or you are only allowed to script, then use PowerShell's Add-Type and you'll be able to do everything in PowerShell you can imagine. The script is only an example (therefore not perfect) and has been written to demonstrate some capabilities of .NET and PowerShell interaction.

How to include the PowerShell library

Library is maybe the wrong word; it' just a script that can be included for simplicity. The includable Threading.ps1 script runs under both PowerShell 2.0 and 3.0. Just put the following code at the start of your main script or directly after the param section (if applicable), to include the Threading.ps1 script or lib, for example:

[array]$loads = "Threading.ps1"
foreach ($load in $loads)
{
    If (!($loadedlibs -like "*$load*"))
    {
           . (Join-Path (Split-Path -parent 
             ($MyInvocation.MyCommand.Definition)) 
              -childpath "$($load)")
    }
}

[System.Guid]$guid = StartThread -scriptBlock { Write "Application"; Sleep 2; 
                         Write "Application 2"; Get-Service; }
GetThreadOutput
Start-Job {Sleep 2000}
StopThread

The example output:

Application
Application 2

Status   Name               DisplayName
------   ----               ----------- 
Stopped  aspnet_state       ASP.NET State Service
Stopped  COMSysApp          COM+ System Application
Running  CryptSvc           Cryptographic Services
Running  CscService         Offline Files
Running  DcomLaunch         DCOM Server Process Launcher
Stopped  defragsvc          Disk Defragmenter
Running  Dhcp               DHCP Client
Running  Dnscache           DNS Client
...

Id   Name   State    HasMoreData   Location    Command
--   ----   -----    -----------   --------    -------
3    Job3   Running  True          localhost   Sleep 2000
True

That's it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)