Как получить доступ к 64-разрядному реестру из 32-разрядного экземпляра Powershell?
Если вы запустите 32-разрядный экземпляр Powershell (% SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe), то поставщик реестра видит только ограниченные 32-разрядные части реестра.
**32-bit console**
PS> (dir HKLM:\SOFTWARE | measure).count - (dir HKLM:\SOFTWARE\wow6432node | measure).count
0
**64-bit console**
PS> (dir HKLM:\SOFTWARE | measure).count - (dir HKLM:\SOFTWARE\wow6432node | measure).count
-5
Есть ли способ заставить провайдера перейти в 64-битный режим? Я мог бы спуститься в [Microsoft.Win32].Net API, или, возможно, WMI, но я бы предпочел. Я использую Powershell v2 CTP3, если это вообще расширяет возможности.
Ответы
Ответ 1
Когда Powershell работает как 32-битный процесс, мне не известно о механизме "переключить его" в режим 64 бит. Весь смысл поддержки виртуализации в 64-битных системах состоит в том, чтобы заставить 32-битные процессы полагать, что они живут в 32-битной ОС...
Однако, я сказал, что использовал предыдущую технику в прошлом, и она очень хорошо работала для меня (следующий код был протестирован на Vista SP1 x64 с Powershell v1). Этот метод основан на том, что исполняемые файлы .NET "Any CPU" будут выполняться как 64-битный процесс, даже если он вызван из 32-битного процесса. Шаги, которые мы будем выполнять:
- Скомпилируйте короткую программу на С#, которая запустит powershell (т.е. очень простая реализация "fork":-))
- Запустите скомпилированную программу на С#
- Скомпилированная программа на С# запустит Powershell, но поскольку это "любой процессор", он будет работать как 64-битный процесс, поэтому он запустит 64-битную Powershell (обратите внимание, что, поскольку это всего лишь доказательство концепции, я ожидаю, что powershell быть в вашем "пути" )
- Новый 64-битный Powershell выполнит наш командный выбор
Это скриншот вышеизложенного в действии (битовая ошибка уведомлений процессов):
Дерево процессов http://img3.imageshack.us/img3/3248/powershellfork.png
Следующая программа ожидает, что все перечисленные файлы будут находиться в одном каталоге. Я рекомендую создать тестовый каталог, например. C:\Temp\PowershellTest и сохраните все файлы там).
Точкой входа в программу будет простой командный символ:
# file "test.ps1"
$basePath = Split-Path -resolve $myInvocation.MyCommand.Path
$exe = Join-Path $basePath test.exe
&"$env:SystemRoot\Microsoft.NET\Framework\v3.5\csc.exe" /nologo /target:exe /out:$exe (Join-Path $basePath test.cs)
&$exe (Join-Path $basePath visibility.ps1)
Он запускает csc (32 бит, но это не имеет значения:-)), а затем запускает результат компилятора csc, передавая один аргумент (полный путь к) visibility.ps1 (это командный файл, который мы хотим запустить на 64-битной Powershell).
Код С# также очень прост:
// file "test.cs"
using System.Diagnostics;
static class Program {
static int Main(string[] args) {
ProcessStartInfo i = new ProcessStartInfo("powershell", args[0]);
i.UseShellExecute = false;
using(Process p = Process.Start(i)) {
p.WaitForExit();
return p.ExitCode;
}
}
}
И, наконец, ваша "видимость" script:
# file "visibility.ps1"
(dir HKLM:\SOFTWARE).count - (dir HKLM:\SOFTWARE\wow6432node).count
Запуск записи script из 32-разрядной версии Powershell теперь дает желаемый результат (просто чтобы показать, что я не обманывал, я сначала запускаю видимость script, а затем используя нашу технологию вилки):
Запуск программы http://img3.imageshack.us/img3/2766/powershellrunc.png
Ответ 2
С .NET API вы можете читать 64-битные значения, например:
$key = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64)
$subKey = $key.OpenSubKey("SOFTWARE\Microsoft\.NETFramework")
$root = $subKey.GetValue("InstallRoot")
Ответ 3
Я считаю, что встроенный командлет Start-Job позволит вам проверить реестр на 64 бита из 32-разрядного экземпляра.
Если нет, используйте Invoke-Command
для возврата к локальной машине wil. 64-разрядная машина будет иметь две конечные точки (64-разрядные и 32-разрядные), а 64-разрядная конечная точка будет по умолчанию.
Пример создания значения реестра и обеспечения его в 64-битном пути, а не 32.
Invoke-Command -scriptblock {
New-ItemProperty -Path HKLM:\SOFTWARE\Acme
-Name NameofNewReg
-PropertyType String -Value "1"
} -computername .
Ответ 4
Небольшое отклонение ответа от Милана заключается в том, чтобы удержать PowerShell с помощью программы С# в соответствии с Bart De Smet blog. Хотя эта запись в блоге сосредоточена на компиляции с .NET 4.0, вы можете скомпилировать ее и с .NET 3.5. Результатом является двоичный файл, который является хостом PowerShell, который может обращаться к 64-битовому реестру при вызове из 32-битного процесса:
using System;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;
namespace PSHost
{
class Program
{
static void Main(string[] args)
{
var config = RunspaceConfiguration.Create();
ConsoleShell.Start(
config,
"Windows PowerShell - Compiled for ANY CPU",
"",
args
);
}
}
}
Ответ 5
Если переменная окружения PROCESSOR_ARCHITEW6432 существует и имеет значение AMD64, тогда вы используете 32-разрядную версию на 64-битной машине.
Затем вам нужно запустить powershell в виртуальном 64-битном пути% windir%\sysnative.
if ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") {
write-warning "changing from 32bit to 64bit PowerShell..."
$powershell=$PSHOME.tolower().replace("syswow64","sysnative").replace("system32","sysnative")
if ($myInvocation.Line) {
&"$powershell\powershell.exe" -NonInteractive -NoProfile $myInvocation.Line
} else {
&"$powershell\powershell.exe" -NonInteractive -NoProfile -file "$($myInvocation.InvocationName)" $args
}
exit $lastexitcode
}
Ответ 6
Чтобы продлить ответ Милана Гардиан, используйте эту функцию для небольших кодовых блоков:
function RunAs-64Bit ([ScriptBlock]$scriptblock)
{
[string]$code = 'using System.Diagnostics; static class Program { static int Main(string[] args) { ProcessStartInfo i = new ProcessStartInfo("powershell", args[0]); i.UseShellExecute = false; using(Process p = Process.Start(i)) { p.WaitForExit(); return p.ExitCode; } } }'
[string]$exeName = $env:temp + '\' + [System.IO.Path]::GetRandomFileName() + '.exe';
$params = New-Object 'System.CodeDom.Compiler.CompilerParameters';
@('mscorlib.dll', 'System.dll', ([System.Reflection.Assembly]::GetAssembly([PSObject]).Location)) | %{ $params.ReferencedAssemblies.Add( $_ ) } | Out-Null
$params.GenerateExecutable = $true
$params.GenerateInMemory = $false;
$params.CompilerOptions = '/optimize';
$params.OutputAssembly = $exeName;
$params.TreatWarningsAsErrors = $false;
$params.WarningLevel = 4;
$csprovider = New-Object 'Microsoft.CSharp.CSharpCodeProvider'; #disposable
try {
$compileResults = $csprovider.CompileAssemblyFromSource($params, $code)
$errors = $compileResults.Errors | ?{ -not $_.IsWarning }
if ($errors.Count -gt 0)
{
Write-Host -f red 'Compiler errors are found.'
foreach ($output in $compileResults.Output) { Write-Host -$output }
foreach ($err in $errors) { Write-Host -f red $('Compile Error: ' + $err); }
}
else
{
$compileResults.Errors | %{ Abr-Write-UtilLog 'Util Get assembly from code' $('Compile Warning: ' + $_); }
$assembly = $compileResults.CompiledAssembly
$commandParam = '-encodedCommand ' + [System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes($scriptblock));
&$exeName $commandParam
}
Remove-Item -force $exeName -ErrorAction 'SilentlyContinue';
} finally{
$csprovider.Dispose();
Remove-Variable 'csprovider';
}
}
Теперь используйте эту функцию для запуска любого скриптового блока (если он не слишком большой) в 64-битном режиме, когда доступен 64-разрядный режим