User:Alvinhochun/Localization/IME/IMM

From ReactOS Wiki
Jump to: navigation, search

Operations of IMM.

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* Same name for WinCE5
ImmCreateSoftKeyboard No
ImmDestroyContext Yes
BOOL ImmDestroyContext(
  _In_ HIMC hIMC
);
ImmDestroyIMCC No* Same name for WinCE5
ImmDestroySoftKeyboard No
ImmDisableIME Yes
BOOL ImmDisableIME(
  _In_ DWORD idThread
);
ImmDisableIme ? 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* Same name for WinCE5
ImmGetIMCCLockCount No* Same name for WinCE5
ImmGetIMCCSize No* Same name for WinCE5
ImmGetIMCLockCount No* Same name for WinCE5
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* Same name for WinCE5
ImmLockIMCC No* Same name for WinCE5
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* Same name for WinCE5
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* Same name for WinCE5
ImmUnlockIMCC No* Same name for WinCE5
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
    • imm32!ImmRegisterClient
      • imm32!ImmInitializeGlobals

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.


Got most of the imm32 winetest running after hacks, but the test ends prematurely at line 1008 (on my local tree).