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.
But before introducing my thoughts, I want to provide you with some PowerShell internals.
PowerShell 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.).

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.

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 2.0 and 4.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! 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:
="1.0" ="utf-8"
<configuration>
<startup>
<supportedRuntime version="v4.0.30319" />
</startup>
</configuration>
You can even change powershell.exe.config ($pshome\) as follows:
="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:
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.
Diving deeper - Threading
As shown in the previous section, neither are you limited to any runtime version in PowerShell nor are you limited in threading capabilities, as I'll show you now.
PowerShell 2.0 provides 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.

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
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. 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.