https://git.reactos.org/?p=reactos.git;a=commitdiff;h=079b36542c87b5f64eb549d1d3836297b61ea6c2

commit 079b36542c87b5f64eb549d1d3836297b61ea6c2
Author:     Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com>
AuthorDate: Wed Dec 13 21:37:15 2023 +0900
Commit:     GitHub <nore...@github.com>
CommitDate: Wed Dec 13 21:37:15 2023 +0900

    [CTFMON][SDK] Add ctfmon.exe (#6149)
    
    ctfmon.exe will be a replacement of our
    kbswitch.exe in the future. That is the
    front-end of Language Bar. It is needed
    to support TIPs.
    - Add ctfmon.exe at base/applications/ctfmon.
    - Add <cicero/cicbase.h>,
      <cicero/CModulePath.h>, and
      <cicero/osinfo.h> headers and use them.
    CORE-19362
---
 base/applications/CMakeLists.txt         |   1 +
 base/applications/ctfmon/CLoaderWnd.cpp  | 100 ++++++++
 base/applications/ctfmon/CLoaderWnd.h    |  26 +++
 base/applications/ctfmon/CMakeLists.txt  |  14 ++
 base/applications/ctfmon/CRegWatcher.cpp | 339 +++++++++++++++++++++++++++
 base/applications/ctfmon/CRegWatcher.h   |  45 ++++
 base/applications/ctfmon/ctfmon.cpp      | 378 +++++++++++++++++++++++++++++++
 base/applications/ctfmon/ctfmon.rc       |  19 ++
 base/applications/ctfmon/precomp.h       |  49 ++++
 base/applications/ctfmon/res/ctfmon.ico  | Bin 0 -> 22486 bytes
 base/applications/ctfmon/resource.h      |   4 +
 sdk/include/reactos/cicero/CModulePath.h |  90 ++++++++
 sdk/include/reactos/cicero/cicbase.h     |  44 ++++
 sdk/include/reactos/cicero/osinfo.h      |  47 ++++
 14 files changed, 1156 insertions(+)

diff --git a/base/applications/CMakeLists.txt b/base/applications/CMakeLists.txt
index 7679233e5f2..dccaaa80438 100644
--- a/base/applications/CMakeLists.txt
+++ b/base/applications/CMakeLists.txt
@@ -6,6 +6,7 @@ add_subdirectory(charmap)
 add_subdirectory(clipbrd)
 add_subdirectory(cmdutils)
 add_subdirectory(control)
+add_subdirectory(ctfmon)
 add_subdirectory(drwtsn32)
 add_subdirectory(dxdiag)
 add_subdirectory(extrac32)
diff --git a/base/applications/ctfmon/CLoaderWnd.cpp 
b/base/applications/ctfmon/CLoaderWnd.cpp
new file mode 100644
index 00000000000..400941be008
--- /dev/null
+++ b/base/applications/ctfmon/CLoaderWnd.cpp
@@ -0,0 +1,100 @@
+/*
+ * PROJECT:     ReactOS CTF Monitor
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Cicero TIP Bar loader window
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#include "precomp.h"
+#include "CLoaderWnd.h"
+#include "CRegWatcher.h"
+
+BOOL CLoaderWnd::s_bUninitedSystem = FALSE;
+BOOL CLoaderWnd::s_bWndClassRegistered = FALSE;
+
+BOOL CLoaderWnd::Init()
+{
+    if (s_bWndClassRegistered)
+        return TRUE; // Already registered
+
+    // Register a window class
+    WNDCLASSEXW wc;
+    ZeroMemory(&wc, sizeof(wc));
+    wc.cbSize           = sizeof(wc);
+    wc.style            = CS_HREDRAW | CS_VREDRAW;
+    wc.hInstance        = g_hInst;
+    wc.hCursor          = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
+    wc.lpfnWndProc      = WindowProc;
+    wc.lpszClassName    = L"CiCTipBarClass";
+    if (!::RegisterClassExW(&wc))
+        return FALSE;
+
+    s_bWndClassRegistered = TRUE; // Remember
+    return TRUE;
+}
+
+HWND CLoaderWnd::CreateWnd()
+{
+    m_hWnd = ::CreateWindowExW(0, L"CiCTipBarClass", NULL, WS_DISABLED,
+                               0, 0, 0, 0, NULL, NULL, g_hInst, NULL);
+    return m_hWnd;
+}
+
+LRESULT CALLBACK
+CLoaderWnd::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch (uMsg)
+    {
+        case WM_CREATE:
+            break;
+
+        case WM_DESTROY:
+            ::PostQuitMessage(0);
+            break;
+
+        case WM_QUERYENDSESSION:
+            // NOTE: We don't support Win95/98/Me
+#ifdef SUPPORT_WIN9X
+            if (!(g_dwOsInfo & OSINFO_NT) && (!g_fWinLogon || (lParam & 
ENDSESSION_LOGOFF)))
+            {
+                ClosePopupTipbar();
+                TF_UninitSystem();
+                CLoaderWnd::s_bUninitedSystem = TRUE;
+            }
+#endif
+            return TRUE;
+
+        case WM_ENDSESSION:
+            if (wParam) // The session is being ended?
+            {
+                if (!s_bUninitedSystem)
+                {
+                    // Un-initialize now
+                    UninitApp();
+                    TF_UninitSystem();
+                    s_bUninitedSystem = TRUE;
+                }
+            }
+            else if (s_bUninitedSystem) // Once un-initialized?
+            {
+                // Re-initialize
+                TF_InitSystem();
+                if (!g_bOnWow64)
+                    GetPopupTipbar(hwnd, !!g_fWinLogon);
+
+                s_bUninitedSystem = FALSE;
+            }
+            break;
+
+        case WM_SYSCOLORCHANGE:
+        case WM_DISPLAYCHANGE:
+            if (!g_bOnWow64) // Is the system x86/x64 native?
+                CRegWatcher::StartSysColorChangeTimer();
+            break;
+
+        default:
+            return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+    }
+
+    return 0;
+}
diff --git a/base/applications/ctfmon/CLoaderWnd.h 
b/base/applications/ctfmon/CLoaderWnd.h
new file mode 100644
index 00000000000..9127a40155e
--- /dev/null
+++ b/base/applications/ctfmon/CLoaderWnd.h
@@ -0,0 +1,26 @@
+/*
+ * PROJECT:     ReactOS CTF Monitor
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Cicero TIP Bar loader window
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#pragma once
+
+class CLoaderWnd
+{
+public:
+    HWND m_hWnd;
+    static BOOL s_bUninitedSystem;
+    static BOOL s_bWndClassRegistered;
+
+    CLoaderWnd() : m_hWnd(NULL) { }
+    ~CLoaderWnd() { }
+
+    BOOL Init();
+    HWND CreateWnd();
+
+protected:
+    static LRESULT CALLBACK
+    WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+};
diff --git a/base/applications/ctfmon/CMakeLists.txt 
b/base/applications/ctfmon/CMakeLists.txt
new file mode 100644
index 00000000000..f88d8f5ad1a
--- /dev/null
+++ b/base/applications/ctfmon/CMakeLists.txt
@@ -0,0 +1,14 @@
+
+list(APPEND SOURCE
+    ctfmon.cpp
+    CLoaderWnd.cpp
+    CRegWatcher.cpp)
+
+add_rc_deps(ctfmon.rc ${CMAKE_CURRENT_SOURCE_DIR}/res/ctfmon.ico)
+add_executable(ctfmon ${SOURCE} ctfmon.rc)
+set_module_type(ctfmon win32gui UNICODE)
+add_dependencies(ctfmon msctf)
+target_link_libraries(ctfmon uuid)
+add_importlibs(ctfmon msctf advapi32 shell32 user32 msvcrt kernel32)
+add_pch(ctfmon precomp.h SOURCE)
+add_cd_file(TARGET ctfmon DESTINATION reactos/system32 FOR all)
diff --git a/base/applications/ctfmon/CRegWatcher.cpp 
b/base/applications/ctfmon/CRegWatcher.cpp
new file mode 100644
index 00000000000..7782b5c096d
--- /dev/null
+++ b/base/applications/ctfmon/CRegWatcher.cpp
@@ -0,0 +1,339 @@
+/*
+ * PROJECT:     ReactOS CTF Monitor
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Registry watcher
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#include "precomp.h"
+#include "CRegWatcher.h"
+
+// The event handles to use in watching
+HANDLE CRegWatcher::s_ahWatchEvents[WATCHENTRY_MAX] = { NULL };
+
+// The registry entries to watch
+WATCHENTRY CRegWatcher::s_WatchEntries[WATCHENTRY_MAX] =
+{
+    { HKEY_CURRENT_USER,  L"Keyboard Layout\\Toggle"                           
}, // WI_TOGGLE
+    { HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\CTF\\TIP"                     
}, // WI_MACHINE_TIF
+    { HKEY_CURRENT_USER,  L"Keyboard Layout\\Preload"                          
}, // WI_PRELOAD
+    { HKEY_CURRENT_USER,  L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run" 
}, // WI_RUN
+    { HKEY_CURRENT_USER,  L"SOFTWARE\\Microsoft\\CTF\\TIP"                     
}, // WI_USER_TIF
+    { HKEY_CURRENT_USER,  L"SOFTWARE\\Microsoft\\Speech"                       
}, // WI_USER_SPEECH
+    { HKEY_CURRENT_USER,  L"Control Panel\\Appearance"                         
}, // WI_APPEARANCE
+    { HKEY_CURRENT_USER,  L"Control Panel\\Colors"                             
}, // WI_COLORS
+    { HKEY_CURRENT_USER,  L"Control Panel\\Desktop\\WindowMetrics"             
}, // WI_WINDOW_METRICS
+    { HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Speech"                       
}, // WI_MACHINE_SPEECH
+    { HKEY_CURRENT_USER,  L"Keyboard Layout"                                   
}, // WI_KEYBOARD_LAYOUT
+    { HKEY_CURRENT_USER,  L"SOFTWARE\\Microsoft\\CTF\\Assemblies"              
}, // WI_ASSEMBLIES
+};
+
+// The timer IDs: For delaying ignitions
+UINT CRegWatcher::s_nSysColorTimerId    = 0;
+UINT CRegWatcher::s_nKbdToggleTimerId   = 0;
+UINT CRegWatcher::s_nRegImxTimerId      = 0;
+
+// %WINDIR%/IME/sptip.dll!TF_CreateLangProfileUtil
+typedef HRESULT (WINAPI* FN_TF_CreateLangProfileUtil)(ITfFnLangProfileUtil**);
+
+BOOL
+CRegWatcher::Init()
+{
+    // NOTE: We don't support Win95/98/Me
+#ifdef SUPPORT_WIN9X
+    if (!(g_dwOsInfo & OSINFO_NT))
+        s_WatchEntries[WI_RUN].hRootKey = HKEY_LOCAL_MACHINE;
+#endif
+
+    // Create some nameless events and initialize them
+    for (SIZE_T iEvent = 0; iEvent < _countof(s_ahWatchEvents); ++iEvent)
+    {
+        s_ahWatchEvents[iEvent] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
+        InitEvent(iEvent, FALSE);
+    }
+
+    // Internat.exe is an enemy of ctfmon.exe
+    KillInternat();
+
+    UpdateSpTip();
+
+    return TRUE;
+}
+
+VOID
+CRegWatcher::Uninit()
+{
+    for (SIZE_T iEvent = 0; iEvent < _countof(s_ahWatchEvents); ++iEvent)
+    {
+        // Close the key
+        WATCHENTRY& entry = s_WatchEntries[iEvent];
+        if (entry.hKey)
+        {
+            ::RegCloseKey(entry.hKey);
+            entry.hKey = NULL;
+        }
+
+        // Close the event handle
+        HANDLE& hEvent = s_ahWatchEvents[iEvent];
+        if (hEvent)
+        {
+            ::CloseHandle(hEvent);
+            hEvent = NULL;
+        }
+    }
+}
+
+BOOL
+CRegWatcher::InitEvent(
+    _In_ SIZE_T iEvent,
+    _In_ BOOL bResetEvent)
+{
+    // Reset the signal status
+    if (bResetEvent)
+        ::ResetEvent(s_ahWatchEvents[iEvent]);
+
+    // Close once to re-open
+    WATCHENTRY& entry = s_WatchEntries[iEvent];
+    if (entry.hKey)
+    {
+        ::RegCloseKey(entry.hKey);
+        entry.hKey = NULL;
+    }
+
+    // Open or create a registry key to watch registry key
+    LSTATUS error;
+    error = ::RegOpenKeyExW(entry.hRootKey, entry.pszSubKey, 0, KEY_READ, 
&entry.hKey);
+    if (error != ERROR_SUCCESS)
+    {
+        error = ::RegCreateKeyExW(entry.hRootKey, entry.pszSubKey, 0, NULL, 0,
+                                  KEY_ALL_ACCESS, NULL, &entry.hKey, NULL);
+        if (error != ERROR_SUCCESS)
+            return FALSE;
+    }
+
+    // Start registry watching
+    error = RegNotifyChangeKeyValue(entry.hKey,
+                                    TRUE,
+                                    REG_NOTIFY_CHANGE_LAST_SET | 
REG_NOTIFY_CHANGE_NAME,
+                                    s_ahWatchEvents[iEvent],
+                                    TRUE);
+    return error == ERROR_SUCCESS;
+}
+
+VOID
+CRegWatcher::UpdateSpTip()
+{
+    // Post message 0x8002 to "SapiTipWorkerClass" windows
+    ::EnumWindows(EnumWndProc, 0);
+
+    // Clear "ProfileInitialized" value
+    HKEY hKey;
+    LSTATUS error = ::RegOpenKeyExW(HKEY_CURRENT_USER, 
L"SOFTWARE\\Microsoft\\CTF\\Sapilayr",
+                                    0, KEY_WRITE, &hKey);
+    if (error == ERROR_SUCCESS)
+    {
+        DWORD dwValue = 0, cbValue = sizeof(dwValue);
+        ::RegSetValueExW(hKey, L"ProfileInitialized", NULL, REG_DWORD, 
(LPBYTE)&dwValue, cbValue);
+        ::RegCloseKey(hKey);
+    }
+
+    // Get %WINDIR%/IME/sptip.dll!TF_CreateLangProfileUtil function
+    HINSTANCE hSPTIP = LoadSystemLibrary(L"IME\\sptip.dll", TRUE);
+    FN_TF_CreateLangProfileUtil fnTF_CreateLangProfileUtil =
+        (FN_TF_CreateLangProfileUtil)::GetProcAddress(hSPTIP, 
"TF_CreateLangProfileUtil");
+    if (fnTF_CreateLangProfileUtil)
+    {
+        // Call it
+        ITfFnLangProfileUtil *pProfileUtil = NULL;
+        HRESULT hr = fnTF_CreateLangProfileUtil(&pProfileUtil);
+        if ((hr == S_OK) && pProfileUtil) // Success!
+        {
+            // Register profile
+            hr = pProfileUtil->RegisterActiveProfiles();
+            if (hr == S_OK)
+                TF_InvalidAssemblyListCacheIfExist(); // Invalidate the 
assembly list cache
+
+            pProfileUtil->Release();
+        }
+    }
+
+    if (hSPTIP)
+        ::FreeLibrary(hSPTIP);
+}
+
+VOID
+CRegWatcher::KillInternat()
+{
+    HKEY hKey;
+    WATCHENTRY& entry = s_WatchEntries[WI_RUN];
+
+    // Delete internat.exe from registry "Run" key
+    LSTATUS error = ::RegOpenKeyExW(entry.hRootKey, entry.pszSubKey, 0, 
KEY_ALL_ACCESS, &hKey);
+    if (error == ERROR_SUCCESS)
+    {
+        ::RegDeleteValueW(hKey, L"internat.exe");
+        ::RegCloseKey(hKey);
+    }
+
+    // Kill the "Indicator" window (that internat.exe creates)
+    HWND hwndInternat = ::FindWindowW(L"Indicator", NULL);
+    if (hwndInternat)
+        ::PostMessageW(hwndInternat, WM_CLOSE, 0, 0);
+}
+
+// Post message 0x8002 to every "SapiTipWorkerClass" window.
+// Called from CRegWatcher::UpdateSpTip
+BOOL CALLBACK
+CRegWatcher::EnumWndProc(
+    _In_ HWND hWnd,
+    _In_ LPARAM lParam)
+{
+    WCHAR ClassName[MAX_PATH];
+
+    UNREFERENCED_PARAMETER(lParam);
+
+    if (::GetClassNameW(hWnd, ClassName, _countof(ClassName)) &&
+        _wcsicmp(ClassName, L"SapiTipWorkerClass") == 0)
+    {
+        PostMessageW(hWnd, 0x8002, 0, 0); // FIXME: Magic number
+    }
+
+    return TRUE;
+}
+
+VOID CALLBACK
+CRegWatcher::SysColorTimerProc(
+    _In_ HWND hwnd,
+    _In_ UINT uMsg,
+    _In_ UINT_PTR idEvent,
+    _In_ DWORD dwTime)
+{
+    UNREFERENCED_PARAMETER(hwnd);
+    UNREFERENCED_PARAMETER(uMsg);
+    UNREFERENCED_PARAMETER(idEvent);
+    UNREFERENCED_PARAMETER(dwTime);
+
+    // Cancel the timer
+    if (s_nSysColorTimerId)
+    {
+        ::KillTimer(NULL, s_nSysColorTimerId);
+        s_nSysColorTimerId = 0;
+    }
+
+    TF_PostAllThreadMsg(15, 16);
+}
+
+VOID
+CRegWatcher::StartSysColorChangeTimer()
+{
+    // Call SysColorTimerProc 0.5 seconds later (Delayed)
+    if (s_nSysColorTimerId)
+    {
+        ::KillTimer(NULL, s_nSysColorTimerId);
+        s_nSysColorTimerId = 0;
+    }
+    s_nSysColorTimerId = ::SetTimer(NULL, 0, 500, SysColorTimerProc);
+}
+
+VOID CALLBACK
+CRegWatcher::RegImxTimerProc(
+    _In_ HWND hwnd,
+    _In_ UINT uMsg,
+    _In_ UINT_PTR idEvent,
+    _In_ DWORD dwTime)
+{
+    UNREFERENCED_PARAMETER(hwnd);
+    UNREFERENCED_PARAMETER(uMsg);
+    UNREFERENCED_PARAMETER(idEvent);
+    UNREFERENCED_PARAMETER(dwTime);
+
+    // Cancel the timer
+    if (s_nRegImxTimerId)
+    {
+        ::KillTimer(NULL, s_nRegImxTimerId);
+        s_nRegImxTimerId = 0;
+    }
+
+    TF_InvalidAssemblyListCache();
+    TF_PostAllThreadMsg(12, 16);
+}
+
+VOID CALLBACK
+CRegWatcher::KbdToggleTimerProc(
+    _In_ HWND hwnd,
+    _In_ UINT uMsg,
+    _In_ UINT_PTR idEvent,
+    _In_ DWORD dwTime)
+{
+    UNREFERENCED_PARAMETER(hwnd);
+    UNREFERENCED_PARAMETER(uMsg);
+    UNREFERENCED_PARAMETER(idEvent);
+    UNREFERENCED_PARAMETER(dwTime);
+
+    // Cancel the timer
+    if (s_nKbdToggleTimerId)
+    {
+        ::KillTimer(NULL, s_nKbdToggleTimerId);
+        s_nKbdToggleTimerId = 0;
+    }
+
+    TF_PostAllThreadMsg(11, 16);
+}
+
+VOID
+CRegWatcher::OnEvent(
+    _In_ SIZE_T iEvent)
+{
+    InitEvent(iEvent, TRUE);
+
+    switch (iEvent)
+    {
+        case WI_TOGGLE:
+        {
+            // Call KbdToggleTimerProc 0.5 seconds later (Delayed)
+            if (s_nKbdToggleTimerId)
+            {
+                ::KillTimer(NULL, s_nKbdToggleTimerId);
+                s_nKbdToggleTimerId = 0;
+            }
+            s_nKbdToggleTimerId = ::SetTimer(NULL, 0, 500, KbdToggleTimerProc);
+            break;
+        }
+        case WI_MACHINE_TIF:
+        case WI_PRELOAD:
+        case WI_USER_TIF:
+        case WI_MACHINE_SPEECH:
+        case WI_KEYBOARD_LAYOUT:
+        case WI_ASSEMBLIES:
+        {
+            if (iEvent == WI_MACHINE_SPEECH)
+                UpdateSpTip();
+
+            // Call RegImxTimerProc 0.2 seconds later (Delayed)
+            if (s_nRegImxTimerId)
+            {
+                ::KillTimer(NULL, s_nRegImxTimerId);
+                s_nRegImxTimerId = 0;
+            }
+            s_nRegImxTimerId = ::SetTimer(NULL, 0, 200, RegImxTimerProc);
+            break;
+        }
+        case WI_RUN: // The "Run" key is changed
+        {
+            KillInternat(); // Deny internat.exe the right to live
+            break;
+        }
+        case WI_USER_SPEECH:
+        case WI_APPEARANCE:
+        case WI_COLORS:
+        case WI_WINDOW_METRICS:
+        {
+            StartSysColorChangeTimer();
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+}
diff --git a/base/applications/ctfmon/CRegWatcher.h 
b/base/applications/ctfmon/CRegWatcher.h
new file mode 100644
index 00000000000..1f75cb7fb4b
--- /dev/null
+++ b/base/applications/ctfmon/CRegWatcher.h
@@ -0,0 +1,45 @@
+/*
+ * PROJECT:     ReactOS CTF Monitor
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Registry watcher
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#pragma once
+
+struct WATCHENTRY
+{
+    HKEY hRootKey;
+    LPCWSTR pszSubKey;
+    HKEY hKey;
+};
+
+#define WATCHENTRY_MAX 12
+
+struct CRegWatcher
+{
+    static HANDLE s_ahWatchEvents[WATCHENTRY_MAX];
+    static WATCHENTRY s_WatchEntries[WATCHENTRY_MAX];
+    static UINT s_nSysColorTimerId, s_nKbdToggleTimerId, s_nRegImxTimerId;
+
+    static BOOL Init();
+    static VOID Uninit();
+    static BOOL InitEvent(_In_ SIZE_T iEvent, _In_ BOOL bResetEvent);
+    static VOID UpdateSpTip();
+    static VOID KillInternat();
+    static VOID StartSysColorChangeTimer();
+    static VOID OnEvent(_In_ SIZE_T iEvent);
+
+protected:
+    static BOOL CALLBACK
+    EnumWndProc(_In_ HWND hWnd, _In_ LPARAM lParam);
+
+    static VOID CALLBACK
+    SysColorTimerProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, 
_In_ DWORD dwTime);
+
+    static VOID CALLBACK
+    KbdToggleTimerProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, 
_In_ DWORD dwTime);
+
+    static VOID CALLBACK
+    RegImxTimerProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ UINT_PTR idEvent, 
_In_ DWORD dwTime);
+};
diff --git a/base/applications/ctfmon/ctfmon.cpp 
b/base/applications/ctfmon/ctfmon.cpp
new file mode 100644
index 00000000000..ebeae0fd933
--- /dev/null
+++ b/base/applications/ctfmon/ctfmon.cpp
@@ -0,0 +1,378 @@
+/*
+ * PROJECT:     ReactOS CTF Monitor
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Providing Language Bar front-end
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#include "precomp.h"
+#include "CRegWatcher.h"
+#include "CLoaderWnd.h"
+
+// ntdll!NtQueryInformationProcess
+typedef NTSTATUS (WINAPI *FN_NtQueryInformationProcess)(HANDLE, 
PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+FN_NtQueryInformationProcess g_fnNtQueryInformationProcess = NULL;
+
+// kernel32!SetProcessShutdownParameters
+typedef BOOL (WINAPI *FN_SetProcessShutdownParameters)(DWORD, DWORD);
+FN_SetProcessShutdownParameters g_fnSetProcessShutdownParameters = NULL;
+
+// kernel32!GetSystemWow64DirectoryW
+typedef UINT (WINAPI *FN_GetSystemWow64DirectoryW)(LPWSTR, UINT);
+FN_GetSystemWow64DirectoryW g_fnGetSystemWow64DirectoryW = NULL;
+
+HINSTANCE   g_hInst         = NULL;     // The application instance
+HINSTANCE   g_hKernel32     = NULL;     // The "kernel32.dll" instance
+UINT        g_uACP          = CP_ACP;   // The active codepage
+BOOL        g_fWinLogon     = FALSE;    // Is it a log-on process?
+HANDLE      g_hCicMutex     = NULL;     // The Cicero mutex
+BOOL        g_bOnWow64      = FALSE;    // Is the app running on WoW64?
+BOOL        g_fNoRunKey     = FALSE;    // Don't write registry key "Run"?
+BOOL        g_fJustRunKey   = FALSE;    // Just write registry key "Run"?
+DWORD       g_dwOsInfo      = 0;        // The OS version info. See GetOSInfo
+CLoaderWnd* g_pLoaderWnd    = NULL;     // TIP Bar loader window
+
+// Is the current process on WoW64?
+static BOOL
+IsWow64(VOID)
+{
+    HMODULE hNTDLL = GetSystemModuleHandle(L"ntdll.dll", FALSE);
+    if (!hNTDLL)
+        return FALSE;
+
+    g_fnNtQueryInformationProcess =
+        (FN_NtQueryInformationProcess)::GetProcAddress(hNTDLL, 
"NtQueryInformationProcess");
+    if (!g_fnNtQueryInformationProcess)
+        return FALSE;
+
+    ULONG_PTR Value = 0;
+    NTSTATUS Status = g_fnNtQueryInformationProcess(::GetCurrentProcess(), 
ProcessWow64Information,
+                                                    &Value, sizeof(Value), 
NULL);
+    if (!NT_SUCCESS(Status))
+        return FALSE;
+
+    return !!Value;
+}
+
+static VOID
+ParseCommandLine(
+    _In_ LPCWSTR pszCmdLine)
+{
+    g_fNoRunKey = g_fJustRunKey = FALSE;
+
+    for (LPCWSTR pch = pszCmdLine; *pch; ++pch)
+    {
+        // Skip space
+        while (*pch == L' ')
+            ++pch;
+
+        if (*pch == UNICODE_NULL)
+            return;
+
+        if ((*pch == L'-') || (*pch == L'/'))
+        {
+            ++pch;
+            switch (*pch)
+            {
+                case L'N': case L'n': // Found "/N" option
+                    g_fNoRunKey = TRUE;
+                    break;
+
+                case L'R': case L'r': // Found "/R" option
+                    g_fJustRunKey = TRUE;
+                    break;
+
+                case UNICODE_NULL:
+                    return;
+
+                default:
+                    break;
+            }
+        }
+    }
+}
+
+static VOID
+WriteRegRun(VOID)
+{
+    if (g_fNoRunKey) // If "/N" option is specified
+        return; // Don't write
+
+    // Open "Run" key
+    HKEY hKey;
+    LSTATUS error = ::RegCreateKeyW(HKEY_CURRENT_USER,
+                                    
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
+                                    &hKey);
+    if (error != ERROR_SUCCESS)
+        return;
+
+    // Write the module path
+    CModulePath ModPath;
+    if (ModPath.Init(L"ctfmon.exe", FALSE))
+    {
+        DWORD cbData = (ModPath.m_cchPath + 1) * sizeof(WCHAR);
+        ::RegSetValueExW(hKey, L"ctfmon.exe", 0, REG_SZ, 
(BYTE*)ModPath.m_szPath, cbData);
+    }
+
+    ::RegCloseKey(hKey);
+}
+
+static HRESULT
+GetGlobalCompartment(
+    _In_ REFGUID guid,
+    _Inout_ ITfCompartment **ppComp)
+{
+    *ppComp = NULL;
+
+    ITfCompartmentMgr *pCompMgr = NULL;
+    HRESULT hr = TF_GetGlobalCompartment(&pCompMgr);
+    if (FAILED(hr))
+        return hr;
+
+    if (!pCompMgr)
+        return E_FAIL;
+
+    hr = pCompMgr->GetCompartment(guid, ppComp);
+    pCompMgr->Release();
+    return hr;
+}
+
+static HRESULT
+SetGlobalCompartmentDWORD(
+    _In_ REFGUID guid,
+    _In_ DWORD dwValue)
+{
+    HRESULT hr;
+    VARIANT vari;
+    ITfCompartment *pComp;
+
+    hr = GetGlobalCompartment(guid, &pComp);
+    if (FAILED(hr))
+        return hr;
+
+    V_VT(&vari) = VT_I4;
+    V_I4(&vari) = dwValue;
+    hr = pComp->SetValue(0, &vari);
+
+    pComp->Release();
+    return hr;
+}
+
+static BOOL
+CheckX64System(
+    _In_ LPWSTR lpCmdLine)
+{
+    // Is the system x64?
+    SYSTEM_INFO SystemInfo;
+    ::GetSystemInfo(&SystemInfo);
+    if (SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_IA64 ||
+        SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64)
+    {
+        return FALSE;
+    }
+
+    // Get GetSystemWow64DirectoryW function
+    g_hKernel32 = GetSystemModuleHandle(L"kernel32.dll", FALSE);
+    g_fnGetSystemWow64DirectoryW =
+        (FN_GetSystemWow64DirectoryW)::GetProcAddress(g_hKernel32, 
"GetSystemWow64DirectoryW");
+    if (!g_fnGetSystemWow64DirectoryW)
+        return FALSE;
+
+    // Build WoW64 ctfmon.exe pathname
+    WCHAR szPath[MAX_PATH];
+    UINT cchPath = g_fnGetSystemWow64DirectoryW(szPath, _countof(szPath));
+    if (!cchPath && FAILED(StringCchCatW(szPath, _countof(szPath), 
L"\\ctfmon.exe")))
+        return FALSE;
+
+    // Create a WoW64 ctfmon.exe process
+    PROCESS_INFORMATION pi;
+    STARTUPINFOW si = { sizeof(si) };
+    si.wShowWindow = SW_SHOWMINNOACTIVE;
+    if (!::CreateProcessW(szPath, lpCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, 
&si, &pi))
+        return FALSE;
+
+    ::CloseHandle(pi.hThread);
+    ::CloseHandle(pi.hProcess);
+    return TRUE;
+}
+
+static BOOL
+InitApp(
+    _In_ HINSTANCE hInstance,
+    _In_ LPWSTR lpCmdLine)
+{
+    g_hInst     = hInstance;    // Save the instance handle
+
+    g_uACP      = ::GetACP();   // Save the active codepage
+    g_bOnWow64  = IsWow64();    // Is the current process on WoW64?
+    g_dwOsInfo  = GetOSInfo();  // Get OS info
+
+    // Create a mutex for Cicero
+    g_hCicMutex = TF_CreateCicLoadMutex(&g_fWinLogon);
+    if (!g_hCicMutex)
+        return FALSE;
+
+    // Write to "Run" registry key for starting up
+    WriteRegRun();
+
+    // Call SetProcessShutdownParameters if possible
+    if (g_dwOsInfo & OSINFO_NT)
+    {
+        g_hKernel32 = GetSystemModuleHandle(L"kernel32.dll", FALSE);
+        g_fnSetProcessShutdownParameters =
+            (FN_SetProcessShutdownParameters)
+                ::GetProcAddress(g_hKernel32, "SetProcessShutdownParameters");
+        if (g_fnSetProcessShutdownParameters)
+            g_fnSetProcessShutdownParameters(0xF0, SHUTDOWN_NORETRY);
+    }
+
+    // Start text framework
+    TF_InitSystem();
+
+    // Start watching registry if x86/x64 native
+    if (!g_bOnWow64)
+        CRegWatcher::Init();
+
+    // Create TIP Bar loader window
+    g_pLoaderWnd = new CLoaderWnd();
+    if (!g_pLoaderWnd || !g_pLoaderWnd->Init())
+        return FALSE;
+
+    if (g_pLoaderWnd->CreateWnd())
+    {
+        // Go to the bottom of the hell
+        ::SetWindowPos(g_pLoaderWnd->m_hWnd, HWND_BOTTOM, 0, 0, 0, 0,
+                       SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+    }
+
+    // Display TIP Bar Popup if x86/x64 native
+    if (!g_bOnWow64)
+        GetPopupTipbar(g_pLoaderWnd->m_hWnd, g_fWinLogon);
+
+    // Do x64 stuffs
+    CheckX64System(lpCmdLine);
+
+    return TRUE;
+}
+
+VOID
+UninitApp(VOID)
+{
+    // Close TIP Bar Popup
+    ClosePopupTipbar();
+
+    // Close the mutex
+    ::CloseHandle(g_hCicMutex);
+    g_hCicMutex = NULL;
+
+    // Quit watching registry if x86/x64 native
+    if (!g_bOnWow64)
+        CRegWatcher::Uninit();
+}
+
+static INT
+DoMainLoop(VOID)
+{
+    MSG msg;
+
+    if (g_bOnWow64) // Is the current process on WoW64?
+    {
+        // Just a simple message loop
+        while (::GetMessageW(&msg, NULL, 0, 0))
+        {
+            ::TranslateMessage(&msg);
+            ::DispatchMessageW(&msg);
+        }
+        return (INT)msg.wParam;
+    }
+
+    // Open the existing event by the name
+    HANDLE hSwitchEvent = ::OpenEventW(SYNCHRONIZE, FALSE, 
L"WinSta0_DesktopSwitch");
+
+    // The target events to watch
+    HANDLE ahEvents[WATCHENTRY_MAX + 1];
+
+    // Borrow some handles from CRegWatcher
+    CopyMemory(ahEvents, CRegWatcher::s_ahWatchEvents, WATCHENTRY_MAX * 
sizeof(HANDLE));
+
+    ahEvents[WI_DESKTOP_SWITCH] = hSwitchEvent; // Add it
+
+    // Another message loop
+    for (;;)
+    {
+        // Wait for target signal
+        DWORD dwWait = ::MsgWaitForMultipleObjects(_countof(ahEvents), 
ahEvents, 0, INFINITE,
+                                                   QS_ALLINPUT);
+        if (dwWait == (WAIT_OBJECT_0 + _countof(ahEvents))) // Is input 
available?
+        {
+            // Do the events
+            while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+            {
+                if (msg.message == WM_QUIT)
+                    goto Quit;
+
+                ::TranslateMessage(&msg);
+                ::DispatchMessageW(&msg);
+            }
+        }
+        else if (dwWait == (WAIT_OBJECT_0 + WI_DESKTOP_SWITCH)) // Desktop 
switch?
+        {
+            SetGlobalCompartmentDWORD(GUID_COMPARTMENT_SPEECH_OPENCLOSE, 0);
+            ::ResetEvent(hSwitchEvent);
+        }
+        else // Do the other events
+        {
+            CRegWatcher::OnEvent(dwWait - WAIT_OBJECT_0);
+        }
+    }
+
+Quit:
+    ::CloseHandle(hSwitchEvent);
+
+    return (INT)msg.wParam;
+}
+
+// The main function for Unicode Win32
+EXTERN_C INT WINAPI
+wWinMain(
+    HINSTANCE hInstance,
+    HINSTANCE hPrevInst,
+    LPWSTR lpCmdLine,
+    INT nCmdShow)
+{
+    UNREFERENCED_PARAMETER(hPrevInst);
+    UNREFERENCED_PARAMETER(nCmdShow);
+
+    // Parse command line
+    ParseCommandLine(lpCmdLine);
+
+    if (g_fJustRunKey) // If "/R" option is specified
+    {
+        // Just write registry and exit
+        WriteRegRun();
+        return 1;
+    }
+
+    // Initialize the application
+    if (!InitApp(hInstance, lpCmdLine))
+        return 0;
+
+    // The main loop
+    INT ret = DoMainLoop();
+
+    // Clean up the loader
+    if (g_pLoaderWnd)
+    {
+        delete g_pLoaderWnd;
+        g_pLoaderWnd = NULL;
+    }
+
+    // Un-initialize app and text framework
+    if (!CLoaderWnd::s_bUninitedSystem)
+    {
+        UninitApp();
+        TF_UninitSystem();
+    }
+
+    return ret;
+}
diff --git a/base/applications/ctfmon/ctfmon.rc 
b/base/applications/ctfmon/ctfmon.rc
new file mode 100644
index 00000000000..fe628048326
--- /dev/null
+++ b/base/applications/ctfmon/ctfmon.rc
@@ -0,0 +1,19 @@
+/*
+ * PROJECT:     ReactOS CTF Monitor
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Providing Language Bar front-end
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#include <windef.h>
+
+#include "resource.h"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS CTF Monitor"
+#define REACTOS_STR_INTERNAL_NAME     "ctfmon"
+#define REACTOS_STR_ORIGINAL_FILENAME "ctfmon.exe"
+#include <reactos/version.rc>
+
+IDI_MAIN ICON "res/ctfmon.ico"
diff --git a/base/applications/ctfmon/precomp.h 
b/base/applications/ctfmon/precomp.h
new file mode 100644
index 00000000000..636739eb9e1
--- /dev/null
+++ b/base/applications/ctfmon/precomp.h
@@ -0,0 +1,49 @@
+/*
+ * PROJECT:     ReactOS CTF Monitor
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Providing Language Bar front-end
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#pragma once
+
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <ndk/pstypes.h>
+#include <shellapi.h>
+#include <shlwapi.h>
+#include <stdlib.h>
+#include <strsafe.h>
+#include <msctf.h>
+#include <ctfutb.h>
+#include <ctffunc.h>
+
+#include <cicero/cicbase.h>
+#include <cicero/osinfo.h>
+#include <cicero/CModulePath.h>
+
+#include "resource.h"
+
+extern HINSTANCE g_hInst;
+extern BOOL g_bOnWow64;
+extern BOOL g_fWinLogon;
+extern DWORD g_dwOsInfo;
+
+VOID UninitApp(VOID);
+
+typedef enum WATCH_INDEX
+{
+    WI_TOGGLE            = 0,
+    WI_MACHINE_TIF       = 1,
+    WI_PRELOAD           = 2,
+    WI_RUN               = 3,
+    WI_USER_TIF          = 4,
+    WI_USER_SPEECH       = 5,
+    WI_APPEARANCE        = 6,
+    WI_COLORS            = 7,
+    WI_WINDOW_METRICS    = 8,
+    WI_MACHINE_SPEECH    = 9,
+    WI_KEYBOARD_LAYOUT   = 10,
+    WI_ASSEMBLIES        = 11,
+    WI_DESKTOP_SWITCH    = 12,
+} WATCH_INDEX;
diff --git a/base/applications/ctfmon/res/ctfmon.ico 
b/base/applications/ctfmon/res/ctfmon.ico
new file mode 100644
index 00000000000..5d7c7626fbf
Binary files /dev/null and b/base/applications/ctfmon/res/ctfmon.ico differ
diff --git a/base/applications/ctfmon/resource.h 
b/base/applications/ctfmon/resource.h
new file mode 100644
index 00000000000..79f0b3152af
--- /dev/null
+++ b/base/applications/ctfmon/resource.h
@@ -0,0 +1,4 @@
+#pragma once
+
+/* Icons */
+#define IDI_MAIN 100
diff --git a/sdk/include/reactos/cicero/CModulePath.h 
b/sdk/include/reactos/cicero/CModulePath.h
new file mode 100644
index 00000000000..2418b157cdd
--- /dev/null
+++ b/sdk/include/reactos/cicero/CModulePath.h
@@ -0,0 +1,90 @@
+/*
+ * PROJECT:     ReactOS Cicero
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Manipulate module path
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#pragma once
+
+struct CModulePath
+{
+    WCHAR m_szPath[MAX_PATH];
+    SIZE_T m_cchPath;
+
+    CModulePath()
+    {
+        m_szPath[0] = UNICODE_NULL;
+        m_cchPath = 0;
+    }
+
+    BOOL Init(_In_ LPCWSTR pszFileName, _In_ BOOL bSysWinDir);
+};
+
+// Get an instance handle that is already loaded
+static inline HINSTANCE
+GetSystemModuleHandle(
+    _In_ LPCWSTR pszFileName,
+    _In_ BOOL bSysWinDir)
+{
+    CModulePath ModPath;
+    if (!ModPath.Init(pszFileName, bSysWinDir))
+        return NULL;
+    return GetModuleHandleW(ModPath.m_szPath);
+}
+
+// Load a system library
+static inline HINSTANCE
+LoadSystemLibrary(
+    _In_ LPCWSTR pszFileName,
+    _In_ BOOL bSysWinDir)
+{
+    CModulePath ModPath;
+    if (!ModPath.Init(pszFileName, bSysWinDir))
+        return NULL;
+    return ::LoadLibraryW(ModPath.m_szPath);
+}
+
+/******************************************************************************/
+
+inline BOOL
+CModulePath::Init(
+    _In_ LPCWSTR pszFileName,
+    _In_ BOOL bSysWinDir)
+{
+    SIZE_T cchPath;
+    if (bSysWinDir)
+    {
+        // Usually C:\Windows or C:\ReactOS
+        cchPath = ::GetSystemWindowsDirectory(m_szPath, _countof(m_szPath));
+    }
+    else
+    {
+        // Usually C:\Windows\system32 or C:\ReactOS\system32
+        cchPath = ::GetSystemDirectoryW(m_szPath, _countof(m_szPath));
+    }
+
+    m_szPath[_countof(m_szPath) - 1] = UNICODE_NULL; // Avoid buffer overrun
+
+    if ((cchPath == 0) || (cchPath > _countof(m_szPath) - 2))
+        goto Failure;
+
+    // Add backslash if necessary
+    if ((cchPath > 0) && (m_szPath[cchPath - 1] != L'\\'))
+    {
+        m_szPath[cchPath + 0] = L'\\';
+        m_szPath[cchPath + 1] = UNICODE_NULL;
+    }
+
+    // Append pszFileName
+    if (FAILED(StringCchCatW(m_szPath, _countof(m_szPath), pszFileName)))
+        goto Failure;
+
+    m_cchPath = wcslen(m_szPath);
+    return TRUE;
+
+Failure:
+    m_szPath[0] = UNICODE_NULL;
+    m_cchPath = 0;
+    return FALSE;
+}
diff --git a/sdk/include/reactos/cicero/cicbase.h 
b/sdk/include/reactos/cicero/cicbase.h
new file mode 100644
index 00000000000..9048dcb865a
--- /dev/null
+++ b/sdk/include/reactos/cicero/cicbase.h
@@ -0,0 +1,44 @@
+/*
+ * PROJECT:     ReactOS Cicero
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Cicero base
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+static inline LPVOID cicMemAllocClear(SIZE_T size)
+{
+    return LocalAlloc(LMEM_ZEROINIT, size);
+}
+
+static inline void cicMemFree(LPVOID ptr)
+{
+    if (ptr)
+        LocalFree(ptr);
+}
+
+#ifdef __cplusplus
+inline void* __cdecl operator new(size_t size) noexcept
+{
+    return cicMemAllocClear(size);
+}
+
+inline void __cdecl operator delete(void* ptr) noexcept
+{
+    cicMemFree(ptr);
+}
+
+inline void __cdecl operator delete(void* ptr, size_t size) noexcept
+{
+    cicMemFree(ptr);
+}
+#endif // __cplusplus
+
+// FIXME: Use msutb.dll and header
+static inline void ClosePopupTipbar(void)
+{
+}
+
+// FIXME: Use msutb.dll and header
+static inline void GetPopupTipbar(HWND hwnd, BOOL fWinLogon)
+{
+}
diff --git a/sdk/include/reactos/cicero/osinfo.h 
b/sdk/include/reactos/cicero/osinfo.h
new file mode 100644
index 00000000000..b260000dfbc
--- /dev/null
+++ b/sdk/include/reactos/cicero/osinfo.h
@@ -0,0 +1,47 @@
+/*
+ * PROJECT:     ReactOS Cicero
+ * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:     Getting OS information
+ * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ 
<katayama.hirofumi...@gmail.com>
+ */
+
+#pragma once
+
+/* The flags of GetOSInfo() */
+#define OSINFO_NT    0x01
+#define OSINFO_CJK   0x10
+#define OSINFO_IMM   0x20
+#define OSINFO_DBCS  0x40
+
+static inline DWORD
+GetOSInfo(VOID)
+{
+    DWORD dwOsInfo = 0;
+
+    /* Check OS version info */
+    OSVERSIONINFOW VerInfo = { sizeof(VerInfo) };
+    GetVersionExW(&VerInfo);
+    if (VerInfo.dwPlatformId == DLLVER_PLATFORM_NT)
+        dwOsInfo |= OSINFO_NT;
+
+    /* Check codepage */
+    switch (GetACP())
+    {
+        case 932: /* Japanese (Japan) */
+        case 936: /* Chinese (PRC, Singapore) */
+        case 949: /* Korean (Korea) */
+        case 950: /* Chinese (Taiwan, Hong Kong) */
+            dwOsInfo |= OSINFO_CJK;
+            break;
+    }
+
+    if (GetSystemMetrics(SM_IMMENABLED))
+        dwOsInfo |= OSINFO_IMM;
+
+    if (GetSystemMetrics(SM_DBCSENABLED))
+        dwOsInfo |= OSINFO_DBCS;
+
+    /* I'm not interested in other flags */
+
+    return dwOsInfo;
+}

Reply via email to