Windows: How to create custom appcompat shims (Application Fixes)? Windows: How to create custom appcompat shims (Application Fixes)? windows windows

Windows: How to create custom appcompat shims (Application Fixes)?


I'm not aware of any way of someone other than Microsoft implementing an appcompat shim.

You might want to investigate Detours, it might provide the functionality you want.


You have to think of this from Raymond Chen's point of view. Imagine if it were possible for somebody other than Microsoft to write compatibility shims. Then whenever Microsoft makes a breaking change, in addition to all their other compatibility work they will also have to write shims for the 3rd party shims that did the wrong thing. Maintaining backward compatibility is hard enough as it is.


It is possible to create custom real application compatibility shims applied with the real shim engine, but doing so is definitely not supported by Microsoft. Raymond Chen would be horrified.

Creating the shim

Shim modules are DLLs that export two functions:

// Called by the shim engine to request the installation of a shim.// Returns an array of requested import hooks and provides its length in the pdwHookCount output parameter.EXTERN_C PHOOKAPI WINAPI GetHookAPIs(LPSTR pszArgs, LPWSTR pwszShim, PDWORD pdwHookCount);// Called by the shim engine at various points in the process's life cycle.EXTERN_C VOID WINAPI NotifyShims(DWORD notification, PVOID data);// Represents a request for an imported function to be redirected to one in this DLL.// Definition based on information from the ReactOS project.typedef struct tagHOOKAPI {    PCSTR DllName;    PCSTR FunctionName;    PVOID HookFunction;    PVOID NextFunction; // Populated by the shim engine    PVOID Reserved[2];} HOOKAPI, *PHOOKAPI;

For a simple shim module that just needs to provide one shim that replaces one Win32 function, your GetHookAPIs implementation can just check pwszShim against your one shim's name, set *pdwHookCount to 1, and return a pointer to a HOOKAPI structure describing the target function and its replacement. The hook function whose address you put in the HookFunction field will be called instead of the target. It can use the NextFunction field, which will by then have been set by the shim engine, to call the real/original function when appropriate. NotifyShims can be a no-op.

Alternatively, I have provided some scaffolding in my custom shim development kit which will be especially helpful if you need to write shim modules containing multiple or more complex shims. I'll demonstrate it making your desired TSLie shim. Starting from the CustomShim project there, let's create Shim_TSLie.h to declare a class for a simple shim:

#pragma once#include "Shim.h"class Shim_TSLie : public Shim {public:    Shim_TSLie();protected:    virtual void RegisterHooks();private:    static int WINAPI Hook_GetSystemMetrics(int nIndex);};

In Shim_TSLie.cpp, let's implement the class, adding a hook on GetSystemMetrics (which MSDN says is in User32.dll) that checks the requested metric and returns zero for SM_REMOTESESSION:

#include "Shim_TSLie.h"SHIM_INSTANCE(TSLie)void Shim_TSLie::RegisterHooks() {    ADD_HOOK("USER32.DLL", "GetSystemMetrics", Hook_GetSystemMetrics);}int WINAPI Shim_TSLie::Hook_GetSystemMetrics(int nIndex) {    if (nIndex == SM_REMOTESESSION) {        ASL_PRINTF(ASL_LEVEL_TRACE, "Lie: returning 0 for SM_REMOTESESSION");        return 0;    } else {        // Get and call the real GetSystemMetrics        DEFINE_NEXT(Hook_GetSystemMetrics);        return next(nIndex);    }}

Edit shimlist.cpp to include and instantiate Shim_TSLie, maybe delete the other example shims, and compile in the Release x86 configuration.

Installing the shim module

On Windows 10, the shim engine will only load shim modules with certain names. I recommend renaming the DLL AcRes.dll since that's an accepted name not used for an existing shim module. After any necessary rename, copy the DLL into the C:\Windows\SysWOW64 folder.

Applying the shim

Now for the hard part. The Compatibility Administrator doesn't let you define shims and doesn't look for shims in non-system databases, so the SDB file that declares and uses the custom shim must be created by other means.

Method 1: ShimDBC

If you get ahold of ShimDBC, you can write XML like this, changing the EXE attributes appropriately and adding any other matching information attributes you need...

<?xml version="1.0" encoding="utf-8"?><DATABASE NAME="Custom Database">    <LIBRARY>        <SHIM NAME="TSLie" FILE="AcRes.dll" RUNTIME_PLATFORM="X86_ANY"/>    </LIBRARY>    <APP NAME="Some App">        <EXE NAME="SomeApp.exe" PRODUCT_NAME="Some Product" RUNTIME_PLATFORM="X86_ANY">            <SHIM NAME="TSLie"/>        </EXE>    </APP></DATABASE>

...and compile it like so, changing the YourXml.xml and YourDatabase.sdb filenames as you like:

shimdbc Custom YourXml.xml YourDatabase.sdb -op X86_ANY

Method 2: AppHelp API

Alternatively, you can write the SDB using the Application Compatibility Database API functions exported from apphelp.dll. Unfortunately, there isn't a public LIB for it, so linking against it would be difficult. You can call the functions to build the SDB with a script for my SprintDLL utility...

call apphelp.dll!SdbCreateDatabase /return native /into pdb (lpwstr "YourDatabase.sdb", int 0)call apphelp.dll!SdbBeginWriteListTag /return int /into tDatabase (slotdata pdb, int 0x7001)call apphelp.dll!SdbWriteDWORDTag (slotdata pdb, int 0x4023, int 1)call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6001, lpwstr "Custom Database")call apphelp.dll!SdbWriteBinaryTag (slotdata pdb, int 0x9007, blockptr(guid {5FB8C914-168C-4B9B-8256-DF8A0F384E3E}), int 0x10)call apphelp.dll!SdbWriteQWORDTag (slotdata pdb, int 0x5001, long 0)call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6022, lpwstr "3.0.0.9")call apphelp.dll!SdbWriteDWORDTag (slotdata pdb, int 0x4021, int 37)call apphelp.dll!SdbWriteDWORDTag (slotdata pdb, int 0x4055, int 0)call apphelp.dll!SdbBeginWriteListTag /return int /into tLibrary (slotdata pdb, int 0x7002)call apphelp.dll!SdbBeginWriteListTag /return int /into tShim (slotdata pdb, int 0x7004)call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6001, lpwstr "TSLie")call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x600A, lpwstr "AcRes.dll")call apphelp.dll!SdbWriteBinaryTag (slotdata pdb, int 0x9010, blockptr(guid {77F84F43-0675-4B79-99EE-E70E0CE45BFC}), int 0x10)call apphelp.dll!SdbWriteDWORDTag (slotdata pdb, int 0x4021, int 37)call apphelp.dll!SdbEndWriteListTag (slotdata pdb, slotdata tShim)call apphelp.dll!SdbEndWriteListTag (slotdata pdb, slotdata tLibrary)call apphelp.dll!SdbBeginWriteListTag /return int /into tExe (slotdata pdb, int 0x7007)// TODO: Change EXE attributes appropriatelycall apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6001, lpwstr "SomeApp.exe")call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6006, lpwstr "Some App")call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6005, lpwstr "Some Vendor")call apphelp.dll!SdbWriteBinaryTag (slotdata pdb, int 0x9004, blockptr(guid {BB751D03-9792-4208-886A-AFDBC5AA0EBE}), int 0x10)call apphelp.dll!SdbWriteBinaryTag (slotdata pdb, int 0x9011, blockptr(guid {59CCFEAA-0A4B-4578-8B20-261A48D19E16}), int 0x10)call apphelp.dll!SdbBeginWriteListTag /return int /into tMatching (slotdata pdb, int 0x7008)// TODO: Change or add matching information as neededcall apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6001, lpwstr "*")call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6010, lpwstr "Some Product")call apphelp.dll!SdbEndWriteListTag (slotdata pdb, slotdata tMatching)call apphelp.dll!SdbBeginWriteListTag /return int /into tShimref (slotdata pdb, int 0x7009)call apphelp.dll!SdbWriteStringTag (slotdata pdb, int 0x6001, lpwstr "TSLie")call apphelp.dll!SdbEndWriteListTag (slotdata pdb, slotdata tShimref)call apphelp.dll!SdbEndWriteListTag (slotdata pdb, slotdata tExe)call apphelp.dll!SdbEndWriteListTag (slotdata pdb, slotdata tDatabase)call apphelp.dll!SdbCloseDatabaseWrite (slotdata pdb)

...that can be invoked like so:

sprintdll run YourScript.sprint

Installing the SDB

Finally, regardless of how you produce the SDB, you can install it with the standard sdbinst utility:

sdbinst YourDatabase.sdb