Sunday, March 11, 2012

Use Caps Lock to switch input languages in Windows

AutoHotKey is a great piece of software to use, if you're on Windows and getting jealous of Xorg's grp:caps_toggle option under Linux.

AutoHotKey scripts can do magic, but for now, let's use a script to switch between English and Greek using Caps Lock.

An initial approach would be to reprogram Caps Lock to send Left Alt + Shift, the default language switch combination in Windows. However, I've also got Spanish enabled on my keyboard, and there doesn't seem to be a way to exclude a language when switching.

So here's an alternative approach, which:
  1. allows selecting each language individually, based on an Left Alt + Shift + number combination,
  2. enables switching between only a subset of the supported input languages.
Start by setting a different Left Alt + Left Shift + number combination for each language under Control Panel / Region and Language / Keyboards and Languages / Change Keyboards / Advanced Key Settings, and disable global Alt-Shift switching. When you're done, your settings should be similar to the following:

Between input languages: (None)
To English (united States) - US: Left Alt + Shift + 0
To Greek (Greece) - Greek: Left Alt + Shift +9
To Spanish (Spain, International Sort) - Spanish: Left Alt + Shift + 8

Since there's no keys to switch languages, an AHK script will have to remember the current input language, and switch to the other one directly. Here's a sample script, using a simple boolean to remember the state:

ingreek = False
CapsLock::
ingreek := not ingreek
OutputDebug, ingreek has become %ingreek%
if ingreek {
    OutputDebug, Sending Alt-Shift-9
    SendInput, {Lalt Down}{LShift Down}9{LShift Up}{LAlt Up}
} else {
    OutputDebug, Sending Alt-Shift-0
    SendInput, {Lalt Down}{LShift Down}0{LShift Up}{LAlt Up}
}

It's my first ever script, so please forgive any style errors :)

So there you have it: you can use Caps Lock to switch between English and Greek, or Left Alt + Shift + 8 to get into Spanish mode directly.

A debugging tip: The OutputDebug call sends output to the system debugger. Download a copy of  SysInternals' DebugView, run it with elevated privileges and toggle Capture / Capture Global Win32, to view the debugging output of the script.

Saturday, March 10, 2012

Have Pentadactyl call gVim for editing textboxes

Pentadactyl is a must-have addon for Firefox, which enables browsing with vim-like bindings. The best part of all: You can actually press Ctrl-I to have Pentadactyl spawn a full gVim while in any textbox.

Which means using gVim with web-based gmail is now possible :)

If Pentadactyl complains it cannot find gVim and you're on Windows, make sure to add C:\Program Files (x86)\Vim\vim73 to your %PATH%, by going to Computer Properties / Advanced System Settings / Environmental variables... and adding this directory to your user path.

Wednesday, March 7, 2012

Moving the Profile folder to a different location under Windows 7 [UPDATE: Now for Windows 10! ]

UPDATE: 2019-12-24: Verified to work for Windows 10 Pro.
UPDATE: 2019-12-24: Added information on image indexes, and troubleshooting guide.
UPDATE: 2019-12-24: Add direct link to pre-built Unattend.xml.

There are many reasons why one may want to move C:\Users and C:\ProgramData to a new location, preferably on a different disk, the most important being separation of OS data from user-specific data.

The most common methods proposed all over the web are:
  • Messing with Registry settings (e.g., changing keys under HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS NT\CurrentVersion\ProfileList)
  • Using robocopy to copy everything over, then make an NTFS junction pointing to the new location (e.g. mklink /j C:\Users D:\Users)
  • using an answer file (unattend.xml) when installing Windows, to change the FolderLocations setting, see http://technet.microsoft.com/en-us/library/cc749305(v=ws.10).aspx
Method 3 seems to be the best-supported, given there's a nice article in Microsoft's KB (http://support.microsoft.com/kb/949977) about it, and that Setup creates the elaborate set of Junctions needed for backwards compatibility in the new location automatically.

Still, after numerous tries, Setup seemed to ignore the answer file I was providing in the root of a connected USB stick. It doesn't matter Microsoft's documentation says otherwise: http://technet.microsoft.com/en-us/library/cc749415(v=ws.10).aspx, no matter what I tried Setup ignored my unattend.xml.

The only way I could make it work was by placing it in the root of the newly installed %SYSTEMDRIVE%, i.e., C:, so here are detailed steps on how to do that:

  1. Download the Windows Automated Installation Kit (WAIK), and install it on a second computer. Or just download a pre-built Unattend.xml and jump directly to step 3
  2. Create an answer file using the Windows System Image Manager containing the necessary entries for the FolderLocations setting, in the oobeSystem phase (both for x86 and amd64 architectures)
  3. Create a bootable USB drive for installing Windows 7
    1. Use DISKPART to completely clear it, create a primary partition, format it as NTFS and mark it as active
    2. Copy the full contents of the Windows 7 installation DVD in the newly-created NTFS
    3. I also had to run BOOTSECT /nt60 J: at this point, although I'm not sure it's necessary
  4. Modify sources\install.wim on the USB stick so that it contains your unattend.xml file as well
    1. [UPDATED] Based on the version of Windows you wish to install, note its index in the install image:
      DISM /Get-ImageInfo /ImageFile:J:\sources\install.wim
    2. Mount the image on a local directory, but replace its index at the /index argument, based on the version you want to install, see step (1) above:
    3. DISM /Mount-Wim /WimFile:J:\sources\install.wim /index:1 /Mountdir:C:\wim 
    4. COPY unattend.xml C:\wim
    5. Unmount the directory
      DISM /Unmount-Wim /MountDir:C:\wim /commit 
If all goes well, you should be able to install Windows 7 from the USB stick ([UPDATED] remember to create a and format a second NTFS partition during Setup!) and see the fruits of your labor:
C:\>echo %userprofile%
D:\Users\vangelis


The setup logs under C:\Windows\Panther\UnattendGC\setupact.txt will confirm your victory:


oobeldr.exe] Status for unattend pass [oobeSystem] = 0x0
[oobeldr.exe] UnattendSearchExplicitPath: Found unattend file at [C:\unattend.xml]; examining for applicability.
[oobeldr.exe] UnattendSearchExplicitPath: Found usable unattend file for pass [oobeSystem] at [C:\unattend.xml].

...
[Shell Unattend] Running 'oobeSystem' pass
[Shell Unattend] FolderLocations: Moved 'C:\Users' to 'D:\Users'
[Shell Unattend] FolderLocations: 'ProfilesDirectory' set to 'D:\Users'
[Shell Unattend] FolderLocations: 'Public' set to 'D:\Users\Public'
[Shell Unattend] FolderLocations: 'Default' set to 'D:\Users\Default'
[Shell Unattend] FolderLocations: 'ProfileImagePath' set to 'D:\Users\Administrator'
[Shell Unattend] FolderLocations: Moved 'C:\ProgramData' to 'D:\ProgramData'
[Shell Unattend] FolderLocations: 'ProgramData' set to 'D:\ProgramData'



Enjoy your user data on a separate partition from the rest of the OS.

I can't get it to work!

The latest version of Windows I have verified this procedure with is Windows 10 1903.
If something breaks, please check the following:
  • Are you sure you have updated the right image in install.wim? Double check that the index you specified at the DISM command line corresponds exactly to the version you are deploying.
  • Are you sure a D: drive exists during installation? Press Shift-F10 during setup and use DIR to explore all drives.
  • It could be that your USB stick itself appears as D: instead of your second partition, which may be at E:. If this is the case, you need to remove your installation medium at the first reboot, right after Setup has completed the initial copy of all files, before it reboots into your hard disk.