Custom Application Compatibility Shim Development Kit
The CustomShim
Visual Studio C++ project here builds a Windows
application compatibility shim
DLL providing two example shims.
You can use it as a starting point to create custom shims that intercept calls to Win32 or COM functions made by 32-bit or 64-bit applications.
It has been tested on Windows 10 2004, but it comes with absolutely no warranty and is definitely not supported by Microsoft.
Installing Custom Shim Modules
To make a shim DLL available to the shim engine, place it in the C:\Windows\System32
or C:\Windows\SysWOW64
(32-bit) directory as appropriate for its bitness.
Windows 10 will only load shims from DLLs with certain names,
so your DLL must be renamed after being compiled.
AcRes.dll
is a good choice because that's the name of a real AppCompat-related file (but not shim module) that resides elsewhere.
Applying Custom Shims
While the Compatibility Administrator can see shims defined in non-system SDB files if the shims are marked as general-purpose, it does not support using them in new application fixes. SDB files that declare and/or use custom shims must therefore be created by other means. The most scalable way is to patch the Compatibility Administrator into ShimDBC, a Microsoft-internal utility that compiles XML into SDB. For example, this XML produces an SDB that demonstrates the two example shims on some Sysinternals utilities:
<?xml version="1.0" encoding="utf-8"?>
<DATABASE NAME="Test Database" ID="{5FB8C914-168C-4B9B-8256-DF8A0F384E3E}">
<LIBRARY>
<SHIM NAME="AcceptEula" FILE="AcRes.dll" RUNTIME_PLATFORM="X86_ANY,AMD64" ID="{92E61B85-313A-4880-B6E4-DEF2567413AD}"/>
<SHIM NAME="FakeSchTask" FILE="AcRes.dll" RUNTIME_PLATFORM="X86_ANY,AMD64" ID="{C1CAD7E2-ACAC-4467-8A6A-D437C51D5918}"/>
</LIBRARY>
<APP NAME="Winobj" ID="{CC4BFC0C-5815-4F08-99C7-4ED13E611FAB}">
<EXE NAME="Winobj.exe" RUNTIME_PLATFORM="X86_ANY" PRODUCT_NAME="Sysinternals Winobj" ID="{B49373D9-BC1E-4941-A43B-7B5814C23D93}">
<SHIM NAME="AcceptEula"/>
</EXE>
</APP>
<APP NAME="AccessChk" ID="{D45B7077-34B6-463B-B046-38FC68B13430}">
<EXE NAME="accesschk.exe" RUNTIME_PLATFORM="X86_ANY" PRODUCT_NAME="Sysinternals AccessChk" ID="{E79328FD-855A-4D8A-92A3-F502E831BD1B}">
<SHIM NAME="AcceptEula"/>
</EXE>
<EXE NAME="accesschk64.exe" RUNTIME_PLATFORM="AMD64" PRODUCT_NAME="Sysinternals AccessChk" ID="{9B67E542-8B80-48D6-B6B9-55616BBDA743}">
<SHIM NAME="AcceptEula"/>
</EXE>
</APP>
<APP NAME="Autoruns" ID="{23897D6A-04BA-43D3-879F-C0F1E934635E}">
<EXE NAME="autorunsc.exe" RUNTIME_PLATFORM="X86_ANY" PRODUCT_NAME="Sysinternals autoruns" ID="{12A4EE20-93F4-4C55-B594-A0ABCD3C3283}">
<SHIM NAME="FakeSchTask"/>
</EXE>
<EXE NAME="autoruns.exe" RUNTIME_PLATFORM="X86_ANY" PRODUCT_NAME="Sysinternals autoruns" ID="{6C4947F7-A387-443D-BB1A-D5BD7C42F908}">
<SHIM NAME="FakeSchTask" COMMAND_LINE="It's a FAAAAAKE!"/>
</EXE>
<EXE NAME="Autoruns64.exe" RUNTIME_PLATFORM="AMD64" PRODUCT_NAME="Sysinternals autoruns" ID="{27AB1563-F153-4CF7-A444-0092750CF0FE}">
<SHIM NAME="FakeSchTask" COMMAND_LINE="It's a 64-bit FAAAAAKE!"/>
</EXE>
</APP>
</DATABASE>
Names of valid matching information attributes for EXE
tags can be found by examining existing fix entries in the Compatibility Administrator.
ID
attributes can omitted, in which case ShimDBC will update the input XML file to generate them.
If a call is not intercepted due to the DLL that makes it ("inex policy"), you can add INCLUDE
tags inside the library SHIM
definitions, e.g. <INCLUDE MODULE="clr.dll"/>
for .NET applications.
To compile the XML file named YourXml.xml
into YourDatabase.sdb
:
shimdbc Custom YourXml.xml YourDatabase.sdb
Alternatively, you can use the somewhat underdocumented Application Compatibility Database API
to write the SDB yourself.
The sdb.sprint
file in this directory is a SprintDLL script that produces an SDB with similar effect as the above XML.
Regardless of how you produce the SDB, you can install it like any other, using the
sdbinst
utility that comes with Windows:
sdbinst YourDatabase.sdb
Getting Shim Logs
To enable logging messages from shims (logged through SE_ShimDPF
e.g. by the ASL_PRINTF
macro):
- Set the
SHIM_DEBUG_LEVEL
environment variable, e.g. to4
for the most verbose logs. - Set the
SHIM_FILE_LOG
environment variable to the filename to write the shim logs to under the%TEMP%
directory. To save the log elsewhere, use../
sequences (forward slash, not backslash) to traverse up out of the temporary directory. The process hosting the shim you're debugging must have write access to the location you specify.
To enable logging messages and reports from the shim engine (apphelp.dll
):
- Set the
SHIMENG_DEBUG_LEVEL
environment variable, e.g. to4
for the most verbose logs. - For the 32-bit shim engine, set the
LogFlags
DWord value in theHKLM\Software\WOW6432Node\Microsoft\Windows NT\CurrentVersion\AppCompatFlags
Registry key to 255 (all reports). Omit theWOW6432Node
path component for the 64-bit shim engine.
Shim engine diagnostics are always saved as %TEMP%\AslLog_[report type]_[process].exe_[pid].txt
.
ApphelpDebug
logs have the log messages; shimengstate
reports may also be interesting.
Warning: Enabling lots of logging at once, especially with .NET processes, can manifest a deadlock bug in the shim engine that brings down the process with a breakpoint-related exit code: 0x80000003 or 0x4000001F.