Difference between revisions of "User:Alvinhochun/Localization/IME/IMM"

From ReactOS Wiki
Jump to: navigation, search
(ReactOS)
Line 1: Line 1:
Operations of IMM under XP.
+
Operations of IMM.
  
== Loading of <code>imm32.dll</code> ==
+
== 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.
 +
 
 +
=== Documented Functions ===
 +
==== <code>ImmGetContext</code> ====
 +
[https://msdn.microsoft.com/en-us/library/windows/desktop/dd318558%28v=vs.85%29.aspx <code>HIMC WINAPI ImmGetContext(HWND hWnd)</code>]
 +
 
 +
"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|<code>ImmGetSaveContext(hWnd, 2)</code>]].
 +
 
 +
==== <code>ImmReleaseContext</code> ====
 +
[https://msdn.microsoft.com/en-us/library/windows/desktop/dd318576(v=vs.85).aspx <code>BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)</code>]
 +
 
 +
Always returns <code>TRUE</code>. (Just checked that even Windows 7 does this too.)
 +
 
 +
=== Undocumented Functions ===
 +
==== <code>ImmRegisterClient</code> ====
 +
<code>int WINAPI ImmRegisterClient(_some_struct(size 0x3E) *arg1, HINSTANCE arg2);</code>
 +
 
 +
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.
 +
 
 +
==== <code>ImmInitializeGlobals</code> ====
 +
<code>int ImmInitializeGlobals(HINSTANCE arg1);</code>
 +
 
 +
* 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
 +
 
 +
==== <code>ImmGetSaveContext</code> ====
 +
<code>HIMC __stdcall ImmGetSaveContext(HWND hWnd, char arg2);</code>
 +
 
 +
* 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 IMM context. Also, debugging notepad shows that ImmCreateContext is never called to create them.
 +
 
 +
== Loading of <code>imm32.dll</code> (XP) ==
 
IMM32 is loaded by <code>user32.dll</code> from <code>_InitializeImmEntryTable</code>. It loads the library (if not already) and fill in a structure of function pointers <code>gImmApiEntries</code>. This function is supposed to be called in the DllMain of USER32 but the relevant code is currently commented out.
 
IMM32 is loaded by <code>user32.dll</code> from <code>_InitializeImmEntryTable</code>. It loads the library (if not already) and fill in a structure of function pointers <code>gImmApiEntries</code>. This function is supposed to be called in the DllMain of USER32 but the relevant code is currently commented out.
  

Revision as of 09:02, 12 May 2015

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.

Documented Functions

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 IMM context. Also, debugging notepad shows that ImmCreateContext is never called to create them.

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.