It is possible to convert the binary data in an image file to plain ASCII text so it can included directly in your PowerShell script, without the need for separate files. This is accomplished with Base64 Encoding. Base64 Encoding basically takes 3 binary bytes (24 bits) and converts them into 4 ASCII characters. I’m going to take my pervious post of NotifyIcon and embed the two icon files into the script text.

To facilitate this I put together a utility to do the encoding and spilt the file into 80 byte length records so it is easy to use in a script. You browse out to an image file and select it. The default output file is placed in the same folder with the txt extension. You can manually change those values. Then click convert and you have a text file of your image. I break the output into 80 byte chunks because I find lines potentially thousands of bytes long inconvenient in an editor or IDE.

To create the utility called Image2Bas64, in a WinForms window I added two TextBox objects for the input and output files.

Add-Type -AssemblyName System.Windows.Forms
# WinForm Setup
$mainForm = New-Object System.Windows.Forms.Form
$mainForm.Font = "Comic Sans MS,9"
$mainForm.ForeColor = "Black"
$mainForm.BackColor = "PaleGoldenrod"
$mainForm.Text = " Image2Base64 Converter"
$mainForm.Width = 570
$mainForm.Height = 200

# Output Box
$textBoxOut = New-Object System.Windows.Forms.TextBox
$textBoxOut.Location = "35, 85"
$textBoxOut.Size = "415, 20"
$mainForm.Controls.Add($textBoxOut)

# Input Box
$textBoxIn = New-Object System.Windows.Forms.TextBox
$textBoxIn.Location = "35, 25"
$textBoxIn.Size = "415, 20"
$mainForm.Controls.Add($textBoxIn)

Added were three buttons; Browse, Convert and Exit. The Browse button calls a function using the OpenFileDialog class object to open a file dialog. I open at the current folder, showing all files. It is the usual Windows Open File dialog so it is normal to use. After selecting an image file I set the input file and output file values and they are displayed. I assume a 3 digit file extensions (jpg, gif, ico, etc) and substitute ‘txt’ for whatever extension was there. I lastly clear the completeLabel so we’re ready for the next convert.

# Browse Input Button
$buttonBrowse = New-Object System.Windows.Forms.Button
$buttonBrowse.Location = "460, 25"
$buttonBrowse.Size = "75, 23"
$buttonBrowse.Text = "Browse"
$buttonBrowse.add_Click({selectFiles})
$mainForm.Controls.Add($buttonBrowse)

function selectFiles {
	$selectForm = New-Object System.Windows.Forms.OpenFileDialog
	$selectForm.Filter = "All Files (*.*)|*.*"
	$selectForm.InitialDirectory = ".\"
	$selectForm.Title = "Select a File to Process"
	$getKey = $selectForm.ShowDialog()
	If ($getKey -eq "OK") {
        	$textBoxIn.Text  = $selectForm.FileName
        	$work = $selectForm.FileName
        	$work = $work.SubString(0, ($work.Length -3)) + "txt"
        	$textBoxOut.Text = $work
        	$completeLabel.Text = "      "
	}
} # End SelectFile

The Convert button uses the Convert class to encode into Base64. After encoding the file into an array, it is broken into 80 byte chunks by inserting a carriage return and line feed and writing the text file. I added a basic error check to insure the file open/close was successful.

Function convertButton {
    $error.clear()
    $inFileName = $textBoxIn.Text
    $outFileName = $textBoxOut.Text
    $encodedImage = [convert]::ToBase64String((get-content $inFileName -encoding byte))
    $encodedImage -replace ".{80}" , "$&`r`n" | set-content $outFileName
    If ($error.count -gt 0) {
        $completeLabel.Text = "Error!"
    }
    Else {
        $completeLabel.Text = "Done!"
    }
} # End convertButton

Add some labels and the exit button and display the form.

Image2Base64

Image2Base64.ps1

____________________________________________________________________________

After encoding the two images into text files with Image2Base64, I insert them into the CapsLockMonitor script. The text block of the encoded image is inserted in a Here-String.

Decode and Set Icons
[string]$upIcon64=@"
AAABAAMAGBgQAAEABADoAQAANgAAABgYAAABAAgAyAYAAAYEAAAYGAAAAQAgAIgJAACWEQAAKAAAABgA
AAAwAAAAAQAEAAAAAAAgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICA
AACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</span>

... more lines ...

/2sTvf9IE73/NxO8/w0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A/gB/APgA
HwDwAA8A4AAHAOAAAwDAAAMAwAABAIAAAQCAAAEAgAABAIAAAQCAAAEAgAABAIAAAQCAAAEAwAADAMAA
AwDgAAcA8AAPAPgAHwD8AD8A/wD/AA==
"@

To decode the icon and make It available for use, the string is converted into a MemoryStream. The stream has a backing store of memory instead disk or a network connection, which we’ll need as we have no file to use. A bitmap image is made from the stream. We collect the icons handle using the GetHicon() method from the bitmap image. The icon is created using the FromHandle method. We now have an icon image available for use have been read from the source script file and decoded and created in memory instead of read from a disk file.

$iconStream=[System.IO.MemoryStream][System.Convert]::FromBase64String($upIcon64)
$iconBmp=[System.Drawing.Bitmap][System.Drawing.Image]::FromStream($iconStream)
$iconHandle=$iconBmp.GetHicon()
$upIcon=[System.Drawing.Icon]::FromHandle($iconHandle)

Lastly, I change the icon calls in the original script to use the string instead of the path. Since it was not longer needed I removed the Get-ScriptDirectory function used to locate the icon files and it’s associated variables.

# Before
$notifyIcon.Icon = $global:callPath + "\up.ico"

# After
$notifyIcon.Icon = $upIcon

The end result is not too unwieldy as the files I encoded were small. A large file or many files can add thousands or even tens of thousands of lines, which can be a bit much. NotifyIcons are a good use of this as system tray icons are small.

You can encode any file type, it doesn’t have to be image to text files. The source could be a pfd, docx or even a another text file.

CapsLockMonitor

CapsLockMonitor.ps1

Advertisements