Microsoft.PowerShell.NanoServer.SDK.psm1
$Script:DotNetCoreRefAsmPath = "${env:SystemDrive}\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore" $Script:NanoPSRefAsmInfoFile = "NanoPSReferenceAssemblyInfo.xml" $Script:HasAdminPrevilege = $null ## Load localized error strings Import-LocalizedData LocalizedData -filename NanoPowerShellSDK.Resource.psd1 #region "Utilities" ## ## Test if the current user has admin previlege ## function Test-AdminPrevilege { if ($null -eq $Script:HasAdminPrevilege) { $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $Principal = [System.Security.Principal.WindowsPrincipal]::new($Identity) $Script:HasAdminPrevilege = $Principal.IsInRole("Administrators") } return $Script:HasAdminPrevilege } ## ## Update the project file ## function Update-ProjectFile { param( [string] $ProjectFile, [string] $OutputType ) $Xml = [xml] (Get-Content -Path $ProjectFile) $RefNode = $Xml.Project.PropertyGroup[0].Item("TargetPlatformVersion") $NewNode = $Xml.CreateNode($RefNode.NodeType, "TargetFrameworkVersion", $RefNode.NamespaceURI) $NewNode.InnerText = "v0.1" $Xml.Project.PropertyGroup[0].InsertBefore($newNode, $refNode) > $null $Xml.Project.PropertyGroup[0].OutputType = $OutputType $Xml.Project.Import[-1].Project = "Microsoft.CoreSys.CSharp.targets" Save-Xml -XmlDoc $Xml -Path $ProjectFile } ## ## Save XmlDocument to the specified file ## function Save-Xml { param( [xml] $XmlDoc, [string] $Path ) $XmlWriterSetting = [System.Xml.XmlWriterSettings]::new() $XmlWriterSetting.Indent = $true $XmlWriterSetting.IndentChars = " " $XmlWriterSetting.NewLineChars = "`r`n" $XmlWriterSetting.NewLineHandling = "Replace" try { $XmlWriter = [System.Xml.XmlWriter]::Create($Path, $XmlWriterSetting) $XmlDoc.Save($XmlWriter) } finally { $XmlWriter.Close() } } ## ## Utility to write terminating error ## function ThrowError { param( [parameter(Mandatory = $true)] [System.Management.Automation.PSCmdlet] $CallerPSCmdlet, [parameter(Mandatory = $true)] [System.String] $ExceptionName, [parameter(Mandatory = $true)] [System.String] $ExceptionMessage, [System.Object] $ExceptionObject, [parameter(Mandatory = $true)] [System.String] $ErrorId, [parameter(Mandatory = $true)] [System.Management.Automation.ErrorCategory] $ErrorCategory ) $exception = New-Object $ExceptionName $ExceptionMessage; $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject $CallerPSCmdlet.ThrowTerminatingError($errorRecord) } ## ## Utility to wait the copying process with a pesudo progress bar ## function Wait-WithPesudoProgressBar { param( [parameter(Mandatory = $true)] [System.Diagnostics.Process] $Process ) $Count = 10 $Activity = 'NanoServer PowerShell SDK Initial Setup' $Status = 'Copy NanoServer PowerShell Reference Assemblies' Write-Progress -Activity $Activity -Status $Status -PercentComplete $Count while (-not $Process.HasExited) { Start-Sleep -Milliseconds 150 if ($Count -lt 99) { $Count = $Count + (100 - $Count) / 10 } Write-Progress -Activity $Activity -Status $Status -PercentComplete $Count } Write-Progress -Activity $Activity -Status $Status -Completed } #endregion "Utilities" ## ## Create a new CSharp project targeting CoreCLR used by Nano PowerShell ## function New-NanoCSharpProject { <# .SYNOPSIS This cmdlet creates a new Visual Studio C# Project targeting CoreCLR and PowerShell deployed in TP5 NanoServer. Initial setup of the SDK will be performed if it's not done already. NOTE: This cmdlet should run in the "Package Manager Console" in Visual Studio 2015. If you don't have Visual Studio 2015 installed, you can download and install 'Visual Studio Community 2015' from https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx. Make sure you select the feature "Windows and Web Development -> Windows 8.1 and Windows Phone 8.0/8.1 Tools -> Tools and Windwos SDKs" before starting installation. .DESCRIPTION This cmdlet helps you to create Visual Studio C# projects targeting the CoreCLR and PowerShell reference assemblies for TP5 NanoServer. - You can choose to create a new project in a new solution, or add a new project to the currently open solution. - You can choose to have the project output either DLL library or EXE program. This cmdlet first checks if the initial setup is done. If not, it will try copying the reference assemblies to the reference assembly folder of Visual Studio 2015. This cmdlet depends on the Visual Studio feature "Windows and Web Development -> Windows 8.1 and Windows Phone 8.0/8.1 Tools -> Tools and Windwos SDKs" to create the projects. If it's not installed, please modify your Visual Studio 2015 to install the feature. .EXAMPLE PM> New-NanoCSharpProject -Path D:\Projects -ProjectName DISMCmdlet -SolutionName DISM -OutputType Library Create a new C# project in a new sulution targeting CoreCLR and PowerShell deployed in TP5 NanoServer. The new project produces a DLL. .EXAMPLE PM> New-NanoCSharpProject -Path D:\Temp -ProjectName Project2 -AddToCurrentSolution -OutputType EXE Create a new C# project in the currently open solution targeting CoreCLR and PowerShell deployed in TP5 NanoServer. The new project produces an EXE. .PARAMETER Path Path of the project .PARAMETER ProjectName Name of the new project .PARAMETER SolutionName Name of the new solution .PARAMETER OutputType Output file type of the new project .PARAMETER AddToCurrentSolution Indicate to create the new project in the currently open solution #> [CmdletBinding(DefaultParameterSetName = "NewSolution")] [Alias("nproj")] param( [parameter(Mandatory=$true, Position = 0)] [string] $Path, [parameter(Mandatory=$true, Position = 1)] [string] $ProjectName, [parameter(ParameterSetName = "NewSolution", Position = 2)] [ValidateNotNullOrEmpty()] [string] $SolutionName, [Parameter(Position = 3)] [ValidateSet("Library", "EXE")] [string] $OutputType = "Library", [parameter(ParameterSetName = "CurrentSolution")] [switch] $AddToCurrentSolution ) ## ## Cmdlet needs to run in Package Management Console in Visual Studio ## if ($Host.Name -ne "Package Manager Host") { $Message = $LocalizedData.NeedToRunInVisualStudio -f $LocalizedData.InstallVisualStudio2015 ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "SupportedInPackageManagerConsoleOnly" ` -CallerPSCmdlet $PSCmdlet ` -ExceptionObject $PSCmdlet ` -ErrorCategory InvalidOperation } ## ## Visual Studio 2015 is required for authoring/debugging CoreCLR code ## if ($DTE.Name -ne "Microsoft Visual Studio" -or $DTE.Version -ne "14.0") { $Message = $LocalizedData.RequireVisualStudio2015 -f $LocalizedData.InstallVisualStudio2015 ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "RequireVisualStudio2015" ` -CallerPSCmdlet $PSCmdlet ` -ExceptionObject $PSCmdlet ` -ErrorCategory InvalidOperation } ## ## Do initial setup if needed ## $CoreCLRRefPath = Join-Path -Path $Script:DotNetCoreRefAsmPath -ChildPath v0.1 $RefAsmInfoPath = Join-Path -Path $CoreCLRRefPath -ChildPath $Script:NanoPSRefAsmInfoFile $ModuleBasePath = $PSCmdlet.MyInvocation.MyCommand.Module.ModuleBase if (-not (Test-Path -Path $RefAsmInfoPath -PathType Leaf)) { $Message = $LocalizedData.AboutToDeployReferenceAssemblies -f $Script:DotNetCoreRefAsmPath Write-Verbose -Message $Message $SourceRefPath = Join-Path -Path $ModuleBasePath -ChildPath SDK\v0.1 $ScriptToRun = @' $WriteProgress = $Host.Name -eq 'Package Manager Host' $Activity = 'NanoServer PowerShell SDK Initial Setup' if (-not (Test-Path -Path '{0}' -PathType Container)) {{ ## Target 'v0.1' folder not exists $null = New-Item -Path '{0}' -ItemType Directory -Force }} else {{ ## Remove old reference assemblies from 'v0.1' folder if ($WriteProgress) {{ $Status = 'Remove Old CoreCLR Reference Assemblies' $ItemsToRemove = @(Get-ChildItem -Path '{0}') $ItemCount = $ItemsToRemove.Count for ($Index = 0; $Index -lt $ItemCount; $Index++) {{ Write-Progress -Activity $Activity -Status $Status -PercentComplete ($Index/$ItemCount*100) Remove-Item -Path $ItemsToRemove[$Index].FullName -Recurse -Force }} Write-Progress -Activity $Activity -Status $Status -Completed }} else {{ Remove-Item -Path '{0}\*' -Recurse -Force }} }} ## Copy TP5 reference assemblies to 'v0.1' folder if ($WriteProgress) {{ $Status = 'Copy TP5 NanoServer PowerShell Reference Assemblies' $ItemsToCopy = @(Get-ChildItem -Path '{1}') $ItemCount = $ItemsToCopy.Count for ($Index = 0; $Index -lt $ItemCount; $Index++) {{ Write-Progress -Activity $Activity -Status $Status -PercentComplete ($Index/$ItemCount*100) Copy-Item -Path $ItemsToCopy[$Index].FullName -Destination '{0}' -Recurse -Force }} Write-Progress -Activity $Activity -Status $Status -Completed }} else {{ Copy-Item -Path '{1}\*' -Destination '{0}' -Recurse -Force }} ## Write the info file [pscustomobject]@{{ NanoPS = 'TP5' }} | Export-Clixml -Path '{2}' -Force Get-Item '{2}' | % {{ $_.Attributes = [System.IO.FileAttributes]::Hidden }} '@ -f $CoreCLRRefPath, $SourceRefPath, $RefAsmInfoPath if (Test-AdminPrevilege) { $ScriptBlockToRun = [scriptblock]::Create($ScriptToRun) & $ScriptBlockToRun } else { $Message = $LocalizedData.NeedAdminPrevilegeMessage -f $Script:DotNetCoreRefAsmPath $Caption = $LocalizedData.NeedAdminPrevilegeCaption if (-not $PSCmdlet.ShouldContinue($Message, $Caption)) { $Message = $LocalizedData.InitialSetupCancelled -f $LocalizedData.FollowManualSteps Write-Warning -Message $Message return } $ByteArray = [System.Text.Encoding]::Unicode.GetBytes($ScriptToRun) $Base64EncodedScript = [System.Convert]::ToBase64String($ByteArray) try { $proc = Start-Process powershell -ArgumentList "-EncodedCommand $Base64EncodedScript" -Verb RunAs -WindowStyle Hidden -PassThru Wait-WithPesudoProgressBar -Process $proc if ($proc.ExitCode -ne 0) { $Message = $LocalizedData.StartProcessExecutionFailed -f $LocalizedData.FollowManualSteps ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "FailedToCopyReferenceAssemblies" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } } catch { $Message = $LocalizedData.StartProcessFailedToStart -f $_.Exception.Message, $LocalizedData.FollowManualSteps ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "FailedToStartAWorkingProcess" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation ` } } $Message = $LocalizedData.DoneDeployingReferenceAssemblies -f $CoreCLRRefPath Write-Verbose -Message $Message } ## ## Create the NanoPS CSharp Project ## $Message = $LocalizedData.AboutToCreateCSharpProject -f $ProjectName Write-Verbose -Message $Message try { ## Fetch the project template Write-Verbose -Message $LocalizedData.FetchProjectTemplate $TemplatePath = $DTE.Solution.GetProjectTemplate("Windows\Windows 8\Windows\Class Library (Windows 8.1)", "CSharp") ## Create the project if ($PSCmdlet.ParameterSetName -eq "NewSolution") { if (-not $PSCmdlet.MyInvocation.BoundParameters.ContainsKey("SolutionName")) { $SolutionName = $ProjectName } $Message = $LocalizedData.CreateProjectWithNewSolution -f $SolutionName Write-Verbose -Message $Message $SolutionDir = Join-Path -Path $Path -ChildPath $SolutionName $ProjectDir = Join-Path -Path $SolutionDir -ChildPath $ProjectName if (-not (Test-Path -Path $SolutionDir -PathType Container)) { $null = New-Item -Path $SolutionDir -ItemType Directory -Force } ## If a solution is currently open, close it first if ($DTE.Solution.IsOpen) { $Message = $LocalizedData.CloseCurrentlyOpenSolution -f ($DTE.Solution.Properties["Name"].Value) Write-Verbose -Message $Message $DTE.Solution.Close($true) } ## Create the new solution $DTE.Solution.Create($SolutionDir, $SolutionName) $SolutionFilePath = $dte.Solution.Properties["Path"].Value $DTE.Solution.SaveAs($SolutionFilePath) } else { $Message = $LocalizedData.CreateProjectInCurrentSolution -f ($DTE.Solution.Properties["Name"].Value) Write-Verbose -Message $Message ## ParameterSet is 'CurrentSolution', but no solution is currently open if (-not $DTE.Solution.IsOpen) { $Message = $LocalizedData.NoCurrentOpenSolution ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId "NoExistingOpenSolution" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } $ExistingProjects = $DTE.Solution.Projects | % ProjectName if ($ProjectName -in $ExistingProjects) { $Message = $LocalizedData.ProjectAlreadyExistsInCurrentSolution -f $ProjectName ThrowError -ExceptionName "System.ArgumentException" ` -ExceptionMessage $Message ` -ErrorId "ProjectAlreadyExists" ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation ` -ExceptionObject $ProjectName } $ProjectDir = Join-Path -Path $Path -ChildPath $ProjectName } ## Create the template project $DTE.Solution.AddFromTemplate($TemplatePath, $ProjectDir, $ProjectName, $false) ## Copy *.Targets files and update .csproj file $Message = $LocalizedData.DeployTargetFilesToProject -f $ProjectDir Write-Verbose -Message $Message Copy-Item -Path $ModuleBasePath\SDK\Microsoft.Coresys.Common.Targets -Destination $ProjectDir -Force Copy-Item -Path $ModuleBasePath\SDK\Microsoft.CoreSys.CSharp.Targets -Destination $ProjectDir -Force $ProjectObj = $DTE.Solution.Projects | ? ProjectName -eq $ProjectName $ProjectFile = $ProjectObj.FullName $SolutionExplorer = $DTE.Windows.Item([EnvDTE.Constants]::vsWindowKindSolutionExplorer) ## Save the project if not yet ## Because we are about to unload it if (-not $ProjectObj.Saved) { $ProjectObj.Save() } $Message = $LocalizedData.UpdateProjectFile -f $ProjectFile Write-Verbose -Message $Message ## Unload the project to update the project file $SolutionExplorer.Activate() $ProjectObj.DTE.ExecuteCommand("Project.UnloadProject") ## Update the project file Update-ProjectFile -ProjectFile $ProjectFile -OutputType $OutputType ## Reload the project after the update $SolutionExplorer.Activate() $ProjectObj.DTE.ExecuteCommand("Project.ReloadProject") ## Refresh the project object $ProjectObj = $DTE.Solution.Projects | ? ProjectName -eq $ProjectName ## Select 'Class1.cs' and open it in code editor $Class1SolutionPath = "$($DTE.Solution.Properties["Name"].Value)\$ProjectName\Class1.cs" $SolutionExplorer.Object.GetItem($Class1SolutionPath).Select([EnvDTE.vsUISelectionType]::vsUISelectionTypeSelect) $Class1FileItem = $ProjectObj.ProjectItems | ? Name -eq "Class1.cs" $Class1FilePath = $Class1FileItem.Properties["FullPath"].Value $DTE.ItemOperations.OpenFile($Class1FilePath, [EnvDTE.Constants]::vsViewKindCode) > $null } catch { if ($_.FullyQualifiedErrorId -eq "System.IO.FileNotFoundException") { $Message = $LocalizedData.RequireExtraVSFeature $ErrorId = "RequireExtraVSFeature" } else { $Message = $LocalizedData.UnknownDTEFailure -f $_.Exception.Message, $LocalizedData.FollowManualSteps $ErrorId = "UnknownDTEFailure" } ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $Message ` -ErrorId $ErrorId ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } $Message = $LocalizedData.DoneCreatingCSharpProject -f $ProjectName Write-Verbose -Message $Message ## ## Write out warning message about the 'Developer mode' prompt in Settings ## if ($PSCmdlet.ParameterSetName -eq "NewSolution") { $SettingProcess = Get-WmiObject Win32_Process -Filter "Name='SystemSettings.exe'" if ($SettingProcess -ne $null -and $SettingProcess.ExecutablePath -eq "$env:windir\ImmersiveControlPanel\SystemSettings.exe") { Write-Warning -Message $LocalizedData.DeveloperModeWarning } } } ## ## Write out the instructions for manually seting up NanoPS SDK and creating C# project targeting it ## function Get-SDKSetupSteps { <# .SYNOPSIS This cmdlet opens up the README.txt for manual setup, which contains the detailed instructions for creating C# projects targeting the CoreCLR and PowerShell reference assemblies for TP5 NanoServer. #> [CmdletBinding()] param() $SdkFolder = Join-Path -Path $PSCmdlet.MyInvocation.MyCommand.Module.ModuleBase -ChildPath SDK $ReadmePath = Join-Path -Path $SdkFolder -ChildPath README.txt Invoke-Item -Path $SdkFolder & "$env:windir\system32\notepad.exe" $ReadmePath } |