AutoHotkey: Resolve Environment Variables in an Input String

As the last little hack session of the year I wanted to build a AutoHotkey function to resolve all occurences of environment variables within an input string.

Normally, this would be pretty straight forward. Use WScript.Shell and call the ExpandEnvironmentStrings function with the input string.

Given that VBScript – and to my understanding WSH – are being deprecated by Microsoft, this solution would run on borrowed time.

Therefore the somewhat more clunky but more “future-proof” method is to rely on locating the %-characters and trying to parse the contents inbetween as environment variables.

We need to be wary of regular %-characters within the string to prevent accidental destruction of string values or false-positives.

So a pure AutoHotkey implementation could look like this:

; Input string
str := "This is 100% true! -- C:\Users\%USERNAME%\test -- %LOCALAPPDATA% -- %"

; Marks the last position of a %-character
lastPos := 0

Loop
{
    ; Find the next environment variable in the string
    pos1 := InStr(str, "%", false, lastPos + 1)
    if !pos1
        break
    pos2 := InStr(str, "%", false, pos1 + 1)
    if !pos2
        break

    ; Extract the environment variable name
    envVar := SubStr(str, pos1 + 1, pos2 - pos1 - 1)
    
    ; Try to get the value of the environment variable
    envValue := EnvGet(envVar)

    ; If the environment variable exists, replace it in the string
    if envValue
        str := SubStr(str, 1, pos1 - 1) . envValue . SubStr(str, pos2 + 1)

    ; Set the last position to the end of the replaced string
    lastPos := pos1
}

; Show the result
MsgBox(str)

This is, however, not the best way to do it. Since Kernel32.dll is loaded automatically in AutoHotkey anyway, using the ExpandEnvironmentStringsW function seems like a much better option:

; Input string
str := "This is 100% true! -- C:\Users\%USERNAME%\test -- %LOCALAPPDATA% -- %"

; Allocate buffer for the expanded string
VarSetStrCapacity(&str2, 1024)

; Call ExpandEnvironmentStringsW to expand environment variables
DllCall("ExpandEnvironmentStringsW", "wstr", str, "wstr", &str2, "uint", 1024)

; Show the result
MsgBox(str2)

Why reinvent the wheel when the platform developer already provides a function for it?

AutoHotkey: Wait for Process to Exit

In today’s small hack session the requirement sounds rather simple: Wait for a given process to exit and then exit the AutohotKey script itself – all while being non-blocking.

The most obvious choice would be a simple loop that checks whether the process still exists. This is also the implementation you will find when searching for this problem on the web. The drawback of this approach is the high cost in runtime, so that is no good; why would I want to burn CPU cycles constantly?

A much better way is to register a callback with Windows and have the operating system notify you once the process has exited.

#Requires AutoHotkey v2.0.2
#SingleInstance Force

DllCall("LoadLibrary", "Str", "Kernel32.dll")

ProcessExitCallback := CallbackCreate(OnProcessExit)

; Function to register a callback for process exit
RegisterForProcessExit(pid) {
    ; Open the process handle with SYNCHRONIZE access
    hProcess := DllCall("OpenProcess", "UInt", 0x00100000, "Int", 0, "UInt", pid, "Ptr")

    if (!hProcess) {
        MsgBox("Failed to open process handle. Error: " DllCall("GetLastError"), "Error opening process handle", "OK IconX")
        return
    }

    ; Create a variable to hold the wait handle
    waitHandle := 0

    ; Register the wait
    success := DllCall("Kernel32.dll\RegisterWaitForSingleObject"
        , "Ptr*", &waitHandle         ; Output wait handle (mutable variable)
        , "Ptr", hProcess            ; The process handle
        , "Ptr", ProcessExitCallback ; The callback function
        , "Ptr", pid                 ; The parameter passed to the callback (PID)
        , "UInt", 0xFFFFFFFF         ; INFINITE wait time
        , "UInt", 0x00000000)        ; WT_EXECUTEONLYONCE

    if (!success) {
        MsgBox("Failed to register wait. Error: " DllCall("GetLastError"), "Error registering callback", "OK IconX")
        DllCall("CloseHandle", "Ptr", hProcess)
        return
    }
}

; The callback function to execute when the process exits
OnProcessExit(param, TimerOrWaitFired) {
    ExitApp(0)
}

; Main script execution
pid := ProcessExist("Notepad.exe") ; Replace with your target process name
if pid
    RegisterForProcessExit(pid)
else
    MsgBox("Notepad.exe is not running!", "Notepad", "OK IconX")

The callback approach has the advantage of being able to run in the background for a long time without noticeable performance impact.

PowerShell: Checking a Font for Specific Glyphs

I had the requirement to check which of the fonts installed on my machine supported Japanese numerals. The easiest way to actually verify this was by doing it through a Powershell script.

The method below only works on Windows machines since it relies on the PresentationCore assembly.

param (
    [switch] $Verbose
)

Add-Type -AssemblyName PresentationCore

# List of Japanese numerals to check
$japaneseNumerals = @(
    [char] 0x4E00, # 一
    [char] 0x4E8C, # 二
    [char] 0x4E09, # 三
    [char] 0x56DB, # 四
    [char] 0x4E94, # 五
    [char] 0x516D, # 六
    [char] 0x4E03, # 七
    [char] 0x516B, # 八
    [char] 0x4E5D, # 九
    [char] 0x5341  # 十
)

# Function to check if a font contains a specific glyph
function FontSupportsCharacter {
    param (
        # The font name to check
        [string] $fontName,
        # The character to check
        [char] $character
    )

    try {
        # Create a GlyphTypeface object for the font
        $typefaces = (New-Object System.Windows.Media.FontFamily($fontName)).GetTypefaces()
        foreach ($typeface in $typefaces) {
            $glyphTypeface = $null
            $typeface.TryGetGlyphTypeface([ref]$glyphTypeface) | Out-Null
            if ($glyphTypeface -and $glyphTypeface.CharacterToGlyphMap.ContainsKey([int][char]$character)) {
                return $true
            }
        }
    } catch {
        # Handle errors, e.g., if the font cannot be loaded
        Write-Verbose -Message "Error loading font: ${fontName}"
    }

    return $false
}

if ($Verbose) {
    $VerbosePreference = "Continue"
}

# Get all installed fonts
$installedFonts = (New-Object System.Drawing.Text.InstalledFontCollection).Families

# Loop through each installed font
foreach ($font in $installedFonts) {
    $glyphFound = $true
    foreach ($numeral in $japaneseNumerals) {
        $exists = FontSupportsCharacter -fontName $font.Name -character $numeral
        if (-not $exists) {
            $glyphFound = $false
            Write-Verbose -Message "Font `"$($font.Name)`" does NOT contain glyph for `"${numeral}`" (U+$([System.String]::Format("{0:X4}", [int][char]$numeral)))."
            break
        }
    }

    if ($glyphFound) {
        Write-Output "Font `"$($font.Name)`" contains all Japanese numerals."
    }
}

Personally, I would expect the operating system to be able to filter fonts, but unfortunately it falls to the user to gain these insights.

OBS – Websocket Stays Open After Exiting

I have been plagued by this issue for a while: After closing OBS, a zombie process would continue to block the TCP port previously used by OBS’s websocket plugin. Trying to query the process ID never yielded any results. So what gives?

In my case, it turns out that the likely culprit was the OBS Droidcam plugin, which just received an update to mitigate exactly this misbehaviour.

After installing the update I can confirm that the zombie process and the listening TCP port no longer occur.

Adding Kagi as a Vivaldi Image Search Engine

Kagi, the search engine I was pretty hyped about a year ago, recently added support for loading up on credits via Paypal and other European-friendly, non-credit-card ways.

I have a ton of things to say about the way they flip-flop about their pricing model but that is neither here nor there. Today’s topic is all about adding Kagi to Vivaldi’s list of image search engines.

If you define the following image search URL for Kagi, the reverse image search will work in Vivaldi:

https://kagi.com/images?q={google:imageURL}&reverse=reference

That is some pretty hype stuff because that means I can fully kagi-fy my browser usage.

If you have Kagi’s Chrome extension installed, you will most likely need to uninstall or disable that extension. For some reason, Vivaldi (at the time of writing) ignores the search engine definitions created through the extension.

VMware Workstation “hostWin32.c:559” error

When running VMware Workstation 16 or 17 on a Windows host, you perhaps encounter the following error:

PANIC: VERIFY bora\vmx\main\hostWin32.c:559

This error happens when you have a Windows VM running and then try to launch a second Windows VM. As soon as the second machine boots into the desktop/login, it crashes, and the machine stops.

Luckily, this VMware forum post gave me a workaround that solved the issue. Hopefully, it will be helpful for you as well.

I am using an Nvidia graphics adapter and have set the “Max Frame Rate” and “Background Application Max Frame Rate” settings via the Nvidia Control Panel to values other than “Off”. This is what caused the issues for me.

The solution: Simply add a program-specific customization for vmware.exe, ensure both settings are set to “Off” in the customization, and you should be good.

SyncBackSE: Schedule a Move Operation on Windows

I have several file-system operations I cannot perform during the day, the machine’s performance would suffer and I would get angry e-mails. So I have to schedule simple move operations.

Now I could do this with Windows’ own task scheduler but I would have to write either a vbscript or a batch file to specify the details. Performing a dry run also sucks. Apparently there’s no dedicated software that gives a new “Schedule Move” or “Schedule Copy” context operation (hint: I’ll develop one once I have beaten Grand Theft Auto V) for quick use, so I started experimenting.

It seems the amazing SyncBackSE fits the bill. I already own a license for this great piece of wizardry to perform sync operations between multiple machines and backup my files. Turns out you can configure a new, one-time job to be your scheduled file mover:

  1. Create a new backup profile and choose the directory above the one you want to move.
  2. Choose “Select Subdirectories and Files” to specify the directory/directories you want to move.
  3. Now select your target directory.
  4. Add a schedule
  5. As a condition set “Move file to target”

SyncBackSE will automatically move your file, produce a nice log for you to review and even allows for a dry run.

Howto: Run Vector Magic Desktop Edition with Wine

Vector Magic is pretty cool. I loved the project back in the days when everyone could use it for free and was happy to see that they started providing a desktop client after going commercial. Even better: The client is utilizing Qt so we have a Windows and a Mac version. But nothing for Linux.

Don’t fret, of course you can run this application with Wine:
The only thing to notice here is that you need to set your Windows version to Windows 98, otherwise the application will always go haywire when loading a picture.

vectormagicwine

All features are working perfectly, no native DLLs needed.

Howto: Fix Wuala’s Copy & Paste on Linux

If you’re using a recent KDE version you’ll notice that Wuala’s Copy & Paste feature doesn’t really seem to work anymore. Instead of using the contents you specify within the application (say, files and folders) it’ll always use the contents of Klipper.

That’s pretty annoying if you don’t know that the contents does indeed come from Klipper. So, just clear your Klipper history and you should be fine.

Fixing mouse issues with VirtualPC -> VMware images

Microsoft often delivers free-to-test disk images of their products (like, for instance, the new Visual Studio 2010 CTP). These images are – who would have expected that? – in Microsoft’s own VirtualPC format. Luckily, the specs for the disk format are available and you can use the VMware Converter to just convert it into a vmdk (or use it directly, which I can’t really recommend).

Upon installing the VMware Tools, though, you’ll notice that the mouse gets stuck in the lower right corner of the screen if you didn’t remove the VirtualPC tools beforehand. Quite annoying, isn’t it? This comment describes how to solve the problem:

  • Fire up regedit, navigate to HKEY_LOCAL_MACHINESYSTEMCurrentControlSetContro lClass.
  • Within the keys under Class look for a value named UpperFilters, containing the value “msvmmouf”.
  • Remove “msvmmouf” from the value and reboot your VM.

Nifty.

Stopping Sweetcron from breaking with too long posts

Okay, now this is really really bad, starting with MySQL 5 there is a strict mode. What does that mean? The strict mode will not silently swallow all your data and just cut off what’s left but throw an exception back at you when you try to insert more data than the field type allows.

Sweetcron is really vulnerable for this kind of problem. It always stopped fetching the feeds to complain about a single post. Now, while fixing the problem on the software side would be preferrable I opted for a simple configuration change on the server side this time.

All you really need to do is altering the my.ini line that activates the strict mode:

sql-mode=”STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION”

will either become

#sql-mode=”STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION”

or

sql-mode=”MYSQL40″

Simply restart the MySQL service afterwards and sweetcron should eat the posts without complaining (I don’t even want to think about the loss of data here, but who cares?).

How to deactivate (revoke) Far Cry 2

Far Cry 2 comes with a nasty copy protection. Actually, it’s more like a strict DRM variant that needs to be activated online and can only be used X times when not properly deactivating it.

You can manually revoke an activation without uninstalling the game by simply calling:

FarCry2.exe /revoke

Easy, huh? I’d prefer Ubisoft to not use any of these bugger DRM crap at all, but then again we all know the story of software piracy (note: pirates probably have a better version without this crap, so this whole thing is totally beyond the point anyway!).

Moving old data to new disks

As I wrote earlier this week one of my disks kept dying on me. The solution – of course – is to replace the faulty device with a brand-new one and move the data to the new disk.

While I had great success in doing so with Acronis True Image’s “Clone Disk” feature I had a little hickup while mounting the disk into the storage cage of my case. So for the time being I had to use the broken drive for another 2 days (don’t do this at home, kids!). As you can imagine the data became desynchronized and since I didn’t really have the time to clone the disk again I opted for another, more time saving method.

Everyone knows how to pack an entire system, store it somewhere in the network and restore it – all thanks to Linux live CDs and my good ol’ friend tar. tar is great for a lot of reasons: It processes .files, it keeps permissions and ownership intact… oh yeah, and it packs things.

So, this time I didn’t need to pack my entire system but only a few selected folders. And I didn’t really need to pack them either. But tar makes a great companion for moving sensitive data between disks, no less. Just start your machine in single user mode (or with a Live CD if you plan on moving your root partition around), mount target/destination, cd into the directory you want to move and use this nifty little command:

tar cd - . | (cd /my/new/home; tar xvpf -)

Yep, that’s all that is to it. Keeps all properties intact, which is important for, well, just about any kind of file/folder.