Сценарий PowerShell для возврата версий.NET Framework на компьютере?
Что такое сценарий PowerShell для возврата версий.NET Framework на компьютере?
Мое первое предположение - это что-то, связанное с WMI. Есть ли что-то лучше?
Это должен быть однострочник, чтобы возвращать только последнюю версию для каждой установки.NET [в каждой строке].
13 ответов
Если вы собираетесь использовать реестр, вам нужно выполнить рекурсию, чтобы получить полную версию для 4.x Framework. Предыдущие ответы оба возвращают номер root в моей системе для.NET 3.0 (где номера WCF и WPF, вложенные в 3.0, выше - я не могу этого объяснить) и не возвращают ничего для 4.0 ..,
РЕДАКТИРОВАТЬ: Для.Net 4.5 и выше, это снова немного изменилось, так что теперь есть хорошая статья MSDN, объясняющая, как преобразовать значение Release в номер версии.Net, это полный крушение поезда:-(
Это выглядит правильно для меня (обратите внимание, что он выводит отдельные номера версий для WCF и WPF на 3.0. Я не знаю, о чем это). Он также выводит как Client, так и Full на 4.0 (если они оба установлены):
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse |
Get-ItemProperty -name Version,Release -EA 0 |
Where { $_.PSChildName -match '^(?!S)\p{L}'} |
Select PSChildName, Version, Release
Основываясь на статье MSDN, вы можете создать справочную таблицу и вернуть номер версии маркетингового продукта для выпусков после 4.5:
$Lookup = @{
378389 = [version]'4.5'
378675 = [version]'4.5.1'
378758 = [version]'4.5.1'
379893 = [version]'4.5.2'
393295 = [version]'4.6'
393297 = [version]'4.6'
394254 = [version]'4.6.1'
394271 = [version]'4.6.1'
394802 = [version]'4.6.2'
394806 = [version]'4.6.2'
460798 = [version]'4.7'
460805 = [version]'4.7'
461308 = [version]'4.7.1'
461310 = [version]'4.7.1'
461808 = [version]'4.7.2'
461814 = [version]'4.7.2'
}
# For One True framework (latest .NET 4x), change the Where-Oject match
# to PSChildName -eq "Full":
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse |
Get-ItemProperty -name Version, Release -EA 0 |
Where-Object { $_.PSChildName -match '^(?!S)\p{L}'} |
Select-Object @{name = ".NET Framework"; expression = {$_.PSChildName}},
@{name = "Product"; expression = {$Lookup[$_.Release]}},
Version, Release
Фактически, поскольку мне постоянно приходится обновлять этот ответ, вот скрипт для генерации сценария выше (с небольшим дополнительным дополнением) из источника уценки для этой веб-страницы. Это, вероятно, сломается в какой-то момент, поэтому я сохраняю текущую копию выше.
# Get the text from github
$url = "https://raw.githubusercontent.com/dotnet/docs/master/docs/framework/migration-guide/how-to-determine-which-versions-are-installed.md"
$md = Invoke-WebRequest $url -UseBasicParsing
$OFS = "`n"
# Replace the weird text in the tables, and the padding
# Then trim the | off the front and end of lines
$map = $md -split "`n" -replace " installed [^|]+" -replace "\s+\|" -replace "\|$" |
# Then we can build the table by looking for unique lines that start with ".NET Framework"
Select-String "^.NET" | Select-Object -Unique |
# And flip it so it's key = value
# And convert ".NET FRAMEWORK 4.5.2" to [version]4.5.2
ForEach-Object {
[version]$v, [int]$k = $_ -replace "\.NET Framework " -split "\|"
" $k = [version]'$v'"
}
# And output the whole script
@"
`$Lookup = @{
$map
}
# For extra effect we could get the Windows 10 OS version and build release id:
try {
`$WinRelease, `$WinVer = Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" ReleaseId, CurrentMajorVersionNumber, CurrentMinorVersionNumber, CurrentBuildNumber, UBR
`$WindowsVersion = "`$(`$WinVer -join '.') (`$WinRelease)"
} catch {
`$WindowsVersion = [System.Environment]::OSVersion.Version
}
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse |
Get-ItemProperty -name Version, Release -EA 0 |
# For The One True framework (latest .NET 4x), change match to PSChildName -eq "Full":
Where-Object { `$_.PSChildName -match '^(?!S)\p{L}'} |
Select-Object @{name = ".NET Framework"; expression = {`$_.PSChildName}},
@{name = "Product"; expression = {`$Lookup[`$_.Release]}},
Version, Release,
# Some OPTIONAL extra output: PSComputerName and WindowsVersion
# The Computer name, so output from local machines will match remote machines:
@{ name = "PSComputerName"; expression = {`$Env:Computername}},
# The Windows Version (works on Windows 10, at least):
@{ name = "WindowsVersion"; expression = { `$WindowsVersion }}
"@
Добавлена поддержка v4.7.2 для скрипта:
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse |
Get-ItemProperty -name Version,Release -EA 0 |
Where { $_.PSChildName -match '^(?![SW])\p{L}'} |
Select PSChildName, Version, Release, @{
name="Product"
expression={
switch -regex ($_.Release) {
"378389" { [Version]"4.5" }
"378675|378758" { [Version]"4.5.1" }
"379893" { [Version]"4.5.2" }
"393295|393297" { [Version]"4.6" }
"394254|394271" { [Version]"4.6.1" }
"394802|394806" { [Version]"4.6.2" }
"460798|460805" { [Version]"4.7" }
"461308|461310" { [Version]"4.7.1" }
"461808|461814" { [Version]"4.7.2" }
{$_ -gt 461814} { [Version]"Undocumented version (> 4.7.2), please update script" }
}
}
}
gci 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' |
sort pschildname -des |
select -fi 1 -exp pschildname
Этот ответ не возвращает 4.5, если он установлен. Ответ ниже от @Jaykul и с помощью recurse делает.
[environment]::Version
Дает вам экземпляр Version
для CLR используется текущая копия PSH (как описано здесь).
Правильный синтаксис:
[System.Runtime.InteropServices.RuntimeEnvironment]::GetSystemVersion()
#or
$PSVersionTable.CLRVersion
GetSystemVersion
Функция возвращает строку, подобную этой:
v2.0.50727 #PowerShell v2.0 in Win 7 SP1
или как это
v4.0.30319 #PowerShell v3.0 (Windows Management Framework 3.0) in Win 7 SP1
$PSVersionTable
это объект только для чтения. Свойство CLRVersion является структурированным номером версии следующим образом:
Major Minor Build Revision
----- ----- ----- --------
4 0 30319 18444
Я нашел это через завершение табуляции в powershell для osx:
[System.Runtime.InteropServices.RuntimeInformation]::get_FrameworkDescription()
.NET Core 4.6.25009.03
Хорошее решение
Попробуйте использовать загружаемый модульDotNetVersionLister (на основе информации реестра и таблицы поиска версий от маркетинговой версии).
Который будет использоваться так:
PS> Get-DotNetVersion -LocalHost
ComputerName : localhost
>=4.x : 4.5.2
v4\Client : Installed
v4\Full : Installed
v3.5 : Installed
v3.0 : Installed
v2.0.50727 : Installed
v1.1.4322 : Not installed (no key)
Ping : True
Error :
Script start time: 09/03/2018 09:21:18
Script end time: 09/03/2018 09:21:19
Или вот так, если вы просто хотите протестировать его на какой-то платформе.NET> = 4. *:
PS> (Get-DotNetVersion -LocalHost).">=4.x"
Script start time: 09/03/2018 09:25:55
Script end time: 09/03/2018 09:25:55
4.5.2
Но это не будет работать (установка / импорт), например, с PS v2.0 (Win 7, Win Server 2010 стандарт) из-за несовместимости...
Мотивация для "наследственных" функций ниже
(Вы можете пропустить чтение и использовать код ниже)
Нам пришлось работать с PS 2.0 на некоторых машинах и не удалось установить / импортировать вышеупомянутый DotNetVersionLister.
На других машинах мы хотели обновить (с PS 2.0) до PS 5.1 (который, в свою очередь, нуждается в .NET Framework> = 4.5) с помощью двух пользовательских систем. Install-DotnetLatestCompany
а также Install-PSLatestCompany
,
Чтобы хорошо провести администраторов через процесс установки / обновления, нам нужно определить версию.NET в этих функциях на всех машинах и существующих версиях PS.
Таким образом, мы использовали также следующие функции, чтобы определить их более безопасно во всех средах...
Функции для устаревших сред PS (например, PS v2.0)
Поэтому следующий код и приведенные ниже (извлеченные) примеры использования полезны здесь (на основе других ответов здесь):
function Get-DotNetVersionByFs {
<#
.SYNOPSIS
NOT RECOMMENDED - try using instead:
Get-DotNetVersion
from DotNetVersionLister module (https://github.com/EliteLoser/DotNetVersionLister),
but it is not usable/importable in PowerShell 2.0
Get-DotNetVersionByReg
reg(istry) based: (available herin as well) but it may return some wrong version or may not work reliably for versions > 4.5
(works in PSv2.0)
Get-DotNetVersionByFs (this):
f(ile) s(ystem) based: determines the latest installed .NET version based on $Env:windir\Microsoft.NET\Framework content
this is unreliable, e.g. if 4.0* is already installed some 4.5 update will overwrite content there without
renaming the folder
(works in PSv2.0)
.EXAMPLE
PS> Get-DotnetVersionByFs
4.0.30319
.EXAMPLE
PS> Get-DotnetVersionByFs -All
1.0.3705
1.1.4322
2.0.50727
3.0
3.5
4.0.30319
.NOTES
from https://stackru.com/a/52078523/1915920
#>
[cmdletbinding()]
param(
[Switch]$All ## do not return only latest, but all installed
)
$list = ls $Env:windir\Microsoft.NET\Framework |
?{ $_.PSIsContainer -and $_.Name -match '^v\d.[\d\.]+' } |
%{ $_.Name.TrimStart('v') }
if ($All) { $list } else { $list | select -last 1 }
}
function Get-DotNetVersionByReg {
<#
.SYNOPSIS
NOT RECOMMENDED - try using instead:
Get-DotNetVersion
From DotNetVersionLister module (https://github.com/EliteLoser/DotNetVersionLister),
but it is not usable/importable in PowerShell 2.0.
Determines the latest installed .NET version based on registry infos under 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP'
.EXAMPLE
PS> Get-DotnetVersionByReg
4.5.51209
.EXAMPLE
PS> Get-DotnetVersionByReg -AllDetailed
PSChildName Version Release
----------- ------- -------
v2.0.50727 2.0.50727.5420
v3.0 3.0.30729.5420
Windows Communication Foundation 3.0.4506.5420
Windows Presentation Foundation 3.0.6920.5011
v3.5 3.5.30729.5420
Client 4.0.0.0
Client 4.5.51209 379893
Full 4.5.51209 379893
.NOTES
from https://stackru.com/a/52078523/1915920
#>
[cmdletbinding()]
param(
[Switch]$AllDetailed ## do not return only latest, but all installed with more details
)
$Lookup = @{
378389 = [version]'4.5'
378675 = [version]'4.5.1'
378758 = [version]'4.5.1'
379893 = [version]'4.5.2'
393295 = [version]'4.6'
393297 = [version]'4.6'
394254 = [version]'4.6.1'
394271 = [version]'4.6.1'
394802 = [version]'4.6.2'
394806 = [version]'4.6.2'
460798 = [version]'4.7'
460805 = [version]'4.7'
461308 = [version]'4.7.1'
461310 = [version]'4.7.1'
461808 = [version]'4.7.2'
461814 = [version]'4.7.2'
}
$list = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse |
Get-ItemProperty -name Version, Release -EA 0 |
# For One True framework (latest .NET 4x), change match to PSChildName -eq "Full":
Where-Object { $_.PSChildName -match '^(?!S)\p{L}'} |
Select-Object `
@{
name = ".NET Framework" ;
expression = {$_.PSChildName}},
@{ name = "Product" ;
expression = {$Lookup[$_.Release]}},
Version, Release
if ($AllDetailed) { $list | sort version } else { $list | sort version | select -last 1 | %{ $_.version } }
}
Пример использования:
PS> Get-DotNetVersionByFs
4.0.30319
PS> Get-DotNetVersionByFs -All
1.0.3705
1.1.4322
2.0.50727
3.0
3.5
4.0.30319
PS> Get-DotNetVersionByReg
4.5.51209
PS> Get-DotNetVersionByReg -AllDetailed
.NET Framework Product Version Release
-------------- ------- ------- -------
v2.0.50727 2.0.50727.5420
v3.0 3.0.30729.5420
Windows Communication Foundation 3.0.4506.5420
Windows Presentation Foundation 3.0.6920.5011
v3.5 3.5.30729.5420
Client 4.0.0.0
Client 4.5.2 4.5.51209 379893
Full 4.5.2 4.5.51209 379893
Не существует надежного способа сделать это для всех платформ и архитектур с использованием простого сценария. Если вы хотите научиться делать это надежно, начните с публикации в блоге Обновленный пример кода обнаружения.NET Framework, который выполняет более глубокую проверку.
Обратитесь к странице Script, чтобы узнать, какие версии.NET установлены на удаленных рабочих станциях.
Сценарий может быть полезен для поиска версии.NET для нескольких компьютеров в сети.
Не красиво Определенно не очень
ls $Env:windir\Microsoft.NET\Framework | ? { $_.PSIsContainer } | select -exp Name -l 1
Это может или не может работать. Но что касается последней версии, то это должно быть довольно надежно, поскольку для старых версий (1.0, 1.1) существуют, по сути, пустые папки, но не более новые - они появляются только после установки соответствующей инфраструктуры.
Тем не менее, я подозреваю, что должен быть лучший способ.
Вот мой подход к этому вопросу после документации msft:
$gpParams = @{
Path = 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full'
ErrorAction = 'SilentlyContinue'
}
$release = Get-ItemProperty @gpParams | Select-Object -ExpandProperty Release
".NET Framework$(
switch ($release) {
({ $_ -ge 528040 }) { ' 4.8'; break }
({ $_ -ge 461808 }) { ' 4.7.2'; break }
({ $_ -ge 461308 }) { ' 4.7.1'; break }
({ $_ -ge 460798 }) { ' 4.7'; break }
({ $_ -ge 394802 }) { ' 4.6.2'; break }
({ $_ -ge 394254 }) { ' 4.6.1'; break }
({ $_ -ge 393295 }) { ' 4.6'; break }
({ $_ -ge 379893 }) { ' 4.5.2'; break }
({ $_ -ge 378675 }) { ' 4.5.1'; break }
({ $_ -ge 378389 }) { ' 4.5'; break }
default { ': 4.5+ not installed.' }
}
)"
Этот пример работает со всеми версиями PowerShell и будет работать вечно, поскольку 4.8 - последняя версия.NET Framework.
Это просто потому, что мне пришлось потратить время на создание / редактирование этого, когда он должен быть широко доступен, поэтому я предоставляю его всем остальным.
Приведенный ниже сценарий выведет в TEMP пару файлов CSV с версиями и статусом уязвимости каждой машины в выбранном (в коде) подразделении. Вы сможете удаленно проводить "аудит безопасности" OU машин.
Powershell 7.0, необходимый для тестовой строки RSAT, необходимой для получения модуля AD Visual Studio Code, необходимого для получения powershell 7.0 (на win7)
К тому времени, как вы это прочитаете, список версий в файле, вероятно, будет устаревшим. Используйте этот веб-сайт https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/versions-and-dependencies, чтобы добавлять новые записи dotnet. Это просто набор ключевых значений в "DotNet4Builds"
Если в CompromisedCheck.csv машина отображается как =0, значит, ее безопасность отключена вручную, и вы должны сообщить, сделал ли это поставщик или подозреваемый сотрудник.
Надеюсь, это поможет тем, кто ищет это для своего бизнеса.
<#
Script Name : Get-DotNetVersions_Tweaked.ps1
Description : This script reports the various .NET Framework versions installed on the local or a remote set of computers
Author : Original by Martin Schvartzman - Edited by Mark Purnell
Reference : https://msdn.microsoft.com/en-us/library/hh925568
#>
$ErrorActionPreference = "Continue”
import-module ActiveDirectory
$searchOU = "OU=OU LEVEL 1,OU=OU LEVEL 2,OU=MACHINES,OU=OUR LAPTOPS,DC=PUT,DC=MY,DC=DOMAIN,DC=CONTROLLER,DC=HERE,DC=OK"
$computerList = Get-ADComputer -searchbase $searchOU -Filter *
function Get-DotNetFrameworkVersion($computerList)
{
$dotNetter = @()
$compromisedCheck = @()
$dotNetRoot = 'SOFTWARE\Microsoft\.NETFramework'
$dotNetRegistry = 'SOFTWARE\Microsoft\NET Framework Setup\NDP'
$dotNet4Registry = 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full'
$dotNet4Builds = @{
'30319' = @{ Version = [System.Version]'4.0' }
'378389' = @{ Version = [System.Version]'4.5' }
'378675' = @{ Version = [System.Version]'4.5.1' ; Comment = '(8.1/2012R2)' }
'378758' = @{ Version = [System.Version]'4.5.1' ; Comment = '(8/7 SP1/Vista SP2)' }
'379893' = @{ Version = [System.Version]'4.5.2' }
'380042' = @{ Version = [System.Version]'4.5' ; Comment = 'and later with KB3168275 rollup' }
'393295' = @{ Version = [System.Version]'4.6' ; Comment = '(Windows 10)' }
'393297' = @{ Version = [System.Version]'4.6' ; Comment = '(NON Windows 10)' }
'394254' = @{ Version = [System.Version]'4.6.1' ; Comment = '(Windows 10)' }
'394271' = @{ Version = [System.Version]'4.6.1' ; Comment = '(NON Windows 10)' }
'394802' = @{ Version = [System.Version]'4.6.2' ; Comment = '(Windows 10 Anniversary Update)' }
'394806' = @{ Version = [System.Version]'4.6.2' ; Comment = '(NON Windows 10)' }
'460798' = @{ Version = [System.Version]'4.7' ; Comment = '(Windows 10 Creators Update)' }
'460805' = @{ Version = [System.Version]'4.7' ; Comment = '(NON Windows 10)' }
'461308' = @{ Version = [System.Version]'4.7.1' ; Comment = '(Windows 10 Fall Creators Update)' }
'461310' = @{ Version = [System.Version]'4.7.1' ; Comment = '(NON Windows 10)' }
'461808' = @{ Version = [System.Version]'4.7.2' ; Comment = '(Windows 10 April & Winserver)' }
'461814' = @{ Version = [System.Version]'4.7.2' ; Comment = '(NON Windows 10)' }
'528040' = @{ Version = [System.Version]'4.8' ; Comment = '(Windows 10 May 2019 Update)' }
'528049' = @{ Version = [System.Version]'4.8' ; Comment = '(NON Windows 10)' }
}
foreach($computerObject in $computerList)
{
$computerName = $computerObject.DNSHostName
write-host("PCName is " + $computerName)
if(test-connection -TargetName $computerName -Quiet -TimeOutSeconds 1 -count 2){
if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computerName))
{
$os = (Get-WMIObject win32_operatingsystem -ComputerName SPL305350).Name
if(!$?){
write-host("wim not available")
$dotNetter += New-Object -TypeName PSObject -Property @{
'ComputerName' = $computerName
'OS' = "WIM not available"
'Build' = "WIM not available"
'Version' = "WIM not available"
'Comment' = "WIM not available"
}
}
else{
if ($netRegKey = $regKey.OpenSubKey("$dotNetRegistry"))
{
foreach ($versionKeyName in $netRegKey.GetSubKeyNames())
{
if ($versionKeyName -match '^v[123]') {
$versionKey = $netRegKey.OpenSubKey($versionKeyName)
$version = [System.Version]($versionKey.GetValue('Version', ''))
write-host("adding old dotnet")
$dotNetter += New-Object -TypeName PSObject -Property @{
ComputerName = $computerName
OS = $os
Build = $version.Build
Version = $version
Comment = ''
}
}
}
}
if ($net4RegKey = $regKey.OpenSubKey("$dotNet4Registry"))
{
if(-not ($net4Release = $net4RegKey.GetValue('Release')))
{
$net4Release = 30319
}
write-host("adding new dotnet")
$dotNetter += New-Object -TypeName PSObject -Property @{
'ComputerName' = $computerName
'OS' = $os
'Build' = $net4Release
'Version' = $dotNet4Builds["$net4Release"].Version
'Comment' = $dotNet4Builds["$net4Release"].Comment
}
}
if ($netRegKey = $regKey.OpenSubKey("$dotNetRoot")){
write-host("Checking for hacked keys")
foreach ($versionKeyName in $netRegKey.GetSubKeyNames())
{
if ($versionKeyName -match '^v[1234]') {
$versionKey = $netRegKey.OpenSubKey($versionKeyName)
write-host("versionKeyName is" + $versionKeyName)
write-host('ASPNetEnforceViewStateMac = ' + $versionKey.GetValue('ASPNetEnforceViewStateMac', ''))
$compromisedCheck += New-Object -TypeName PSObject -Property @{
'ComputerName' = $computerName
'version' = $versionKeyName
'compromisedCheck' = ('ASPNetEnforceViewStateMac = ' + $versionKey.GetValue('ASPNetEnforceViewStateMac', ''))
}
}
}
}
}
}
}
else{
write-host("could not connect to machine")
$dotNetter += New-Object -TypeName PSObject -Property @{
'ComputerName' = $computerName
'OS' = $os
'Build' = "Could not connect"
'Version' = "Could not connect"
'Comment' = "Could not connect"
}
}
}
$dotNetter | export-CSV c:\temp\DotNetVersions.csv
$compromisedCheck | export-CSV C:\temp\CompromisedCheck.csv
}
get-dotnetframeworkversion($computerList)
Вот общая идея:
Получите дочерние элементы в каталоге.NET Framework, которые являются контейнерами, имена которых соответствуют шаблону v число точка число. Сортируйте их по убыванию имени, возьмите первый объект и верните его свойство name.
Вот сценарий:
(Get-ChildItem -Path $Env:windir\Microsoft.NET\Framework | Where-Object {$_.PSIsContainer -eq $true } | Where-Object {$_.Name -match 'v\d\.\d'} | Sort-Object -Property Name -Descending | Select-Object -First 1).Name
Я хотел бы попробовать это в PowerShell: работал для меня!
(Get-ItemProperty "HKLM: Программное обеспечение \Microsoft\NET Framework Setup\NDP\v4\Full"). Версия
Если вы установили Visual Studio на свой компьютер, откройте командную строку разработчика Visual Studio и введите следующую команду: clrver
В нем будут перечислены все установленные на этом компьютере версии.NET Framework.
Это производная от предыдущей публикации, но в моих тестах используется последняя версия.NETframework 4.
get-itemproperty -name version,release "hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\FULL"
Это позволит вам вызвать команду на удаленный компьютер:
invoke-command -computername server01 -scriptblock {get-itemproperty -name version,release "hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\FULL" | select pscomputername,version,release}
Что устанавливает эту возможность с помощью ADModule и префикса соглашения об именах:
get-adcomputer -Filter 'name -like "*prefix*"' | % {invoke-command -computername $_.name -scriptblock {get-itemproperty -name version,release "hklm:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\FULL" | select pscomputername,version,release} | ft
Я не разбираюсь в моем синтаксисе PowerShell, но я думаю, что вы могли бы просто вызвать http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.runtimeenvironment.getsystemversion.aspx. Это вернет версию в виде строки (что-то вроде v2.0.50727
, Я думаю).