Como posso executar o PowerShell com o tempo de execução do .NET 4?

234

Estou atualizando um script do PowerShell que gerencia alguns assemblies do .NET. O script foi escrito para os assemblies criados no .NET 2 (a mesma versão da estrutura com a qual o PowerShell é executado), mas agora precisa trabalhar com os assemblies do .NET 4 e com os assemblies do .NET 2.

Como o .NET 4 oferece suporte à execução de aplicativos criados em versões mais antigas da estrutura, parece que a solução mais simples é iniciar o PowerShell com o tempo de execução do .NET 4 quando preciso executá-lo nos assemblies do .NET 4.

Como posso executar o PowerShell com o tempo de execução do .NET 4?

Imperador XLII
fonte
Duplicado de stackoverflow.com/questions/1940983/… .
precisa saber é o seguinte
8
Atualmente, a solução mais fácil seria instalar o CTP do Powershell 3.0, que usa CLRVersion: 4.0.30319.1.
jon Z
2
Qualquer pessoa que ainda esteja com o PowerShell 2, consulte a resposta de Tim Lewis para uma solução localizada que não requer edição de nenhuma configuração em toda a máquina.
Eric Eskildsen
1
Para uma solução que não seja do sistema e sem arquivo, veja esta resposta
vkrzv 27/09/19

Respostas:

147

O PowerShell (o mecanismo) funciona bem no .NET 4.0. O PowerShell (o host do console e o ISE ) não, simplesmente porque eles foram compilados em versões mais antigas do .NET. Há uma configuração de registro que alterará a estrutura do .NET carregada em todo o sistema , o que permitirá que o PowerShell use as classes do .NET 4.0:

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

Para atualizar apenas o ISE para usar o .NET 4.0, você pode alterar o arquivo de configuração ($ psHome \ powershell_ise.exe.config) para ter um pedaço como este:

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

Você pode criar aplicativos .NET 4.0 que chamam o PowerShell usando a API do PowerShell (System.Management.Automation.PowerShell), mas essas etapas ajudarão a fazer com que os hosts integrados do PowerShell funcionem no .NET 4.0.


Remova as chaves do registro quando não precisar mais delas. Essas são chaves de toda a máquina e migram forçosamente TODOS os aplicativos para o .NET 4.0, mesmo aplicativos usando .net 2 e .net 3.5


Automação de partida
fonte
9
Só para esclarecer, o powershell.exe (o aplicativo host do console) é um aplicativo nativo - não gerenciado.
Keith Hill
4
Eu descobri o meu problema de cima. Você deve colocar o arquivo de configuração no diretório de 64 bits ao executar em um sistema operacional de 64 bits. O executável do powershell de 32 bits parece captar a alteração muito bem a partir daí.
31411 Chris McKenzie
11
Apenas um pequeno conselho. Remova as chaves do registro quando não precisar mais delas. Acabei de perder uma tonelada de tempo tentando descobrir por que não consegui criar um projeto .NET 3.5 em que estou trabalhando.
Klark
7
A solução de modificação de registro proposta tem efeitos colaterais desagradáveis ​​se você estiver usando a segmentação múltipla (por exemplo, escrevendo aplicativos .NET 2.0 no VS2010). Cuidado.
Todd Sprang
9
Observe que a Microsoft adverte fortemente contra isso: "Embora seja possível forçar o PowerShell 2.0 a executar com o .NET Framework 4.0 usando vários mecanismos, como criar um arquivo de configuração para o PowerShell ou editar o registro, esses mecanismos não são suportados e podem ter efeitos colaterais negativos em outras funcionalidades do PowerShell, como comunicação remota do PowerShell e cmdlets com assemblies de modo misto ". connect.microsoft.com/PowerShell/feedback/details/525435/… O Powershell 3.0 possui suporte nativo para o .NET 4.0.
Timbo
238

A melhor solução que encontrei está na postagem do blog Usando as versões mais recentes do .NET com PowerShell . Isso permite que o powershell.exe seja executado com assemblies do .NET 4.

Simplesmente modifique (ou crie) $pshome\powershell.exe.configpara que ele contenha o seguinte:

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

Notas adicionais de configuração rápida:

Locais e arquivos são um pouco dependentes da plataforma; no entanto, fornecerá uma síntese inline de como fazer a solução funcionar para você.

  • Você pode encontrar a localização do PowerShell no seu computador executando cd $pshome na janela do Powershell (não funciona no prompt do DOS).
    • Caminho será algo como (exemplo) C:\Windows\System32\WindowsPowerShell\v1.0\
  • O nome do arquivo para colocar a configuração é: powershell.exe.configse você PowerShell.exeestiver sendo executado (crie o arquivo de configuração, se necessário).
    • Se PowerShellISE.Exeestiver em execução, será necessário criar o arquivo de configuração complementar comoPowerShellISE.Exe.config
cmo999
fonte
23
Definitivamente, a maneira correta de fazer isso. Isso altera apenas o comportamento do PowerShell, não qualquer outro aplicativo .NET em sua máquina ...
Erik A. Brandstadmoen
4
Isso funciona bem, mas afeta todo o seu PowerShell. Se você quiser apenas algumas das funcionalidades, faça uma cópia da pasta PowerShell e edite o arquivo lá.
Matt
8
Eu adicionei um arquivo como mencionado acima. No entanto, não posso mais executar o PowerShell com esse arquivo presente - recebo o erro "O volume de um arquivo foi alterado externamente para que o arquivo aberto não seja mais válido". Alguma ideia?
21711 JoshL
13
@ Josh - em um sistema de 64 bits, descobri que o .exe.config precisa acessar o SysWOW64 \ WindowsPowershell (a pasta de 32 bits), mesmo se você estiver tentando executar o powershell de 64 bits. Caso contrário, você receberá o erro 'alterado externamente'.
6111 Sam
4
O powershell.exe.config precisa estar em dois locais .... C: \ Windows \ System32 \ WindowsPowerShell \ v1.0 \ e C: \ Windows \ SysWOW64 \ WindowsPowerShell \ v1.0 \
Jonesome restabelece Monica
28

Tenha muito cuidado ao usar a abordagem de chave do registro. Essas são chaves de toda a máquina e migram forçosamente TODOS os aplicativos para o .NET 4.0.

Muitos produtos não funcionam se migrados forçosamente e isso é um auxílio para testes e não um mecanismo de qualidade da produção. O Visual Studio 2008 e 2010, MSBuild , turbotax e vários sites, SharePoint e assim por diante não devem ser migrados automaticamente.

Se você precisar usar o PowerShell com 4.0, isso deve ser feito por aplicativo com um arquivo de configuração, verifique com a equipe do PowerShell a recomendação precisa. É provável que isso interrompa alguns comandos existentes do PowerShell.

Madhu Talluri
fonte
Muito bom ponto sobre o uso da chave do Registro. Felizmente, o aplicativo iniciador com o arquivo de configuração está funcionando bem. Nossos scripts usam principalmente comandos do sistema de arquivos e chamadas diretas do .NET, e não percebemos nenhum problema com os comandos quebrados. Como o .NET 4 é amplamente compatível com o .NET 2.0, não seria provável que houvesse muitos comandos quebrados (embora nunca seja demais ter cuidado :).
Imperador XLII
26

Se você precisar executar apenas um único comando, bloco de script ou arquivo de script no .NET 4, tente usar os Arquivos de Configuração de Ativação do .NET 4 para iniciar apenas uma única instância do PowerShell usando a versão 4 do CLR.

Detalhes completos:

http://blog.codeassassin.com/2011/03/23/executing-individual-powershell-commands-using-net-4/

Um exemplo de módulo do PowerShell:

https://gist.github.com/882528

Jason Stangroome
fonte
21

Se você ainda está preso no PowerShell v1.0 ou v2.0, aqui está minha variação da excelente resposta de Jason Stangroome.

Crie um powershell4.cmdlugar no seu caminho com o seguinte conteúdo:

@echo off
:: http://stackoverflow.com/questions/7308586/using-batch-echo-with-special-characters
if exist %~dp0powershell.exe.activation_config goto :run
echo.^<?xml version="1.0" encoding="utf-8" ?^>                 > %~dp0powershell.exe.activation_config
echo.^<configuration^>                                        >> %~dp0powershell.exe.activation_config
echo.  ^<startup useLegacyV2RuntimeActivationPolicy="true"^>  >> %~dp0powershell.exe.activation_config
echo.    ^<supportedRuntime version="v4.0"/^>                 >> %~dp0powershell.exe.activation_config
echo.  ^</startup^>                                           >> %~dp0powershell.exe.activation_config
echo.^</configuration^>                                       >> %~dp0powershell.exe.activation_config
:run
:: point COMPLUS_ApplicationMigrationRuntimeActivationConfigPath to the directory that this cmd file lives in
:: and the directory contains a powershell.exe.activation_config file which matches the executable name powershell.exe
set COMPLUS_ApplicationMigrationRuntimeActivationConfigPath=%~dp0
%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe %*
set COMPLUS_ApplicationMigrationRuntimeActivationConfigPath=

Isso permitirá que você inicie uma instância do console do PowerShell em execução no .NET 4.0.

Você pode ver a diferença no meu sistema em que eu tenho o PowerShell 2.0 examinando a saída dos dois comandos a seguir executados no cmd.

C:\>powershell -ExecutionPolicy ByPass -Command $PSVersionTable

Name                           Value
----                           -----
CLRVersion                     2.0.50727.5485
BuildVersion                   6.1.7601.17514
PSVersion                      2.0
WSManStackVersion              2.0
PSCompatibleVersions           {1.0, 2.0}
SerializationVersion           1.1.0.1
PSRemotingProtocolVersion      2.1


C:\>powershell4.cmd -ExecutionPolicy ByPass -Command $PSVersionTable

Name                           Value
----                           -----
PSVersion                      2.0
PSCompatibleVersions           {1.0, 2.0}
BuildVersion                   6.1.7601.17514
CLRVersion                     4.0.30319.18408
WSManStackVersion              2.0
PSRemotingProtocolVersion      2.1
SerializationVersion           1.1.0.1
Tim Lewis
fonte
3
Essa é de longe a melhor resposta, pois é uma alteração muito localizada e não faz nenhuma alteração persistente no sistema. Coisa boa!
Sebastian
fantástico! Você pode ajudar aqui? stackoverflow.com/questions/39801315/…
johny why
@ TimLewis, é possível enviar várias instruções para a mesma instância do ps4.cmd?
johny why
@johnywhy, enviar várias instruções para o .cmd é o mesmo que enviar várias instruções para o .exe, porque o .cmd usa% * para passar todos os seus parâmetros para o .exe. No entanto, não faz diferença, pois você ainda precisa ter cuidado com o modo como o cmd.exe analisa a linha de comando quando passa os parâmetros para o executável que está iniciando. Vou dar uma olhada na sua outra pergunta de excesso de pilha e abordar detalhes específicos.
Tim Lewis
Tentei usar essa técnica em combinação com o parâmetro de linha de comando -Version docs.microsoft.com/en-us/powershell/scripting/core-powershell/… Infelizmente, isso não funciona; minha versão mais recente do PowerShell (5.1.17134.407), conforme determinado em $ PSVersionTable.PSVersion, é lançada.
eisenpony
17

Aqui está o conteúdo do arquivo de configuração que usei para oferecer suporte aos assemblies .NET 2.0 e .NET 4:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <!-- http://msdn.microsoft.com/en-us/library/w4atty68.aspx -->
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" />
    <supportedRuntime version="v2.0.50727" />
  </startup>
</configuration>

Além disso, aqui está uma versão simplificada do código compatível com PowerShell 1.0 que usei para executar nossos scripts a partir dos argumentos da linha de comando passados:

class Program {
  static void Main( string[] args ) {
    Console.WriteLine( ".NET " + Environment.Version );

    string script = "& " + string.Join( " ", args );
    Console.WriteLine( script );
    Console.WriteLine( );

    // Simple host that sends output to System.Console
    PSHost host = new ConsoleHost( this );
    Runspace runspace = RunspaceFactory.CreateRunspace( host );

    Pipeline pipeline = runspace.CreatePipeline( );
    pipeline.Commands.AddScript( script );

    try {
      runspace.Open( );
      IEnumerable<PSObject> output = pipeline.Invoke( );
      runspace.Close( );

      // ...
    }
    catch( RuntimeException ex ) {
      string psLine = ex.ErrorRecord.InvocationInfo.PositionMessage;
      Console.WriteLine( "error : {0}: {1}{2}", ex.GetType( ), ex.Message, psLine );
      ExitCode = -1;
    }
  }
}

Além do tratamento básico de erros mostrado acima, também injetamos uma trapinstrução no script para exibir informações adicionais de diagnóstico (semelhante à função Resolve-Error de Jeffrey Snover ).

Imperador XLII
fonte
10

As outras respostas são anteriores a 2012 e concentram-se em "invadir" o PowerShell 1.0 ou o PowerShell 2.0 para direcionar versões mais recentes do .NET Framework e do Common Language Runtime (CLR).

No entanto, como foi escrito em muitos comentários, desde 2012 (quando o PowerShell 3.0 chegou), uma solução muito melhor é instalar a versão mais recente do PowerShell . Ele segmentará automaticamente o CLR v4.0.30319. Isso significa .NET 4.0, 4.5, 4.5.1, 4.5.2 ou 4.6 (esperado em 2015), pois todas essas versões são substituições no local. Use $PSVersionTableou consulte o tópico Determinar o thread da versão do PowerShell instalado se não tiver certeza da sua versão do PowerShell.

No momento da redação deste artigo, a versão mais recente do PowerShell é a 4.0 e pode ser baixada com o Windows Management Framework (link de pesquisa do Google) .

Jeppe Stig Nielsen
fonte
2
Os requisitos de sistema para o Windows Management Framework 4.0 (eles são semelhantes para o 3.0) são: Windows 7, Windows Embedded Standard 7, Windows Server 2008 R2, Windows Server 2012.
Peter Mortensen
9

Na verdade, você pode executar o PowerShell usando o .NET 4 sem afetar outros aplicativos .NET. Eu precisava fazer isso para usar a nova propriedade "Host" do HttpWebRequest, mas a alteração do "OnlyUseLatestCLR" interrompeu o Fiddler, pois isso não poderia ser usado no .NET 4.

Os desenvolvedores do PowerShell obviamente previram isso e adicionaram uma chave do Registro para especificar qual versão do Framework ele deve usar. Um pequeno problema é que você precisa se apropriar da chave do registro antes de alterá-la, pois nem os administradores têm acesso.

  • HKLM: \ Software \ Microsoft \ Powershell \ 1 \ PowerShellEngine \ RuntimeVersion (64 bits e 32 bits)
  • HKLM: \ Software \ Wow6432Node \ Microsoft \ PowerShell \ 1 \ PowerShellEngine \ RuntimeVersion (32 bits na máquina de 64 bits)

Altere o valor dessa chave para a versão necessária. Lembre-se, porém, de que alguns snap-ins podem não carregar mais, a menos que sejam compatíveis com o .NET 4 (o WASP é o único com o qual tive problemas, mas não o uso mesmo). VMWare , SQL Server 2008 , PSCX, Active Directory (Microsoft e Quest Software ) e SCOM funcionam bem.

Justin Yancey
fonte
+1 Essa é uma alternativa muito importante (e melhor) que a outra entrada de registro que afetará todos os aplicativos .net, mas essa solução afeta apenas o powershell.
Christian Mikkelsen
Depois de implementar o "OnlyUseLatestCLR", meu Fiddler quebrou, e alguns scripts do PowerShell não funcionam mais porque não conseguem entrar em contato com determinados servidores. Alterei manualmente os valores de volta para 0 no regedt32 e agora tudo está funcionando novamente. Obrigado!
Neville
O que são WASP, PSCX e SCOM (neste contexto)?
Peter Mortensen
7

Se você não deseja modificar os arquivos de registro ou app.config, uma maneira alternativa é criar um aplicativo de console .NET 4 simples que imite o que o PowerShell.exe faz e hospede o PowerShell ConsoleShell.

Consulte a Opção 2 - Hospedando o Windows PowerShell por conta própria

Primeiro, adicione uma referência aos assemblies System.Management.Automation e Microsoft.PowerShell.ConsoleHost que podem ser encontrados em % programfiles% \ Reference Assemblies \ Microsoft \ WindowsPowerShell \ v1.0

Em seguida, use o seguinte código:

using System;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;

namespace PSHostCLRv4
{
    class Program
    {
        static int Main(string[] args)
        {
            var config = RunspaceConfiguration.Create();
                return ConsoleShell.Start(
                config,
                "Windows PowerShell - Hosted on CLR v4\nCopyright (C) 2010 Microsoft Corporation. All rights reserved.",
                "",
                args
            );
        }
    }
}
Grant Holliday
fonte
6

Como outra opção, a versão mais recente do PoshConsole inclui binários direcionados ao .NET 4 RC (que funcionam bem com a versão RTM) sem nenhuma configuração.

Jaykul
fonte
1

Basta executar o powershell.exe com COMPLUS_versiona variável de ambiente definida como v4.0.30319. Por exemplo, do cmd.exe ou do arquivo .bat:

set COMPLUS_version=v4.0.30319
powershell -file c:\scripts\test.ps1
vkrzv
fonte