functions/New-WELCEventChannelManifest.ps1
function New-WELCEventChannelManifest { <# .SYNOPSIS New-WELCEventChannelManifest Creates Manifest- and DLL-file for register custom Windows EventLog Channels .DESCRIPTION Creates Manifest- and DLL-file for register custom Windows EventLog Channels Once compiled, the files can be registered into a Windows EventLog system to allow custom logs. .PARAMETER InputObject WELC.Channeldefinition object to create manifest and the dll file from Can be piped in with somthing like: Import-WELCChannelDefinition -Path C:\EventLogs\WinEventLogCustomization.xlsx -OutputChannelDefinition Excel template file can be created/opened with Open-WELCExcelTemplate .PARAMETER ChannelFullName The full name for a EventChannel. Valid Formats are: "FolderRoot/ChannelName" "FolderRoot/SubFolder1/SubFolder2/ChannelName" String have to be specified correctly with slashes ("/"), NOT with backslash ("\")! .PARAMETER FolderRoot Name of the folder within the Windows EventLog System under "Application and Services" .PARAMETER FolderSecondLevel Name of the folder within the "FolderRoot" .PARAMETER FolderThirdLevel Name of the folder within the "FolderSecondLevel" .PARAMETER ChannelName Name of the EventChannel within it's folder .PARAMETER ChannelSymbol Name of the ChannelSymbol Optional. If not specified, the name will be derived from the ChannelName .PARAMETER ProviderName Name of the EventLog Provider Optional. If not specified, the name will be derived from the folder name(s) .PARAMETER ProviderSymbol Name of the ProviderSymbol Optional. If not specified, the name will be derived from the ProviderName .PARAMETER OutputFile The filename of the manifest to create. Please specify only a FILE name, not a full qualified path If not specified, the name will be derived from the ChannelFullName/ ChannelName .PARAMETER DestinationPath Output path for manifest- and the dll file to register an EventChannel within the Windows EventLog system .PARAMETER WhatIf If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. .PARAMETER Confirm If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. .NOTES Author: Andreas Bellstedt Adopted from Russell Tomkins "Project Sauron" Author: Russell Tomkins Github: https://www.github.com/russelltomkins/ProjectSauron Originbal description: --------------------- Name: Create-Manifest.ps1 Version: 1.1 Author: Russell Tomkins - Microsoft Premier Field Engineer Blog: https://aka.ms/russellt Refer to this blog series for more details http://blogs.technet.microsoft.com/russellt/2017/03/23/project-sauron-part-1 .LINK https://github.com/AndiBellstedt/WinEventLogCustomization .EXAMPLE PS C:\> New-WELCEventChannelManifest -InputObject $ChannelDefinition Creates the manfifest- and DLL-file to register a custom EventLog channel in Windows Output depend on content in Excel file. Each root channel will be a manifest- (,man) and a DLL-file. Assuming that the variable $ChannelDefinition contains a WELC.ChannelDefinition object(list) PS C:\> $ChannelDefinition = Import-WELCChannelDefinition -Path CustomEventLogChannel.xlsx .EXAMPLE PS C:\> Import-WELCChannelDefinition -Path CustomEventLogChannel.xlsx | New-WELCEventChannelManifest Creates the Manfifest file and compile dll file(s) from the content of 'CustomEventLogChannel.xlsx' .EXAMPLE PS C:\> New-WELCEventChannelManifest -ChannelFullName "ChannelFolder/ChannelName" Creates a manifest (ChannelFolder.man) and compile a dll file (ChannelFolder.dll) with a single EventLogChannel "ChannelName" and a folder "ChannelFolder" .EXAMPLE PS C:\> "MyFolder/MyChannel1", "MyFolder/MyChannel2", "MyFolder/MyChannel3", "MyFolder/MyChannel4" | New-WELCEventChannelManifest Creates a manifest (MyChannel.man) and compile a dll file (MyChannel.dll) with 4 EventLogChannels MyChannel1-4 in the folder "MyFolder" #> [CmdletBinding( SupportsShouldProcess = $true, PositionalBinding = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ManualDefinition' )] Param( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "InputObject", Position = 0 )] [ValidateNotNullOrEmpty()] [Alias("Object", "In", "ChannelDefinition")] [WELC.ChannelDefinition[]] $InputObject, [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "ManualFullChannelName", Position = 0 )] [ValidateNotNullOrEmpty()] [Alias("FullName")] [String] $ChannelFullName, [Parameter(ParameterSetName = "ManualFullChannelName")] [String] $ChannelSymbol, [Parameter(ParameterSetName = "ManualFullChannelName")] [String] $ProviderName, [Parameter(ParameterSetName = "ManualFullChannelName")] [String] $ProviderSymbol, [Parameter( Mandatory = $true, ParameterSetName = "ManualDefinition", Position = 0 )] [ValidateNotNullOrEmpty()] [Alias("Root", "FolderNameRoot", "RootFolderName")] [String] $FolderRoot, [Parameter( Mandatory = $false, ParameterSetName = "ManualDefinition", Position = 1 )] [ValidateNotNullOrEmpty()] [Alias("SecondLevel", "FolderNameSecondLevel", "SecondLevelFolderName")] [String] $FolderSecondLevel, [Parameter( Mandatory = $false, ParameterSetName = "ManualDefinition", Position = 2 )] [ValidateNotNullOrEmpty()] [Alias("ThirdLevel", "FolderNameThirdLevel", "ThirdLevelFolderName")] [String] $FolderThirdLevel, [Parameter( Mandatory = $true, ParameterSetName = "ManualDefinition", Position = 3 )] [ValidateNotNullOrEmpty()] [String] $ChannelName, [Alias("FileName")] [String] $OutputFile, [ValidateNotNullOrEmpty()] [Alias("OutputPath")] [String] $DestinationPath = ".\" ) Begin { #region Constants Write-PSFMessage -Level Debug -Message "Initalizing constants" # Path to csc.exe in windows [String]$WindowsCSCPath = "$($env:windir)\Microsoft.NET\Framework64\v4.0.30319" Write-PSFMessage -Level Debug -Message ".NET Framework in '$($WindowsCSCPath)'" # Compilation tools from the windows SDK. The required executables are "mc.exe", "rc.exe" and "rcdll.dll". There is another tool "ecmangen.exe" (EventChannel ManifestGenerator) which is usefull to check and maintain the manifest files. [String]$CompilationToolPath = "$($MyInvocation.MyCommand.Module.ModuleBase)\bin" Write-PSFMessage -Level Debug -Message "Binary CompilationTool is in '$($CompilationToolPath)'" # Path where the output files, and some other temp files from the compilation process are stored. [String]$TempPath = "$($env:TEMP)\WELC_$([guid]::NewGuid().guid)" Write-PSFMessage -Level Debug -Message "Operating in temporary path '$($TempPath)'" #endregion Constants #region Variables $channelDefinitions = @() #endregion Variables #region Validity checks Write-PSFMessage -Level Debug -Message "Initial parameter validation" # Check for required resscoures und compilation folder if ($DestinationPath.EndsWith('\')) { $DestinationPath = $DestinationPath.TrimEnd('\') } $DestinationPath = Resolve-Path $DestinationPath -ErrorAction Stop | Select-Object -ExpandProperty Path # Check for temp folder if ($TempPath.EndsWith('\')) { $TempPath = $TempPath.TrimEnd('\') } if (Test-Path -Path $TempPath -IsValid) { if (-not (Test-Path -Path $TempPath -PathType Container)) { Write-PSFMessage -Level Debug -Message "Creating temporary directory '$($TempPath)'" New-Item -Path $TempPath -ItemType Directory -Force | Out-Null $TempPath = Resolve-Path $TempPath -ErrorAction Stop | Select-Object -ExpandProperty Path } } else { throw "$($TempPath) is not a valid path" } # Check for required resscoures und compilation folder if ($CompilationToolPath.EndsWith('\')) { $CompilationToolPath = $CompilationToolPath.TrimEnd('\') } Resolve-Path $CompilationToolPath -ErrorAction Stop | Out-Null Test-Path -Path "$($CompilationToolPath)\mc.exe" -ErrorAction Stop | Out-Null Test-Path -Path "$($CompilationToolPath)\rc.exe" -ErrorAction Stop | Out-Null Test-Path -Path "$($CompilationToolPath)\rcdll.dll" -ErrorAction Stop | Out-Null Write-PSFMessage -Level Debug -Message "Binary tools found in CompilationTool path '$($CompilationToolPath)'" #endregion Validity checks } Process { Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($PsCmdlet.ParameterSetName)" switch ($pscmdlet.ParameterSetName) { "InputObject" { $channelDefinitions += foreach ($item in $InputObject) { $item } } "ManualFullChannelName" { $channelDefinitions += foreach ($_channelFullName in $ChannelFullName) { # Validate the parameters - if SecondLevel is specified, ThirdLevel has to be present also Write-PSFMessage -Level Debug -Message "Validating ChannelFullName '$($_channelFullName)'" if ($_channelFullName -match (Get-PSFConfigValue -FullName WinEventLogCustomization.MatchString.ChannelName)) { $_channelName = $_channelFullName } else { Stop-PSFFunction -Message "Invalid format on ChannelFullName '$($_channelFullName)'. Valid format for ChannelFullName must be somthing like 'FolderRoot-FolderSecondLevel-FolderThirdLevel/ChannelName' or 'FolderRoot/ChannelName'" -EnableException $true } if (-not $ChannelSymbol) { $_channelSymbol = [String]::Join("_", $_channelFullName.Split("-").Split("/").ToUpper()) Write-PSFMessage -Level Debug -Message "ChannelSymbol not specified. Derive value '$($_channelSymbol)' from ChannelFullName" } else { if ($ChannelSymbol -match (Get-PSFConfigValue -FullName WinEventLogCustomization.MatchString.ChannelSymbol)) { $_channelSymbol = $ChannelSymbol.ToUpper() } else { Stop-PSFFunction -Message "Invalid format on ChannelSymbol '$($ChannelSymbol)'. Valid format for ChannelSymbol must be somthing like 'FolderRoot_FolderSecondLevel_FolderThirdLevel_ChannelName' or 'FolderRoot_ChannelName'" -EnableException $true } } if (-not $ProviderName) { $_providerName = $_channelFullName.Replace( "/$($_channelFullName.Split("/")[-1])", "") Write-PSFMessage -Level Debug -Message "ProviderName not specified. Derive value '$($_providerName)' from ChannelFullName" } else { if ($ProviderName -match (Get-PSFConfigValue -FullName WinEventLogCustomization.MatchString.ProviderName)) { $_providerName = $ProviderName } else { Stop-PSFFunction -Message "Invalid format on ProviderName '$($ProviderName)'. Valid format for ProviderName must be somthing like 'FolderRoot-FolderSecondLevel-FolderThirdLevel' or 'FolderRoot'" -EnableException $true } } if (-not $ProviderSymbol) { $_providerSymbol = [String]::Join("_", ($_channelFullName.Replace( "/$($_channelFullName.Split("/")[-1])", "")).Split("-").ToUpper()) Write-PSFMessage -Level Debug -Message "ChannelSymbol not specified. Derive value '$($_providerSymbol)' from ChannelFullName" } else { if ($ProviderSymbol -match (Get-PSFConfigValue -FullName WinEventLogCustomization.MatchString.ProviderSymbol)) { $_providerSymbol = $ProviderSymbol.ToUpper() } else { Stop-PSFFunction -Message "Invalid format on ProviderSymbol '$($ProviderSymbol)'. Valid Format for ProviderSymbol must be somthing like 'FolderRoot-FolderSecondLevel-FolderThirdLevel' or 'FolderRoot'" -EnableException $true } } # Create custom "WEC.ChannelDefinition" object Write-PSFMessage -Level Verbose -Message "Create ChannelDefinition object from '$($_channelFullName)'" [PSCustomObject]@{ ProviderSymbol = $_providerSymbol ProviderName = $_providerName ChannelName = $_channelName ChannelSymbol = $_channelSymbol } # Cleanup the mess of variables Write-PSFMessage -Level Debug -Message "Cleanup variables" Remove-Variable _channelSymbol, _channelName, _providerSymbol, _providerName -Force -ErrorAction Ignore -WarningAction Ignore -Verbose:$false -Confirm:$false -WhatIf:$false -Debug:$false } } "ManualDefinition" { # Validate the parameters - if SecondLevel is specified, ThirdLevel has to be present also if ($FolderSecondLevel -and ($null -eq $FolderThirdLevel)) { Write-PSFMessage -Level Warning -Message "Parameter 'FolderSecondLevel' was specified, but 'FolderThirdLevel' is missing." Write-PSFMessage -Level Warning -Message "By design, only 'FolderRoot' or all the FolderPaths has to be specified." Stop-PSFFunction -Message "Aborting creation." } # Build variables for custom ChannelDefinition object Write-PSFMessage -Level Debug -Message "Arranging data for ChannelDefinition object" [Array]$_folderNames = $FolderRoot, $FolderSecondLevel, $FolderThirdLevel | ForEach-Object { if ($_) { $_ } } [Array]$_providerSymbols = $FolderRoot.toupper(), $FolderSecondLevel.toupper(), $FolderThirdLevel.toupper() | ForEach-Object { if ($_) { $_ } } [Array]$_channelSymbols = $_providerSymbols + $ChannelName.toupper() $_providerName = [String]::Join("-", $_folderNames) $_providerSymbol = [String]::Join("_", $_providerSymbols) $_channelName = [String]::Join("-", $_folderNames) + "/" + $ChannelName $_channelSymbol = [String]::Join("_", $_channelSymbols) # Create custom "WEC.ChannelDefinition" object Write-PSFMessage -Level Verbose -Message "Create ChannelDefinition object for '$($_channelName)'" $channelDefinitions = [PSCustomObject]@{ ProviderSymbol = $_providerSymbol ProviderName = $_providerName ChannelName = $_channelName ChannelSymbol = $_channelSymbol } # Cleanup the mess of variables Write-PSFMessage -Level Debug -Message "Cleanup variables" Remove-Variable _channelSymbol, _channelName, _providerSymbol, _providerName, _channelSymbols, _providerSymbols, _folderNames -Force -ErrorAction Ignore -Confirm:$false -WhatIf:$false -Debug:$false } Default { throw "Undefined ParameterSet. Developers mistake." } } } End { Write-PSFMessage -Level Verbose -Message "Collected $($channelDefinitions.Count) channel definition$(if($channelDefinitions.Count -gt 1){"s"})" [array]$baseNames = $channelDefinitions | Select-Object -ExpandProperty ProviderName | Foreach-Object { $_.split("-")[0] } | Sort-Object -Unique Write-PSFMessage -Level Verbose -Message "Going to create $($baseNames.Count) manifest file$(if($baseNames.Count -gt 1){"s"}) from collected channel definition$(if($channelDefinitions.Count -gt 1){"s"})" foreach ($baseName in $baseNames) { # Shorten Name for file if ($pscmdlet.ParameterSetName -like "InputObject") { if ($OutputFile) { $OutputFile = $OutputFile.Replace( ".$($OutputFile.Split(".")[-1])", "") $fileName = $OutputFile + "_" + $baseName.Replace(" ", "") } else { $fileName = $baseName.Replace(" ", "") } } else { if ($OutputFile) { $fileName = $OutputFile.Replace( ".$($OutputFile.Split(".")[-1])", "") } else { $fileName = $baseName.Replace(" ", "") } } # The Resource and Message DLL that will be referenced in the manifest $fileNameDLL = $fileName + ".dll" $fullNameDLLTemp = $TempPath + "\" + $fileNameDLL $fullNameDLLDestination = $DestinationPath + "\" + $fileNameDLL # The Manifest file $fileNameManifest = $fileName + ".man" $fullNameManifestTemp = $TempPath + "\" + $fileNameManifest Write-PSFMessage -Level Verbose -Message "Arraging manifest: $($fileName) ('$($DestinationPath + "\" + $fileNameManifest)', '$($fullNameDLLDestination)')" # Filter down the the full channel list $channelSelection = $channelDefinitions | Where-Object ProviderName -like "$($baseName)*" # Extract the provider information from input $providers = $channelSelection | Select-Object -Property ProviderSymbol, ProviderName -Unique | Foreach-Object { $_ | Select-Object *, @{n = "ProviderGuid"; e = { ([guid]::NewGuid()).Guid } } } #region Create the manifest XML document Write-PSFMessage -Level Verbose -Message "Working on group '$($baseName)' with $(([array]$channelSelection).Count) channel definitions in $(([array]$providers).count) folders" #region Basic XML object definition Write-PSFMessage -Level Debug -Message "Start building manifest XML document" # Create the manifest XML document $XmlWriter = [System.XMl.XmlTextWriter]::new($fullNameManifestTemp, $null) # Set the formatting $xmlWriter.Formatting = "Indented" $xmlWriter.Indentation = "4" # Write the XML decleration $xmlWriter.WriteStartDocument() # Create instrumentation manifest $xmlWriter.WriteStartElement("instrumentationManifest") $xmlWriter.WriteAttributeString("xsi:schemaLocation", "http://schemas.microsoft.com/win/2004/08/events eventman.xsd") $xmlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/win/2004/08/events") $xmlWriter.WriteAttributeString("xmlns:win", "http://manifests.microsoft.com/win/2004/08/windows/events") $xmlWriter.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") $xmlWriter.WriteAttributeString("xmlns:xs", "http://www.w3.org/2001/XMLSchema") $xmlWriter.WriteAttributeString("xmlns:trace", "http://schemas.microsoft.com/win/2004/08/events/trace") # Create instrumentation, events and provider elements $xmlWriter.WriteStartElement("instrumentation") $xmlWriter.WriteStartElement("events") #endregion Basic XML object definition foreach ($provider in $providers) { Write-PSFMessage -Level Verbose -Message "Writing provider '$($provider.ProviderName)' (GUID:$($provider.ProviderGUID))" # Start the provider $xmlWriter.WriteStartElement("provider") $xmlWriter.WriteAttributeString("name", $provider.ProviderName) $xmlWriter.WriteAttributeString("guid", "{$($provider.ProviderGUID)}") $xmlWriter.WriteAttributeString("symbol", $provider.ProviderSymbol) $xmlWriter.WriteAttributeString("resourceFileName", $fullNameDLLDestination) $xmlWriter.WriteAttributeString("messageFileName", $fullNameDLLDestination) $xmlWriter.WriteAttributeString("parameterFileName", $fullNameDLLDestination) # Start channels collection $xmlWriter.WriteStartElement("channels") [array]$channels = $channelSelection | Where-Object ProviderSymbol -eq $provider.ProviderSymbol | Select-Object -Property ChannelName, ChannelSymbol ForEach ($channelItem in $channels) { Write-PSFMessage -Level Verbose -Message "Writing channel '$($channelItem.ChannelName)'" # Start the channel $xmlWriter.WriteStartElement("channel") $xmlWriter.WriteAttributeString("name", $channelItem.ChannelName) $xmlWriter.WriteAttributeString("chid", ($channelItem.ChannelName).Replace(' ', '')) $xmlWriter.WriteAttributeString("symbol", $channelItem.ChannelSymbol) $xmlWriter.WriteAttributeString("type", "Admin") $xmlWriter.WriteAttributeString("enabled", "false") # Closing the channel $xmlWriter.WriteEndElement() } # Closing the channels $xmlWriter.WriteEndElement() # Closing the provider $xmlWriter.WriteEndElement() } #region Basic XML object definition $xmlWriter.WriteEndElement() # Closing events $xmlWriter.WriteEndElement() # Closing Instrumentation $xmlWriter.WriteEndElement() # Closing instrumentationManifest # End the XML Document $xmlWriter.WriteEndDocument() # Finish The Document $xmlWriter.Finalize $xmlWriter.Flush() $xmlWriter.Close() #endregion Basic XML object definition Write-PSFMessage -Level Verbose -Message "Manifest file '$($fileNameManifest)' has been generated ($( [math]::Round( ((Get-ChildItem -Path $fullNameManifestTemp).length / 1KB),1))KB)" #endregion Create The Manifest XML Document #region Compile the manifest to DLL Write-PSFMessage -Level Verbose -Message "Starting the compilation process on '$($fileNameDLL)'" $tempFilesExisting = @() $finalFilesExisting = @() $finalFilesExpected = @($fullNameManifestTemp, $fullNameDLLTemp) #region generates "**.h", "**.rc" and "**TEMP.BIN" file from xml manifest Write-PSFMessage -Level Debug -Message "Generate '$($fileName).h', '$($fileName).rc' and '$($fileName)TEMP.BIN' files from xml manifest" $tempFilesExpected = @("$($TempPath)\$($fileName).h", "$($TempPath)\$($fileName).rc", "$($TempPath)\$($fileName)TEMP.BIN") $tempFilesExpected | Get-ChildItem -ErrorAction SilentlyContinue | Remove-Item -Force -Confirm:$false Start-Process ` -FilePath "$($CompilationToolPath)\mc.exe" ` -ArgumentList $fullNameManifestTemp ` -WorkingDirectory $TempPath ` -NoNewWindow ` -Wait Write-PSFMessage -Level Debug -Message "Validating generated files" foreach ($tempFile in $tempFilesExpected) { if (Test-Path -Path $tempFile -NewerThan (Get-Date).AddSeconds(-5)) { $tempFilesExisting += Get-ChildItem $tempFile -ErrorAction Stop } else { Stop-PSFFunction -Message "Expected temp file '$($tempFile)' is present, but has a too old timestamp. Something went wrong. Aborting process" -EnableException $true } } Write-PSFMessage -Level Debug -Message "File generated: $([string]::Join(", ", $tempFilesExpected))" #endregion generates "**.h", "**.rc" and "**TEMP.BIN" file from xml manifest #region generates "**.cs" file from xml manifest Write-PSFMessage -Level Debug -Message "Generate '$($fileName).cs' file from xml manifest" $tempFilesExpected = @( "$($TempPath)\$($fileName).cs" ) $tempFilesExpected | Get-ChildItem -ErrorAction SilentlyContinue | Remove-Item -Force -Confirm:$false Start-Process ` -FilePath "$($CompilationToolPath)\mc.exe" ` -ArgumentList "-css NameSpace $($fullNameManifestTemp)" ` -WorkingDirectory $TempPath ` -NoNewWindow ` -Wait Write-PSFMessage -Level Debug -Message "Validating generated '$($fileName).cs' file" foreach ($tempFile in $tempFilesExpected) { if (Test-Path -Path $tempFile -NewerThan (Get-Date).AddSeconds(-5)) { $tempFilesExisting += Get-ChildItem $tempFile -ErrorAction Stop } else { Stop-PSFFunction -Message "Expected temp file '$($tempFile)' is present, but has a too old timestamp. Something went wrong. Aborting process" -EnableException $true } } Write-PSFMessage -Level Debug -Message "CS file generated: $([string]::Join(", ", $tempFilesExpected)) " #endregion generates "**.cs" file from xml manifest #region generates "**.res" file from xml manifest Write-PSFMessage -Level Debug -Message "Generate '$fileName).res' file from '$($fileName).rc' file" $tempFilesExpected = @("$($TempPath)\$($fileName).res") $tempFilesExpected | Get-ChildItem -ErrorAction SilentlyContinue | Remove-Item -Force -Confirm:$false Start-Process ` -FilePath "$($CompilationToolPath)\rc.exe" ` -ArgumentList "$($fileName).rc" ` -WorkingDirectory $TempPath ` -Wait ` -WindowStyle Hidden Write-PSFMessage -Level Debug -Message "Validating generated '$($fileName).res' file" foreach ($tempFile in $tempFilesExpected) { if (Test-Path -Path $tempFile -NewerThan (Get-Date).AddSeconds(-5)) { $tempFilesExisting += Get-ChildItem $tempFile -ErrorAction Stop } else { Stop-PSFFunction -Message "Expected temp file '$($tempFile)' is present, but has a too old timestamp. Something went wrong. Aborting process" -EnableException $true } } Write-PSFMessage -Level Debug -Message "Res file generated: $([string]::Join(", ", $tempFilesExpected)) " #endregion generates "**.res" file from xml manifest #region final compilation of the dll file Write-PSFMessage -Level Debug -Message "Finally compiling '$fileName).dll' file from generated meta files" Start-Process ` -FilePath "$($WindowsCSCPath)\csc.exe" ` -ArgumentList "/win32res:$($TempPath)\$($fileName).res /unsafe /target:library /out:$($TempPath)\$($fileName).dll $($TempPath)\$($fileName).cs" ` -WorkingDirectory $TempPath ` -Wait ` -WindowStyle Hidden Write-PSFMessage -Level Debug -Message "Validating generated '$($fileName).dll' file" foreach ($FinalFile in $finalFilesExpected) { if (Test-Path -Path $FinalFile -NewerThan (Get-Date).AddSeconds(-15)) { $finalFilesExisting += Get-ChildItem $FinalFile -ErrorAction Stop } else { Stop-PSFFunction -Message "Expected temp file '$($FinalFile)' is present, but has a too old timestamp. Something went wrong. Aborting process" -EnableException $true } } Write-PSFMessage -Level Debug -Message "DLL file generated: $($TempPath)\$($fileName).dll" if ($pscmdlet.ShouldProcess("'$($fileNameManifest)' and '$($fileNameDLL)' in '$($DestinationPath)'", "Create")) { Write-PSFMessage -Level Verbose -Message "Writing final $($finalFilesExisting.Count) files to '$($DestinationPath)'" $finalFilesExisting | Copy-Item -Destination $DestinationPath -Force -ErrorAction Stop } #endregion final compilation of the dll file Write-PSFMessage -Level Verbose -Message "Finished process group '$($baseName)'" #endregion Compile the manifest to DLL } #region Cleanup Write-PSFMessage -Level Verbose -Message "Cleaning up temporary path '$($TempPath)'" Remove-Item -Path $TempPath -Force -Recurse -ErrorAction SilentlyContinue #endregion Cleanup } } |