Monday, February 26, 2018

SprintDLL does not currently do arrays

I happened to notice recently that it's not possible to work with arbitrarily sized arrays of things in SprintDLL. You can allocate arbitrarily sized buffers, but the slot kind has to be a pointer. Though it is possible to avoid invalid cast errors by using the offset keyword of copyslot, the copied value goes after the pointer to the buffer, not actually in the buffer, so that either mangles the buffer pointer or writes to unallocated memory. For fixed size arrays, you can use block slots and the field keyword, but that gets unwieldy for large arrays and doesn't work at all for arbitrarily sized arrays. I will see about correcting this oversight.

Sunday, February 25, 2018

The -File and -Directory switches on Get-ChildItem didn't always exist

The Get-ChildItem cmdlet has -File and -Directory switches that limit the results to files and subdirectories, respectively. These are very nice, but I found when writing scripts for people on older versions of Windows (specifically Windows 7 or earlier) that they didn't always exist. To support those versions, you have to use the PSIsContainer property. So this is equivalent to gci -File:

gci | ? { -not $_.PSIsContainer }

For directories, of course, the PSIsContainer property is true.

Saturday, February 24, 2018

Unmangling ASCII text interpreted as Unicode

Sometimes programs take buffers of ASCII strings (or 8-bit characters in general) and mistakenly pass them to functions that expect Unicode (16-bit) strings. This causes pairs of 8-bit characters to fuse together, combining their byte representations into a Unicode code point. To attempt* to recover the original ASCII from nonsense Unicode on the clipboard, you can use this PowerShell command:
[System.Text.Encoding]::ASCII.GetString([System.Text.Encoding]::Unicode.GetBytes((gcb)))

*The attempt might not be 100% successful if the smashed-together bytes form invalid UTF-16 sequences. The very last character of the string can be lost if the original text had a non-multiple-of-two length.

Based on my Super User answer.

Friday, February 23, 2018

If a program's UI is tiny or jumbled, override the DPI

My laptop's high-DPI 4K screen reveals a lot of programs' DPI-related misbehavior. Some programs try to implement DPI awareness but actually draw some UI controls really tiny (relative to the huge resolution) or get the controls jumbled up and overlapping. The solution is to override their declaration of DPI awareness and have the system do the fuzzy but proportional scaling. That can be done in the Compatibility tab of the Properties window of the shortcut or executable - checking the "override high DPI scaling behavior" box and choosing System in the dropdown list does the job. In versions of Windows prior to 10, there's just one DPI-related checkbox and no dropdown; checking the box should be sufficient.

Wednesday, February 21, 2018

PowerShell remembers commands from closed windows

Occasionally I close PowerShell windows before remembering to look at some details in the commands I used (especially when there are a lot of numbers involved). New versions of PowerShell store the console history across sessions, so pressing the up arrow in a new prompt will show the last command executed in any PowerShell window. You can even press Ctrl+R to search for a command you used.

Sunday, February 18, 2018

PowerShell nested functions won't affect variables in the outer scope

I recently converted a handful of PowerShell scripts to a single script file that defines several functions. I noticed that the script that used a helper function to do its job no longer worked; the nested function's changes to the variables created by the parent function weren't preserved outside the nested function. Previously I had been using $script:variableName in the inner function instead of $variableName, but since they were no longer script-scoped, that didn't help.

Some Googling turned up this Stack Overflow Q&A that suggested using Set-Variable to create the variables with the AllScope option. That appears to have worked. I can now do this to initialize them:

Set-Variable -Name someText, someBool, anotherBool -Option AllScope
$someText = ''
$someBool = $false
$anotherBool = $false

They can then be used in both the inner and outer functions without any special qualifiers.

Disabling PowerShell's "can't backspace any more" beep

Sometimes if you attempt to backspace beyond the beginning of the line, PowerShell will emit a beep. If you would prefer that PowerShell be quiet, you can use this command to change that option:

Set-PSReadlineOption -BellStyle None