我正在尝试使用此命令在c#中编译powershell源代码:
Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $source
但c#-6.0功能不起作用,例如:
Add-Type : c:\Users\...\AppData\Local\Temp\2\d2q5hn5b.0.cs(101) : Unexpected character '$'
代码:
new Problem($"... ({identifier})", node)
我正在使用powershell-v5.1
有没有办法解决这个问题?
答案 0 :(得分:7)
Powershell uses CodeDomProvider
to compile their assemblies.随框架提供的版本仅支持C#5,因此默认情况下没有新功能可用。
但是,如果您提供另一个CodeDomProvider
,则可以编译任何语言,也可以编译C#6。 There is a CodeDomProvider
available for Roslyn(新的.NET编译器)。您可以download it from NuGet使用Add-Type
包含程序集。然后创建编译器的实例并将其传递给-CodeDomProvider
属性。
答案 1 :(得分:2)
要扩展到Patrick Hoffmans's solution,我对在unbob's solution中使用反射方法感到不舒服,因为这将来可能会中断。
我制定了以下powershell
代码,而不是使用.NET
命名的类和接口:
#requires -Version 5
# download https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/ and extract with 7-zip to a location, enter that location on the next line
$DotNetCodeDomLocation = 'C:\Utils\microsoft.codedom.providers.dotnetcompilerplatform.2.0.1'
Add-Type -Path "$DotNetCodeDomLocation\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll"
# using Invoke-Expression moves this class definition to runtime, so it will work after the add-type and the ps5 class interface implementation will succeed
# This uses the public interface ICompilerSettings instead of the private class CompilerSettings
Invoke-Expression -Command @"
class RoslynCompilerSettings : Microsoft.CodeDom.Providers.DotNetCompilerPlatform.ICompilerSettings
{
[string] get_CompilerFullPath()
{
return "$DotNetCodeDomLocation\tools\RoslynLatest\csc.exe"
}
[int] get_CompilerServerTimeToLive()
{
return 10
}
}
"@
$DotNetCodeDomProvider = [Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider]::new([RoslynCompilerSettings]::new())
然后可以在以下示例中使用它:
要使用程序集参考示例将类型直接添加到powershell
实例中(需要roslyn
编译器以及与脚本捆绑在一起的上述代码):
Add-Type -CodeDomProvider $DotNetCodeDomProvider -TypeDefinition $your_source_code_block -ReferencedAssemblies @([System.Reflection.Assembly]::GetAssembly([hashtable]).Location)
要将代码编译为dll
以便在将来的脚本或其他脚本中加载(仅要求将生成的dll
文件与脚本捆绑在一起):
$DotNetAssemblyParameters = [System.CodeDom.Compiler.CompilerParameters]::new(
@([System.Reflection.Assembly]::GetAssembly([hashtable]).Location),
'path_and_name_for_saved.dll',
$false
)
# you can adjust more compilation settings here if you want, see
# https://docs.microsoft.com/en-us/dotnet/api/system.codedom.compiler.compilerparameters?view=netframework-4.7.2
$compilationResults = $DotNetCodeDomProvider.CompileAssemblyFromSource(
$DotNetAssemblyParameters,
$your_source_code_block
)
经过编译的dll
然后可以与简单的Add-Type
一起使用:
Add-Type -Path 'path_and_name_for_saved.Dll'
如果您将.NET
powershell
和CodeDomProvider
捆绑在一起,则可以在主脚本中内联使用dll
中最新的roslyn
编译器编译器,也可以将C#
代码编译为dll
,这样就不必在每次运行脚本时都重新编译它,从而可以更轻松地移植并缩短脚本运行时间。>
答案 2 :(得分:1)
当我尝试Hoffman先生的方法时,我收到以下错误:
Add-Type : Could not find a part of the path 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\bin\roslyn\csc.exe'.
user1676558 mentions two solutions:
作为一名(小联盟)PowerShell黑客,基于对source code的检查,我提出了自己的PowerShellian解决方案:
$dncpTypes = Add-Type -Path C:\<path where I put the dll>\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll -PassThru
$dncpTypTab = [ordered]@{}
$dncpTypes | %{$dncpTypTab[$_.Name] = $_}
$compSetCtor = $dncpTypTab.CompilerSettings.GetConstructor(@([string],[int]))
$compSettings = $compSetCtor.Invoke(@('C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe', 10))
$cscpOtherCtor = $dncpTypTab.CSharpCodeProvider.GetConstructor('NonPublic,Instance', $null, @($dncpTypTab.ICompilerSettings), $null)
$roslynProvider = $cscpOtherCtor.Invoke($compSettings)
毋庸置疑,有关于这是否是一个错误的关于innertubes的一些讨论。看起来提供程序是针对ASP.NET的,并在那里做正确的事情。人们也不同意在哪里拿起csc.exe。我怀疑这可能会继续in flux。
[后来编辑:在VS2017中,csc似乎生活在$ {env:ProgramFiles(x86)} \ Microsoft Visual Studio \ 2017 \ Enterprise \ MSBuild \ 15.0 \ Bin \ Roslyn \ csc.exe。]