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.).
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 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! 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.
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
VirtualProtect
RtlCaptureContext
RtlLookupFunctionEntry
RtlVirtualUnwind
Instead the following have been added:
KERNEL32
InterlockedDecrement
GetFileAttributesW
GetCurrentDirectoryW
SetConsoleTitleW
GetModuleHandleA
InterlockedCompareExchange
InterlockedExchange
SHELL32
SHGetKnownFolderPath
ADVAPI32
RegGetValueW
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
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)
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)
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+)
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:
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":
Opening the online help is still possible as it has been in PS 2.0 times:
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:
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.
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. 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.