User:Alvinhochun/Localization/IME/IMM
Operations of IMM.
Contents
Windows 2000
Studying from Windows 2000 seems easier since XP has lots of TSF-related code stuffed around.
I may not touch TSF code before this has some progress...
Initialization
imm32.dll is loaded by user32.dll, similar to that in XP.
imm32.dll
Functions
Exported Functions
Exported Functions in imm32.dll
| |||
---|---|---|---|
Function Name | Documented? | Decl | Remarks |
ImmActivateLayout |
No | ||
ImmAssociateContext |
Yes | HIMC ImmAssociateContext( _In_ HWND hWnd, _In_ HIMC hIMC ); |
|
ImmAssociateContextEx |
Yes | BOOL ImmAssociateContextEx( _In_ HWND hWnd, _In_ HIMC hIMC, _In_ DWORD dwFlags ); |
|
ImmCallImeConsoleIME |
No | ||
ImmConfigureIMEA ImmConfigureIMEW |
Yes | BOOL ImmConfigureIME( _In_ HKL hKL, _In_ HWND hWnd, _In_ DWORD dwMode, _In_ LPVOID lpData ); |
|
ImmCreateContext |
Yes | HIMC ImmCreateContext(void); |
|
ImmCreateIMCC |
No | ||
ImmCreateSoftKeyboard |
No | ||
ImmDestroyContext |
Yes | BOOL ImmDestroyContext( _In_ HIMC hIMC ); |
|
ImmDestroyIMCC |
No | ||
ImmDestroySoftKeyboard |
No | ||
ImmDisableIME |
Yes | BOOL ImmDisableIME( _In_ DWORD idThread ); |
|
ImmDisableIme |
No | Direct alias of ImmDisableIME
| |
ImmEnumInputContext |
Yes | BOOL ImmEnumInputContext( _In_ DWORD idThread, _In_ IMCENUMPROC lpfn, _In_ LPARAM lParam ); |
|
ImmEnumRegisterWordA ImmEnumRegisterWordW |
Yes | UINT ImmEnumRegisterWord( _In_ HKL hKL, _In_ REGISTERWORDENUMPROC lpfnEnumProc, _In_opt_ LPCTSTR lpszReading, _In_ DWORD dwStyle, _In_opt_ LPCTSTR lpszRegister, _In_ LPVOID lpData ); |
|
ImmEscapeA ImmEscapeW |
Yes | LRESULT ImmEscape( _In_ HKL hKL, _In_ HIMC hIMC, _In_ UINT uEscape, _Inout_ LPVOID lpData ); |
|
ImmFreeLayout |
No | ||
ImmGenerateMessage |
No | ||
ImmGetCandidateListA ImmGetCandidateListW |
Yes | DWORD ImmGetCandidateList( _In_ HIMC hIMC, _In_ DWORD dwIndex, _Out_opt_ LPCANDIDATELIST lpCandList, _In_ DWORD dwBufLen ); |
|
ImmGetCandidateListCountA ImmGetCandidateListCountW |
Yes | DWORD ImmGetCandidateListCount( _In_ HIMC hIMC, _Out_ LPDWORD lpdwListCount ); |
|
ImmGetCandidateWindow |
Yes | BOOL ImmGetCandidateWindow( _In_ HIMC hIMC, _In_ DWORD dwIndex, _Out_ LPCANDIDATEFORM lpCandidate ); |
|
ImmGetCompositionFontA ImmGetCompositionFontW |
Yes | BOOL ImmGetCompositionFont( _In_ HIMC hIMC, _Out_ LPLOGFONT lplf ); |
|
ImmGetCompositionStringA ImmGetCompositionStringW |
Yes | LONG ImmGetCompositionString( _In_ HIMC hIMC, _In_ DWORD dwIndex, _Out_opt_ LPVOID lpBuf, _In_ DWORD dwBufLen ); |
|
ImmGetCompositionWindow |
Yes | BOOL ImmGetCompositionWindow( _In_ HIMC hIMC, _Out_ LPCOMPOSITIONFORM lpCompForm ); |
|
ImmGetContext |
Yes | HIMC ImmGetContext( _In_ HWND hWnd ); |
|
ImmGetConversionListA ImmGetConversionListW |
Yes | DWORD ImmGetConversionList( _In_ HKL hKL, _In_ HIMC hIMC, _In_ LPCTSTR lpSrc, _Out_ LPCANDIDATELIST lpDst, _In_ DWORD dwBufLen, _In_ UINT uFlag ); |
|
ImmGetConversionStatus |
Yes | BOOL ImmGetConversionStatus( _In_ HIMC hIMC, _Out_opt_ LPDWORD lpfdwConversion, _Out_opt_ LPDWORD lpfdwSentence ); |
|
ImmGetDefaultIMEWnd |
Yes | HWND ImmGetDefaultIMEWnd( _In_ HWND hWnd ); |
|
ImmGetDescriptionA ImmGetDescriptionW |
Yes | UINT ImmGetDescription( _In_ HKL hKL, _Out_opt_ LPTSTR lpszDescription, _In_ UINT uBufLen ); |
|
ImmGetGuideLineA ImmGetGuideLineW |
Yes | DWORD ImmGetGuideLine( _In_ HIMC hIMC, _In_ DWORD dwIndex, _Out_opt_ LPTSTR lpBuf, _In_ DWORD dwBufLen ); |
|
ImmGetHotKey |
No | ||
ImmGetIMCCLockCount |
No | ||
ImmGetIMCCSize |
No | ||
ImmGetIMCLockCount |
No | ||
ImmGetIMEFileNameA ImmGetIMEFileNameW |
Yes | UINT ImmGetIMEFileName( _In_ HKL hKL, _Out_opt_ LPTSTR lpszFileName, _In_ UINT uBufLen ); |
|
ImmGetImeInfoEx |
No | ||
ImmGetImeMenuItemsA ImmGetImeMenuItemsW |
Yes | DWORD ImmGetImeMenuItems( _In_ HIMC hIMC, _In_ DWORD dwFlags, _In_ DWORD dwType, _Out_opt_ LPIMEMENUITEMINFO lpImeParentMenu, _Out_opt_ LPIMEMENUITEMINFO lpImeMenu, _In_ DWORD dwSize ); |
|
ImmGetOpenStatus |
Yes | BOOL ImmGetOpenStatus( _In_ HIMC hIMC ); |
|
ImmGetProperty |
Yes | DWORD ImmGetProperty( _In_ HKL hKL, _In_ DWORD fdwIndex ); |
|
ImmGetRegisterWordStyleA ImmGetRegisterWordStyleW |
Yes | UINT ImmGetRegisterWordStyle( _In_ HKL hKL, _In_ UINT nItem, _Out_ LPSTYLEBUF lpStyleBuf ); |
|
ImmGetStatusWindowPos |
Yes | BOOL ImmGetStatusWindowPos( _In_ HIMC hIMC, _Out_ LPPOINT lpptPos ); |
|
ImmGetVirtualKey |
Yes | UINT ImmGetVirtualKey( _In_ HWND hWnd ); |
|
ImmIMPGetIMEA ImmIMPGetIMEW |
No | ||
ImmIMPQueryIMEA ImmIMPQueryIMEW |
No | ||
ImmIMPSetIMEA ImmIMPSetIMEW |
No | ||
ImmInstallIMEA ImmInstallIMEW |
Yes | HKL ImmInstallIME( _In_ LPCTSTR lpszIMEFileName, _In_ LPCTSTR lpszLayoutText ); |
|
ImmIsIME |
Yes | BOOL ImmIsIME( _In_ HKL hKL ); |
|
ImmIsUIMessageA ImmIsUIMessageW |
Yes | BOOL ImmIsUIMessage( _In_ HWND hWndIME, _In_ UINT msg, _In_ WPARAM wParam, _In_ LPARAM lParam ); |
|
ImmLoadIME |
No | ||
ImmLoadLayout |
No | ||
ImmLockClientImc |
No | ||
ImmLockIMC |
No | ||
ImmLockIMCC |
No | ||
ImmLockImeDpi |
No | ||
ImmNotifyIME |
Yes | BOOL ImmNotifyIME( _In_ HIMC hIMC, _In_ DWORD dwAction, _In_ DWORD dwIndex, _In_ DWORD dwValue ); |
|
ImmPenAuxInput |
No | ||
ImmProcessKey |
No | ||
ImmPutImeMenuItemsIntoMappedFile |
No | ||
ImmRegisterClient |
No | ||
ImmRegisterWordA ImmRegisterWordW |
Yes | BOOL ImmRegisterWord( _In_ HKL hKL, _In_ LPCTSTR lpszReading, _In_ DWORD dwStyle, _In_ LPCTSTR lpszRegister ); |
|
ImmReleaseContext |
Yes | BOOL ImmReleaseContext( _In_ HWND hWnd, _In_ HIMC hIMC ); |
|
ImmRequestMessageA ImmRequestMessageW |
Yes | LRESULT ImmRequestMessage( _In_ HIMC hIMC, _In_ WPARAM wParam, _In_ LPARAM lParam ); |
|
ImmReSizeIMCC |
No | ||
ImmSendIMEMessageExA ImmSendIMEMessageExW |
No | ||
ImmSendMessageToActiveDefImeWndW |
No | ||
ImmSetActiveContext |
No | ||
ImmSetActiveContextConsoleIME |
No | ||
ImmSetCandidateWindow |
Yes | BOOL ImmSetCandidateWindow( _In_ HIMC hIMC, _In_ LPCANDIDATEFORM lpCandidate ); |
|
ImmSetCompositionFontA ImmSetCompositionFontW |
Yes | BOOL ImmSetCompositionFont( _In_ HIMC hIMC, _In_ LPLOGFONT lplf ); |
|
ImmSetCompositionStringA ImmSetCompositionStringW |
Yes | BOOL ImmSetCompositionString( _In_ HIMC hIMC, _In_ DWORD dwIndex, _In_opt_ LPVOID lpComp, _In_ DWORD dwCompLen, _In_opt_ LPVOID lpRead, _In_ DWORD dwReadLen ); |
|
ImmSetCompositionWindow |
Yes | BOOL ImmSetCompositionWindow( _In_ HIMC hIMC, _In_ LPCOMPOSITIONFORM lpCompForm ); |
|
ImmSetConversionStatus |
Yes | BOOL ImmSetConversionStatus( _In_ HIMC hIMC, _In_ DWORD fdwConversion, _In_ DWORD fdwSentence ); |
|
ImmSetHotKey |
No | ||
ImmSetOpenStatus |
Yes | BOOL ImmSetOpenStatus( _In_ HIMC hIMC, _In_ BOOL fOpen ); |
|
ImmSetStatusWindowPos |
Yes | BOOL ImmSetStatusWindowPos( _In_ HIMC hIMC, _In_ LPPOINT lpptPos ); |
|
ImmShowSoftKeyboard |
No | ||
ImmSimulateHotKey |
Yes | BOOL ImmSimulateHotKey( _In_ HWND hWnd, _In_ DWORD dwHotKeyID ); |
|
ImmSystemHandler |
No | ||
ImmTranslateMessage |
No | ||
ImmUnlockClientImc |
No | ||
ImmUnlockIMC |
No | ||
ImmUnlockIMCC |
No | ||
ImmUnlockImeDpi |
No | ||
ImmUnregisterWordA ImmUnregisterWordW |
Yes | BOOL ImmUnregisterWord( _In_ HKL hKL, _In_ LPCTSTR lpszReading, _In_ DWORD dwStyle, _In_ LPCTSTR lpszUnregister ); |
|
ImmWINNLSEnableIME |
No | ||
ImmWINNLSGetEnableStatus |
No | ||
ImmWINNLSGetIMEHotkey |
No |
Documented Functions
ImmCreateContext
This calls NtUserCreateInputContext
.
TODO
ImmGetContext
HIMC WINAPI ImmGetContext(HWND hWnd)
"The application must call ImmReleaseContext when it is finished with the input context[,]" says MSDN. However, given the behaviour of ImmReleaseContext, it really does nothing.
This performs a NULL check and then call ImmGetSaveContext(hWnd, 2)
.
ImmReleaseContext
BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
Always returns TRUE
. (Just checked that even Windows 7 does this too.)
Undocumented Functions
ImmRegisterClient
int WINAPI ImmRegisterClient(_some_struct(size 0x3E) *arg1, HINSTANCE arg2);
This function doesn't really seem to register anything, but only initialize some variables. Something like:
- gSharedInfo = *arg1;
- gpsi = first byte of gSharedInfo;
- return ImmInitializeGlobals(arg2);
This is called within user32.dll too.
ImmInitializeGlobals
int ImmInitializeGlobals(HINSTANCE arg1);
- Stores arg1 to ghInst if not NULL
- if not initialized already (gfInitialized == 0), then:
- RtlInitializeCriticalSection(&gcsImeDpi)
- gHighestUserAddress = SYSTEM_BASIC_INFORMATION.MaximumUserModeAddress (no idea what it is used for yet)
- gfInitialized = 1
ImmGetSaveContext
HIMC __stdcall ImmGetSaveContext(HWND hWnd, char arg2);
- If gpsi == 0 || !(gpsi->_byte_at_offset_2 & 8) then return NULL
- If hWnd is NULL then get HIMC by NtUserGetThreadState(4)
- Else:
- Gets PWND by HMValidateHandle(hWnd, 1)
- If window not owned by current process then return NULL
- Gets HIMC from PWND->hImc
- If NULL and arg2 has bit 0 set then try to get hIMC by NtUserQueryWindow(hWnd, 8)
- _some_struct *ret = ImmLockClientImc(HIMC)
- If ret is NULL then return NULL
- If arg2 has bit 1 set and byte at offset 8 of ret has bit 7 set then ImmUnlockClientImc and return NULL
- ImmUnlockClientImc and return HIMC
From this, it appears that each thread and window has its default IME context. It is possible that each window uses the context of the thread it is created in.
Also, debugging notepad shows that ImmCreateContext is never called to create them.
Kernel Functions
NtUserCreateInputContext
DWORD
APIENTRY
NtUserCreateInputContext(
void *dwUnknown1);
From ImmCreateContext
: return value of ImmLocalAlloc(8, 0x2c)
is passed as the first parameter, so it is probably a pointer.
This appears to call EnterCrit
and then call win32k!CreateInputContext
.
TODO
CreateInputContext
int __stdcall CreateInputContext(void *arg1);
This is known to be called by NtUserCreateInputContext
, and also by xxxCreateThreadInfo
.
bd032cb4 a005df13 win32k!CreateInputContext bd032d0c a005d594 win32k!xxxCreateThreadInfo+0x5d9 bd032d20 a005d4c7 win32k!UserThreadCallout+0x9d bd032d3c 80496f4c win32k!W32pThreadCallout+0x3b bd032d54 8046140e nt!PsConvertToGuiThread+0xad bd032ddc 80465b62 nt!KiBBTUnexpectedRange+0xc 00000000 00000000 nt!KiThreadStartup+0x16
Seems that the thread IME context is created when a UI thread is created?
Not called when a window is created.
TODO
Loading of imm32.dll
(XP)
IMM32 is loaded by user32.dll
from _InitializeImmEntryTable
. It loads the library (if not already) and fill in a structure of function pointers gImmApiEntries
. This function is supposed to be called in the DllMain of USER32 but the relevant code is currently commented out.
In ReactOS, the related functions are located under win32ss\user\user32\misc\imm.c
. The functions it loads is different from that of Windows XP.
A simple trace (some information is omitted) (hopefully I got it correct):
- user32!DllMain
- user32!_InitializeImmEntryTable
- GetModuleHandle (and fail)
- LoadLibrary(imm32.dll)
- imm32!DllMain
- imm32!ImmInitializeGlobals
- user32!User32InitializeImmEntryTable
- user32!_InitializeImmEntryTable
- GetProcAddress and fill in the function table
- imm32!ImmRegisterClient
- imm32!ImmInitializeGlobals
- user32!_InitializeImmEntryTable
- imm32!DllMain
- imm32!ImmRegisterClient
- imm32!ImmInitializeGlobals
- user32!_InitializeImmEntryTable
TODO: Log all calls to Imm* functions with stack traces.
ReactOS
As of r67617, attempting to load IMM from user32 crashes:
Stack trace (click "Expand" to show):
f9af5b1c f9baa0e6 00000000 00000000 00000059 win32k!EngpCreatePDEV+0xf8 [d:\reactos\src\win32ss\gdi\eng\pdevobj.c @ 331] f9af5b3c f9c2cc84 00000000 ffffffff 00000000 win32k!EngpGetPDEV+0x96 [d:\reactos\src\win32ss\gdi\eng\pdevobj.c @ 572] f9af5b58 f9c2b515 00000000 00000000 00000000 win32k!GreOpenDCW+0x44 [d:\reactos\src\win32ss\gdi\ntgdi\dclife.c @ 641] f9af5cec 80513ec9 00000000 00000000 00000000 win32k!NtGdiOpenDCW+0x1b5 [d:\reactos\src\win32ss\gdi\ntgdi\dclife.c @ 752] f9af5d1c 80513a41 f9c2b360 0021edb8 00000020 nt!KiSystemCallTrampoline+0x19 [d:\reactos\src\ntoskrnl\include\internal\i386\ke.h @ 725] f9af5d5c 80403e03 0021ee0c 7c92cf6e badb0d00 nt!KiSystemServiceHandler+0x221 [d:\reactos\src\ntoskrnl\ke\i386\traphdlr.c @ 1716] f9af5d5c 7c92cf6e 0021ee0c 7c92cf6e badb0d00 nt!KiFastCallEntry+0x8c 0021edac 77bd7e47 77bb8b16 0021ede0 00000000 ntdll!KiFastSystemCallRet 0021edb0 77bb8b16 0021ede0 00000000 00000000 gdi32!ZwGdiOpenDCW+0xc 0021ee0c 77bb7648 00000000 00000000 00000001 gdi32!IntCreateDICW+0x1d6 [d:\reactos\src\win32ss\gdi\gdi32\objects\dc.c @ 69] 0021ee20 77a58455 77a92be8 00000000 00000000 gdi32!CreateICW+0x18 [d:\reactos\src\win32ss\gdi\gdi32\objects\dc.c @ 222] 0021ee98 77a5bc4c 77ad0d50 00000000 00000020 user32!LookupIconIdFromDirectoryEx+0x115 [d:\reactos\src\win32ss\user\user32\windows\cursoricon.c @ 2236] 0021ef6c 77a58298 77a20000 00007f00 00000020 user32!CURSORICON_LoadImageW+0x30c [d:\reactos\src\win32ss\user\user32\windows\cursoricon.c @ 1529] 0021ef94 77a57ef2 00000000 00007f00 00000002 user32!LoadImageW+0xd8 [d:\reactos\src\win32ss\user\user32\windows\cursoricon.c @ 2189] 0021efb4 75e26cd7 00000000 00007f00 00004000 user32!LoadCursorW+0x72 [d:\reactos\src\win32ss\user\user32\windows\cursoricon.c @ 2086] 0021efec 75e26d7f 00000001 0021f010 75e2994e IMM32!IMM_RegisterIMEClass+0x37 [d:\reactos\src\dll\win32\imm32\imm.c @ 390] 0021eff8 75e2994e 75e20000 00000001 00000000 IMM32!DllMain+0x8f [d:\reactos\src\dll\win32\imm32\imm.c @ 407] 0021f010 75e29a09 75e20000 00000001 00000000 IMM32!__DllMainCRTStartup+0xae [d:\reactos\src\lib\sdk\crt\startup\crtdll.c @ 201] 0021f024 7c928774 75e20000 00000001 00000000 IMM32!DllMainCRTStartup+0x29 [d:\reactos\src\lib\sdk\crt\startup\crtdll.c @ 171] 0021f038 7c925194 75e299e0 75e20000 00000001 ntdll!LdrpCallInitRoutine+0x14 [d:\reactos\src\dll\ntdll\ldr\ldrutils.c @ 217] 0021f0ec 7c928f6d 00000000 003a0043 0052005c ntdll!LdrpRunInitializeRoutines+0x434 [d:\reactos\src\dll\ntdll\ldr\ldrinit.c @ 814] 0021f330 7c9235b2 00000000 002249f0 0021f5ac ntdll!LdrpLoadDll+0x30d [d:\reactos\src\dll\ntdll\ldr\ldrutils.c @ 2580] 0021f57c 77d8a52e 002249f0 0021f5ac 0021f5a0 ntdll!LdrLoadDll+0x282 [d:\reactos\src\dll\ntdll\ldr\ldrapi.c @ 400] 0021f5dc 77d8a312 0021f5fc 00000000 00000000 kernel32!LoadLibraryExW+0x1ce [d:\reactos\src\dll\win32\kernel32\client\loader.c @ 366] 0021f5f0 77a4fe18 0021f5fc 003a0043 0052005c kernel32!LoadLibraryW+0x12 [d:\reactos\src\dll\win32\kernel32\client\loader.c @ 181] 0021f808 77a4f6f4 0021f944 77a4e000 19650412 user32!IntInitializeImmEntryTable+0xe8 [d:\reactos\src\win32ss\user\user32\misc\imm.c @ 86] 0021f810 77a4e000 19650412 00050000 00000000 user32!InitializeImmEntryTable+0x14 [d:\reactos\src\win32ss\user\user32\misc\imm.c @ 188] 0021f944 7c928774 77a20000 00000001 00000000 user32!DllMain+0xa0 [d:\reactos\src\win32ss\user\user32\misc\dllmain.c @ 536] 0021f958 7c925194 77a4df60 77a20000 00000001 ntdll!LdrpCallInitRoutine+0x14 [d:\reactos\src\dll\ntdll\ldr\ldrutils.c @ 217] 0021fa0c 7c928f6d 00000000 00690077 0073006e ntdll!LdrpRunInitializeRoutines+0x434 [d:\reactos\src\dll\ntdll\ldr\ldrinit.c @ 814] 0021fc50 7c9235b2 00000000 00000000 00000000 ntdll!LdrpLoadDll+0x30d [d:\reactos\src\dll\ntdll\ldr\ldrutils.c @ 2580] 0021fe9c 10005b87 00000000 00000000 0021fecc ntdll!LdrLoadDll+0x282 [d:\reactos\src\dll\ntdll\ldr\ldrapi.c @ 400] 0021fefc 1000448b 002209eb 002209f2 00000003 csrsrv!CsrLoadServerDll+0xa7 [d:\reactos\src\subsystems\win32\csrsrv\server.c @ 147] 0021ff5c 10002ba8 0000000a 00220f48 00000000 csrsrv!CsrParseServerCommandLine+0x4fb [d:\reactos\src\subsystems\win32\csrsrv\init.c @ 683] 0021ff70 004010a1 0000000a 00220f48 0000000d csrsrv!CsrServerInitialization+0x188 [d:\reactos\src\subsystems\win32\csrsrv\init.c @ 1013] 0021ff88 00401579 0000000a 00220f48 00220f74 csrss!_main+0x81 [d:\reactos\src\subsystems\win32\csrss\csrss.c @ 75] 0021fff4 00000000 7ffdf000 ec0100ed 00000000 csrss!NtProcessStartup+0x399 [d:\reactos\src\lib\sdk\nt\entry_point.c @ 199]
It seems to attempt to load a cursor on IMM_RegisterIMEClass
which seems to cause problems in gdi code.
Perhaps IMM_RegisterIMEClass
may not need to be called there?
Attempted to sync Wine 1.7.37 (CORE-9685), seems to not crash anymore.