Generate Windows drivers from any OS
Goal
For now, Kalamine is only able to generate drivers for Windows if launched from
Windows with wkalamine and MSKLC installed (or with proprietary software
like KbdEdit).
The idea is to use the Zig toolchain, which is compatible with C and can easily cross-compile.
I’ll put in this issue all informations I found and my attempts to do that.
Information search
Note: all tests are performed on a Windows 10 x86_64 installation.
Installation of a keyboard layout on Windows
The installation process has two parts:
- Copy the DLL layout to the right folder
- Make an entry in the registry (there is a dynamic part in that, done by the EXE)
Keyboard DLL on Windows
When we produce an EXE with wkalamine and MSKLC, the installation path of the
DLL is here (for Ergo‑L layout):
C:\Windows\System32\ergol.dll
KbdEdit it’s:
C:\Windows\System32\kbdXXXX.dll
And there is many more layout DLLs starting with kbd in this folder.
- 64 bits DLL are in
System32/(named like that to not break old 32 bits app compatibility which has hardcoded the path) - 32 bits DLL are in
SysWOW64/(System Windows 32 bits Over Windows 64 bits)
I tried to change the DLL generated by wkalamine for Bépolar. I switched
amd64\bepolar.dll by ergol.dll and kept the name bepolar.dll. The EXE
installed it in C:\Windows\System32\bepolar.dll as if it were Bépolar, no
warning. The layout and its name was Ergo‑L: they seem to be encoded inside the
DLL.
Useful links
- winkbdlayouts: an amazing source of informations on how keyboard layouts work on Windows
- MON NOM ACCENTUÉ (French): more explanations on Windows keyboard layouts
- sam.elborai blog: explains the C code of DLL
- Neo Layout: German layout source code
- Windows-driver-samples: an example of keyboard layout source code by Microsoft
- Keyboard scan codes image
Attempts to create the DLL using Zig on WSL (Linux)
With C files
I tried to compile the C and H of Bépolar layout on a Linux environment with
Zig 0.13.0 (latest stable release at the time), copying all the needed H files
from C:\Program Files (x86)\Microsoft Keyboard Layout Creator 1.4\inc without
success (a lot of errors, I might be missing some Windows H files).
Most of the H files are copyrighted, not sure it will be possible to include them in Kalamine anyway.
With Zig files
I tried to rewrite it in pure Zig and add only what I need from the H files. The sources are here: zig-keyboard-layout-dll
Compilation command
I compared simple DLL output with the reel one from MSKLC with Dependency Walker to see if I can have the same entrypoint and config.
The compilation command I ended up with is:
zig build-lib -O ReleaseFast -target x86_64-windows-msvc -dynamic bepolar.zig --subsystem native
Explanations:
-
-O ReleaseFast: build in fast mode (unsafe), since the C is natively unsafe too -
-target x86_64-windows-msvc: Windows x86_64 target with MSVC ABI (not sure for the usefulness of the MSVC ABI part) -
-dynamic: to make a DLL -
--subsystem native: in Dependency Walker, Subsystem at Native instead of GUI
We can add:
-
-fstrip: remove debugger annotations (in Dependency Walker, Symbols at None instead of CV) and do not generate a .pdb anymore -
--version 4.0.0: in Dependency Walker, Image Ver at 4.0 instead of 0.0
I didn’t find how to not generate with the Zig compilation:
-
_DllMainCRTStartup -
__xl_a -
__xl_z -
_tls_end -
_tls_index -
_tls_start -
_tls_used
Source code translation
The code is coming from:
- the C and H output of
wkalaminefor Bépolar - the MSKLC installation folder
(
C:\Program Files (x86)\Microsoft Keyboard Layout Creator 1.4\inc)
It helps a lot to understand Windows coding conventions with:
- https://learn.microsoft.com/en-us/windows/win32/learnwin32/windows-coding-conventions
- https://learn.microsoft.com/en-us/windows/win32/learnwin32/working-with-strings
And guide to translate C to Zig.
Test
I installed a regular Bépolar layout and then replaced its DLL by my Zig one. For now it crashes the start of Windows, don’t do that, it’s annoying to delete the DLL with a prompt in safe mode.
I want to compare the resulting output of the DLL function KbdLayerDescriptor
of the good DLL and mine without actually loading it with Windows altoghether.
These commands output nothing:
rundll32 C:\Windows\System32\ergol.dll,KbdLayerDescriptor
rundll32 ergol.dll,KbdLayerDescriptor
rundll32 .\bepolar.dll,KbdLayerDescriptor
I have seen on the web:
dumpbin /EXPORTS ergol.dll
But I don’t know how to have that dumpbin EXE, it might be useful though.
I might try to create a program that imports the DLL, or compare the assembler code with Compiler Explorer.
I don’t know much of system programming (and C) and I’m stuck. Not sure what to do from there to go forward with the test, so I share all I know here, just in case someone wants to give it a try. I’ll update this issue if I find more informations.