MemoryMappedFile.psm1
[Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidAssignmentToAutomaticVariable', 'IsWindows', Justification = 'IsWindows doesnt exist in PS5.1' )] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseDeclaredVarsMoreThanAssignments', 'IsWindows', Justification = 'IsWindows doesnt exist in PS5.1' )] [CmdletBinding()] param() $baseName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath) $script:PSModuleInfo = Import-PowerShellDataFile -Path "$PSScriptRoot\$baseName.psd1" $script:PSModuleInfo | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } $scriptName = $script:PSModuleInfo.Name Write-Debug "[$scriptName] - Importing module" if ($PSEdition -eq 'Desktop') { $IsWindows = $true } #region [functions] - [private] Write-Debug "[$scriptName] - [functions] - [private] - Processing folder" #region [functions] - [private] - [Get-InternalPSModule] Write-Debug "[$scriptName] - [functions] - [private] - [Get-InternalPSModule] - Importing" function Get-InternalPSModule { <# .SYNOPSIS Performs tests on a module. .EXAMPLE Test-PSModule -Name 'World' "Hello, World!" #> [CmdletBinding()] param ( # Name of the person to greet. [Parameter(Mandatory)] [string] $Name ) Write-Output "Hello, $Name!" } Write-Debug "[$scriptName] - [functions] - [private] - [Get-InternalPSModule] - Done" #endregion [functions] - [private] - [Get-InternalPSModule] #region [functions] - [private] - [Set-InternalPSModule] Write-Debug "[$scriptName] - [functions] - [private] - [Set-InternalPSModule] - Importing" function Set-InternalPSModule { <# .SYNOPSIS Performs tests on a module. .EXAMPLE Test-PSModule -Name 'World' "Hello, World!" #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', Justification = 'Reason for suppressing' )] [CmdletBinding()] param ( # Name of the person to greet. [Parameter(Mandatory)] [string] $Name ) Write-Output "Hello, $Name!" } Write-Debug "[$scriptName] - [functions] - [private] - [Set-InternalPSModule] - Done" #endregion [functions] - [private] - [Set-InternalPSModule] Write-Debug "[$scriptName] - [functions] - [private] - Done" #endregion [functions] - [private] #region [functions] - [public] Write-Debug "[$scriptName] - [functions] - [public] - Processing folder" #region [functions] - [public] - [Close-MemoryMappedFile] Write-Debug "[$scriptName] - [functions] - [public] - [Close-MemoryMappedFile] - Importing" function Close-MemoryMappedFile { <# .SYNOPSIS Closes and disposes an existing memory-mapped file by name. .DESCRIPTION This function attempts to open an existing memory-mapped file with the specified name. If the file exists, it disposes of the file and frees the associated memory resources. If the file does not exist or has already been closed, a warning is displayed instead. This operation is useful for cleaning up unmanaged resources created with memory-mapped files. .EXAMPLE Close-MemoryMappedFile -Name 'MySharedMemory' Output: ```powershell VERBOSE: Memory-mapped file 'MySharedMemory' closed successfully. ``` Closes the memory-mapped file named 'MySharedMemory' and disposes of its resources. .LINK https://psmodule.io/MemoryMappedFile/Functions/Close-MemoryMappedFile #> [CmdletBinding()] param( # The name of the memory-mapped file to close and dispose. [Parameter(Mandatory)] [string] $Name ) try { $item = [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting($Name) } catch { return $null } if ($item) { $item.Dispose() Write-Verbose "Memory-mapped file '$Name' closed successfully." } } #SkipTest:FunctionTest:Will add a test for this function in a future PR Write-Debug "[$scriptName] - [functions] - [public] - [Close-MemoryMappedFile] - Done" #endregion [functions] - [public] - [Close-MemoryMappedFile] #region [functions] - [public] - [Get-MemoryMappedFile] Write-Debug "[$scriptName] - [functions] - [public] - [Get-MemoryMappedFile] - Importing" function Get-MemoryMappedFile { <# .SYNOPSIS Retrieves an existing memory-mapped file by name. .DESCRIPTION This function attempts to open an existing memory-mapped file identified by the provided name. If the file does not exist or an error occurs during access, the function returns $null instead of throwing an exception. .EXAMPLE Get-MemoryMappedFile -Name 'SharedMemoryBlock' Output: ```powershell SafeMemoryMappedFileHandle -------------------------- Microsoft.Win32.SafeHandles.SafeMemoryMappedFileHandle ``` Retrieves the memory-mapped file named 'SharedMemoryBlock'. .OUTPUTS System.IO.MemoryMappedFiles.MemoryMappedFile .NOTES The memory-mapped file object if successful. null. Returned when the specified memory-mapped file does not exist or an error occurs. .LINK https://psmodule.io/Memory/Functions/Get-MemoryMappedFile #> [OutputType([System.IO.MemoryMappedFiles.MemoryMappedFile])] [CmdletBinding()] param( # The name of the memory-mapped file to open. [Parameter(Mandatory)] [string] $Name ) try { return [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting($Name) } catch { return $null } } #SkipTest:FunctionTest:Will add a test for this function in a future PR Write-Debug "[$scriptName] - [functions] - [public] - [Get-MemoryMappedFile] - Done" #endregion [functions] - [public] - [Get-MemoryMappedFile] #region [functions] - [public] - [New-MemoryMappedFile] Write-Debug "[$scriptName] - [functions] - [public] - [New-MemoryMappedFile] - Importing" function New-MemoryMappedFile { <# .SYNOPSIS Creates a memory-mapped file from a specified file path and name. .DESCRIPTION This function creates a memory-mapped file using a specified name and backing file path. If the file exists, it uses the existing file and its size. If the file does not exist, a new file is created at the specified path. The memory-mapped file is created with a default size of 1MB unless otherwise specified. The function returns a [System.IO.MemoryMappedFiles.MemoryMappedFile] object. .EXAMPLE New-MemoryMappedFile -Name 'SharedMap' -Path 'C:\Temp\shared.dat' Output: ```powershell Capacity : 1048576 SafeMemoryMappedFileHandle : Microsoft.Win32.SafeHandles.SafeMemoryMappedFileHandle ``` Creates a memory-mapped file named 'SharedMap' backed by 'C:\Temp\shared.dat' with a default size of 1MB. .OUTPUTS System.IO.MemoryMappedFiles.MemoryMappedFile .NOTES Represents the memory-mapped file created using the specified parameters. The returned object can be used for inter-process communication or efficient file access. .LINK https://psmodule.io/MemoryMapped/Functions/New-MemoryMappedFile/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'The function only creates a memory-mapped file and does not modify system state in a way that requires confirmation.' )] [OutputType([System.IO.MemoryMappedFiles.MemoryMappedFile])] [CmdletBinding()] param( # The unique name for the memory-mapped file. [Parameter(Mandatory)] [string] $Name, # Specifies the path to the file backing the memory-mapped file. [Parameter(Mandatory)] [string] $Path, # Specifies the size of the memory-mapped file in bytes. Defaults to 1MB. [Parameter()] [long] $Size = 1Mb ) if (Test-Path $Path) { $file = Get-Item -Path $Path $Size = $file.Length if ($Size -gt 0) { Write-Verbose "Opening existing file from '$Path' as '$Name'" return [System.IO.MemoryMappedFiles.MemoryMappedFile]::CreateFromFile( $Path, [System.IO.FileMode]::OpenOrCreate, $Name ) } } Write-Verbose "Opening new file at '$Path' as '$Name'" return [System.IO.MemoryMappedFiles.MemoryMappedFile]::CreateFromFile( $Path, [System.IO.FileMode]::OpenOrCreate, $Name, $Size ) } #SkipTest:FunctionTest:Will add a test for this function in a future PR Write-Debug "[$scriptName] - [functions] - [public] - [New-MemoryMappedFile] - Done" #endregion [functions] - [public] - [New-MemoryMappedFile] #region [functions] - [public] - [Read-MemoryMappedFileContent] Write-Debug "[$scriptName] - [functions] - [public] - [Read-MemoryMappedFileContent] - Importing" function Read-MemoryMappedFileContent { <# .SYNOPSIS Reads the content of an existing memory-mapped file. .DESCRIPTION This function opens a memory-mapped file by name and reads its entire content as a UTF-8 encoded string. If the file does not exist or cannot be accessed, the function returns null. Leading and trailing null characters are trimmed from the result. .EXAMPLE Read-MemoryMappedFileContent -Name 'SharedBuffer' Output: ```powershell Hello from shared memory! ``` Reads the contents of the memory-mapped file named 'SharedBuffer' and outputs the decoded string. .OUTPUTS string .NOTES The UTF-8 decoded string content of the memory-mapped file. Returns null if the file is inaccessible. .LINK https://psmodule.io/MemoryMapped/Functions/Read-MemoryMappedFileContent #> [OutputType([string])] [CmdletBinding()] param( # The unique name for the memory-mapped file. [Parameter(Mandatory)] [string] $Name ) begin {} process { try { $mmf = [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting($Name) } catch { return $null } $accessor = $mmf.CreateViewAccessor() $buffer = New-Object byte[] $accessor.Capacity $null = $accessor.ReadArray(0, $buffer, 0, $accessor.Capacity) $content = ([System.Text.Encoding]::UTF8.GetString($buffer)).Trim([char]0) return $content } end {} clean { if ($accessor) { $accessor.Dispose() } } } #SkipTest:FunctionTest:Will add a test for this function in a future PR Write-Debug "[$scriptName] - [functions] - [public] - [Read-MemoryMappedFileContent] - Done" #endregion [functions] - [public] - [Read-MemoryMappedFileContent] #region [functions] - [public] - [Set-MemoryMappedFile] Write-Debug "[$scriptName] - [functions] - [public] - [Set-MemoryMappedFile] - Importing" function Set-MemoryMappedFile { <# .SYNOPSIS Creates or returns an existing memory-mapped file by name. .DESCRIPTION Checks whether a memory-mapped file with the given name already exists. If it does, returns it. If not, creates a new memory-mapped file backed by the specified path and size. This function ensures idempotent access to shared memory regions through named mapping. .EXAMPLE Set-MemoryMappedFile -Name 'MySharedMap' -Path 'C:\temp\shared.dat' -Size 2MB Output: ```powershell Capacity : 2097152 Name : MySharedMap SafeMemoryMappedFileHandle : Microsoft.Win32.SafeHandles.SafeMemoryMappedFileHandle ``` Returns a new memory-mapped file named 'MySharedMap' backed by 'C:\temp\shared.dat' with a size of 2MB. .OUTPUTS System.IO.MemoryMappedFiles.MemoryMappedFile .NOTES Represents the created or retrieved memory-mapped file object. Provides access to shared memory backed by a file with support for named access and reuse. .LINK https://psmodule.io/MemoryMapped/Functions/Set-MemoryMappedFile/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'The function only creates a memory-mapped file and does not modify system state in a way that requires confirmation.' )] [OutputType([System.IO.MemoryMappedFiles.MemoryMappedFile])] [CmdletBinding()] param( # The unique name for the memory-mapped file. [Parameter(Mandatory)] [string] $Name, # Path to the file backing the memory-mapped region. [Parameter(Mandatory)] [string] $Path, # Size in bytes of the memory-mapped file. Defaults to 1MB if not specified. [Parameter()] [long] $Size = 1MB ) $mmf = Get-MemoryMappedFile -Name $Name if ($mmf) { return $mmf } return New-MemoryMappedFile -Name $Name -Path $Path -Size $Size } #SkipTest:FunctionTest:Will add a test for this function in a future PR Write-Debug "[$scriptName] - [functions] - [public] - [Set-MemoryMappedFile] - Done" #endregion [functions] - [public] - [Set-MemoryMappedFile] #region [functions] - [public] - [Set-MemoryMappedFileContent] Write-Debug "[$scriptName] - [functions] - [public] - [Set-MemoryMappedFileContent] - Importing" function Set-MemoryMappedFileContent { <# .SYNOPSIS Writes string content into a memory-mapped file with the given name and path. .DESCRIPTION This function writes UTF-8 encoded content into a memory-mapped file at the specified path. It creates or updates the file using the size of the input content. If the -PassThru switch is used, the resulting memory-mapped file object is returned. An internal accessor is used to perform the write operation, and proper disposal is handled in the clean block. .EXAMPLE Set-MemoryMappedFileContent -Name "LogMap" -Path "C:\Temp\log.map" -Content "Hello, Memory Map" Output: ```powershell (No output unless -PassThru is specified) ``` Writes "Hello, Memory Map" to a memory-mapped file named 'LogMap' located at 'C:\Temp\log.map'. .OUTPUTS System.IO.MemoryMappedFiles.MemoryMappedFile .NOTES Returns the memory-mapped file object when -PassThru is used. .LINK https://psmodule.io/MemoryMapped/Functions/Set-MemoryMappedFileContent/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'The function only updates a memory-mapped file.' )] [OutputType([System.IO.MemoryMappedFiles.MemoryMappedFile])] [CmdletBinding()] param( # The name to assign to the memory-mapped file. [Parameter(Mandatory)] [string] $Name, # The file system path where the memory-mapped file is stored. [Parameter(Mandatory)] [string] $Path, # The string content to write into the memory-mapped file. [Parameter(Mandatory, ValueFromPipeline)] [string] $Content, # If specified, returns the memory-mapped file object after writing. [Parameter()] [switch] $PassThru ) begin {} process { Write-Verbose "Setting content for memory-mapped file '$Name'." $bytes = [System.Text.Encoding]::UTF8.GetBytes($Content) Write-Verbose "Writing content of size $($bytes.Length) bytes to memory-mapped file '$Name'." $size = $bytes.Length $mmf = Set-MemoryMappedFile -Name $Name -Path $Path -Size $size try { $accessor = $mmf.CreateViewAccessor() } catch { throw "Failed to create view accessor for memory-mapped file '$Name'. Exception: $($_.Exception.Message)" } if (-not $accessor) { throw "Failed to create view accessor for memory-mapped file '$Name'." } $accessor.WriteArray(0, $bytes, 0, $size) Write-Verbose "Content written successfully to memory-mapped file '$Name'." } end { if ($PassThru) { Write-Verbose "Returning memory-mapped file '$Name'." return $mmf } } clean { if ($accessor) { $accessor.Flush() $accessor.Dispose() } if ($mmf) { $mmf.Dispose() } } } #SkipTest:FunctionTest:Will add a test for this function in a future PR Write-Debug "[$scriptName] - [functions] - [public] - [Set-MemoryMappedFileContent] - Done" #endregion [functions] - [public] - [Set-MemoryMappedFileContent] #region [functions] - [public] - [Show-MemoryMappedFile] Write-Debug "[$scriptName] - [functions] - [public] - [Show-MemoryMappedFile] - Importing" function Show-MemoryMappedFile { <# .SYNOPSIS Continuously displays the contents of a memory-mapped file in the console. .DESCRIPTION This function reads and displays the contents of a specified memory-mapped file in a loop, refreshing the view at a user-defined interval. It uses `Read-MemoryMappedFileContent` to retrieve data and clears the console after each refresh cycle. .EXAMPLE Show-MemoryMappedFile -Name 'SharedBuffer' -RefreshSeconds 2 Output: ```powershell Data: Hello from another process! Timestamp: 2025-06-01T12:00:00 ``` Continuously displays the content of the 'SharedBuffer' memory-mapped file, refreshing every 2 seconds. .LINK https://psmodule.io/MemoryMapped/Functions/Show-MemoryMappedFile #> [CmdletBinding()] param( # The name of the memory-mapped file to read and display. [Parameter(Mandatory)] [string] $Name, # The number of seconds to wait between refreshes of the displayed content. [Parameter()] [int] $RefreshSeconds = 1 ) begin {} process { while ($true) { Read-MemoryMappedFileContent -Name $Name Start-Sleep -Seconds $RefreshSeconds Clear-Host } } end {} } #SkipTest:FunctionTest:Will add a test for this function in a future PR Write-Debug "[$scriptName] - [functions] - [public] - [Show-MemoryMappedFile] - Done" #endregion [functions] - [public] - [Show-MemoryMappedFile] Write-Debug "[$scriptName] - [functions] - [public] - Done" #endregion [functions] - [public] #region [variables] - [private] Write-Debug "[$scriptName] - [variables] - [private] - Processing folder" #region [variables] - [private] - [MemoryMappedFiles] Write-Debug "[$scriptName] - [variables] - [private] - [MemoryMappedFiles] - Importing" $script:MemoryMappedFiles = @{} # TODO: Start by creating a memory-mapped file for holding the file overview. # TODO: Add logic for other instances of the module to connect to the same memory-mapped file. Write-Debug "[$scriptName] - [variables] - [private] - [MemoryMappedFiles] - Done" #endregion [variables] - [private] - [MemoryMappedFiles] Write-Debug "[$scriptName] - [variables] - [private] - Done" #endregion [variables] - [private] #region Member exporter $exports = @{ Alias = '*' Cmdlet = '' Function = @( 'Close-MemoryMappedFile' 'Get-MemoryMappedFile' 'New-MemoryMappedFile' 'Read-MemoryMappedFileContent' 'Set-MemoryMappedFile' 'Set-MemoryMappedFileContent' 'Show-MemoryMappedFile' ) Variable = '' } Export-ModuleMember @exports #endregion Member exporter |