diff --git a/windows/src/engine/keyman/UfrmKeyman7Main.pas b/windows/src/engine/keyman/UfrmKeyman7Main.pas index 7f948d0518..a33e69cea4 100644 --- a/windows/src/engine/keyman/UfrmKeyman7Main.pas +++ b/windows/src/engine/keyman/UfrmKeyman7Main.pas @@ -292,6 +292,8 @@ type procedure UnregisterControllerWindows; // I4731 function IsSysTrayWindow(AHandle: THandle): Boolean; procedure GetTrayIconHandle; // I4731 + procedure WatchDogKeyEvent; + protected procedure DoInterfaceHotkey(Target: Integer); @@ -849,6 +851,14 @@ begin if (TKeymanHint(lParam) = KH_EXITPRODUCT) and (wParam = mrOk) then UnloadProduct; end; + KMC_WATCHDOG_KEYEVENT: + WatchDogKeyEvent; + KMC_WATCHDOG_FAKEFREEZE: + begin + TDebugLogClient.Instance.WriteMessage('kmc_fakefreeze begin', []); + Sleep(5000); + TDebugLogClient.Instance.WriteMessage('kmc_fakefreeze end', []); + end; //TOUCH KMC_CONTEXT: //TOUCH begin //TOUCH if LParam <> 0 then @@ -857,6 +867,13 @@ begin end; end; +procedure TfrmKeyman7Main.WatchDogKeyEvent; +begin + TDebugLogClient.Instance.WriteMessage('Attempting to send WatchDogKeyEvent', []); + if kmint.KeymanEngineControl <> nil then + kmint.KeymanEngineControl.WatchDogKeyEvent; +end; + procedure TfrmKeyman7Main.DoInterfaceHotkey(Target: Integer); begin if not Assigned(FRunningProduct) then diff --git a/windows/src/engine/keyman32/LowLevelHookWatchDog.cpp b/windows/src/engine/keyman32/LowLevelHookWatchDog.cpp new file mode 100644 index 0000000000..3da1c11feb --- /dev/null +++ b/windows/src/engine/keyman32/LowLevelHookWatchDog.cpp @@ -0,0 +1,105 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Created by mcdurdin on 2025-11-17 + * + * Handle situations where Windows silently uninstalls our low level keyboard + * hook, and dynamically reinstall it. The hook can be uninstalled when + * keyman.exe becomes unresponsive for more than 200msec (default timeout) -- + * this can be due to something Keyman is doing, but it could also happen during + * high system load. The hook will only be uninstalled if a key is pressed while + * Keyman is unresponsive. + * + * Per Windows documentation: + * https://learn.microsoft.com/en-us/windows/win32/winmsg/lowlevelkeyboardproc#remarks + * + * This works by tracking last event times for both the WH_KEYBOARD_LL hook and + * the WH_GETMESSAGE hook. These two hooks both receive key events, but the + * WH_GETMESSAGE hook runs in the focused thread context, whereas WH_KEYBOARD_LL + * runs in keyman.exe main thread context. The WH_KEYBOARD_LL hook receives the + * key event first. + * + * This (static) class is only used in the keyman.exe main thread context. + * + * Notification of HookIsAlive is a straightforward call from the low level + * keyboard hook procedure, but the KeyEventReceivedInGetMessageProc function + * must be signalled across processes. We have chosen to do this with a posted + * message to the master controller, which is then handled by + * UfrmKeyman7Main.pas, and passed through Keyman_WatchDogKeyEvent. + * + * The message handler was implemented in UfrmKeyman7Main.pas rather than in + * kmhook_getmessage.cpp, because it appears that kmnGetMessageProc does not see + * messages that were posted by it or child functions (this is by observation, not + * documentation -- I was not able to find documentation on this, but surmise it + * is to prevent deadlock/infinite loop scenarios, which could easily lockup + * Windows entirely). + * + * No key data is passed in the event, only the information that a key was + * pressed. + */ + +#include "pch.h" + +/** + * minimum number of milliseconds between the last LowLevel and GetMessage + * events before we assume the low level hook has been uninstalled, and we need + * to reinstall it. + */ +#define WATCHDOG_THRESHOLD 1000 + +static ULONGLONG LastLowLevelEventTick = 0; +static ULONGLONG LastGetMessageEventTick = 0; + +void LowLevelHookWatchDog::HookIsAlive() { + // ULONGLONG Previous = LastLowLevelEventTick; + LastLowLevelEventTick = GetTickCount64(); + // SendDebugMessageFormat("LowLevelHookWatchDog::HookIsAlive currentLL=%llu currentGM=%llu (lastLL=%llu)", LastLowLevelEventTick, LastGetMessageEventTick, Previous); +} + +void LowLevelHookWatchDog::KeyEventReceivedInGetMessageProc() { + // ULONGLONG Previous = LastGetMessageEventTick; + LastGetMessageEventTick = GetTickCount64(); + // SendDebugMessageFormat("LowLevelHookWatchDog::KeyEventReceivedInGetMessageProc currentLL=%llu currentGM=%llu (lastGM=%llu)", LastLowLevelEventTick, LastGetMessageEventTick, Previous); + + // This is a good place to check if we are still alive -- shortly after each + // keystroke event in the GetMessage hook, as this means at worst we'll have + // one or two keystrokes where Keyman must recover + if(!CheckIfHookIsAlive()) { + ReinstallHook(); + } +} + +bool LowLevelHookWatchDog::CheckIfHookIsAlive() { + if(LastGetMessageEventTick < LastLowLevelEventTick) { + // this shouldn't be possible but rather safe than sorry + return true; + } + + return LastGetMessageEventTick - LastLowLevelEventTick < WATCHDOG_THRESHOLD; +} + +void LowLevelHookWatchDog::ReinstallHook() { + //keyman32.cpp: + SendDebugMessageFormat( + "Attempting to reinstall hook because watchdog threshold exceeded by %llu msec (last LL=%llu last GM=%llu)", + LastGetMessageEventTick - LastLowLevelEventTick, + LastLowLevelEventTick, + LastGetMessageEventTick + ); + + if(!RestartLowLevelHook()) { + SendDebugMessage("Attempt to reinstall low level hook may have failed, see previous log messages"); + } else { + SendDebugMessage("Attempt to reinstall low level hook succeeded"); + } + + // We should assume the hook is alive at this point to avoid repeated resets + HookIsAlive(); +} + +#ifndef _WIN64 +extern "C" void __declspec(dllexport) WINAPI Keyman_WatchDogKeyEvent() { + SendDebugMessageFormat("Keyman_WatchDogKeyEvent"); + LowLevelHookWatchDog::KeyEventReceivedInGetMessageProc(); +} +#endif \ No newline at end of file diff --git a/windows/src/engine/keyman32/LowLevelHookWatchDog.h b/windows/src/engine/keyman32/LowLevelHookWatchDog.h new file mode 100644 index 0000000000..cd2ea07ff1 --- /dev/null +++ b/windows/src/engine/keyman32/LowLevelHookWatchDog.h @@ -0,0 +1,32 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Created by mcdurdin on 2025-11-17 + */ + +/** + * @brief Watch for scenarios where the low level keyboard hook may be + * uninstalled by Windows, e.g. when there is heavy system load, and + * reinstall it. + */ +class LowLevelHookWatchDog { +public: + /** + * @brief Update the watchdog timestamp to current time, because the low + * level hook is receiving messages successfully + */ + static void HookIsAlive(); + + /** + * @brief Update the watchdog GetMessageProc timestamp to current time; + * this is called by the Keyman_WatchDogKeyEvent function, when + * a message posted from the GetMessageProc hook is received by + * keyman.exe. + */ + static void KeyEventReceivedInGetMessageProc(); + +private: + static bool CheckIfHookIsAlive(); + static void ReinstallHook(); +}; + diff --git a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp index 47d77fc1ad..4b40846b93 100644 --- a/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp +++ b/windows/src/engine/keyman32/k32_lowlevelkeyboardhook.cpp @@ -141,6 +141,8 @@ LRESULT _kmnLowLevelKeyboardProc( SendDebugEntry(); + LowLevelHookWatchDog::HookIsAlive(); + PKBDLLHOOKSTRUCT hs = (PKBDLLHOOKSTRUCT) lParam; BOOL extended = hs->flags & LLKHF_EXTENDED ? TRUE : FALSE; diff --git a/windows/src/engine/keyman32/keyman32.cpp b/windows/src/engine/keyman32/keyman32.cpp index aadd695906..6c8f34116f 100644 --- a/windows/src/engine/keyman32/keyman32.cpp +++ b/windows/src/engine/keyman32/keyman32.cpp @@ -279,6 +279,24 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_GetInitialised(BOOL *FSingleA return Globals::get_Keyman_Initialised(); } +#ifndef _WIN64 +BOOL InitLowLevelHook() { + HINSTANCE hinst = GetModuleHandle(LIBRARY_NAME); + + *Globals::hhookLowLevelKeyboardProc() = SetWindowsHookExW(WH_KEYBOARD_LL, (HOOKPROC) kmnLowLevelKeyboardProc, hinst, Globals::get_FSingleThread()); // I4124 + return Globals::get_hhookLowLevelKeyboardProc() != NULL; +} + +BOOL UninitLowLevelHook() { + BOOL RetVal = TRUE; + if(Globals::get_hhookLowLevelKeyboardProc() && !UnhookWindowsHookEx(Globals::get_hhookLowLevelKeyboardProc())) // I4124 + RetVal = FALSE; + + *Globals::hhookLowLevelKeyboardProc() = NULL; + return RetVal; +} +#endif + BOOL InitHooks() { HINSTANCE hinst = GetModuleHandle(LIBRARY_NAME); @@ -286,7 +304,7 @@ BOOL InitHooks() *Globals::hhookGetMessage() = SetWindowsHookExW(WH_GETMESSAGE, (HOOKPROC) kmnGetMessageProc, hinst, Globals::get_FSingleThread()); *Globals::hhookCallWndProc() = SetWindowsHookExW(WH_CALLWNDPROC, (HOOKPROC) kmnCallWndProc, hinst, Globals::get_FSingleThread()); #ifndef _WIN64 - *Globals::hhookLowLevelKeyboardProc() = SetWindowsHookExW(WH_KEYBOARD_LL, (HOOKPROC) kmnLowLevelKeyboardProc, hinst, Globals::get_FSingleThread()); // I4124 + InitLowLevelHook(); #endif; return @@ -304,19 +322,14 @@ BOOL UninitHooks() if(Globals::get_hhookGetMessage() && !UnhookWindowsHookEx(Globals::get_hhookGetMessage())) RetVal = FALSE; - else - *Globals::hhookGetMessage() = NULL; + *Globals::hhookGetMessage() = NULL; if(Globals::get_hhookCallWndProc() && !UnhookWindowsHookEx(Globals::get_hhookCallWndProc())) RetVal = FALSE; - else - *Globals::hhookCallWndProc() = NULL; + *Globals::hhookCallWndProc() = NULL; #ifndef _WIN64 - if(Globals::get_hhookLowLevelKeyboardProc() && !UnhookWindowsHookEx(Globals::get_hhookLowLevelKeyboardProc())) // I4124 - RetVal = FALSE; - else - *Globals::hhookLowLevelKeyboardProc() = NULL; + RetVal = UninitLowLevelHook() && RetVal; #endif return RetVal; @@ -473,6 +486,27 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_RestartEngine() return TRUE; } +BOOL RestartLowLevelHook() { + BOOL result=true; + +#ifndef _WIN64 + if(!Globals::get_Keyman_Initialised()) { + return FALSE; + } + + if(!UninitLowLevelHook()) { + SendDebugMessageFormat("Failed to uninstall low level hook. GetLastError = %d", GetLastError()); + result = FALSE; + } + if(!InitLowLevelHook()) { + SendDebugMessageFormat("Failed to install low level hook. GetLastError = %d", GetLastError()); + result = FALSE; + } + +#endif + return result; +} + //--------------------------------------------------------------------------------------------------------- // // Utility guff functions diff --git a/windows/src/engine/keyman32/keyman32.def b/windows/src/engine/keyman32/keyman32.def index a61a78b463..f457075ce8 100644 --- a/windows/src/engine/keyman32/keyman32.def +++ b/windows/src/engine/keyman32/keyman32.def @@ -49,3 +49,5 @@ EXPORTS Keyman_UnregisterControllerThread SetCustomPostKeyCallback + + Keyman_WatchDogKeyEvent diff --git a/windows/src/engine/keyman32/keyman32.vcxproj b/windows/src/engine/keyman32/keyman32.vcxproj index ba15db6516..9d02f8dacd 100644 --- a/windows/src/engine/keyman32/keyman32.vcxproj +++ b/windows/src/engine/keyman32/keyman32.vcxproj @@ -361,6 +361,7 @@ + Create Create @@ -450,6 +451,7 @@ + diff --git a/windows/src/engine/keyman32/keymancontrol.h b/windows/src/engine/keyman32/keymancontrol.h index 70a0c5d39e..73f30f77de 100644 --- a/windows/src/engine/keyman32/keymancontrol.h +++ b/windows/src/engine/keyman32/keymancontrol.h @@ -47,6 +47,12 @@ #define KMC_PROFILECHANGED 18 // 9.0.426.0 // I3933 +#define KMC_HINTRESPONSE 19 // 14.0 HIWORD(wParam) = ModalResult, lParam = hint enum + +#define KMC_WATCHDOG_FAKEFREEZE 20 // 19.0 - pause Keyman for 5 seconds for debug purposes to test stability +#define KMC_WATCHDOG_KEYEVENT 21 // 19.0 - let the LowLevelHookWatchDog know that input has happened on another thread + + #define PC_UPDATE 0 // Tell Keyman to update its display of active keyboard #define PC_UPDATE_LANGUAGESWITCH 1 // Tell Keyman to update its display of active keyboard and then open language switch form #define PC_HOTKEYCHANGE 2 // Tell Keyman that a hotkey was pressed to switch keyboard diff --git a/windows/src/engine/keyman32/keymanengine.h b/windows/src/engine/keyman32/keymanengine.h index c784b04897..aa8f6ffa30 100644 --- a/windows/src/engine/keyman32/keymanengine.h +++ b/windows/src/engine/keyman32/keymanengine.h @@ -263,6 +263,7 @@ void keybd_shift(LPINPUT pInputs, int* n, BOOL isReset, LPBYTE const kbd); #include "k32_tsf.h" #include "k32_visualkeyboardinterface.h" +#include "LowLevelHookWatchDog.h" void ReportActiveKeyboard(WORD wCommand); // I3933 // I3949 void SelectKeyboardHKL(DWORD hkl, BOOL foreground); // I3933 // I3949 // I4271 @@ -273,4 +274,6 @@ void ProcessModifierChange(UINT key, BOOL isUp, BOOL isExtended); // I4793 BOOL SetupCoreEnvironment(km_core_option_item **test_env_opts); void DeleteCoreEnvironment(km_core_option_item *test_env_opts); +BOOL RestartLowLevelHook(); + #endif // _KEYMANENGINE_H diff --git a/windows/src/engine/keyman32/kmhook_getmessage.cpp b/windows/src/engine/keyman32/kmhook_getmessage.cpp index 5c7af7ce8a..4fee835254 100644 --- a/windows/src/engine/keyman32/kmhook_getmessage.cpp +++ b/windows/src/engine/keyman32/kmhook_getmessage.cpp @@ -146,6 +146,15 @@ LRESULT _kmnGetMessageProc(int nCode, WPARAM wParam, LPARAM lParam) } if ((mp->message == WM_KEYDOWN || mp->message == WM_SYSKEYDOWN || mp->message == WM_KEYUP || mp->message == WM_SYSKEYUP)) { // I4642 + + // To help our low level keyboard hook recover if system load causes it to + // be uninstalled, we tell the controller process that we should have seen a + // keystroke event in the low level keyboard hook; this gets passed into the + // LowLevelHookWatchDog via keyman.exe + if(mp->message == WM_KEYDOWN || mp->message == WM_SYSKEYDOWN) { + Globals::PostMasterController(wm_keyman_control, KMC_WATCHDOG_KEYEVENT, 0); + } + BYTE scan = KEYMSG_LPARAM_SCAN(mp->lParam); CheckScheduledRefresh(); _td->LastScanCode = scan; diff --git a/windows/src/engine/kmcomapi/com/system/keymancontrol.pas b/windows/src/engine/kmcomapi/com/system/keymancontrol.pas index 90bab26746..ef6fa8be83 100644 --- a/windows/src/engine/kmcomapi/com/system/keymancontrol.pas +++ b/windows/src/engine/kmcomapi/com/system/keymancontrol.pas @@ -76,6 +76,7 @@ type TKeyman32ControllerSendMessageFunction = function(msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; TKeyman32ControllerPostMessageFunction = procedure(msg: UINT; wParam: WPARAM; lParam: LPARAM); stdcall; TKeyman32UpdateTouchPanelVisibilityFunction = procedure(Value: BOOL); stdcall; + TKeyman32WatchDogKeyEvent = procedure; stdcall; TKeymanControl = class(TKeymanAutoObject, IKeymanCustomisationAccess, IIntKeymanControl, IKeymanControl, IKeymanEngineControl) private @@ -97,6 +98,7 @@ type FKeyman_PostMasterController: TKeyman32ControllerPostMessageFunction; FKeyman_StartExit: TKeyman32ExitFunction; // I3092 FKeyman_UpdateTouchPanelVisibility: TKeyman32UpdateTouchPanelVisibilityFunction; + FKeyman_WatchDogKeyEvent: TKeyman32WatchDogKeyEvent; procedure LoadKeyman32; procedure StartKeyman32; procedure Do_Keyman_Exit; @@ -148,6 +150,7 @@ type procedure DisableUserInterface; safecall; procedure EnableUserInterface; safecall; procedure UpdateTouchPanelVisibility(Value: Boolean); safecall; + procedure WatchDogKeyEvent; safecall; // 32 bit only procedure DiagnosticTestException; safecall; @@ -524,6 +527,25 @@ begin {$ENDIF} end; +(** + * Tell the LowLevelHookWatchDog that a key event has been received + *) +procedure TKeymanControl.WatchDogKeyEvent; +begin +{$IFDEF WIN64} + Error(Cardinal(E_NOTIMPL)); +{$ELSE} + KL.MethodEnter(Self, 'WatchDogKeyEvent', []); + try + LoadKeyman32; + FKeyman_WatchDogKeyEvent; + finally + KL.MethodExit(Self, 'WatchDogKeyEvent'); + end; +{$ENDIF} +end; + + procedure TKeymanControl.RegisterControllerThread(Value: LongWord); begin {$IFDEF WIN64} @@ -750,6 +772,9 @@ begin @FKeyman_SendMasterController := ProcAddr('Keyman_SendMasterController'); @FKeyman_PostMasterController := ProcAddr('Keyman_PostMasterController'); @FKeyman_UpdateTouchPanelVisibility := ProcAddr('Keyman_UpdateTouchPanelVisibility'); +{$IFNDEF WIN64} + @FKeyman_WatchDogKeyEvent := ProcAddr('Keyman_WatchDogKeyEvent'); +{$ENDIF} end; end; diff --git a/windows/src/global/delphi/general/KeymanControlMessages.pas b/windows/src/global/delphi/general/KeymanControlMessages.pas index 0fd5a6a1b5..67712e1747 100644 --- a/windows/src/global/delphi/general/KeymanControlMessages.pas +++ b/windows/src/global/delphi/general/KeymanControlMessages.pas @@ -54,6 +54,9 @@ const KMC_HINTRESPONSE = 19; // 14.0 HIWORD(wParam) = ModalResult, lParam = hint enum + KMC_WATCHDOG_FAKEFREEZE = 20; // 19.0 - pause Keyman for 5 seconds for debug purposes to test stability + KMC_WATCHDOG_KEYEVENT = 21; // 19.0 - let the LowLevelHookWatchDog know that input has happened on another thread + PC_UPDATE = 0; PC_UPDATE_LANGUAGESWITCH = 1; PC_HOTKEYCHANGE = 2; diff --git a/windows/src/global/delphi/general/KeymanEngineControl.pas b/windows/src/global/delphi/general/KeymanEngineControl.pas index 5afd6f9013..762755c172 100644 --- a/windows/src/global/delphi/general/KeymanEngineControl.pas +++ b/windows/src/global/delphi/general/KeymanEngineControl.pas @@ -1,18 +1,18 @@ (* Name: KeymanControlRestart Copyright: Copyright (C) SIL International. - Documentation: - Description: + Documentation: + Description: Create Date: 19 Jun 2007 Modified Date: 19 Jun 2007 Authors: mcdurdin - Related Files: - Dependencies: + Related Files: + Dependencies: - Bugs: - Todo: - Notes: + Bugs: + Todo: + Notes: History: 19 Jun 2007 - mcdurdin - Initial version *) unit KeymanEngineControl; @@ -43,6 +43,10 @@ type procedure UnregisterMasterController(Value: LongWord); safecall; procedure RegisterControllerThread(Value: LongWord); safecall; procedure UnregisterControllerThread(Value: LongWord); safecall; + + // New in 19.0 + + procedure WatchDogKeyEvent; safecall; // 32 bit only end; implementation diff --git a/windows/src/support/fakefreeze/.gitignore b/windows/src/support/fakefreeze/.gitignore new file mode 100644 index 0000000000..2959b40eec --- /dev/null +++ b/windows/src/support/fakefreeze/.gitignore @@ -0,0 +1 @@ +x64/ diff --git a/windows/src/support/fakefreeze/fakefreeze.cpp b/windows/src/support/fakefreeze/fakefreeze.cpp new file mode 100644 index 0000000000..4e78f43657 --- /dev/null +++ b/windows/src/support/fakefreeze/fakefreeze.cpp @@ -0,0 +1,39 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Created by mcdurdin on 2025-11-17 + * + * Tell keyman.exe to pause for 5 seconds to force Windows to silently uninstall + * the low level keyboard hook, per Windows documentation at + * https://learn.microsoft.com/en-us/windows/win32/winmsg/lowlevelkeyboardproc#remarks + * + * See LowLevelHookWatchDog.cpp for more detail + */ +#include +#include +#include + +#define KMC_WATCHDOG_FAKEFREEZE 20 + +int main() { + std::cout << "Posting a freeze message to Keyman master controller\n"; + UINT wm_keyman_control = RegisterWindowMessage(L"WM_KEYMAN_CONTROL"); + HWND hwnd = FindWindow(L"TfrmKeyman7Main", NULL); + if (hwnd == NULL) { + std::cout << "Keyman master controller window not found\n"; + return 1; + } + + if (!PostMessage(hwnd, wm_keyman_control, KMC_WATCHDOG_FAKEFREEZE, 0)) { + std::cout << "Error calling Keyman KMC_WATCHDOG_FAKEFREEZE\n"; + } + std::cout << "Sleeping 5 seconds...\n"; + + for (int i = 1; i <= 5; i++) { + Sleep(1000); + std::cout << "..." << i << "\n"; + } + std::cout << "Keyman should be responsive again now\n"; + + return 0; +} diff --git a/windows/src/support/fakefreeze/fakefreeze.sln b/windows/src/support/fakefreeze/fakefreeze.sln new file mode 100644 index 0000000000..f9c6b43a48 --- /dev/null +++ b/windows/src/support/fakefreeze/fakefreeze.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36616.10 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fakefreeze", "fakefreeze.vcxproj", "{BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Debug|x64.ActiveCfg = Debug|x64 + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Debug|x64.Build.0 = Debug|x64 + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Debug|x86.ActiveCfg = Debug|Win32 + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Debug|x86.Build.0 = Debug|Win32 + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Release|x64.ActiveCfg = Release|x64 + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Release|x64.Build.0 = Release|x64 + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Release|x86.ActiveCfg = Release|Win32 + {BDDE094F-B409-4DF5-AA9E-6DEC09B362CE}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BA12682B-94DC-4E10-AE3F-743D64317F12} + EndGlobalSection +EndGlobal diff --git a/windows/src/support/fakefreeze/fakefreeze.vcxproj b/windows/src/support/fakefreeze/fakefreeze.vcxproj new file mode 100644 index 0000000000..bdc42144ea --- /dev/null +++ b/windows/src/support/fakefreeze/fakefreeze.vcxproj @@ -0,0 +1,131 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {bdde094f-b409-4df5-aa9e-6dec09b362ce} + fakefreeze + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + + + \ No newline at end of file diff --git a/windows/src/support/fakefreeze/fakefreeze.vcxproj.filters b/windows/src/support/fakefreeze/fakefreeze.vcxproj.filters new file mode 100644 index 0000000000..8b54f2cabc --- /dev/null +++ b/windows/src/support/fakefreeze/fakefreeze.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file