Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3525 lines
101 KiB

//
// default.cpp
//
#include "private.h"
#include "globals.h"
#include "imelist.h"
#include "tim.h"
#include "nuimgr.h"
#include "nuihkl.h"
#include "nuictrl.h"
#include "catmgr.h"
#include "imelist.h"
#include "ptrary.h"
#include "ic.h"
#include "assembly.h"
#include "profiles.h"
#include "imelist.h"
#include "ithdmshl.h"
#include "marshal.h"
#include "mstub.h"
#include "smblock.h"
#include "timlist.h"
#include "thdutil.h"
#include "utb.h"
#include "mui.h"
#include "hotkey.h"
#include "profiles.h"
#include "lbaddin.h"
#include "cregkey.h"
CPtrArray<SYSTHREAD> *g_rgSysThread = NULL;
BOOL IsConsoleWindow(HWND hWnd);
void InitThreadHook(DWORD dwThreadId);
void UninitThreadHooks(SYSTHREAD *psfn);
void DestroyMarshalWindow(SYSTHREAD* psfn, HWND hwnd);
UINT _CBTHook(int nCode, WPARAM wParam, LPARAM lParam);
UINT _ShellProc(int nCode, WPARAM wParam, LPARAM lParam);
UINT _GetMsgHook(WPARAM wParam, LPARAM lParam);
UINT _KeyboardHook(WPARAM wParam, LPARAM lParam);
// crazy workaround for a system bug
// sometimes our hook procs will be called after
// we are detached. By this time we've unmapped
// our shared memory, so keep around a local copy.
static HHOOK s_hSysShellHook = 0;
static HHOOK s_hSysGetMsgHook = 0;
static HHOOK s_hSysCBTHook = 0;
// handling old input CPL
static BOOL g_fLoadedCPLName = FALSE;
static BOOL g_fCHWin9x = FALSE;
static BOOL g_fCHNT4 = FALSE;
TCHAR g_szKbdCPLName[128];
TCHAR g_szKbdCPLTitle[128];
TCHAR g_szWinCHCPLName[128];
TCHAR g_szWinCHCPLTitle[128];
TCHAR g_szNTCPLName[128];
TCHAR g_szNTCPLTitle[128];
TCHAR g_szOldCPLMsg[256];
TCHAR g_szCPLButton[128];
TCHAR g_szCPLGroupBox[128];
TCHAR g_szCHNT4CPLName[128];
TCHAR g_szCHNT4CPLTitle1[128];
TCHAR g_szCHNT4CPLTitle2[128];
TCHAR g_szCH9xKbdCPLTitle[128];
const CHAR c_szIntlCPLFetchClass[] = "IntlNewInputLocaleWndlClass";
// CPL window name and input locale tab title name RC ids
#define NTCPLNAMEID 1
#define NTCPLTITLEID 107
#define WINCPLNAMEID 102
//#define WINCPLTITLEID 42
#define WINCPLTITLEID 104
#define WINCPLCHNAMEID 112
#define WINCPLCHTITLEID 107
#define CHNT4CPLNAMEID 64
#define CHNT4CPLTITLEID1 1
#define CHNT4CPLTITLEID2 2
// CPL file names
#define MAINCPL TEXT("main.cpl")
#define INTLCPL TEXT("intl.cpl")
#define CHIMECPL TEXT("cime.cpl")
inline int SafeGetWindowText(HWND hWnd, LPTSTR szString, int nMaxCount)
{
int iRet;
iRet = GetWindowText(hWnd, szString, nMaxCount);
if (nMaxCount > 0)
{
// make sure the string is NULL terminated
// we're not supposed to have to do this, but we're seeing a bug
// where GetWindowText won't NULL terminate the string if it
// occupies the whole buffer.
if (iRet < nMaxCount && iRet >= 0)
{
szString[iRet] = 0;
}
else
{
szString[nMaxCount-1] = 0;
}
}
return iRet;
}
//+---------------------------------------------------------------------------
//
// InitStaticHooks
//
//+---------------------------------------------------------------------------
void InitStaticHooks()
{
Assert(GetSharedMemory() != NULL);
#if 1
if (GetSharedMemory() == NULL && ! IsSharedMemoryCreated())
{
// Shared memory already closed.
return;
}
#endif
s_hSysShellHook = GetSharedMemory()->hSysShellHook.GetHandle(g_bOnWow64);
s_hSysGetMsgHook = GetSharedMemory()->hSysGetMsgHook.GetHandle(g_bOnWow64);
s_hSysCBTHook = GetSharedMemory()->hSysCBTHook.GetHandle(g_bOnWow64);
}
//+---------------------------------------------------------------------------
//
// FindSYSTHREAD
//
//+---------------------------------------------------------------------------
SYSTHREAD *FindSYSTHREAD()
{
SYSTHREAD *psfn;
if (g_dwTLSIndex == (DWORD)-1)
return NULL;
psfn = (SYSTHREAD *)TlsGetValue(g_dwTLSIndex);
return psfn;
}
//+---------------------------------------------------------------------------
//
// GetSYSTHREAD
//
//+---------------------------------------------------------------------------
SYSTHREAD *GetSYSTHREAD()
{
SYSTHREAD *psfn;
if (g_dwTLSIndex == (DWORD)-1)
return NULL;
psfn = (SYSTHREAD *)TlsGetValue(g_dwTLSIndex);
if (!psfn)
{
//
// we don't allocate psfn after detached.
//
if (g_fDllProcessDetached)
return NULL;
psfn = (SYSTHREAD *)cicMemAllocClear(sizeof(SYSTHREAD));
if (!TlsSetValue(g_dwTLSIndex, psfn))
{
cicMemFree(psfn);
psfn = NULL;
}
if (psfn)
{
psfn->dwThreadId = GetCurrentThreadId();
psfn->dwProcessId = GetCurrentProcessId();
CicEnterCriticalSection(g_csInDllMain);
if (!g_rgSysThread)
g_rgSysThread = new CPtrArray<SYSTHREAD>;
if (g_rgSysThread)
{
if (g_rgSysThread->Insert(0, 1))
g_rgSysThread->Set(0, psfn);
}
CicLeaveCriticalSection(g_csInDllMain);
//
// init nModalLangBarId
//
psfn->nModalLangBarId = -1;
EnsureTIMList(psfn);
}
}
return psfn;
}
//+---------------------------------------------------------------------------
//
// FreeSYSTHREAD2
//
//+---------------------------------------------------------------------------
void FreeSYSTHREAD2(SYSTHREAD *psfn)
{
Assert(psfn); // it's caller's responsibility to pass in a valid psfn
Assert(psfn->ptim == NULL); // someone leaked us?
Assert(psfn->pipp == NULL); // someone leaked us?
Assert(psfn->pdam == NULL); // someone leaked us?
UninitThreadHooks(psfn);
UninitLangBarAddIns(psfn);
delete psfn->_pGlobalCompMgr;
psfn->_pGlobalCompMgr = NULL;
if (psfn->plbim)
{
psfn->plbim->_RemoveSystemItems(psfn);
}
FreeMarshaledStubs(psfn);
if (psfn->plbim)
{
TraceMsg(TF_GENERAL, "FreeSYSTHREAD2 clean up plbim");
//
// Clean up a pointer that is marshalled to UTB.
//
delete psfn->plbim;
psfn->plbim = NULL;
}
if (psfn->ptim)
psfn->ptim->ClearLangBarItemMgr();
CicEnterCriticalSection(g_csInDllMain);
if (g_rgSysThread)
{
int nCnt = g_rgSysThread->Count();
while (nCnt)
{
nCnt--;
if (g_rgSysThread->Get(nCnt) == psfn)
{
g_rgSysThread->Remove(nCnt, 1);
break;
}
}
}
CicLeaveCriticalSection(g_csInDllMain);
if (psfn->pAsmList)
{
delete psfn->pAsmList;
psfn->pAsmList = NULL;
}
//
// remove the timlist entry for the current thread.
//
psfn->pti = NULL;
g_timlist.RemoveThread(psfn->dwThreadId);
DestroySharedHeap(psfn);
DestroySharedBlocks(psfn);
cicMemFree(psfn);
}
void FreeSYSTHREAD()
{
SYSTHREAD *psfn = (SYSTHREAD *)TlsGetValue(g_dwTLSIndex);
if (psfn)
{
//
// don't call ClearLangBarAddIns in FreeSYSTHREAD2.
// it is not safe to call this in DllMain(PROCESS_DETACH).
//
ClearLangBarAddIns(psfn, CLSID_NULL);
FreeSYSTHREAD2(psfn);
TlsSetValue(g_dwTLSIndex, NULL);
}
}
//+---------------------------------------------------------------------------
//
// EnsureAssemblyList
//
//+---------------------------------------------------------------------------
CAssemblyList *EnsureAssemblyList(SYSTHREAD *psfn, BOOL fUpdate)
{
if (!fUpdate && psfn->pAsmList)
return psfn->pAsmList;
if (!psfn->pAsmList)
psfn->pAsmList = new CAssemblyList;
if (psfn->pAsmList)
{
psfn->pAsmList->Load();
UpdateSystemLangBarItems(psfn, NULL, TRUE);
if (psfn->plbim && psfn->plbim->_GetLBarItemCtrl())
psfn->plbim->_GetLBarItemCtrl()->_AsmListUpdated(TRUE);
}
return psfn->pAsmList;
}
//+---------------------------------------------------------------------------
//
// UpdateRegIMXHandler()
//
//+---------------------------------------------------------------------------
void UpdateRegIMXHandler()
{
SYSTHREAD *psfn = GetSYSTHREAD();
//
// clear Category cache
//
CCategoryMgr::FreeCatCache();
TF_InitMlngInfo();
//
// Update Assembly list
//
if (psfn && psfn->pAsmList)
{
EnsureAssemblyList(psfn, TRUE);
if (!psfn->pAsmList->FindAssemblyByLangId(GetCurrentAssemblyLangId(psfn)))
{
CAssembly *pAsm = psfn->pAsmList->GetAssembly(0);
if (pAsm)
ActivateAssembly(pAsm->GetLangId(), ACTASM_NONE);
}
}
}
//+---------------------------------------------------------------------------
//
// GetCurrentAssemblyLangid
//
//+---------------------------------------------------------------------------
LANGID GetCurrentAssemblyLangId(SYSTHREAD *psfn)
{
if (!psfn)
{
psfn = GetSYSTHREAD();
if (!psfn)
return 0;
}
if (!psfn->langidCurrent)
{
HKL hKL = GetKeyboardLayout(NULL);
psfn->langidPrev = psfn->langidCurrent;
psfn->langidCurrent = LANGIDFROMHKL(hKL);
}
return psfn->langidCurrent;
}
//+---------------------------------------------------------------------------
//
// SetCurrentAssemblyLangid
//
//+---------------------------------------------------------------------------
void SetCurrentAssemblyLangId(SYSTHREAD *psfn, LANGID langid)
{
psfn->langidPrev = psfn->langidCurrent;
psfn->langidCurrent = langid;
}
//+---------------------------------------------------------------------------
//
// CheckVisibleWindowEnumProc
//
// find any other visible window in the thread.
//
//+---------------------------------------------------------------------------
typedef struct tag_CHECKVISIBLEWND {
BOOL fVisibleFound;
HWND hwndBeingDestroyed;
HWND hwndMarshal;
} CHECKVISIBLEWND;
BOOL CheckVisibleWindowEnumProc(HWND hwnd, LPARAM lParam)
{
CHECKVISIBLEWND *pcmw = (CHECKVISIBLEWND *)lParam;
LONG_PTR style;
//
// skip itself.
//
if (pcmw->hwndMarshal == hwnd)
return TRUE;
//
// skip one being destroyed.
//
if (pcmw->hwndBeingDestroyed == hwnd)
return TRUE;
//
// skip IME windows.
//
style = GetClassLongPtr(hwnd, GCL_STYLE);
if (style & CS_IME)
return TRUE;
//
// skip disable windows if it is not NT4.
//
// we disabled code on NT4 because mashaling window is not in HWND_MSG.
//
if (IsOnNT5())
{
style = GetWindowLongPtr(hwnd, GWL_STYLE);
if (style & WS_DISABLED)
return TRUE;
}
//
// skip in visible windows.
//
if (!IsWindowVisible(hwnd))
return TRUE;
//
// skip in destroy windows.
//
// #624872
// This is private user32 function.
// Due to dead lock of LdrpLoaderLock, we don't use delay load.
if (IsWindowInDestroy(hwnd))
return TRUE;
//
// ok, we found visible window and stop enumerating.
//
pcmw->fVisibleFound = TRUE;
return FALSE;
}
#ifdef CUAS_ENABLE
//+---------------------------------------------------------------------------
//
// CheckNoWindowEnumProc
//
// find any other window in the thread.
//
//+---------------------------------------------------------------------------
typedef struct tag_CHECKNOWND {
BOOL fWindowFound;
HWND hwndBeingDestroyed;
} CHECKNOWND;
BOOL CheckNoWindowEnumProc(HWND hwnd, LPARAM lParam)
{
CHECKNOWND *pcmw = (CHECKNOWND *)lParam;
//
// skip one being destroyed.
//
if (pcmw->hwndBeingDestroyed == hwnd)
return TRUE;
//
// ok, we found window and stop enumerating.
//
pcmw->fWindowFound = TRUE;
return FALSE;
}
#endif // CUAS_ENABLE
//+---------------------------------------------------------------------------
//
// IsConsoleWindow
//
//+---------------------------------------------------------------------------
#define CONSOLE_WINDOW_CLASS TEXT("ConsoleWindowClass")
BOOL IsConsoleWindow(HWND hWnd)
{
if (IsOnNT())
{
int n;
char szClass[33];
n = GetClassName(hWnd, szClass, sizeof(szClass)-1);
szClass[n] = TEXT('\0');
if (lstrcmp(szClass, CONSOLE_WINDOW_CLASS) == 0)
{
return TRUE;
}
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Input_EnumChildWndProc
//
// disable all controls that is in legacy keyboard property page.
//+---------------------------------------------------------------------------
BOOL CALLBACK Input_EnumChildWndProc(HWND hwnd, LPARAM lParam)
{
EnableWindow(hwnd, FALSE);
ShowWindow(hwnd, SW_HIDE);
return TRUE;
}
//+---------------------------------------------------------------------------
//
// IntlCPLFetchWndProc
//
//+---------------------------------------------------------------------------
LRESULT CALLBACK IntlCPLFetchWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case (WM_CREATE) :
{
HWND hwndStatic;
HWND hwndButton;
HWND hwndGroup;
RECT rc;
HFONT hFont;
GetWindowRect(hwnd, &rc);
hwndGroup = CreateWindow(TEXT("button"), g_szCPLGroupBox,
WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
0, 0, 0, 0,
hwnd, NULL, g_hInst, NULL
);
MoveWindow(hwndGroup, 0, 0, rc.right-rc.left-20, 110, TRUE);
hwndStatic = CreateWindow(TEXT("static"), g_szOldCPLMsg,
WS_CHILD | WS_VISIBLE | SS_LEFT,
0, 0, 0, 0,
hwnd, NULL, g_hInst, NULL
);
MoveWindow(hwndStatic, 50, 20, rc.right-rc.left-80, 50, TRUE);
hwndButton = CreateWindow(TEXT("button"), g_szCPLButton,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON,
0, 0, 0, 0,
hwnd, NULL, g_hInst, NULL
);
MoveWindow(hwndButton, 50, 75, 100, 25, TRUE);
hFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hwndGroup, WM_SETFONT, (WPARAM)hFont, TRUE);
SendMessage(hwndStatic, WM_SETFONT, (WPARAM)hFont, TRUE);
SendMessage(hwndButton, WM_SETFONT, (WPARAM)hFont, TRUE);
return FALSE;
}
case (WM_COMMAND) :
{
switch (LOWORD(wParam))
{
case ( BN_CLICKED ):
TF_RunInputCPL();
return FALSE;
default:
break;
}
}
case (WM_PAINT) :
{
HDC hdc;
HICON hIcon;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if (hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(ID_ICON_TEXT_SERVICE)))
{
DrawIconEx(hdc, 10, 20, hIcon, 32, 32, 0, NULL, DI_NORMAL);
ReleaseDC(hwnd, hdc);
}
EndPaint(hwnd, &ps);
return FALSE;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
//+---------------------------------------------------------------------------
//
// CreateCPLFetchWindow
//
// Creating the fetch window to bring up the new Text Service cpl.
//+---------------------------------------------------------------------------
void CreateCPLFetchWindow(HWND hwnd)
{
RECT rc;
HWND hwndCPLFetch;
WNDCLASSEX wndclass;
if (!(hwndCPLFetch = FindWindowEx(hwnd, NULL, c_szIntlCPLFetchClass, NULL)))
{
EnumChildWindows(hwnd, (WNDENUMPROC)Input_EnumChildWndProc, 0);
GetWindowRect(hwnd, &rc);
memset(&wndclass, 0, sizeof(wndclass));
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.hInstance = g_hInst;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.lpfnWndProc = IntlCPLFetchWndProc;
wndclass.lpszClassName = c_szIntlCPLFetchClass;
RegisterClassEx(&wndclass);
hwndCPLFetch = CreateWindowEx(0, c_szIntlCPLFetchClass, "",
WS_VISIBLE | WS_CHILD | WS_TABSTOP,
rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
hwnd, NULL, g_hInst, NULL
);
MoveWindow(hwndCPLFetch, 10, 10, rc.right-rc.left-10, rc.bottom-rc.top-10, TRUE);
ShowWindow(hwndCPLFetch, SW_SHOW);
}
}
//+---------------------------------------------------------------------------
//
// Intl_EnumChildWndProc
//
// filtering the legacy keyboard property page.
//+---------------------------------------------------------------------------
BOOL CALLBACK Intl_EnumChildWndProc(HWND hwnd, LPARAM lParam)
{
TCHAR szWndName[MAX_PATH];
TCHAR szKbdPage[MAX_PATH];
if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, NULL))
return TRUE;
SafeGetWindowText(hwnd, szWndName, MAX_PATH);
if (*szWndName == TEXT('\0'))
return TRUE;
if (IsOnNT())
StringCopyArray(szKbdPage, g_szNTCPLTitle);
else
{
LONG_PTR lpWndHandle;
if (lstrcmp(szWndName, g_szKbdCPLTitle) == 0)
return FALSE;
//
// Thunk call isn't good way to load 16bit module from here.
// So look up the window instance handle to determine keyboard
// "Language" tab window.
// This is Win9x specification and we can detect 32bit handle instance
// from HIWORD value form GWLP_HINSTANCE.
//
lpWndHandle = GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
if (HIWORD((DWORD) (LONG_PTR) lpWndHandle) != 0)
return FALSE;
StringCopyArray(szKbdPage, g_szKbdCPLTitle);
}
if ((lstrcmp(szWndName, szKbdPage) == 0) ||
(!IsOnNT() && lstrcmp(szWndName, szKbdPage) != 0))
{
CreateCPLFetchWindow(hwnd);
return FALSE;
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Intl_CH9xIMEEnumChildWndProc
//
// filtering the chinese Win9x special IME layout setting cpl.
//+---------------------------------------------------------------------------
BOOL CALLBACK Intl_CH9xIMEEnumChildWndProc(HWND hwnd, LPARAM lParam)
{
TCHAR szWndName[MAX_PATH];
TCHAR szKbdPage[MAX_PATH];
LONG_PTR lpWndHandle;
if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, NULL))
return TRUE;
SafeGetWindowText(hwnd, szWndName, MAX_PATH);
if (*szWndName == TEXT('\0'))
return TRUE;
if (lstrcmp(szWndName, g_szKbdCPLTitle) == 0)
return FALSE;
//
// Thunk call isn't good way to load 16bit module from here.
// So look up the window instance handle to determine keyboard
// "Language" tab window.
// This is Win9x specification and we can detect 32bit handle instance
// from HIWORD value form GWLP_HINSTANCE.
//
lpWndHandle = GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
//if (HIWORD((DWORD) (LONG_PTR) lpWndHandle) != 0 &&
// (lstrcmp(szWndName, g_szCH9xKbdCPLTitle) != 0))
// Need to show up Chinese IME hotkey setting pages
if (HIWORD((DWORD) (LONG_PTR) lpWndHandle) != 0)
return FALSE;
StringCopyArray(szKbdPage, g_szKbdCPLTitle);
if (!IsOnNT() && lstrcmp(szWndName, szKbdPage) != 0)
{
CreateCPLFetchWindow(hwnd);
return FALSE;
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// Intl_CHEnumChildWndProc
//
// filtering the chinese NT4 special IME layout setting cpl.
//+---------------------------------------------------------------------------
BOOL CALLBACK Intl_CHEnumChildWndProc(HWND hwnd, LPARAM lParam)
{
TCHAR szWndName[MAX_PATH];
if (GetCurrentThreadId() != GetWindowThreadProcessId(hwnd, NULL))
return TRUE;
SafeGetWindowText(hwnd, szWndName, MAX_PATH);
if (*szWndName == TEXT('\0'))
return TRUE;
//if ((lstrcmp(szWndName, g_szCHNT4CPLTitle1) == 0) ||
// (lstrcmp(szWndName, g_szCHNT4CPLTitle2) == 0))
// Need to show up Chinese IME hotkey setting pages
if ((lstrcmp(szWndName, g_szCHNT4CPLTitle1) == 0))
{
CreateCPLFetchWindow(hwnd);
return FALSE;
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// GetDialogCaptionTitle
//
//+---------------------------------------------------------------------------
BOOL GetDialogCaptionTitle(HINSTANCE hInst, LPCTSTR lpName, LPTSTR lpTitle, int cchTitleMax)
{
BOOL bRet = FALSE;
HRSRC hrsrc = NULL;
hrsrc = FindResourceA(hInst, lpName, RT_DIALOG);
if (hrsrc)
{
PVOID pTemp;
DWORD dwCodePage;
LANGID langRes = 0;
UINT dwTitleOffset;
TCHAR szCodePage[10];
pTemp = (PVOID)LoadResource(hInst, (HRSRC)hrsrc);
if (pTemp == NULL)
goto Exit;
if (*((char *)pTemp) == 1)
dwTitleOffset = sizeof(DLGTEMPLATEEX) + 4;
else
dwTitleOffset = sizeof(DLGTEMPLATE) + 4;
langRes = GetPlatformResourceLangID();
if (GetLocaleInfo(MAKELCID(langRes, SORT_DEFAULT),
LOCALE_IDEFAULTANSICODEPAGE,
szCodePage,
ARRAYSIZE(szCodePage)))
{
szCodePage[ARRAYSIZE(szCodePage)-1] = 0;
if (!AsciiToNumDec(szCodePage, &dwCodePage))
{
dwCodePage = GetACP();
}
}
else
dwCodePage = GetACP();
if (WideCharToMultiByte(dwCodePage, NULL,
(LPCWSTR)((char *)pTemp + dwTitleOffset), -1,
lpTitle, cchTitleMax,
NULL, NULL))
bRet = TRUE;
}
Exit:
return bRet;
}
//+---------------------------------------------------------------------------
//
// CheckLegacyInputCPL
//
//+---------------------------------------------------------------------------
void CheckLegacyInputCPL(HWND hwndFore)
{
if (hwndFore && !IsOnNT51())
{
TCHAR szWndName[MAX_PATH];
TCHAR szWndName2[MAX_PATH];
TCHAR szWndName3[MAX_PATH];
//
// Load legacy keyboard cpl name and tab titles.
//
if (!g_fLoadedCPLName)
{
HANDLE hrsrc = NULL;
HINSTANCE hIntlInst = NULL;
HINSTANCE hMainInst = NULL;
HINSTANCE hCHIMEInst = NULL;
//
// Get the default CPL input locale Tab title name string
//
if (!LoadString(g_hInst, IDS_CPL_WIN9X_KBDCPLTITLE, g_szKbdCPLTitle, sizeof(g_szKbdCPLTitle)))
StringCopyArray(g_szKbdCPLTitle, TEXT("Speed"));
if (!LoadString(g_hInst, IDS_CPL_WINNT_KBDCPLTITLE, g_szNTCPLTitle, sizeof(g_szNTCPLTitle)))
StringCopyArray(g_szNTCPLTitle, TEXT("Input Locales"));
//
// Load CPL files to read CPL name and titles
//
hMainInst = LoadSystemLibraryEx(MAINCPL, NULL, LOAD_LIBRARY_AS_DATAFILE);
hIntlInst = LoadSystemLibraryEx(INTLCPL, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (!LoadString(hMainInst, WINCPLNAMEID, g_szKbdCPLName, sizeof(g_szKbdCPLName)))
StringCopyArray(g_szKbdCPLName, TEXT("Keyboard Properties"));
if (IsOnNT())
{
if (!LoadString(hIntlInst, NTCPLNAMEID, g_szNTCPLName, sizeof(g_szNTCPLName)))
StringCopyArray(g_szNTCPLName, TEXT("Regional Options"));
if (!GetDialogCaptionTitle(hIntlInst, (LPTSTR)(LONG_PTR)NTCPLTITLEID, g_szNTCPLTitle, ARRAYSIZE(g_szNTCPLTitle)))
StringCopyArray(g_szNTCPLTitle, TEXT("Input Locales"));
if (!IsOnNT5())
{
switch (GetACP())
{
case 936:
case 950:
hCHIMEInst = LoadSystemLibraryEx(CHIMECPL, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (!LoadString(hCHIMEInst, CHNT4CPLNAMEID, g_szCHNT4CPLName, sizeof(g_szCHNT4CPLName)))
*g_szCHNT4CPLName = TEXT('\0');
if (!GetDialogCaptionTitle(hCHIMEInst, (LPTSTR)(LONG_PTR)CHNT4CPLTITLEID1, g_szCHNT4CPLTitle1, ARRAYSIZE(g_szCHNT4CPLTitle1)))
*g_szCHNT4CPLTitle1 = TEXT('\0');
if (!GetDialogCaptionTitle(hCHIMEInst, (LPTSTR)(LONG_PTR)CHNT4CPLTITLEID2, g_szCHNT4CPLTitle2, ARRAYSIZE(g_szCHNT4CPLTitle2)))
*g_szCHNT4CPLTitle2 = TEXT('\0');
g_fCHNT4 = TRUE;
break;
}
}
}
else
{
switch (GetACP())
{
case 936:
case 950:
if (!LoadString(hMainInst, WINCPLCHNAMEID, g_szWinCHCPLName, sizeof(g_szWinCHCPLName)))
*g_szWinCHCPLName = TEXT('\0');
if (!GetDialogCaptionTitle(hMainInst, (LPTSTR)(LONG_PTR)WINCPLCHTITLEID, g_szCH9xKbdCPLTitle, ARRAYSIZE(g_szCH9xKbdCPLTitle)))
*g_szCH9xKbdCPLTitle = TEXT('\0');
g_fCHWin9x = TRUE;
break;
}
if (!GetDialogCaptionTitle(hMainInst, (LPTSTR)(LONG_PTR)WINCPLTITLEID, g_szKbdCPLTitle, ARRAYSIZE(g_szKbdCPLTitle)))
StringCopyArray(g_szKbdCPLTitle, TEXT("Speed"));
}
if (hMainInst)
FreeLibrary(hMainInst);
if (hIntlInst)
FreeLibrary(hIntlInst);
if (hCHIMEInst)
FreeLibrary(hCHIMEInst);
if (!LoadString(g_hInst, IDS_CPL_INPUT_DISABLED, g_szOldCPLMsg, sizeof(g_szOldCPLMsg)))
StringCopyArray(g_szOldCPLMsg, TEXT("This dialog has been updated. \r\n\r\nPlease use the Text Input Settings applet in the Control Panel."));
if (!LoadString(g_hInst, IDS_CPL_INPUT_CHAANGE_BTN, g_szCPLButton, sizeof(g_szCPLButton)))
StringCopyArray(g_szCPLButton, TEXT("&Change..."));
if (!LoadString(g_hInst, IDS_CPL_INPUT_GROUPBOX, g_szCPLGroupBox, sizeof(g_szCPLGroupBox)))
StringCopyArray(g_szCPLGroupBox, TEXT("Input Languages and Methods"));
g_fLoadedCPLName = TRUE;
}
if (GetCurrentThreadId() != GetWindowThreadProcessId(hwndFore, NULL))
return;
SafeGetWindowText(hwndFore, szWndName, MAX_PATH);
StringCopyArray(szWndName2, szWndName);
StringCopyArray(szWndName3, szWndName);
int nSize = lstrlen(g_szNTCPLName);
*(szWndName3 + min(ARRAYSIZE(szWndName3), nSize)) = TEXT('\0');
if (IsOnNT() && *szWndName3 && lstrcmp(szWndName3, g_szNTCPLName) == 0)
{
EnumChildWindows(hwndFore, (WNDENUMPROC)Intl_EnumChildWndProc, 0);
return;
}
nSize = lstrlen(g_szKbdCPLName);
*(szWndName + min(ARRAYSIZE(szWndName), nSize)) = TEXT('\0');
if (*szWndName && lstrcmp(szWndName, g_szKbdCPLName) == 0)
{
if (!IsOnNT() && !FindWindowEx(hwndFore, NULL, NULL, g_szKbdCPLTitle))
return;
EnumChildWindows(hwndFore, (WNDENUMPROC)Intl_EnumChildWndProc, 0);
return;
}
if (g_fCHWin9x)
{
nSize = lstrlen(g_szWinCHCPLName);
*(szWndName2 + min(ARRAYSIZE(szWndName2), nSize)) = TEXT('\0');
if (*g_szWinCHCPLName && lstrcmp(szWndName2, g_szWinCHCPLName) == 0)
{
if (FindWindowEx(hwndFore, NULL, NULL, g_szWinCHCPLName))
EnumChildWindows(hwndFore, (WNDENUMPROC)Intl_CH9xIMEEnumChildWndProc, 0);
}
}
if (g_fCHNT4)
{
nSize = lstrlen(g_szCHNT4CPLName);
*(szWndName2 + min(ARRAYSIZE(szWndName2), nSize)) = TEXT('\0');
if (*g_szCHNT4CPLName && lstrcmp(szWndName2, g_szCHNT4CPLName) == 0)
{
if (FindWindowEx(hwndFore, NULL, NULL, g_szCHNT4CPLName))
EnumChildWindows(hwndFore, (WNDENUMPROC)Intl_CHEnumChildWndProc, 0);
}
}
}
}
//+---------------------------------------------------------------------------
//
// OnForegroundChanged
//
//+---------------------------------------------------------------------------
BOOL IsParentWindow(HWND hwnd, HWND hwndParent)
{
while (hwnd)
{
if (hwnd == hwndParent)
return TRUE;
hwnd = GetParent(hwnd);
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// OnForegroundChanged
//
//+---------------------------------------------------------------------------
BOOL OnForegroundChanged(HWND hwndFocus)
{
HWND hwndFore;
if (!hwndFocus)
hwndFocus = GetFocus();
hwndFore = GetForegroundWindow();
TraceMsg(TF_GENERAL, "OnForegroundChanged %x %x %x", GetCurrentThreadId(), hwndFore, hwndFocus);
//
// if foreground window is NULL
// OR foregrond window is minimized
// OR focus window is notify tray window,
//
// we keep the previous status.
//
if (!hwndFore ||
IsIconic(hwndFore) ||
IsNotifyTrayWnd(hwndFocus ? hwndFocus : hwndFore))
{
return FALSE;
}
//
// we want to update both SharedMem->hwndForegorundPrev and
// SharedMem->dwFocusThreadPrev, if the foregorund window was changed.
//
if (hwndFore != GetSharedMemory()->hwndForeground)
{
GetSharedMemory()->hwndForegroundPrev = GetSharedMemory()->hwndForeground;
GetSharedMemory()->dwFocusThreadPrev = GetSharedMemory()->dwFocusThread;
}
GetSharedMemory()->hwndForeground = hwndFore;
if (hwndFocus)
{
DWORD dwFocusThread;
DWORD dwFocusProcess;
dwFocusThread = GetWindowThreadProcessId(hwndFocus, &dwFocusProcess);
if (hwndFore &&
(dwFocusThread != GetWindowThreadProcessId(hwndFore, NULL)))
{
if (!IsParentWindow(hwndFocus, hwndFore))
return FALSE;
}
//
// Even the foregorund window was not changed, we may need to check
// the thread of focus window. New focus window is in different
// thread. Then we need to make TFPRIV_ONSETTHREADFOCUS message.
//
DWORD dwFocusThreadPrev = GetSharedMemory()->dwFocusThread;
GetSharedMemory()->dwFocusThread = dwFocusThread;
GetSharedMemory()->dwFocusProcess = dwFocusProcess;
if (dwFocusThreadPrev != GetSharedMemory()->dwFocusThread)
GetSharedMemory()->dwFocusThreadPrev = dwFocusThreadPrev;
}
else if (hwndFore)
{
//
// The focus window is not in the current thread... So at first we
// try to get the thread id of the foreground window.
// The focus window may not be in the foreground window's thread. But
// it is ok, as long as we track the focus in the focus thread.
//
GetSharedMemory()->dwFocusThread = GetWindowThreadProcessId(GetSharedMemory()->hwndForeground, &GetSharedMemory()->dwFocusProcess);
}
else
{
GetSharedMemory()->dwFocusThread = 0;
GetSharedMemory()->dwFocusProcess = 0;
}
if (GetSharedMemory()->dwFocusThread != GetSharedMemory()->dwLastFocusSinkThread)
{
//
// Perf:
//
// See SysGetMsgProc()!
// Now, only thread that has TIM needs to receive
// TFPRIV_ONKILLTHREADFOCUS or TF_PRIV_ONSETTHREADFOCUS.
// We should check the target thread has TIM or not. So we can
// save the number of these post messages.
//
if (GetSharedMemory()->dwFocusThreadPrev != 0)
{
PostThreadMessage(GetSharedMemory()->dwFocusThreadPrev,
g_msgPrivate,
TFPRIV_ONKILLTHREADFOCUS,
0);
}
if (GetSharedMemory()->dwFocusThread != 0)
{
PostThreadMessage(GetSharedMemory()->dwFocusThread,
g_msgPrivate,
TFPRIV_ONSETTHREADFOCUS,
0);
}
GetSharedMemory()->dwLastFocusSinkThread = GetSharedMemory()->dwFocusThread;
}
//
// Checking legacy keyboard CPL.
//
CheckLegacyInputCPL(hwndFore);
return TRUE;
}
//+---------------------------------------------------------------------------
//
// OnIMENotify
//
//+---------------------------------------------------------------------------
void OnIMENotify()
{
SYSTHREAD *psfn;
if (psfn = GetSYSTHREAD())
{
if (psfn->plbim && psfn->plbim->_GetLBarItemWin32IME())
{
psfn->plbim->_GetLBarItemWin32IME()->UpdateIMEIcon();
}
}
}
#ifdef CHECKFEIMESELECTED
//+---------------------------------------------------------------------------
//
// CheckFEIMESelected
//
// This function checks the current selected FEIME is active in Cicero
// Assembly. If it is not activated, we calls ActivateAssemblyItem().
//
//+---------------------------------------------------------------------------
void CheckFEIMESelected(SYSTHREAD *psfn, HKL hKL)
{
int i;
CAssembly *pAsm;
BOOL fFound;
Assert(psfn);
if (!psfn->pAsmList)
return;
if (!IsPureIMEHKL(hKL))
return;
pAsm = psfn->pAsmList->FindAssemblyByLangId(LANGIDFROMHKL(hKL));
if (!pAsm)
return;
//
// Windows #311672
//
// EUDCEDIT.EXE calls ActivateKeyboardLayout() to activate IME hKL.
// Cicero should not break the API on WinXP. Skip SmartVoice hardcode.
//
//
#if 0
//
// SmartVoice Hack
//
// we want to remove this smart voice hack section to solve
// general ActivateKayboardLayout() problem. However Office10 wants
// the safest fix for this problem. So we check SmartVoice IME here
// to minimize the rish of CheckFEIMESelected() call.
//
{
static const char c_szSmartVoiceIME[] = "smartv20.ime";
char szIMEFile[MAX_PATH];
if (!ImmGetIMEFileNameA(hKL, szIMEFile, sizeof(szIMEFile)))
return;
if (lstrcmpi(szIMEFile, c_szSmartVoiceIME))
return;
}
#endif
//
// check if the hKL is substituted by the activate Item.
//
// We try to find the active Item first.
//
for (i = 0; i < pAsm->Count(); i++)
{
ASSEMBLYITEM *pItem = pAsm->GetItem(i);
if (!pItem)
continue;
if (!IsEqualGUID(pItem->catid, GUID_TFCAT_TIP_KEYBOARD))
continue;
if (!pItem->fEnabled)
continue;
if (!pItem->fActive)
continue;
if (pItem->hklSubstitute == hKL)
{
//
// #383710 OfficeXP's RichEd20.dll calls ActivateKeyboardlayout()
// with Korean IME hKL even though it is running on AIMM mode.
// we need to adjust the assembly item.
//
CThreadInputMgr *ptim = psfn->ptim;
if (ptim)
{
if (ptim->_GetFocusDocInputMgr())
{
ActivateAssemblyItem(psfn, LANGIDFROMHKL(hKL), pItem, 0);
}
else
{
//
// we could not have a chance to sync the current hKL
// init hklBeingActivated and try when DIM gets the focus.
//
psfn->hklBeingActivated = NULL;
}
}
return;
}
}
//
// Ok we could not find active Item with hKL as its substitute hKL.
// Let's find it from non-active Item, too.
//
if (psfn->ptim && psfn->ptim->_GetFocusDocInputMgr())
{
for (i = 0; i < pAsm->Count(); i++)
{
ASSEMBLYITEM *pItem = pAsm->GetItem(i);
if (!pItem)
continue;
if (!IsEqualGUID(pItem->catid, GUID_TFCAT_TIP_KEYBOARD))
continue;
if (!pItem->fEnabled)
continue;
if (pItem->hklSubstitute == hKL)
{
ActivateAssemblyItem(psfn, LANGIDFROMHKL(hKL), pItem, 0);
return;
}
}
}
fFound = FALSE;
for (i = 0; i < pAsm->Count(); i++)
{
ASSEMBLYITEM *pItem;
pItem= pAsm->GetItem(i);
if (!pItem)
continue;
if (!IsEqualGUID(pItem->catid, GUID_TFCAT_TIP_KEYBOARD))
continue;
if (pItem->hkl != hKL)
continue;
fFound = TRUE;
if (!pItem->fActive)
{
//
// This item is not activated.
// Call ActivateAssemblyItem() now and return.
//
ActivateAssemblyItem(psfn, LANGIDFROMHKL(hKL), pItem, 0);
return;
}
}
//
// we could not find the activated hKL in our Asmlist.
//
if (!fFound)
{
UnknownFEIMESelected(LANGIDFROMHKL(hKL));
}
}
#endif CHECKFEIMESELECTED
//+---------------------------------------------------------------------------
//
// OnShellLanguage
//
//+---------------------------------------------------------------------------
void OnShellLanguage(HKL hKL)
{
SYSTHREAD *psfn;
HWND hwndFore = GetForegroundWindow();
if (IsConsoleWindow(hwndFore))
{
DWORD dwThreadId = GetWindowThreadProcessId(hwndFore, NULL);
g_timlist.SetConsoleHKL(dwThreadId, hKL);
if (OnForegroundChanged(NULL))
MakeSetFocusNotify(g_msgSetFocus, 0, 0);
MakeSetFocusNotify(g_msgLBUpdate,
TF_LBU_NTCONSOLELANGCHANGE,
(LPARAM)hKL);
return;
}
psfn = GetSYSTHREAD();
if (!psfn)
return;
if (psfn->hklBeingActivated == hKL)
psfn->hklBeingActivated = NULL;
if (LANGIDFROMHKL(hKL) != GetCurrentAssemblyLangId(psfn))
{
//
// if it is in Cicero aware and the hKL does not match with
// current assembly, someone else might call ActivateKayboardLayout().
// we need to change the current assembly right away..
//
// ActivateAssembly(LANGIDFROMHKL(hKL), ACTASM_ONSHELLLANGCHANGE);
//
//
// WM_INPUTLANGCHNAGEREQUEST is being queued now.
// Post another message to confirm the hKL.
//
PostThreadMessage(GetCurrentThreadId(),
g_msgPrivate,
TFPRIV_POSTINPUTCHANGEREQUEST,
0);
}
else
{
if (psfn->plbim)
UpdateSystemLangBarItems(psfn,
hKL,
!psfn->plbim->InAssemblyChange());
}
if (IsPureIMEHKL(hKL))
{
OnIMENotify();
//
// Temp rolling back SmartVoice (Cic#4580) fix. Since we got some
// regression like Cic#4713 and so on.
//
#ifdef CHECKFEIMESELECTED
//
// check this hkl is activated in Cicero Assembly.
//
CheckFEIMESelected(psfn, hKL);
#endif
}
}
//+---------------------------------------------------------------------------
//
// UninitThread
//
//+---------------------------------------------------------------------------
typedef HRESULT (*PFNCTFIMETHREADDETACH)(void);
void UninitThread()
{
DWORD dwThreadId = GetCurrentThreadId();
SYSTHREAD *psfn = FindSYSTHREAD();
if (psfn)
psfn->fCUASDllDetachInOtherOrMe = TRUE;
// g_SharedMemory.Close();
#if 1
if (GetSharedMemory() == NULL && ! IsSharedMemoryCreated())
{
// Shared memory already closed.
return;
}
#endif
if (GetSharedMemory()->dwFocusThread == dwThreadId)
{
GetSharedMemory()->dwFocusThread = 0;
GetSharedMemory()->dwFocusProcess = 0;
GetSharedMemory()->hwndForeground = NULL;
}
if (GetSharedMemory()->dwFocusThreadPrev == dwThreadId)
{
GetSharedMemory()->hwndForegroundPrev = NULL;
GetSharedMemory()->dwFocusThreadPrev = 0;
}
if (GetSharedMemory()->dwLastFocusSinkThread == dwThreadId)
{
GetSharedMemory()->dwLastFocusSinkThread = 0;
}
//
// Issue:
//
// UninitThread() is called from DLL_THREAD_DETACH so
// we should not call MakeSetFocusNotify() because it uses
// ciritical section and could cause dead lock.
//
MakeSetFocusNotify(g_msgThreadTerminate, 0, (LPARAM)dwThreadId);
if (psfn && GetSystemMetrics(SM_SHUTTINGDOWN))
{
psfn->fUninitThreadOnShuttingDown = TRUE;
}
//
// Tell msctfime that msctf's thread_detach is being called.
// So it can deactivate TIM now. If we don't do this now,
// it may deactivate TIM afte msctf's thread detach is called.
//
if (g_fCUAS && g_szCUASImeFile[0])
{
HINSTANCE hInstMsctfime;
hInstMsctfime = GetSystemModuleHandle(g_szCUASImeFile);
if (hInstMsctfime)
{
PFNCTFIMETHREADDETACH pfn = NULL;
pfn = (PFNCTFIMETHREADDETACH)GetProcAddress(hInstMsctfime,
"CtfImeThreadDetach");
if (pfn)
pfn();
}
}
}
//+---------------------------------------------------------------------------
//
// SysShellProc
//
//+---------------------------------------------------------------------------
LRESULT CALLBACK SysShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
TraceMsg(TF_GENERAL, "SysShellProc %x %x %x", nCode, wParam, lParam);
HHOOK hHook;
if (g_fDllProcessDetached)
{
hHook = s_hSysShellHook;
goto Exit;
}
_try
{
hHook = GetSharedMemory()->hSysShellHook.GetHandle(g_bOnWow64);
_ShellProc(nCode, wParam, lParam);
}
_except(CicExceptionFilter(GetExceptionInformation()))
{
Assert(0);
}
Exit:
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
//+---------------------------------------------------------------------------
//
// _ShellProc
//
//+---------------------------------------------------------------------------
UINT _ShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HWND hwndActive;
//
// give AIMM the Shell events.
//
CThreadInputMgr *ptim;
SYSTHREAD *psfn;
if (psfn = GetSYSTHREAD())
{
if (ptim = psfn->ptim)
{
if (ptim->GetSysHookSink())
{
ptim->GetSysHookSink()->OnSysShellProc(nCode, wParam, lParam);
}
}
}
switch (nCode)
{
case HSHELL_LANGUAGE:
OnShellLanguage((HKL)lParam);
break;
case HSHELL_WINDOWACTIVATED:
//TraceMsg(TF_GENERAL, "SysShellProc: HSHELL_WINDOWACTIVATED %x", GetCurrentThreadId());
GetSharedMemory()->fInFullScreen = lParam ? TRUE : FALSE;
hwndActive = GetActiveWindow();
if (hwndActive &&
(GetWindowThreadProcessId(hwndActive, NULL) == GetCurrentThreadId()))
{
if (OnForegroundChanged(NULL))
MakeSetFocusNotify(g_msgSetFocus, 0, 0);
}
else
{
hwndActive = GetForegroundWindow();
goto CheckConsole;
}
break;
case HSHELL_WINDOWCREATED:
hwndActive = (HWND)wParam;
CheckConsole:
if (hwndActive && IsOnNT() && IsConsoleWindow(hwndActive))
{
DWORD dwProcessId;
DWORD dwThreadId = GetWindowThreadProcessId(hwndActive,
&dwProcessId);
if ((nCode == HSHELL_WINDOWCREATED) ||
!(g_timlist.GetFlags(dwThreadId) & TLF_NTCONSOLE))
g_timlist.AddThreadProcess(dwThreadId,
dwProcessId,
NULL,
TLF_NTCONSOLE);
if (OnForegroundChanged(NULL))
{
HKL hklConsole = g_timlist.GetConsoleHKL(dwThreadId);
if (!hklConsole)
{
hklConsole = GetKeyboardLayout(dwThreadId);
g_timlist.SetConsoleHKL(dwThreadId, hklConsole);
}
MakeSetFocusNotify(g_msgSetFocus, 0, 0);
MakeSetFocusNotify(g_msgLBUpdate,
TF_LBU_NTCONSOLELANGCHANGE,
(LPARAM) (HKL)hklConsole);
}
}
break;
}
return 1;
}
//+---------------------------------------------------------------------------
//
// OnSetWindowFocus()
//
//+---------------------------------------------------------------------------
void OnSetWindowFocus(SYSTHREAD *psfn, HWND hwnd)
{
CThreadInputMgr *ptim;
Assert(psfn)
if (psfn->hklDelayActive)
{
ActivateKeyboardLayout(psfn->hklDelayActive, 0);
psfn->hklDelayActive = NULL;
}
if (ptim = psfn->ptim)
{
if (hwnd)
{
if (ptim->GetSysHookSink())
ptim->GetSysHookSink()->OnPreFocusDIM(hwnd);
Assert(GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId());
CDocumentInputManager *pdim;
pdim = ptim->_GetAssoc(hwnd);
//
// we don't want to clear focus dim if there is no
// foreground window.
//
if (pdim || GetForegroundWindow())
{
// focus dim will be clear if pdim is NULL.
ptim->_SetFocus(pdim, TRUE);
}
}
}
//
// update foregorund window handle and thread id.
// Because shell hook is posted event, we need to update
// before MakeSetFocusNotify. Otherwise we miss timing to
// update them.
//
// We can not call GetFocus() since it may return
// the previous focus during CBT hook.
// When the focus moves back from embedded OLE server,
// GetFocus() may get the the OLE server's window handle.
//
if (OnForegroundChanged(hwnd))
{
//
// If hwndFocus is NULL, focus is moved to other thread.
//
MakeSetFocusNotify(g_msgSetFocus, 0, 0);
}
}
//+---------------------------------------------------------------------------
//
// OnSetWindowFocusHandler()
//
//+---------------------------------------------------------------------------
void OnSetWindowFocusHandler(SYSTHREAD *psfn, MSG *pmsg)
{
if (!psfn)
return;
//
// We're destroying the marhacl window right now. We don't have
// any more visible windows.
//
if (psfn->uDestroyingMarshalWnd)
{
goto Exit;
}
HWND hwndFocus = GetFocus();
if (hwndFocus)
{
//
// review review
//
// Don't we need to call
// OnForegroundChanged() if psfn->hwndBegin
// gFocued() is NULL?
// Maybe no, OnForegroundChanged() is
// called in activatewindow.
//
if (psfn->hwndBeingFocused == hwndFocus)
{
OnSetWindowFocus(psfn, hwndFocus);
}
else
{
//
// #476100
//
// if we miss this, we need to post
// TFPRIV_ONSETWINDOWFOCUS again.
// Because the focus thread might processed
// this meesage already and it does not
// call OnSetWindowFocus().
//
DWORD dwFocusWndThread = GetWindowThreadProcessId(hwndFocus, NULL);
if (psfn->dwThreadId != dwFocusWndThread)
{
PostThreadMessage(dwFocusWndThread,
g_msgPrivate,
TFPRIV_ONSETWINDOWFOCUS,
(LPARAM)-1);
}
else if (pmsg->lParam == (LPARAM)-2)
{
if (psfn->ptim && psfn->ptim->_GetFocusDocInputMgr())
{
HWND hwndAssoc;
hwndAssoc = psfn->ptim->_GetAssoced(psfn->ptim->_GetFocusDocInputMgr());
//
// lParam is -2 because the SetFocus(dim) is already
// called.
// hwndAssoc is NULL. Now we're in Cicero aware.
//
// So we just do OnForegroundChanged(). Don't call
// OnSetWindowFocus().
//
// Bug#623920 - Don't need to check up hwndAssoc since the current focus
// window has the right dim value and also need to update language bar even
// with hwndAssoc
//
//if (!hwndAssoc)
//
{
if (OnForegroundChanged(hwndFocus))
{
MakeSetFocusNotify(g_msgSetFocus, 0, 0);
}
}
}
}
else if ((pmsg->lParam == (LPARAM)-1) ||
(psfn->dwThreadId == GetWindowThreadProcessId(GetForegroundWindow(), NULL)))
{
//
// #479926
//
// The first SetFocus() in the thread
// may break the order of CBT hook
// because xxxSetFocus() calls
// xxxActivateWindow() and this cause
// another xxxSetFocus(). After
// xxxActivateWindow() returns
// the first xxxSetFocus() updates
// the spwndFocus.
//
// see ntuser\kernel\focusact.c
//
// Now we need to hack. We're 100% sure
// if the focus window and the fore-
// ground window is in same thread,
// we can do _SetFocus(dim).
// but if focusdim does not have a associated window,
// Cicero App might call SetFocus() already. Then
// we don't do anything.
//
if (psfn->ptim && psfn->ptim->_GetFocusDocInputMgr())
{
HWND hwndAssoc;
hwndAssoc = psfn->ptim->_GetAssoced(psfn->ptim->_GetFocusDocInputMgr());
if (hwndAssoc && hwndFocus != hwndAssoc)
OnSetWindowFocus(psfn, hwndFocus);
}
else if (!psfn->ptim ||
psfn->ptim->_IsNoFirstSetFocusAfterActivated() ||
psfn->ptim->_IsInternalFocusedDim())
{
OnSetWindowFocus(psfn, hwndFocus);
}
}
}
}
//
// try to update Kana status every time
// focus changes.
//
//
// when the focus is changed, we need to make
// notification again.
//
psfn->fInitCapsKanaIndicator = FALSE;
StartKanaCapsUpdateTimer(psfn);
Exit:
psfn->hwndBeingFocused = NULL;
psfn->fSetWindowFocusPosted = FALSE;
}
//--------------------------------------------------------------------------
//
// IsPostedMessage
//
//--------------------------------------------------------------------------
__inline BOOL IsPostedMessage()
{
DWORD dwQueueStatus = GetQueueStatus(QS_POSTMESSAGE);
return (HIWORD(dwQueueStatus) & QS_POSTMESSAGE) ? TRUE : FALSE;
}
//--------------------------------------------------------------------------
//
// RemovePrivateMessage
//
//--------------------------------------------------------------------------
void RemovePrivateMessage(SYSTHREAD *psfn, HWND hwnd, UINT uMsg)
{
MSG msg;
UINT nQuitCode;
BOOL fQuitReceived = FALSE;
DWORD dwPMFlags = PM_REMOVE | PM_NOYIELD;
if (!IsPostedMessage())
return;
//
// Cic#4666 PostPet v1.12 fault.
// PostPet.exe cause av when it receives its internal message in
// CBT_DESTROYWINDOW hook when it is terminated.
// At this time, the child thread is calling SendMessage() to the window
// in the main thread. So calling PeekMessage() may receive the message
// and pass it to PostPet window.
//
// I found Win98 has a bug in PM_QS_POSTMESSAGE. Win98's PeekMessage()
// handles the message that is sent from other thread without
// PM_QS_SENDMESSAGE.
//
// If we has to fix this problem on Win98, I think it is better to have
// another compatibility flag so we can skip this PeekMessage() in
// PostPet.exe. In PostPet.exe, it is ok not to clean queue since
// this happens only app termination.
//
if (IsOnNT5())
dwPMFlags |= PM_QS_POSTMESSAGE;
while (PeekMessage(&msg, hwnd, uMsg, uMsg, dwPMFlags ))
{
if (msg.message == WM_QUIT)
{
nQuitCode = (UINT)(msg.wParam);
fQuitReceived = TRUE;
break;
}
//
// should we dispatch the message to the marshal window?
//
#if 0
//
// Cic#4869
//
// we don't want to dispatch this message to marshal window.
// This HCBT_DESTROYWINDOW may be in OLEAUT32.DLL's DllMain() and
// dispatching this message could cause the reentry to OLEAUT32's
// DLLMain() because we do delay load.
//
//
// dispatch if this message is for marshal window.
//
if (psfn->hwndMarshal && (psfn->hwndMarshal == msg.hwnd))
{
DispatchMessage(&msg);
}
#endif
//
// Cic#4699
//
// Exception MSUIM.Msg.MuiMgrDirtyUpdate private message.
// If we get this message, reset CLangBarItemMgr::_fDirtyUpdateHandling
//
if (psfn->hwndMarshal && (psfn->hwndMarshal == msg.hwnd) &&
msg.message == g_msgNuiMgrDirtyUpdate &&
psfn->plbim)
{
psfn->plbim->ResetDirtyUpdate();
}
}
if (fQuitReceived)
PostQuitMessage(nQuitCode);
}
//+---------------------------------------------------------------------------
//
// CheckQueueOnLastWindowDestroyed()
//
// Super EnumWindow hack.
//
// When the last visible window in the thread is destroyed.
//
// 1. we destroy the marshal worker window on NT4. (Cic #658)
// Because some application may found Cic marshal window
// by calling EnumWindow.
//
//
// 2. we need to clean up the thread queue. (Cic #3080)
// Because some application calls GetMessage() or PeekMessage()
// with specific window handle or message soour private messages
// remain in the queue. Then WM_QUIT message won't be handled..
//
// this is not complete solution but at least we can avoid
// the ghost windows or remained message in the queue.
//
//+---------------------------------------------------------------------------
void CheckQueueOnLastWindowDestroyed(SYSTHREAD *psfn, HWND hwnd)
{
BOOL fOnNT4;
//
// we don't have to do this on ctfmon process.
//
if (psfn->fCTFMON)
return;
//
// check if it's nt4.
//
fOnNT4 = (IsOnNT() && !IsOnNT5()) ? TRUE : FALSE;
#if 0
if (!fOnNT4)
{
//
// If there is no posted message, we don't have to do this.
// EnumThreadWindow() is slow....
//
if (!IsPostedMessage())
return;
}
#endif
LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
if (style & WS_CHILD)
return;
if (hwnd == psfn->hwndMarshal)
return;
//
// skip IME windows.
//
style = GetClassLongPtr(hwnd, GCL_STYLE);
if (style & CS_IME)
return;
//
// check the focus window first.
//
// if there is a focus window and it is not a child
// of the window that is being destroyed, we don't
// have to destroy marshal window.
//
HWND hwndTmp = GetFocus();
if (hwndTmp &&
(GetCurrentThreadId() != GetWindowThreadProcessId(hwndTmp, NULL)))
hwndTmp = NULL;
if (hwndTmp)
{
BOOL fParentFound = FALSE;
do {
if (hwndTmp == hwnd)
fParentFound = TRUE;
hwndTmp = GetParent(hwndTmp);
} while(hwndTmp);
if (!fParentFound)
return;
}
CHECKVISIBLEWND cmw;
cmw.hwndMarshal = psfn->hwndMarshal;
cmw.hwndBeingDestroyed = hwnd;
cmw.fVisibleFound = FALSE;
EnumThreadWindows(psfn->dwThreadId,
CheckVisibleWindowEnumProc,
(LPARAM)&cmw);
if (!cmw.fVisibleFound)
{
BOOL fInDestroyingMarshalWnd = FALSE;
if (psfn->uDestroyingMarshalWnd)
fInDestroyingMarshalWnd = TRUE;
psfn->uDestroyingMarshalWnd++;
DestroyMarshalWindow(psfn, hwnd);
#ifdef CUAS_ENABLE
//
// Under CUAS, we need to deactivate TIM to destroy all TIP's window
// when there is no visible window in this thread.
// And we destroy the default IME window so we can restore TIM for
// CUAS when the default IME window is created again in this thread.
// There is no way to know if the default IME window finds another
// top level window if it is created during DestroyWindow().
//
if (CtfImmIsCiceroEnabled() &&
!CtfImmIsTextFrameServiceDisabled() &&
!psfn->fCUASInCreateDummyWnd &&
!psfn->fDeactivatingTIP)
{
if (!psfn->fCUASInCtfImmLastEnabledWndDestroy)
{
psfn->fCUASInCtfImmLastEnabledWndDestroy = TRUE;
CtfImmLastEnabledWndDestroy(0);
if (!(InSendMessageEx(NULL) & ISMEX_SEND))
CtfImmCoUninitialize();
psfn->fCUASInCtfImmLastEnabledWndDestroy = FALSE;
}
if (!fInDestroyingMarshalWnd)
{
HWND hwndImmDef = ImmGetDefaultIMEWnd(hwnd);
if (hwndImmDef)
{
DestroyWindow(hwndImmDef);
}
}
psfn->fCUASNoVisibleWindowChecked = TRUE;
}
#endif CUAS_ENABLE
psfn->uDestroyingMarshalWnd--;
}
}
void DestroyMarshalWindow(SYSTHREAD* psfn, HWND hwnd)
{
BOOL fOnNT4;
if (IsPostedMessage())
{
if (psfn->hwndMarshal)
RemovePrivateMessage(psfn, psfn->hwndMarshal, 0);
RemovePrivateMessage(psfn, NULL, g_msgPrivate);
RemovePrivateMessage(psfn, NULL, g_msgRpcSendReceive);
RemovePrivateMessage(psfn, NULL, g_msgThreadMarshal);
RemovePrivateMessage(psfn, NULL, g_msgStubCleanUp);
}
//
// #339621
//
// This is rare but. We need to clear ShareMem->dwFocusThread and
// dwFocusProcess. Otherwise we will get another PostThreadMessage()
// with TFPRIV_ONKILLTHREADFOCUS later. And SQL setup hungs.
//
if (GetSharedMemory()->dwFocusThread == psfn->dwThreadId)
GetSharedMemory()->dwFocusThread = 0;
if (GetSharedMemory()->dwFocusProcess == psfn->dwProcessId)
GetSharedMemory()->dwFocusProcess = 0;
//
// check if it's nt4.
//
fOnNT4 = (IsOnNT() && !IsOnNT5()) ? TRUE : FALSE;
if (fOnNT4 && IsWindow(psfn->hwndMarshal))
{
DestroyWindow(psfn->hwndMarshal);
psfn->hwndMarshal = NULL;
}
}
//+---------------------------------------------------------------------------
//
// CreateDummyWndForDefIMEWnd
//
//+---------------------------------------------------------------------------
#ifdef CUAS_ENABLE
BOOL g_fCDWRegistered = FALSE;
const CHAR c_szDummyWndForDefIMEWnd[] = "CicDUmmyWndForDefIMEWnd";
//+---------------------------------------------------------------------------
//
// CicDummyForDefIMEWndProc
//
// This needs to be user mode wndproc. Otherwise system does not create
// a default IME window
//
//+---------------------------------------------------------------------------
LRESULT CALLBACK CicDummyForDefIMEWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void CreateDummyWndForDefIMEWnd()
{
HWND hwnd;
if (!g_fCDWRegistered)
{
WNDCLASSEX wndclass;
memset(&wndclass, 0, sizeof(wndclass));
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.hInstance = g_hInst;
wndclass.hCursor = NULL;
wndclass.lpfnWndProc = CicDummyForDefIMEWndProc;
wndclass.lpszClassName = c_szDummyWndForDefIMEWnd;
if (RegisterClassEx(&wndclass))
g_fCDWRegistered = TRUE;
}
//
// call CraeteWindow() to create a default IME window.
//
hwnd = CreateWindowEx(0, c_szDummyWndForDefIMEWnd, NULL,
WS_POPUP,
0,0,0,0,
NULL, NULL, g_hInst, NULL);
if (hwnd)
DestroyWindow(hwnd);
}
#endif // CUAS_ENABLE
#ifdef CUAS_ENABLE
//+---------------------------------------------------------------------------
//
// UninitThreadHooksIfNoWindow()
//
// When the last window in the thread is destroyed.
// Unhook thread local hook for SetThreadDesktop().
//
//+---------------------------------------------------------------------------
void UninitThreadHooksIfNoWindow(SYSTHREAD* psfn, HWND hwnd)
{
CHECKNOWND cmw;
cmw.hwndBeingDestroyed = hwnd;
cmw.fWindowFound = FALSE;
EnumThreadWindows(psfn->dwThreadId,
CheckNoWindowEnumProc,
(LPARAM)&cmw);
if (! cmw.fWindowFound)
{
DestroyMarshalWindow(psfn, hwnd);
if (IsWindow(psfn->hwndMarshal) &&
(psfn->hwndMarshal != hwnd) )
{
DestroyWindow(psfn->hwndMarshal);
psfn->hwndMarshal = NULL;
}
UninitThreadHooks(psfn);
}
}
#endif // CUAS_ENABLE
//+---------------------------------------------------------------------------
//
// SysCBTProc
//
//+---------------------------------------------------------------------------
LRESULT CALLBACK SysCBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
SYSTHREAD *psfn;
HHOOK hHook;
if (g_fDllProcessDetached)
{
hHook = s_hSysCBTHook;
goto Exit;
}
_try
{
hHook = GetSharedMemory()->hSysCBTHook.GetHandle(g_bOnWow64);
InitThreadHook(GetCurrentThreadId());
switch (nCode)
{
case HCBT_CREATEWND:
if ((psfn = GetSYSTHREAD()) &&
psfn->hklDelayActive)
{
if (ActivateKeyboardLayout(psfn->hklDelayActive, 0))
psfn->hklDelayActive = NULL;
}
break;
case HCBT_ACTIVATE:
_CBTHook(HCBT_ACTIVATE, wParam, lParam);
break;
case HCBT_SETFOCUS:
_CBTHook(HCBT_SETFOCUS, wParam, lParam);
break;
case HCBT_DESTROYWND:
if (psfn = GetSYSTHREAD())
{
CheckQueueOnLastWindowDestroyed(psfn, (HWND)wParam);
#ifdef CUAS_ENABLE
UninitThreadHooksIfNoWindow(psfn, (HWND)wParam);
#endif // CUAS_ENABLE
}
break;
}
}
_except(CicExceptionFilter(GetExceptionInformation()))
{
Assert(0);
}
Exit:
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
UINT _CBTHook(int nCode, WPARAM wParam, LPARAM lParam)
{
SYSTHREAD *psfn;
switch (nCode)
{
case HCBT_ACTIVATE:
if (wParam)
{
if (((HWND)wParam == GetForegroundWindow()) &&
OnForegroundChanged(NULL))
MakeSetFocusNotify(g_msgSetFocus, 0, 0);
}
break;
case HCBT_SETFOCUS:
if (psfn = GetSYSTHREAD())
{
#ifdef CUAS_ENABLE
//
// Cic#5254
//
// After we detect no more visible window,
// some window could become visible.
// Since we destroyed the default IME window,
// we need to recreate it.
//
// Here is a hack to do. Call a dummy CreateWindow()
// to create a default IME window in this thread.
//
if (psfn->fCUASNoVisibleWindowChecked)
{
psfn->fCUASInCreateDummyWnd = TRUE;
CreateDummyWndForDefIMEWnd();
psfn->fCUASInCreateDummyWnd = FALSE;
psfn->fCUASNoVisibleWindowChecked = FALSE;
}
#endif
psfn->hwndBeingFocused = (HWND)wParam;
if (!psfn->fSetWindowFocusPosted)
{
PostThreadMessage(GetCurrentThreadId(),
g_msgPrivate,
TFPRIV_ONSETWINDOWFOCUS,
(LPARAM)wParam);
psfn->fSetWindowFocusPosted = TRUE;
}
}
break;
}
return 1;
}
//+---------------------------------------------------------------------------
//
// RemoveThisMessage
//
//+---------------------------------------------------------------------------
BOOL RemoveThisMessage(MSG *pmsg)
{
MSG msg;
SYSTHREAD *psfn;
if (psfn = GetSYSTHREAD())
{
if (psfn->uMsgRemoved)
{
Assert(psfn->uMsgRemoved == pmsg->message);
// Assert(psfn->dwMsgTime == pmsg->time);
return TRUE;
}
Assert(!psfn->uMsgRemoved);
psfn->uMsgRemoved = pmsg->message;
psfn->dwMsgTime = pmsg->time;
}
PeekMessage(&msg, NULL, pmsg->message, pmsg->message, PM_REMOVE | PM_NOYIELD);
return FALSE;
}
//+---------------------------------------------------------------------------
//
// HandledThisMessage
//
//+---------------------------------------------------------------------------
void FinishThisMessage(MSG *pmsg)
{
SYSTHREAD *psfn;
if (psfn = GetSYSTHREAD())
{
psfn->uMsgRemoved = 0;
psfn->dwMsgTime = 0;
}
}
//+---------------------------------------------------------------------------
//
// PostInputChangeRequestHandler()
//
// this is the function that is called by TFPRIV_POSTINPUTCHANGEREQUEST.
// We need to confirm the current hKL mathes with Cicero assembly language.
// And we need to check the substitute hKL is selected on Cicero control.
//
//+---------------------------------------------------------------------------
void PostInputChangeRequestHandler()
{
SYSTHREAD *psfn = GetSYSTHREAD();
if (!psfn)
return;
//
// If the current hKL does not match with
// Cicero assembly language, we call
// ActivateAssembly() to sync to the current
// hKL. Someone accepted this language change.
//
HKL hKL = GetKeyboardLayout(0);
if (LANGIDFROMHKL(hKL) != GetCurrentAssemblyLangId(psfn))
{
//
// #494602, Corel Draw 10 calls LoadKeyboardLayout and ActivateKeyboardLayout.
// If specified hKL doesn't exist in our assembly list, then should update.
//
if (! IsPureIMEHKL(hKL) && psfn->pAsmList)
{
CAssembly *pAsm = psfn->pAsmList->FindAssemblyByLangId(LANGIDFROMHKL(hKL));
if (! pAsm)
{
CAssemblyList::InvalidCache();
EnsureAssemblyList(psfn, TRUE);
}
}
ActivateAssembly(LANGIDFROMHKL(hKL), ACTASM_NONE);
}
else
{
CThreadInputMgr *ptim = psfn->ptim;
if (ptim && ptim->_GetFocusDocInputMgr())
{
ASSEMBLYITEM *pItem = NULL;
if (psfn->pAsmList)
{
CAssembly *pAsm = psfn->pAsmList->FindAssemblyByLangId(LANGIDFROMHKL(hKL));
if (pAsm)
pItem = pAsm->GetSubstituteItem(hKL);
}
if (pItem)
ActivateAssemblyItem(psfn, LANGIDFROMHKL(hKL), pItem, AAIF_CHANGEDEFAULT);
}
}
}
//+---------------------------------------------------------------------------
//
// InputLangChangeHandler
//
//+---------------------------------------------------------------------------
void InputLangChangeHandler(MSG *pmsg)
{
SYSTHREAD *psfn;
IMM32HOTKEY *pHotKey;
HKL hKL = GetKeyboardLayout(0);
psfn = GetSYSTHREAD();
if (psfn)
psfn->hklBeingActivated = NULL;
if (IsInLangChangeHotkeyStatus())
{
pmsg->message = WM_NULL;
return;
}
if (pHotKey = IsInImmHotkeyStatus(psfn, LANGIDFROMHKL(hKL)))
{
//
// if we're hooking in IMM32's HotKey, we need to skip
// this INPUTLANGUAGECHANGEREQUEST.
//
pmsg->message = WM_NULL;
#ifdef SIMULATE_EATENKEYS
CancelImmHotkey(psfn, pmsg->hwnd, pHotKey);
#endif
//
// Chinese IME-NONIME toggle Hack for NT.
//
// On Win9x, we're using real IME as a dummy hKL of CH-Tips.
// we can forward the hotkey request to Assembly here.
//
if ((pHotKey->dwId == IME_CHOTKEY_IME_NONIME_TOGGLE) ||
(pHotKey->dwId == IME_THOTKEY_IME_NONIME_TOGGLE))
{
if (!IsOnNT())
{
PostThreadMessage(GetCurrentThreadId(),
g_msgPrivate,
TFPRIV_ACTIVATELANG,
0x0409);
}
else
{
ToggleCHImeNoIme(psfn, LANGIDFROMHKL(hKL), LANGIDFROMHKL(hKL));
}
}
}
//
// WM_INPUTLANGCHNAGEREQUEST is being queued now.
// Post another message to confirm the hKL.
//
PostThreadMessage(GetCurrentThreadId(),
g_msgPrivate,
TFPRIV_POSTINPUTCHANGEREQUEST,
0);
}
//+---------------------------------------------------------------------------
//
// _InsideLoaderLock
//
//+---------------------------------------------------------------------------
BOOL _InsideLoaderLock()
{
return (NtCurrentTeb()->ClientId.UniqueThread ==
((PRTL_CRITICAL_SECTION)(NtCurrentPeb()->LoaderLock))->OwningThread);
}
//+---------------------------------------------------------------------------
//
// _OwnedLoaderLockBySomeone
//
//+---------------------------------------------------------------------------
BOOL _OwnedLoaderLockBySomeone()
{
return ((PRTL_CRITICAL_SECTION)(NtCurrentPeb()->LoaderLock))->OwningThread ? TRUE : FALSE;
}
LONG WINAPI CicExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionInfo)
{
(LONG)RtlUnhandledExceptionFilter(pExceptionInfo);
return(EXCEPTION_EXECUTE_HANDLER);
}
//+---------------------------------------------------------------------------
//
// SysGetMsgProc
//
//+---------------------------------------------------------------------------
LRESULT CALLBACK SysGetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HHOOK hHook;
if (g_fDllProcessDetached)
{
hHook = s_hSysGetMsgHook;
goto Exit;
}
_try
{
hHook = GetSharedMemory()->hSysGetMsgHook.GetHandle(g_bOnWow64);
if (nCode == HC_ACTION && (wParam & PM_REMOVE)) // bug 29656: sometimes w/ word wParam is set to PM_REMOVE | PM_NOYIELD
{ // PM_NOYIELD is meaningless in win32 and sould be ignored
_GetMsgHook(wParam, lParam);
}
}
_except(CicExceptionFilter(GetExceptionInformation()))
{
Assert(0);
}
Exit:
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
UINT _GetMsgHook(WPARAM wParam, LPARAM lParam)
{
MSG *pmsg;
UINT uMsg;
CThreadInputMgr *ptim;
SYSTHREAD *psfn;
pmsg = (MSG *)lParam;
uMsg = pmsg->message;
switch (uMsg)
{
case WM_ACTIVATEAPP:
TraceMsg(TF_GENERAL, "SysGetMsgProc: WM_ACTIVATEAPP %x %x", GetCurrentThreadId(), pmsg->wParam);
if (pmsg->wParam)
{
OnForegroundChanged(NULL);
}
break;
case WM_INPUTLANGCHANGEREQUEST:
InputLangChangeHandler(pmsg);
break;
default:
if (uMsg == g_msgPrivate)
{
psfn = GetSYSTHREAD();
if (psfn && psfn->pti)
{
DWORD dwFlags = TLFlagFromTFPriv(pmsg->wParam);
psfn->pti->dwFlags &= ~dwFlags;
}
switch (LOWORD(pmsg->wParam))
{
case TFPRIV_ONSETWINDOWFOCUS:
OnSetWindowFocusHandler(psfn, pmsg);
break;
case TFPRIV_ONKILLTHREADFOCUS:
//
// #497764
//
// PENJPN.DLL calls LoadImage() in ThreadFocusSink.
// But it needs loader lock because it calls
// GetModuleFileName().
//
// So we can not call ThreadFocusSink while someone
// holds the loader lock.
//
if (_OwnedLoaderLockBySomeone() && !_InsideLoaderLock())
{
Assert(0);
DWORD dwCurrentThread = GetCurrentThreadId();
if (GetSharedMemory()->dwFocusThread != dwCurrentThread)
{
PostThreadMessage(dwCurrentThread,
g_msgPrivate,
TFPRIV_ONKILLTHREADFOCUS,
0);
}
break;
}
// fall through...
case TFPRIV_ONSETTHREADFOCUS:
if (psfn && (ptim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn)))
{
ptim->_OnThreadFocus(pmsg->wParam == TFPRIV_ONSETTHREADFOCUS);
}
break;
case TFPRIV_UPDATEDISPATTR:
if (psfn && (ptim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn)))
{
ptim->UpdateDispAttr();
}
break;
case TFPRIV_LANGCHANGE:
if (psfn && psfn->plbim && psfn->plbim->_GetLBarItemCtrl())
{
BOOL bRet = ActivateNextAssembly((BOOL)(pmsg->lParam));
}
break;
case TFPRIV_KEYTIPCHANGE:
if (psfn && psfn->plbim && psfn->plbim->_GetLBarItemCtrl())
{
ActivateNextKeyTip((BOOL)(pmsg->lParam));
}
break;
case TFPRIV_GLOBALCOMPARTMENTSYNC:
if (psfn)
{
if (psfn->_pGlobalCompMgr)
psfn->_pGlobalCompMgr->NotifyGlobalCompartmentChange((DWORD)(pmsg->lParam));
}
break;
case TFPRIV_SETMODALLBAR:
SetModalLBarId(HIWORD((DWORD)pmsg->lParam),
LOWORD((DWORD)pmsg->lParam));
break;
case TFPRIV_RELEASEMODALLBAR:
SetModalLBarId(-1, -1);
break;
case TFPRIV_UPDATE_REG_KBDTOGGLE:
InitLangChangeHotKey();
break;
case TFPRIV_UPDATE_REG_IMX:
UpdateRegIMXHandler();
break;
case TFPRIV_REGISTEREDNEWLANGBAR:
// TraceMsg(TF_GENERAL, "TFPRIV_REGISTEREDNEWLANGBAR current thread %x", GetCurrentThreadId());
MakeSetFocusNotify(g_msgSetFocus, 0, 0);
break;
case TFPRIV_SYSCOLORCHANGED:
if (psfn)
FlushIconIndex(psfn);
break;
case TFPRIV_LOCKREQ:
if (psfn)
{
psfn->_fLockRequestPosted = FALSE;
CInputContext::_PostponeLockRequestCallback(psfn, NULL);
}
break;
case TFPRIV_POSTINPUTCHANGEREQUEST:
PostInputChangeRequestHandler();
break;
case TFPRIV_LANGBARCLOSED:
LangBarClosed();
break;
case TFPRIV_ACTIVATELANG:
ActivateAssembly((LANGID)pmsg->lParam, ACTASM_NONE);
break;
case TFPRIV_ENABLE_MSAA:
if (psfn && (ptim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn)))
{
ptim->_InitMSAA();
}
break;
case TFPRIV_DISABLE_MSAA:
if (psfn && (ptim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn)))
{
ptim->_UninitMSAA();
}
break;
}
}
else if ((uMsg == g_msgSetFocus) ||
(uMsg == g_msgThreadTerminate) ||
(uMsg == g_msgThreadItemChange) ||
(uMsg == g_msgShowFloating) ||
(uMsg == g_msgLBUpdate))
{
SetFocusNotifyHandler(uMsg, pmsg->wParam, pmsg->lParam);
}
else if (uMsg == g_msgLBarModal)
{
DispatchModalLBar((DWORD)pmsg->wParam, pmsg->lParam);
}
#ifdef DEBUG
else if ((uMsg == g_msgRpcSendReceive) ||
#ifdef POINTER_MARSHAL
(uMsg == g_msgPointerMarshal) ||
#endif // POINTER_MARSHAL
(uMsg == g_msgThreadMarshal) ||
(uMsg == g_msgStubCleanUp))
{
if (!pmsg->hwnd)
{
Assert(0);
}
}
#endif
break;
}
return 1;
}
//+---------------------------------------------------------------------------
//
// StartKanaCapsUpdateTimer
//
//+---------------------------------------------------------------------------
void StartKanaCapsUpdateTimer(SYSTHREAD *psfn)
{
if (GetCurrentAssemblyLangId(psfn) != 0x0411)
return;
if (!IsWindow(psfn->hwndMarshal))
return;
SetTimer(psfn->hwndMarshal, MARSHALWND_TIMER_UPDATEKANACAPS, 300, NULL);
}
//+---------------------------------------------------------------------------
//
// KanaCapsUpdate
//
//+---------------------------------------------------------------------------
void KanaCapsUpdate(SYSTHREAD *psfn)
{
static SHORT g_sCaps = 0;
static SHORT g_sKana = 0;
if (GetCurrentAssemblyLangId(psfn) != 0x0411)
return;
SHORT sCaps = g_sCaps;
SHORT sKana = g_sKana;
g_sCaps = GetKeyState(VK_CAPITAL) & 0x01;
g_sKana = GetKeyState(VK_KANA) & 0x01;
//
// if psfn->fInitCapsKanaIndicator is true, it is enough to make a
// notification only when status is changed.
//
if ((sCaps != g_sCaps) ||
(sKana != g_sKana) ||
!psfn->fInitCapsKanaIndicator)
{
MakeSetFocusNotify(g_msgLBUpdate, TF_LBU_CAPSKANAKEY,
(LPARAM)((g_sCaps ? TF_LBUF_CAPS : 0) |
(g_sKana ? TF_LBUF_KANA : 0)));
psfn->fInitCapsKanaIndicator = TRUE;
}
}
//+---------------------------------------------------------------------------
//
// CheckKoreanMouseClick
//
//+---------------------------------------------------------------------------
BOOL CheckKoreanMouseClick(SYSTHREAD *psfn, WPARAM wParam, LPARAM lParam)
{
//
// check KeyUp and VK_PROCESSKEY
//
if (!(HIWORD(lParam) & KF_UP) || ((wParam & 0xff) != VK_PROCESSKEY))
return FALSE;
//
// if the current language is not 0x412, return.
//
if (GetCurrentAssemblyLangId(psfn) != 0x412)
return FALSE;
//
// If toolbar is clicked, we eat this VK_PROCESSKEY.
//
POINT pt;
HWND hwnd;
if (!GetCursorPos(&pt))
return FALSE;
hwnd = WindowFromPoint(pt);
if (!hwnd)
return FALSE;
DWORD dwTimFlags = g_timlist.GetFlags(GetWindowThreadProcessId(hwnd, NULL));
return (dwTimFlags & TLF_CTFMONPROCESS) ? TRUE : FALSE;
}
//+---------------------------------------------------------------------------
//
// IsJapaneseNonIMEVKKANJI
//
//+---------------------------------------------------------------------------
BOOL IsJapaneseNonIMEVKKANJI(WPARAM wParam)
{
if ((wParam & 0xff) != VK_KANJI)
return FALSE;
HKL hkl = GetKeyboardLayout(0);
if (IsPureIMEHKL(hkl))
return FALSE;
if (PRIMARYLANGID(LANGIDFROMHKL(hkl)) != LANG_JAPANESE)
return FALSE;
return TRUE;
}
//+---------------------------------------------------------------------------
//
// IsKoreanNonIMEVKJUNJA
//
//+---------------------------------------------------------------------------
BOOL IsKoreanNonIMEVKJUNJA(WPARAM wParam)
{
if ((wParam & 0xff) != VK_JUNJA)
return FALSE;
HKL hkl = GetKeyboardLayout(0);
if (IsPureIMEHKL(hkl))
return FALSE;
if (PRIMARYLANGID(LANGIDFROMHKL(hkl)) != LANG_KOREAN)
return FALSE;
return TRUE;
}
//+---------------------------------------------------------------------------
//
// ThreadKeyboardProc
//
//+---------------------------------------------------------------------------
//
// Workaround for global keyboard hook
//
// On IA64 platform, Cicero install two global keyboard hooks which is 64bit and 32bit code.
// When any keyboard event occur on one apps instance, there two global keyboard hook procedure
// (SysKeyboardProc) called from win32k xxxCallHook2.
// If xxxCallHook2 detect different instance between current and receiver which is 64bit and 32bit,
// this function notify by InterSendMsg.
//
LRESULT CALLBACK ThreadKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HHOOK hHook = NULL;
UINT ret = 0;
SYSTHREAD *psfn = GetSYSTHREAD();
if (psfn)
hHook = psfn->hThreadKeyboardHook;
if (g_fDllProcessDetached)
goto Exit;
if (nCode == HC_ACTION)
{
_try
{
ret = _KeyboardHook(wParam, lParam);
}
_except(CicExceptionFilter(GetExceptionInformation()))
{
Assert(0);
}
}
Exit:
if ((ret == 0) && hHook)
return CallNextHookEx(hHook, nCode, wParam, lParam);
else
return ret;
}
UINT _KeyboardHook(WPARAM wParam, LPARAM lParam)
{
SYSTHREAD *psfn = GetSYSTHREAD();
CThreadInputMgr *ptim = NULL;
BOOL fEaten;
HRESULT hr;
if (psfn)
ptim = CThreadInputMgr::_GetThisFromSYSTHREAD(psfn);
//
// If we're in Modal Lang bar mode (menu is shown.),
// we want to eat keys.
//
if (HandleModalLBar((HIWORD(lParam) & KF_UP) ? WM_KEYUP : WM_KEYDOWN,
wParam, lParam))
return 1;
if (CheckKoreanMouseClick(psfn, wParam, lParam))
return 1;
UpdateModifiers(wParam, lParam);
if ((HIWORD(lParam) & KF_UP))
StartKanaCapsUpdateTimer(psfn);
if (psfn)
{
if (CheckLangChangeHotKey(psfn, wParam, lParam))
{
//
// Cic#4645 We need to forward this key envent ot the next hook.
// mstsc.exe (TS client) needs it.
//
return 0;
// goto Exit;
// return 1;
}
}
//
// On CUAS, Imm32's Hotkey is simulated in ImmProcessKey
//
if (!psfn || !CtfImmIsCiceroStartedInThread())
CheckImm32HotKey(wParam, lParam);
if (HandleDBEKeys(wParam, lParam))
{
//
// #519671
//
// If there is a focus DIM and the current asm item is not Japanese
// TIP, we switch the assembly to Japanese TIP and open it.
//
// we do this after calling HandleDBEKeys(). So TIP's keyboard
// event sink for VK_KANJI won't be called.
//
if (IsJapaneseNonIMEVKKANJI(wParam))
ToggleJImeNoIme(psfn);
//
// Cic#4645 We need to forward this key envent ot the next hook.
// mstsc.exe (TS client) needs it.
//
return 0;
// goto Exit;
// return 1;
}
if (ptim)
{
ptim->_NotifyKeyTraceEventSink(wParam, lParam);
if (ptim->_ProcessHotKey(wParam, lParam, TSH_SYSHOTKEY, FALSE, FALSE))
return 1;
//
// give AIMM the key events.
//
if (ptim->GetSysHookSink())
{
hr = ptim->GetSysHookSink()->OnSysKeyboardProc(wParam, lParam);
if (hr == S_OK)
return 1;
}
//
// At last we can call KeyStrokemMgr.
//
if (!ptim->_AppWantsKeystrokes() &&
ptim->_IsKeystrokeFeedEnabled() &&
wParam != VK_PROCESSKEY &&
(!(HIWORD(lParam) & (KF_MENUMODE | KF_ALTDOWN)) || IsKoreanNonIMEVKJUNJA(wParam)))
{
hr = (HIWORD(lParam) & KF_UP) ? ptim->KeyUp(wParam, lParam, &fEaten) :
ptim->KeyDown(wParam, lParam, &fEaten);
if (hr == S_OK && fEaten)
return 1;
}
//
// F10 SysKeyDown work arround.
//
// KSMGR won't be called on WM_SYSKEYDOWN/UP. So we foward F10
// through AsynKeyHandler to support SyncLock in KS callback.
//
// we don't have to do this if there is no foreground keyboard tip.
//
if (((wParam & 0xff) == VK_F10) &&
(ptim->GetForegroundKeyboardTip() != TF_INVALID_GUIDATOM))
{
fEaten = FALSE;
if (ptim->_AsyncKeyHandler(wParam,
lParam,
TIM_AKH_SIMULATEKEYMSGS,
&fEaten) && fEaten)
return 1;
}
}
return 0;
}
//+---------------------------------------------------------------------------
//
// ThreadMouseProc
//
//+---------------------------------------------------------------------------
LRESULT CALLBACK ThreadMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HHOOK hHook = NULL;
SYSTHREAD *psfn = GetSYSTHREAD();
if (psfn)
hHook = psfn->hThreadMouseHook;
if (g_fDllProcessDetached)
goto Exit;
if (nCode == HC_ACTION)
{
BOOL bRet = FALSE;
if ((wParam == WM_MOUSEMOVE) || (wParam == WM_NCMOUSEMOVE))
goto Exit;
_try
{
bRet = HandleModalLBar((UINT)wParam, 0,
MAKELPARAM(((MOUSEHOOKSTRUCT *)lParam)->pt.x,
((MOUSEHOOKSTRUCT *)lParam)->pt.y));
}
_except(CicExceptionFilter(GetExceptionInformation()))
{
Assert(0);
}
if (bRet)
return 1;
}
Exit:
if (hHook)
return CallNextHookEx(hHook, nCode, wParam, lParam);
else
return 0;
}
//+---------------------------------------------------------------------------
//
// UninitHooks
//
//+---------------------------------------------------------------------------
void UninitHooks()
{
HHOOK _h;
if ( (_h = GetSharedMemory()->hSysShellHook.GetHandle(g_bOnWow64)) != NULL)
{
UnhookWindowsHookEx(_h);
GetSharedMemory()->hSysShellHook.SetHandle(g_bOnWow64, NULL);
}
if ( (_h = GetSharedMemory()->hSysCBTHook.GetHandle(g_bOnWow64)) != NULL)
{
UnhookWindowsHookEx(_h);
GetSharedMemory()->hSysCBTHook.SetHandle(g_bOnWow64, NULL);
}
if ( (_h = GetSharedMemory()->hSysGetMsgHook.GetHandle(g_bOnWow64)) != NULL)
{
UnhookWindowsHookEx(_h);
GetSharedMemory()->hSysGetMsgHook.SetHandle(g_bOnWow64, NULL);
}
}
//+---------------------------------------------------------------------------
//
// InitHooks
//
//+---------------------------------------------------------------------------
void InitHooks()
{
Assert(! GetSharedMemory()->hSysShellHook.GetHandle(g_bOnWow64));
GetSharedMemory()->hSysShellHook.SetHandle(g_bOnWow64, SetWindowsHookEx(WH_SHELL, SysShellProc, g_hInst, 0));
//
// nb: we could move GetMsgHook to per-thread hook if we get rid of
// TFPRIV_MARSHALINTERFACE for non-cicero apps.
// TFPRIV_MARSHALINTERFACE is necesary because Tipbar does not
// know if the target thread has TIM. If there is no TIP or
// the GetMsgHook, the tipbar waits until timeout.
// To solve this problem, we must have TIM's thread list
// in shared mem. Maybe we should do this.
//
Assert(! GetSharedMemory()->hSysGetMsgHook.GetHandle(g_bOnWow64));
if (IsOnNT())
{
// we need a W hook on NT to work-around an os dbcs/unicode translation bug (4243)
GetSharedMemory()->hSysGetMsgHook.SetHandle(g_bOnWow64, SetWindowsHookExW(WH_GETMESSAGE, SysGetMsgProc, g_hInst, 0));
}
else
{
GetSharedMemory()->hSysGetMsgHook.SetHandle(g_bOnWow64, SetWindowsHookExA(WH_GETMESSAGE, SysGetMsgProc, g_hInst, 0));
}
Assert(! GetSharedMemory()->hSysCBTHook.GetHandle(g_bOnWow64));
GetSharedMemory()->hSysCBTHook.SetHandle(g_bOnWow64, SetWindowsHookEx(WH_CBT, SysCBTProc, g_hInst, 0));
InitStaticHooks();
}
//+---------------------------------------------------------------------------
//
// InitThreadHooks
//
//+---------------------------------------------------------------------------
void InitThreadHook(DWORD dwThreadId)
{
SYSTHREAD *psfn = GetSYSTHREAD();
if (!psfn)
return;
if (psfn->hThreadKeyboardHook && psfn->hThreadMouseHook)
return;
PVOID pvLdrLockCookie = NULL;
ULONG ulLockState = 0;
// make sure that no one else owns the loader lock because we
// could otherwise deadlock
LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY, &ulLockState,
&pvLdrLockCookie);
if (ulLockState == LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED)
{
__try {
if (!psfn->hThreadKeyboardHook)
{
//
// Install Local keyboard hook with hMod value.
// win32k mantain hMod ref count even another global hook
// detached.
//
psfn->hThreadKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, ThreadKeyboardProc, g_hInst, dwThreadId);
}
if (!psfn->hThreadMouseHook)
{
//
// Install Local keyboard hook with hMod value.
// win32k mantain hMod ref count even another global hook
// detached.
//
psfn->hThreadMouseHook = SetWindowsHookEx(WH_MOUSE, ThreadMouseProc, g_hInst, dwThreadId);
}
}
_except(CicExceptionFilter(GetExceptionInformation()))
{
}
LdrUnlockLoaderLock(0, pvLdrLockCookie);
}
}
//+---------------------------------------------------------------------------
//
// UninitThreadHooks
//
//+---------------------------------------------------------------------------
void UninitThreadHooks(SYSTHREAD *psfn)
{
if (!psfn)
return;
if (psfn->hThreadKeyboardHook)
{
UnhookWindowsHookEx(psfn->hThreadKeyboardHook);
psfn->hThreadKeyboardHook = NULL;
}
if (psfn->hThreadMouseHook)
{
UnhookWindowsHookEx(psfn->hThreadMouseHook);
psfn->hThreadMouseHook = NULL;
}
}
//+---------------------------------------------------------------------------
//
// TF_InitSystem
//
// Called by ctfmon on a single thread.
//+---------------------------------------------------------------------------
extern "C" BOOL WINAPI TF_InitSystem(void)
{
SYSTHREAD *psfn;
g_fCTFMONProcess = TRUE;
g_timlist.Init(TRUE);
if (psfn = GetSYSTHREAD())
{
g_gcomplist.Init(psfn);
EnsureAsmCacheFileMap();
EnsureAssemblyList(psfn);
psfn->fCTFMON = TRUE;
}
InitHooks();
return TRUE;
}
//+---------------------------------------------------------------------------
//
// TF_UninitSystem
//
// Called by ctfmon on a single thread.
//+---------------------------------------------------------------------------
extern "C" BOOL WINAPI TF_UninitSystem(void)
{
CThreadMarshalWnd::DestroyAll();
UninitAsmCacheFileMap();
g_timlist.Uninit();
SYSTHREAD *psfn = FindSYSTHREAD();
g_gcomplist.Uninit(psfn);
UninitHooks();
return TRUE;
}
//+---------------------------------------------------------------------------
//
// TF_InitThreadSystem
//
//+---------------------------------------------------------------------------
BOOL TF_InitThreadSystem(void)
{
DWORD dwThreadId = GetCurrentThreadId();
//
// we should not see the timlist entry of this thread. This thread
// is starting now.
// the thread with same ID was terminated incorrectly so there was no
// chance to clean timlist up.
//
if (g_timlist.IsThreadId(dwThreadId))
{
g_timlist.RemoveThread(dwThreadId);
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// TF_UninitThreadSystem
//
//+---------------------------------------------------------------------------
BOOL TF_UninitThreadSystem(void)
{
SYSTHREAD *psfn = FindSYSTHREAD();
g_gcomplist.Uninit(psfn);
FreeSYSTHREAD();
return TRUE;
}
//+---------------------------------------------------------------------------
//
// UninitProcess()
//
//+---------------------------------------------------------------------------
void UninitProcess()
{
DWORD dwProcessId = GetCurrentProcessId();
//
// FreeSYSTHREAD2() removes psfn from PtrArray.
//
if (g_rgSysThread)
{
while(g_rgSysThread->Count())
{
SYSTHREAD *psfn = g_rgSysThread->Get(0);
if (psfn)
FreeSYSTHREAD2(psfn);
}
delete g_rgSysThread;
g_rgSysThread = NULL;
}
//
// remove all timlist entries for the current process.
//
g_timlist.RemoveProcess(dwProcessId);
CCategoryMgr::UninitGlobal();
}
//+---------------------------------------------------------------------------
//
// InitAppCompatFlags
//
//+---------------------------------------------------------------------------
BOOL InitAppCompatFlags()
{
TCHAR szAppCompatKey[MAX_PATH];
TCHAR szFileName[MAX_PATH];
if (::GetModuleFileName(NULL, // handle to module
szFileName, // file name of module
sizeof(szFileName)/sizeof(TCHAR)) == 0)
return FALSE;
TCHAR szModuleName[MAX_PATH];
LPTSTR pszFilePart = NULL;
::GetFullPathName(szFileName, // file name
sizeof(szModuleName)/sizeof(TCHAR),
szModuleName, // path buffer
&pszFilePart); // address of file name in path
if (pszFilePart == NULL)
return FALSE;
StringCopyArray(szAppCompatKey, c_szAppCompat);
StringCatArray(szAppCompatKey, pszFilePart);
CMyRegKey key;
if (key.Open(HKEY_LOCAL_MACHINE, szAppCompatKey, KEY_READ) == S_OK)
{
DWORD dw;
if (key.QueryValue(dw, c_szCompatibility) == S_OK)
g_dwAppCompatibility = dw;
}
//
// Ciero #4605
//
// hack for 16bit apps on Win9x platform.
// all 16bit apps shrare one PPI (process info) and this means that
// there is one main thread for WaitForInputIdle() for all 16 bit apps.
// so we stop using WaitForInputIdle().
//
if (!IsOnNT())
{
if (!lstrcmpi(pszFilePart, "kernel32.dll"))
g_dwAppCompatibility |= CIC_COMPAT_NOWAITFORINPUTIDLEONWIN9X;
}
return TRUE;
}
//+---------------------------------------------------------------------------
//
// InitCUASFlag
//
//+---------------------------------------------------------------------------
void InitCUASFlag()
{
CMyRegKey key;
CMyRegKey keyIMM;
if (key.Open(HKEY_LOCAL_MACHINE, c_szCtfShared, KEY_READ) == S_OK)
{
DWORD dw;
if (key.QueryValue(dw, c_szCUAS) == S_OK)
g_fCUAS = dw ? TRUE : FALSE;
}
g_szCUASImeFile[0] = '\0';
if (g_fCUAS)
{
if (keyIMM.Open(HKEY_LOCAL_MACHINE, c_szIMMKey, KEY_READ) == S_OK)
{
TCHAR szCUASImeFile[16];
if (keyIMM.QueryValueCch(szCUASImeFile, c_szCUASIMEFile, ARRAYSIZE(szCUASImeFile)) == S_OK)
lstrcpy(g_szCUASImeFile, szCUASImeFile);
}
}
}
//+---------------------------------------------------------------------------
//
// TF_DllDetachInOther
//
//+---------------------------------------------------------------------------
extern "C" BOOL WINAPI TF_DllDetachInOther()
{
SYSTHREAD *psfn = FindSYSTHREAD();
if (psfn)
psfn->fCUASDllDetachInOtherOrMe = TRUE;
return TRUE;
}