[IMM32] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / dll / win32 / imm32 / imm.c
index 78b2b90..86dfa2e 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#define WIN32_NO_STATUS
-
-//#include <stdarg.h>
+#include <stdarg.h>
 #include <stdio.h>
 
-#include <windef.h>
-#include <winbase.h>
-//#include "wingdi.h"
-#include <winuser.h>
-//#include "winerror.h"
-#include <wine/debug.h>
-//#include "imm.h"
-#include <ddk/imm.h>
-#include <winnls.h>
-#include <winreg.h>
-#include <wine/list.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "imm.h"
+#include "ddk/imm.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "wine/list.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(imm);
 
-#define MAKE_FUNCPTR(f) typeof(f) * p##f
+#define IMM_INIT_MAGIC 0x19650412
+BOOL WINAPI User32InitializeImmEntryTable(DWORD);
+
 typedef struct _tagImmHkl{
     struct list entry;
     HKL         hkl;
@@ -49,34 +49,38 @@ typedef struct _tagImmHkl{
     HWND        UIWnd;
 
     /* Function Pointers */
-    MAKE_FUNCPTR(ImeInquire);
-    MAKE_FUNCPTR(ImeConfigure);
-    MAKE_FUNCPTR(ImeDestroy);
-    MAKE_FUNCPTR(ImeEscape);
-    MAKE_FUNCPTR(ImeSelect);
-    MAKE_FUNCPTR(ImeSetActiveContext);
-    MAKE_FUNCPTR(ImeToAsciiEx);
-    MAKE_FUNCPTR(NotifyIME);
-    MAKE_FUNCPTR(ImeRegisterWord);
-    MAKE_FUNCPTR(ImeUnregisterWord);
-    MAKE_FUNCPTR(ImeEnumRegisterWord);
-    MAKE_FUNCPTR(ImeSetCompositionString);
-    MAKE_FUNCPTR(ImeConversionList);
-    MAKE_FUNCPTR(ImeProcessKey);
-    MAKE_FUNCPTR(ImeGetRegisterWordStyle);
-    MAKE_FUNCPTR(ImeGetImeMenuItems);
+    BOOL (WINAPI *pImeInquire)(IMEINFO *, WCHAR *, const WCHAR *);
+    BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *);
+    BOOL (WINAPI *pImeDestroy)(UINT);
+    LRESULT (WINAPI *pImeEscape)(HIMC, UINT, void *);
+    BOOL (WINAPI *pImeSelect)(HIMC, BOOL);
+    BOOL (WINAPI *pImeSetActiveContext)(HIMC, BOOL);
+    UINT (WINAPI *pImeToAsciiEx)(UINT, UINT, const BYTE *, DWORD *, UINT, HIMC);
+    BOOL (WINAPI *pNotifyIME)(HIMC, DWORD, DWORD, DWORD);
+    BOOL (WINAPI *pImeRegisterWord)(const WCHAR *, DWORD, const WCHAR *);
+    BOOL (WINAPI *pImeUnregisterWord)(const WCHAR *, DWORD, const WCHAR *);
+    UINT (WINAPI *pImeEnumRegisterWord)(REGISTERWORDENUMPROCW, const WCHAR *, DWORD, const WCHAR *, void *);
+    BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void *, DWORD, const void *, DWORD);
+    DWORD (WINAPI *pImeConversionList)(HIMC, const WCHAR *, CANDIDATELIST *, DWORD, UINT);
+    BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE *);
+    UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, STYLEBUFW *);
+    DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, IMEMENUITEMINFOW *, IMEMENUITEMINFOW *, DWORD);
 } ImmHkl;
-#undef MAKE_FUNCPTR
 
 typedef struct tagInputContextData
 {
         DWORD           dwLock;
         INPUTCONTEXT    IMC;
+        DWORD           threadID;
 
         ImmHkl          *immKbd;
         UINT            lastVK;
+        BOOL            threadDefault;
+        DWORD           magic;
 } InputContextData;
 
+#define WINE_IMC_VALID_MAGIC 0x56434D49
+
 typedef struct _tagTRANSMSG {
     UINT message;
     WPARAM wParam;
@@ -84,21 +88,16 @@ typedef struct _tagTRANSMSG {
 } TRANSMSG, *LPTRANSMSG;
 
 typedef struct _tagIMMThreadData {
+    struct list entry;
+    DWORD threadID;
     HIMC defaultContext;
     HWND hwndDefault;
+    BOOL disableIME;
+    DWORD windowRefs;
 } IMMThreadData;
 
-static DWORD tlsIndex = 0;
 static struct list ImmHklList = LIST_INIT(ImmHklList);
-
-/* MSIME messages */
-static UINT WM_MSIME_SERVICE;
-static UINT WM_MSIME_RECONVERTOPTIONS;
-static UINT WM_MSIME_MOUSE;
-static UINT WM_MSIME_RECONVERTREQUEST;
-static UINT WM_MSIME_RECONVERT;
-static UINT WM_MSIME_QUERYPOSITION;
-static UINT WM_MSIME_DOCUMENTFEED;
+static struct list ImmThreadDataList = LIST_INIT(ImmThreadDataList);
 
 static const WCHAR szwWineIMCProperty[] = {'W','i','n','e','I','m','m','H','I','M','C','P','r','o','p','e','r','t','y',0};
 
@@ -107,14 +106,30 @@ static const WCHAR szLayoutTextW[] = {'L','a','y','o','u','t',' ','T','e','x','t
 static const WCHAR szImeRegFmt[] = {'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','K','e','y','b','o','a','r','d',' ','L','a','y','o','u','t','s','\\','%','0','8','l','x',0};
 
 static const WCHAR szwIME[] = {'I','M','E',0};
+static const WCHAR szwDefaultIME[] = {'D','e','f','a','u','l','t',' ','I','M','E',0};
 
-static LRESULT WINAPI DefIME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
-                                        LPARAM lParam);
+static CRITICAL_SECTION threaddata_cs;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+    0, 0, &threaddata_cs,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": threaddata_cs") }
+};
+static CRITICAL_SECTION threaddata_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
+static BOOL disable_ime;
 
-#define is_himc_ime_unicode(p)  (p->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE)
-#define is_kbd_ime_unicode(p)  (p->imeInfo.fdwProperty & IME_PROP_UNICODE)
+static inline BOOL is_himc_ime_unicode(const InputContextData *data)
+{
+    return !!(data->immKbd->imeInfo.fdwProperty & IME_PROP_UNICODE);
+}
+
+static inline BOOL is_kbd_ime_unicode(const ImmHkl *hkl)
+{
+    return !!(hkl->imeInfo.fdwProperty & IME_PROP_UNICODE);
+}
 
 static BOOL IMM_DestroyContext(HIMC hIMC);
+static InputContextData* get_imc_data(HIMC hIMC);
 
 static inline WCHAR *strdupAtoW( const char *str )
 {
@@ -218,60 +233,98 @@ static DWORD convert_candidatelist_AtoW(
     return ret;
 }
 
-static IMMThreadData* IMM_GetThreadData(void)
+static IMMThreadData *IMM_GetThreadData(HWND hwnd, DWORD thread)
 {
-    IMMThreadData* data = TlsGetValue(tlsIndex);
-    if (!data)
+    IMMThreadData *data;
+    DWORD process;
+
+    if (hwnd)
+    {
+        if (!(thread = GetWindowThreadProcessId(hwnd, &process))) return NULL;
+        if (process != GetCurrentProcessId()) return NULL;
+    }
+    else if (thread)
     {
-        data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
-                         sizeof(IMMThreadData));
-        TlsSetValue(tlsIndex,data);
-        TRACE("Thread Data Created\n");
+        HANDLE h = OpenThread(THREAD_QUERY_INFORMATION, FALSE, thread);
+        if (!h) return NULL;
+        process = GetProcessIdOfThread(h);
+        CloseHandle(h);
+        if (process != GetCurrentProcessId()) return NULL;
     }
+    else
+        thread = GetCurrentThreadId();
+
+    EnterCriticalSection(&threaddata_cs);
+    LIST_FOR_EACH_ENTRY(data, &ImmThreadDataList, IMMThreadData, entry)
+        if (data->threadID == thread) return data;
+
+    data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data));
+    data->threadID = thread;
+    list_add_head(&ImmThreadDataList,&data->entry);
+    TRACE("Thread Data Created (%x)\n",thread);
     return data;
 }
 
-static void IMM_FreeThreadData(void)
+static BOOL IMM_IsDefaultContext(HIMC imc)
 {
-    IMMThreadData* data = TlsGetValue(tlsIndex);
-    if (data)
-    {
-        IMM_DestroyContext(data->defaultContext);
-        DestroyWindow(data->hwndDefault);
-        HeapFree(GetProcessHeap(),0,data);
-        TRACE("Thread Data Destroyed\n");
-    }
+    InputContextData *data = get_imc_data(imc);
+
+    if (!data)
+        return FALSE;
+
+    return data->threadDefault;
 }
 
-static HMODULE LoadDefaultWineIME(void)
+static void IMM_FreeThreadData(void)
 {
-    char buffer[MAX_PATH], libname[32], *name, *next;
-    HMODULE module = 0;
-    HKEY hkey;
+    IMMThreadData *data;
 
-    TRACE("Attempting to fall back to wine default IME\n");
-
-    strcpy( buffer, "x11" );  /* default value */
-    /* @@ Wine registry key: HKCU\Software\Wine\Drivers */
-    if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Drivers", &hkey ))
+    EnterCriticalSection(&threaddata_cs);
+    LIST_FOR_EACH_ENTRY(data, &ImmThreadDataList, IMMThreadData, entry)
     {
-        DWORD type, count = sizeof(buffer);
-        RegQueryValueExA( hkey, "Ime", 0, &type, (LPBYTE) buffer, &count );
-        RegCloseKey( hkey );
+        if (data->threadID == GetCurrentThreadId())
+        {
+            list_remove(&data->entry);
+            LeaveCriticalSection(&threaddata_cs);
+            IMM_DestroyContext(data->defaultContext);
+            HeapFree(GetProcessHeap(),0,data);
+            TRACE("Thread Data Destroyed\n");
+            return;
+        }
     }
+    LeaveCriticalSection(&threaddata_cs);
+}
 
-    name = buffer;
-    while (name)
-    {
-        next = strchr( name, ',' );
-        if (next) *next++ = 0;
-
-        snprintf( libname, sizeof(libname), "wine%s.drv", name );
-        if ((module = LoadLibraryA( libname )) != 0) break;
-        name = next;
-    }
+static HMODULE load_graphics_driver(void)
+{
+    static const WCHAR display_device_guid_propW[] = {
+        '_','_','w','i','n','e','_','d','i','s','p','l','a','y','_',
+        'd','e','v','i','c','e','_','g','u','i','d',0 };
+    static const WCHAR key_pathW[] = {
+        'S','y','s','t','e','m','\\',
+        'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+        'C','o','n','t','r','o','l','\\',
+        'V','i','d','e','o','\\','{',0};
+    static const WCHAR displayW[] = {'}','\\','0','0','0','0',0};
+    static const WCHAR driverW[] = {'G','r','a','p','h','i','c','s','D','r','i','v','e','r',0};
 
-    return module;
+    HMODULE ret = 0;
+    HKEY hkey;
+    DWORD size;
+    WCHAR path[MAX_PATH];
+    WCHAR key[ARRAY_SIZE( key_pathW ) + ARRAY_SIZE( displayW ) + 40];
+    UINT guid_atom = HandleToULong( GetPropW( GetDesktopWindow(), display_device_guid_propW ));
+
+    if (!guid_atom) return 0;
+    memcpy( key, key_pathW, sizeof(key_pathW) );
+    if (!GlobalGetAtomNameW( guid_atom, key + lstrlenW(key), 40 )) return 0;
+    lstrcatW( key, displayW );
+    if (RegOpenKeyW( HKEY_LOCAL_MACHINE, key, &hkey )) return 0;
+    size = sizeof(path);
+    if (!RegQueryValueExW( hkey, driverW, NULL, NULL, (BYTE *)path, &size )) ret = LoadLibraryW( path );
+    RegCloseKey( hkey );
+    TRACE( "%s %p\n", debugstr_w(path), ret );
+    return ret;
 }
 
 /* ImmHkl loading and freeing */
@@ -294,8 +347,7 @@ static ImmHkl *IMM_GetImmHkl(HKL hkl)
 
     ptr->hkl = hkl;
     if (ImmGetIMEFileNameW(hkl, filename, MAX_PATH)) ptr->hIME = LoadLibraryW(filename);
-    if (!ptr->hIME)
-        ptr->hIME = LoadDefaultWineIME();
+    if (!ptr->hIME) ptr->hIME = load_graphics_driver();
     if (ptr->hIME)
     {
         LOAD_FUNCPTR(ImeInquire);
@@ -345,6 +397,12 @@ static ImmHkl *IMM_GetImmHkl(HKL hkl)
 }
 #undef LOAD_FUNCPTR
 
+HWND WINAPI __wine_get_ui_window(HKL hkl)
+{
+    ImmHkl *immHkl = IMM_GetImmHkl(hkl);
+    return immHkl->UIWnd;
+}
+
 static void IMM_FreeAllImmHkl(void)
 {
     ImmHkl *ptr,*cursor2;
@@ -363,43 +421,16 @@ static void IMM_FreeAllImmHkl(void)
     }
 }
 
-static void IMM_RegisterMessages(void)
-{
-    WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService");
-    WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions");
-    WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation");
-    WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest");
-    WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert");
-    WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition");
-    WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed");
-}
-
-static void IMM_RegisterIMEClass(void)
-{
-    WNDCLASSW wndClass;
-
-    ZeroMemory(&wndClass, sizeof(WNDCLASSW));
-
-    wndClass.style = CS_GLOBALCLASS;
-    wndClass.lpfnWndProc = (WNDPROC) DefIME_WindowProc;
-    wndClass.cbWndExtra = 2 * sizeof(LONG_PTR);
-    wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
-    wndClass.lpszClassName = szwIME;
-
-    RegisterClassW(&wndClass);
-}
-
 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved)
 {
     TRACE("%p, %x, %p\n",hInstDLL,fdwReason,lpReserved);
     switch (fdwReason)
     {
         case DLL_PROCESS_ATTACH:
-            IMM_RegisterMessages();
-            tlsIndex = TlsAlloc();
-            if (tlsIndex == TLS_OUT_OF_INDEXES)
+            if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC))
+            {
                 return FALSE;
-            IMM_RegisterIMEClass();
+            }
             break;
         case DLL_THREAD_ATTACH:
             break;
@@ -410,8 +441,6 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved)
             if (lpReserved) break;
             IMM_FreeThreadData();
             IMM_FreeAllImmHkl();
-            TlsFree(tlsIndex);
-            UnregisterClassW(szwIME, NULL);
             break;
     }
     return TRUE;
@@ -427,6 +456,16 @@ static void ImmInternalPostIMEMessage(InputContextData *data, UINT msg, WPARAM w
        PostMessageW(target, msg, wParam, lParam);
 }
 
+/* for sending messages as the IME */
+static void ImmInternalSendIMEMessage(InputContextData *data, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    HWND target = GetFocus();
+    if (!target)
+       SendMessageW(data->IMC.hWnd,msg,wParam,lParam);
+    else
+       SendMessageW(target, msg, wParam, lParam);
+}
+
 static LRESULT ImmInternalSendIMENotify(InputContextData *data, WPARAM notify, LPARAM lParam)
 {
     HWND target;
@@ -452,18 +491,90 @@ static HIMCC ImmCreateBlankCompStr(void)
     return rc;
 }
 
+static InputContextData* get_imc_data(HIMC hIMC)
+{
+    InputContextData *data = hIMC;
+
+    if (hIMC == NULL)
+        return NULL;
+
+    if(IsBadReadPtr(data, sizeof(InputContextData)) || data->magic != WINE_IMC_VALID_MAGIC)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return NULL;
+    }
+    return data;
+}
+
+static HIMC get_default_context( HWND hwnd )
+{
+    HIMC ret;
+    IMMThreadData* thread_data = IMM_GetThreadData( hwnd, 0 );
+
+    if (!thread_data) return 0;
+
+    if (thread_data->defaultContext)
+    {
+        ret = thread_data->defaultContext;
+        LeaveCriticalSection(&threaddata_cs);
+        return ret;
+    }
+
+    /* can't create a default context in another thread */
+    if (thread_data->threadID != GetCurrentThreadId())
+    {
+        LeaveCriticalSection(&threaddata_cs);
+        return 0;
+    }
+
+    LeaveCriticalSection(&threaddata_cs);
+
+    ret = ImmCreateContext();
+    if (!ret) return 0;
+    ((InputContextData*)ret)->threadDefault = TRUE;
+
+    /* thread_data is in the current thread so we can assume it's still valid */
+    EnterCriticalSection(&threaddata_cs);
+
+    if (thread_data->defaultContext) /* someone beat us */
+    {
+        IMM_DestroyContext( ret );
+        ret = thread_data->defaultContext;
+    }
+    else thread_data->defaultContext = ret;
+
+    LeaveCriticalSection(&threaddata_cs);
+    return ret;
+}
+
+static BOOL IMM_IsCrossThreadAccess(HWND hWnd,  HIMC hIMC)
+{
+    InputContextData *data;
+
+    if (hWnd)
+    {
+        DWORD thread = GetWindowThreadProcessId(hWnd, NULL);
+        if (thread != GetCurrentThreadId()) return TRUE;
+    }
+    data = get_imc_data(hIMC);
+    if (data && data->threadID != GetCurrentThreadId())
+        return TRUE;
+
+    return FALSE;
+}
+
 /***********************************************************************
  *             ImmAssociateContext (IMM32.@)
  */
 HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
 {
     HIMC old = NULL;
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %p):\n", hWnd, hIMC);
 
-    if (!IMM_GetThreadData()->defaultContext)
-        IMM_GetThreadData()->defaultContext = ImmCreateContext();
+    if(hIMC && !data)
+        return NULL;
 
     /*
      * If already associated just return
@@ -471,16 +582,20 @@ HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
     if (hIMC && data->IMC.hWnd == hWnd)
         return hIMC;
 
+    if (hIMC && IMM_IsCrossThreadAccess(hWnd, hIMC))
+        return NULL;
+
     if (hWnd)
     {
+        HIMC defaultContext = get_default_context( hWnd );
         old = RemovePropW(hWnd,szwWineIMCProperty);
 
         if (old == NULL)
-            old = IMM_GetThreadData()->defaultContext;
+            old = defaultContext;
         else if (old == (HIMC)-1)
             old = NULL;
 
-        if (hIMC != IMM_GetThreadData()->defaultContext)
+        if (hIMC != defaultContext)
         {
             if (hIMC == NULL) /* Meaning disable imm for that window*/
                 SetPropW(hWnd,szwWineIMCProperty,(HANDLE)-1);
@@ -499,21 +614,10 @@ HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
     if (!hIMC)
         return old;
 
-    if (IsWindow(data->IMC.hWnd))
+    if(GetActiveWindow() == data->IMC.hWnd)
     {
-        /*
-         * Post a message that your context is switching
-         */
         SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, FALSE, ISC_SHOWUIALL);
-    }
-
-    data->IMC.hWnd = hWnd;
-
-    if (IsWindow(data->IMC.hWnd))
-    {
-        /*
-         * Post a message that your context is switching
-         */
+        data->IMC.hWnd = hWnd;
         SendMessageW(data->IMC.hWnd, WM_IME_SETCONTEXT, TRUE, ISC_SHOWUIALL);
     }
 
@@ -538,10 +642,8 @@ BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
 {
     TRACE("(%p, %p, 0x%x):\n", hWnd, hIMC, dwFlags);
 
-    if (!IMM_GetThreadData()->defaultContext)
-        IMM_GetThreadData()->defaultContext = ImmCreateContext();
-
-    if (!hWnd) return FALSE;
+    if (!hWnd)
+        return FALSE;
 
     switch (dwFlags)
     {
@@ -549,8 +651,12 @@ BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
         ImmAssociateContext(hWnd,hIMC);
         return TRUE;
     case IACE_DEFAULT:
-        ImmAssociateContext(hWnd,IMM_GetThreadData()->defaultContext);
+    {
+        HIMC defaultContext = get_default_context( hWnd );
+        if (!defaultContext) return FALSE;
+        ImmAssociateContext(hWnd,defaultContext);
         return TRUE;
+    }
     case IACE_IGNORENOCONTEXT:
         if (GetPropW(hWnd,szwWineIMCProperty))
             ImmAssociateContext(hWnd,hIMC);
@@ -642,6 +748,7 @@ HIMC WINAPI ImmCreateContext(void)
     InputContextData *new_context;
     LPGUIDELINE gl;
     LPCANDIDATEINFO ci;
+    int i;
 
     new_context = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(InputContextData));
 
@@ -669,43 +776,53 @@ HIMC WINAPI ImmCreateContext(void)
     gl->dwSize = sizeof(GUIDELINE);
     ImmUnlockIMCC(new_context->IMC.hGuideLine);
 
+    for (i = 0; i < ARRAY_SIZE(new_context->IMC.cfCandForm); i++)
+        new_context->IMC.cfCandForm[i].dwIndex = ~0u;
+
     /* Initialize the IME Private */
     new_context->IMC.hPrivate = ImmCreateIMCC(new_context->immKbd->imeInfo.dwPrivateDataSize);
 
+    new_context->IMC.fdwConversion = new_context->immKbd->imeInfo.fdwConversionCaps;
+    new_context->IMC.fdwSentence = new_context->immKbd->imeInfo.fdwSentenceCaps;
+
     if (!new_context->immKbd->pImeSelect(new_context, TRUE))
     {
         TRACE("Selection of IME failed\n");
         IMM_DestroyContext(new_context);
         return 0;
     }
-    SendMessageW(GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)GetKeyboardLayout(0));
+    new_context->threadID = GetCurrentThreadId();
+    SendMessageW(GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)new_context->immKbd);
 
     new_context->immKbd->uSelected++;
     TRACE("Created context %p\n",new_context);
 
+    new_context->magic = WINE_IMC_VALID_MAGIC;
     return new_context;
 }
 
 static BOOL IMM_DestroyContext(HIMC hIMC)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("Destroying %p\n",hIMC);
 
-    if (hIMC)
-    {
-        data->immKbd->uSelected --;
-        data->immKbd->pImeSelect(hIMC, FALSE);
-        SendMessageW(data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)GetKeyboardLayout(0));
+    if (!data)
+        return FALSE;
 
-        ImmDestroyIMCC(data->IMC.hCompStr);
-        ImmDestroyIMCC(data->IMC.hCandInfo);
-        ImmDestroyIMCC(data->IMC.hGuideLine);
-        ImmDestroyIMCC(data->IMC.hPrivate);
-        ImmDestroyIMCC(data->IMC.hMsgBuf);
+    data->immKbd->uSelected --;
+    data->immKbd->pImeSelect(hIMC, FALSE);
+    SendMessageW(data->IMC.hWnd, WM_IME_SELECT, FALSE, (LPARAM)data->immKbd);
+
+    ImmDestroyIMCC(data->IMC.hCompStr);
+    ImmDestroyIMCC(data->IMC.hCandInfo);
+    ImmDestroyIMCC(data->IMC.hGuideLine);
+    ImmDestroyIMCC(data->IMC.hPrivate);
+    ImmDestroyIMCC(data->IMC.hMsgBuf);
+
+    data->magic = 0;
+    HeapFree(GetProcessHeap(),0,data);
 
-        HeapFree(GetProcessHeap(),0,data);
-    }
     return TRUE;
 }
 
@@ -714,7 +831,7 @@ static BOOL IMM_DestroyContext(HIMC hIMC)
  */
 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
 {
-    if (hIMC != IMM_GetThreadData()->defaultContext)
+    if (!IMM_IsDefaultContext(hIMC) && !IMM_IsCrossThreadAccess(NULL, hIMC))
         return IMM_DestroyContext(hIMC);
     else
         return FALSE;
@@ -725,7 +842,14 @@ BOOL WINAPI ImmDestroyContext(HIMC hIMC)
  */
 BOOL WINAPI ImmDisableIME(DWORD idThread)
 {
-    FIXME("(%d): stub\n", idThread);
+    if (idThread == (DWORD)-1)
+        disable_ime = TRUE;
+    else {
+        IMMThreadData *thread_data = IMM_GetThreadData(NULL, idThread);
+        if (!thread_data) return FALSE;
+        thread_data->disableIME = TRUE;
+        LeaveCriticalSection(&threaddata_cs);
+    }
     return TRUE;
 }
 
@@ -885,7 +1009,7 @@ DWORD WINAPI ImmGetCandidateListA(
   HIMC hIMC, DWORD dwIndex,
   LPCANDIDATELIST lpCandList, DWORD dwBufLen)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     LPCANDIDATEINFO candinfo;
     LPCANDIDATELIST candlist;
     DWORD ret = 0;
@@ -896,8 +1020,7 @@ DWORD WINAPI ImmGetCandidateListA(
        return 0;
 
     candinfo = ImmLockIMCC(data->IMC.hCandInfo);
-    if ( dwIndex >= candinfo->dwCount ||
-         dwIndex >= (sizeof(candinfo->dwOffset) / sizeof(DWORD)) )
+    if (dwIndex >= candinfo->dwCount || dwIndex >= ARRAY_SIZE(candinfo->dwOffset))
         goto done;
 
     candlist = (LPCANDIDATELIST)((LPBYTE)candinfo + candinfo->dwOffset[dwIndex]);
@@ -924,7 +1047,7 @@ done:
 DWORD WINAPI ImmGetCandidateListCountA(
   HIMC hIMC, LPDWORD lpdwListCount)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     LPCANDIDATEINFO candinfo;
     DWORD ret, count;
 
@@ -956,7 +1079,7 @@ DWORD WINAPI ImmGetCandidateListCountA(
 DWORD WINAPI ImmGetCandidateListCountW(
   HIMC hIMC, LPDWORD lpdwListCount)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     LPCANDIDATEINFO candinfo;
     DWORD ret, count;
 
@@ -989,7 +1112,7 @@ DWORD WINAPI ImmGetCandidateListW(
   HIMC hIMC, DWORD dwIndex,
   LPCANDIDATELIST lpCandList, DWORD dwBufLen)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     LPCANDIDATEINFO candinfo;
     LPCANDIDATELIST candlist;
     DWORD ret = 0;
@@ -1000,8 +1123,7 @@ DWORD WINAPI ImmGetCandidateListW(
        return 0;
 
     candinfo = ImmLockIMCC(data->IMC.hCandInfo);
-    if ( dwIndex >= candinfo->dwCount ||
-         dwIndex >= (sizeof(candinfo->dwOffset) / sizeof(DWORD)) )
+    if (dwIndex >= candinfo->dwCount || dwIndex >= ARRAY_SIZE(candinfo->dwOffset))
         goto done;
 
     candlist = (LPCANDIDATELIST)((LPBYTE)candinfo + candinfo->dwOffset[dwIndex]);
@@ -1028,14 +1150,17 @@ done:
 BOOL WINAPI ImmGetCandidateWindow(
   HIMC hIMC, DWORD dwIndex, LPCANDIDATEFORM lpCandidate)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("%p, %d, %p\n", hIMC, dwIndex, lpCandidate);
 
     if (!data || !lpCandidate)
         return FALSE;
 
-    if ( dwIndex >= (sizeof(data->IMC.cfCandForm) / sizeof(CANDIDATEFORM)) )
+    if (dwIndex >= ARRAY_SIZE(data->IMC.cfCandForm))
+        return FALSE;
+
+    if (data->IMC.cfCandForm[dwIndex].dwIndex != dwIndex)
         return FALSE;
 
     *lpCandidate = data->IMC.cfCandForm[dwIndex];
@@ -1068,7 +1193,7 @@ BOOL WINAPI ImmGetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf)
  */
 BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %p):\n", hIMC, lplf);
 
@@ -1083,52 +1208,70 @@ BOOL WINAPI ImmGetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf)
 
 /* Helpers for the GetCompositionString functions */
 
-static INT CopyCompStringIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE target, INT tlen,
-                                     BOOL unicode )
+/* Source encoding is defined by context, source length is always given in respective characters. Destination buffer
+   length is always in bytes. */
+static INT CopyCompStringIMEtoClient(const InputContextData *data, const void *src, INT src_len, void *dst,
+        INT dst_len, BOOL unicode)
 {
-    INT rc;
+    int char_size = unicode ? sizeof(WCHAR) : sizeof(char);
+    INT ret;
 
-    if (is_himc_ime_unicode(data) && !unicode)
-        rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)source, slen, (LPSTR)target, tlen, NULL, NULL);
-    else if (!is_himc_ime_unicode(data) && unicode)
-        rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)source, slen, (LPWSTR)target, tlen) * sizeof(WCHAR);
+    if (is_himc_ime_unicode(data) ^ unicode)
+    {
+        if (unicode)
+            ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR));
+        else
+            ret = WideCharToMultiByte(CP_ACP, 0, src, src_len, dst, dst_len, NULL, NULL);
+        ret *= char_size;
+    }
     else
     {
-        int dlen = (unicode)?sizeof(WCHAR):sizeof(CHAR);
-        memcpy( target, source, min(slen,tlen)*dlen);
-        rc = slen*dlen;
+        if (dst_len)
+        {
+            ret = min(src_len * char_size, dst_len);
+            memcpy(dst, src, ret);
+        }
+        else
+            ret = src_len * char_size;
     }
 
-    return rc;
+    return ret;
 }
 
-static INT CopyCompAttrIMEtoClient(InputContextData *data, LPBYTE source, INT slen, LPBYTE ssource, INT sslen,
-                                   LPBYTE target, INT tlen, BOOL unicode )
+/* Composition string encoding is defined by context, returned attributes correspond to string, converted according to
+   passed mode. String length is in characters, attributes are in byte arrays. */
+static INT CopyCompAttrIMEtoClient(const InputContextData *data, const BYTE *src, INT src_len, const void *comp_string,
+        INT str_len, BYTE *dst, INT dst_len, BOOL unicode)
 {
+    union
+    {
+        const void *str;
+        const WCHAR *strW;
+        const char *strA;
+    } string;
     INT rc;
 
+    string.str = comp_string;
+
     if (is_himc_ime_unicode(data) && !unicode)
     {
-        rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, sslen, NULL, 0, NULL, NULL);
-        if (tlen)
+        rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL);
+        if (dst_len)
         {
-            const BYTE *src = source;
-            LPBYTE dst = target;
             int i, j = 0, k = 0;
 
-            if (rc < tlen)
-                tlen = rc;
-            for (i = 0; i < sslen; ++i)
+            if (rc < dst_len)
+                dst_len = rc;
+            for (i = 0; i < str_len; ++i)
             {
                 int len;
 
-                len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)ssource + i, 1,
-                                          NULL, 0, NULL, NULL);
+                len = WideCharToMultiByte(CP_ACP, 0, string.strW + i, 1, NULL, 0, NULL, NULL);
                 for (; len > 0; --len)
                 {
                     dst[j++] = src[k];
 
-                    if (j >= tlen)
+                    if (j >= dst_len)
                         goto end;
                 }
                 ++k;
@@ -1139,23 +1282,21 @@ static INT CopyCompAttrIMEtoClient(InputContextData *data, LPBYTE source, INT sl
     }
     else if (!is_himc_ime_unicode(data) && unicode)
     {
-        rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, sslen, NULL, 0);
-        if (tlen)
+        rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0);
+        if (dst_len)
         {
-            const BYTE *src = source;
-            LPBYTE dst = target;
             int i, j = 0;
 
-            if (rc < tlen)
-                tlen = rc;
-            for (i = 0; i < sslen; ++i)
+            if (rc < dst_len)
+                dst_len = rc;
+            for (i = 0; i < str_len; ++i)
             {
-                if (IsDBCSLeadByte(((LPSTR)ssource)[i]))
+                if (IsDBCSLeadByte(string.strA[i]))
                     continue;
 
                 dst[j++] = src[i];
 
-                if (j >= tlen)
+                if (j >= dst_len)
                     break;
             }
             rc = j;
@@ -1163,8 +1304,8 @@ static INT CopyCompAttrIMEtoClient(InputContextData *data, LPBYTE source, INT sl
     }
     else
     {
-        memcpy( target, source, min(slen,tlen));
-        rc = slen;
+        memcpy(dst, src, min(src_len, dst_len));
+        rc = src_len;
     }
 
     return rc;
@@ -1247,7 +1388,7 @@ static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf,
                                       DWORD dwBufLen, BOOL unicode)
 {
     LONG rc = 0;
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     LPCOMPOSITIONSTRING compstr;
     LPBYTE compdata;
 
@@ -1359,7 +1500,7 @@ LONG WINAPI ImmGetCompositionStringW(
  */
 BOOL WINAPI ImmGetCompositionWindow(HIMC hIMC, LPCOMPOSITIONFORM lpCompForm)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %p)\n", hIMC, lpCompForm);
 
@@ -1367,7 +1508,7 @@ BOOL WINAPI ImmGetCompositionWindow(HIMC hIMC, LPCOMPOSITIONFORM lpCompForm)
         return FALSE;
 
     *lpCompForm = data->IMC.cfCompForm;
-    return 1;
+    return TRUE;
 }
 
 /***********************************************************************
@@ -1385,20 +1526,19 @@ HIMC WINAPI ImmGetContext(HWND hWnd)
         SetLastError(ERROR_INVALID_WINDOW_HANDLE);
         return NULL;
     }
-    if (!IMM_GetThreadData()->defaultContext)
-        IMM_GetThreadData()->defaultContext = ImmCreateContext();
 
     rc = GetPropW(hWnd,szwWineIMCProperty);
     if (rc == (HIMC)-1)
         rc = NULL;
     else if (rc == NULL)
-        rc = IMM_GetThreadData()->defaultContext;
+        rc = get_default_context( hWnd );
 
     if (rc)
     {
         InputContextData *data = rc;
         data->IMC.hWnd = hWnd;
     }
+
     TRACE("returning %p\n", rc);
 
     return rc;
@@ -1486,7 +1626,7 @@ DWORD WINAPI ImmGetConversionListW(
 BOOL WINAPI ImmGetConversionStatus(
   HIMC hIMC, LPDWORD lpfdwConversion, LPDWORD lpfdwSentence)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("%p %p %p\n", hIMC, lpfdwConversion, lpfdwSentence);
 
@@ -1501,16 +1641,110 @@ BOOL WINAPI ImmGetConversionStatus(
     return TRUE;
 }
 
+static BOOL needs_ime_window(HWND hwnd)
+{
+    WCHAR classW[8];
+
+    if (GetClassNameW(hwnd, classW, ARRAY_SIZE(classW)) && !lstrcmpW(classW, szwIME))
+        return FALSE;
+    if (GetClassLongPtrW(hwnd, GCL_STYLE) & CS_IME) return FALSE;
+
+    return TRUE;
+}
+
+/***********************************************************************
+ *             __wine_register_window (IMM32.@)
+ */
+BOOL WINAPI __wine_register_window(HWND hwnd)
+{
+    HWND new = NULL;
+    IMMThreadData *thread_data;
+    TRACE("(%p)\n", hwnd);
+
+    if (!needs_ime_window(hwnd))
+        return FALSE;
+
+    thread_data = IMM_GetThreadData(hwnd, 0);
+    if (!thread_data)
+        return FALSE;
+
+    if (thread_data->disableIME || disable_ime)
+    {
+        TRACE("IME for this thread is disabled\n");
+        LeaveCriticalSection(&threaddata_cs);
+        return FALSE;
+    }
+    thread_data->windowRefs++;
+    TRACE("windowRefs=%u, hwndDefault=%p\n",
+          thread_data->windowRefs, thread_data->hwndDefault);
+
+    /* Create default IME window */
+    if (thread_data->windowRefs == 1)
+    {
+        /* Do not create the window inside of a critical section */
+        LeaveCriticalSection(&threaddata_cs);
+        new = CreateWindowExW( 0, szwIME, szwDefaultIME,
+                               WS_POPUP | WS_DISABLED | WS_CLIPSIBLINGS,
+                               0, 0, 1, 1, 0, 0, 0, 0);
+        /* thread_data is in the current thread so we can assume it's still valid */
+        EnterCriticalSection(&threaddata_cs);
+        /* See if anyone beat us */
+        if (thread_data->hwndDefault == NULL)
+        {
+            thread_data->hwndDefault = new;
+            new = NULL;
+            TRACE("Default is %p\n", thread_data->hwndDefault);
+        }
+    }
+
+    LeaveCriticalSection(&threaddata_cs);
+
+    /* Clean up an unused new window outside of the critical section */
+    if (new != NULL)
+        DestroyWindow(new);
+    return TRUE;
+}
+
+/***********************************************************************
+ *             __wine_unregister_window (IMM32.@)
+ */
+void WINAPI __wine_unregister_window(HWND hwnd)
+{
+    HWND to_destroy = 0;
+    IMMThreadData *thread_data;
+    TRACE("(%p)\n", hwnd);
+
+    thread_data = IMM_GetThreadData(hwnd, 0);
+    if (!thread_data) return;
+
+    thread_data->windowRefs--;
+    TRACE("windowRefs=%u, hwndDefault=%p\n",
+          thread_data->windowRefs, thread_data->hwndDefault);
+
+    /* Destroy default IME window */
+    if (thread_data->windowRefs == 0 && thread_data->hwndDefault)
+    {
+        to_destroy = thread_data->hwndDefault;
+        thread_data->hwndDefault = NULL;
+    }
+    LeaveCriticalSection(&threaddata_cs);
+
+    if (to_destroy) DestroyWindow( to_destroy );
+}
+
 /***********************************************************************
  *             ImmGetDefaultIMEWnd (IMM32.@)
  */
 HWND WINAPI ImmGetDefaultIMEWnd(HWND hWnd)
 {
-    if (IMM_GetThreadData()->hwndDefault == NULL)
-        IMM_GetThreadData()->hwndDefault = CreateWindowExW( WS_EX_TOOLWINDOW,
-                    szwIME, NULL, WS_POPUP, 0, 0, 1, 1, 0, 0, 0, 0);
-    TRACE("Default is %p\n",IMM_GetThreadData()->hwndDefault);
-    return IMM_GetThreadData()->hwndDefault;
+    HWND ret;
+    IMMThreadData* thread_data = IMM_GetThreadData(hWnd, 0);
+    if (!thread_data)
+        return NULL;
+    ret = thread_data->hwndDefault;
+    LeaveCriticalSection(&threaddata_cs);
+    TRACE("Default is %p\n",ret);
+    return ret;
 }
 
 /***********************************************************************
@@ -1526,6 +1760,8 @@ UINT WINAPI ImmGetDescriptionA(
 
   /* find out how many characters in the unicode buffer */
   len = ImmGetDescriptionW( hKL, NULL, 0 );
+  if (!len)
+    return 0;
 
   /* allocate a buffer of that size */
   buf = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof (WCHAR) );
@@ -1541,7 +1777,10 @@ UINT WINAPI ImmGetDescriptionA(
 
   HeapFree( GetProcessHeap(), 0, buf );
 
-  return len;
+  if (len == 0)
+    return 0;
+
+  return len - 1;
 }
 
 /***********************************************************************
@@ -1553,6 +1792,7 @@ UINT WINAPI ImmGetDescriptionW(HKL hKL, LPWSTR lpszDescription, UINT uBufLen)
 
   FIXME("(%p, %p, %d): semi stub\n", hKL, lpszDescription, uBufLen);
 
+  if (!hKL) return 0;
   if (!uBufLen) return lstrlenW( name );
   lstrcpynW( lpszDescription, name, uBufLen );
   return lstrlenW( lpszDescription );
@@ -1624,7 +1864,7 @@ UINT WINAPI ImmGetIMEFileNameW(HKL hKL, LPWSTR lpszFileName, UINT uBufLen)
     HKEY hkey;
     DWORD length;
     DWORD rc;
-    WCHAR regKey[sizeof(szImeRegFmt)/sizeof(WCHAR)+8];
+    WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8];
 
     wsprintfW( regKey, szImeRegFmt, (ULONG_PTR)hKL );
     rc = RegOpenKeyW( HKEY_LOCAL_MACHINE, regKey, &hkey);
@@ -1667,7 +1907,7 @@ UINT WINAPI ImmGetIMEFileNameW(HKL hKL, LPWSTR lpszFileName, UINT uBufLen)
  */
 BOOL WINAPI ImmGetOpenStatus(HIMC hIMC)
 {
-  InputContextData *data = hIMC;
+  InputContextData *data = get_imc_data(hIMC);
   static int i;
 
     if (!data)
@@ -1770,7 +2010,7 @@ UINT WINAPI ImmGetRegisterWordStyleW(
  */
 BOOL WINAPI ImmGetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %p)\n", hIMC, lpptPos);
 
@@ -1842,7 +2082,7 @@ HKL WINAPI ImmInstallIMEW(
     HKL hkl;
     DWORD rc;
     HKEY hkey;
-    WCHAR regKey[sizeof(szImeRegFmt)/sizeof(WCHAR)+8];
+    WCHAR regKey[ARRAY_SIZE(szImeRegFmt)+8];
 
     TRACE ("(%s, %s):\n", debugstr_w(lpszIMEFileName),
                           debugstr_w(lpszLayoutText));
@@ -1906,28 +2146,20 @@ BOOL WINAPI ImmIsIME(HKL hKL)
 BOOL WINAPI ImmIsUIMessageA(
   HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
 {
-    BOOL rc = FALSE;
-
     TRACE("(%p, %x, %ld, %ld)\n", hWndIME, msg, wParam, lParam);
     if ((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
             (msg == WM_IME_SETCONTEXT) ||
             (msg == WM_IME_NOTIFY) ||
             (msg == WM_IME_COMPOSITIONFULL) ||
             (msg == WM_IME_SELECT) ||
-            (msg == 0x287 /* FIXME: WM_IME_SYSTEM */) ||
-            (msg == WM_MSIME_RECONVERTOPTIONS) ||
-            (msg == WM_MSIME_MOUSE) ||
-            (msg == WM_MSIME_RECONVERTREQUEST) ||
-            (msg == WM_MSIME_RECONVERT) ||
-            (msg == WM_MSIME_QUERYPOSITION) ||
-            (msg == WM_MSIME_DOCUMENTFEED))
+            (msg == 0x287 /* FIXME: WM_IME_SYSTEM */))
     {
         if (hWndIME)
             SendMessageA(hWndIME, msg, wParam, lParam);
 
-        rc = TRUE;
+        return TRUE;
     }
-    return rc;
+    return FALSE;
 }
 
 /***********************************************************************
@@ -1936,28 +2168,20 @@ BOOL WINAPI ImmIsUIMessageA(
 BOOL WINAPI ImmIsUIMessageW(
   HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
 {
-    BOOL rc = FALSE;
-
     TRACE("(%p, %x, %ld, %ld)\n", hWndIME, msg, wParam, lParam);
     if ((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
             (msg == WM_IME_SETCONTEXT) ||
             (msg == WM_IME_NOTIFY) ||
             (msg == WM_IME_COMPOSITIONFULL) ||
             (msg == WM_IME_SELECT) ||
-            (msg == 0x287 /* FIXME: WM_IME_SYSTEM */) ||
-            (msg == WM_MSIME_RECONVERTOPTIONS) ||
-            (msg == WM_MSIME_MOUSE) ||
-            (msg == WM_MSIME_RECONVERTREQUEST) ||
-            (msg == WM_MSIME_RECONVERT) ||
-            (msg == WM_MSIME_QUERYPOSITION) ||
-            (msg == WM_MSIME_DOCUMENTFEED))
+            (msg == 0x287 /* FIXME: WM_IME_SYSTEM */))
     {
         if (hWndIME)
             SendMessageW(hWndIME, msg, wParam, lParam);
 
-        rc = TRUE;
+        return TRUE;
     }
-    return rc;
+    return FALSE;
 }
 
 /***********************************************************************
@@ -1966,13 +2190,21 @@ BOOL WINAPI ImmIsUIMessageW(
 BOOL WINAPI ImmNotifyIME(
   HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %d, %d, %d)\n",
         hIMC, dwAction, dwIndex, dwValue);
 
+    if (hIMC == NULL)
+    {
+        SetLastError(ERROR_SUCCESS);
+        return FALSE;
+    }
+
     if (!data || ! data->immKbd->pNotifyIME)
+    {
         return FALSE;
+    }
 
     return data->immKbd->pNotifyIME(hIMC,dwAction,dwIndex,dwValue);
 }
@@ -2042,11 +2274,11 @@ BOOL WINAPI ImmRegisterWordW(
  */
 BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
 {
-  static int shown = 0;
+  static BOOL shown = FALSE;
 
   if (!shown) {
      FIXME("(%p, %p): stub\n", hWnd, hIMC);
-     shown = 1;
+     shown = TRUE;
   }
   return TRUE;
 }
@@ -2056,14 +2288,14 @@ BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
 */
 LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
 
-    if (data && IsWindow(data->IMC.hWnd))
-        return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
+    if (data) return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
 
-     return 0;
+    SetLastError(ERROR_INVALID_HANDLE);
+    return 0;
 }
 
 /***********************************************************************
@@ -2071,14 +2303,14 @@ LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
 */
 LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
 
-    if (data && IsWindow(data->IMC.hWnd))
-        return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
+    if (data) return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
 
-     return 0;
+    SetLastError(ERROR_INVALID_HANDLE);
+    return 0;
 }
 
 /***********************************************************************
@@ -2087,20 +2319,22 @@ LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
 BOOL WINAPI ImmSetCandidateWindow(
   HIMC hIMC, LPCANDIDATEFORM lpCandidate)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %p)\n", hIMC, lpCandidate);
 
     if (!data || !lpCandidate)
         return FALSE;
 
-    TRACE("\t%x, %x, (%i,%i), (%i,%i - %i,%i)\n",
-            lpCandidate->dwIndex, lpCandidate->dwStyle,
-            lpCandidate->ptCurrentPos.x, lpCandidate->ptCurrentPos.y,
-            lpCandidate->rcArea.top, lpCandidate->rcArea.left,
-            lpCandidate->rcArea.bottom, lpCandidate->rcArea.right);
+    if (IMM_IsCrossThreadAccess(NULL, hIMC))
+        return FALSE;
+
+    TRACE("\t%x, %x, %s, %s\n",
+          lpCandidate->dwIndex, lpCandidate->dwStyle,
+          wine_dbgstr_point(&lpCandidate->ptCurrentPos),
+          wine_dbgstr_rect(&lpCandidate->rcArea));
 
-    if ( lpCandidate->dwIndex >= (sizeof(data->IMC.cfCandForm) / sizeof(CANDIDATEFORM)) )
+    if (lpCandidate->dwIndex >= ARRAY_SIZE(data->IMC.cfCandForm))
         return FALSE;
 
     data->IMC.cfCandForm[lpCandidate->dwIndex] = *lpCandidate;
@@ -2115,10 +2349,16 @@ BOOL WINAPI ImmSetCandidateWindow(
  */
 BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     TRACE("(%p, %p)\n", hIMC, lplf);
 
     if (!data || !lplf)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (IMM_IsCrossThreadAccess(NULL, hIMC))
         return FALSE;
 
     memcpy(&data->IMC.lfFont.W,lplf,sizeof(LOGFONTA));
@@ -2135,10 +2375,16 @@ BOOL WINAPI ImmSetCompositionFontA(HIMC hIMC, LPLOGFONTA lplf)
  */
 BOOL WINAPI ImmSetCompositionFontW(HIMC hIMC, LPLOGFONTW lplf)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     TRACE("(%p, %p)\n", hIMC, lplf);
 
     if (!data || !lplf)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (IMM_IsCrossThreadAccess(NULL, hIMC))
         return FALSE;
 
     data->IMC.lfFont.W = *lplf;
@@ -2161,7 +2407,7 @@ BOOL WINAPI ImmSetCompositionStringA(
     WCHAR *CompBuffer = NULL;
     WCHAR *ReadBuffer = NULL;
     BOOL rc;
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %d, %p, %d, %p, %d):\n",
             hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
@@ -2216,7 +2462,7 @@ BOOL WINAPI ImmSetCompositionStringW(
     CHAR *CompBuffer = NULL;
     CHAR *ReadBuffer = NULL;
     BOOL rc;
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %d, %p, %d, %p, %d):\n",
             hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
@@ -2269,14 +2515,21 @@ BOOL WINAPI ImmSetCompositionWindow(
   HIMC hIMC, LPCOMPOSITIONFORM lpCompForm)
 {
     BOOL reshow = FALSE;
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %p)\n", hIMC, lpCompForm);
-    TRACE("\t%x, (%i,%i), (%i,%i - %i,%i)\n",lpCompForm->dwStyle,
-          lpCompForm->ptCurrentPos.x, lpCompForm->ptCurrentPos.y, lpCompForm->rcArea.top,
-          lpCompForm->rcArea.left, lpCompForm->rcArea.bottom, lpCompForm->rcArea.right);
+    if (lpCompForm)
+        TRACE("\t%x, %s, %s\n", lpCompForm->dwStyle,
+              wine_dbgstr_point(&lpCompForm->ptCurrentPos),
+              wine_dbgstr_rect(&lpCompForm->rcArea));
 
     if (!data)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (IMM_IsCrossThreadAccess(NULL, hIMC))
         return FALSE;
 
     data->IMC.cfCompForm = *lpCompForm;
@@ -2303,11 +2556,17 @@ BOOL WINAPI ImmSetConversionStatus(
   HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence)
 {
     DWORD oldConversion, oldSentence;
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("%p %d %d\n", hIMC, fdwConversion, fdwSentence);
 
     if (!data)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (IMM_IsCrossThreadAccess(NULL, hIMC))
         return FALSE;
 
     if ( fdwConversion != data->IMC.fdwConversion )
@@ -2333,11 +2592,17 @@ BOOL WINAPI ImmSetConversionStatus(
  */
 BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("%p %d\n", hIMC, fOpen);
 
     if (!data)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (IMM_IsCrossThreadAccess(NULL, hIMC))
         return FALSE;
 
     if (data->immKbd->UIWnd == NULL)
@@ -2366,14 +2631,20 @@ BOOL WINAPI ImmSetOpenStatus(HIMC hIMC, BOOL fOpen)
  */
 BOOL WINAPI ImmSetStatusWindowPos(HIMC hIMC, LPPOINT lpptPos)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     TRACE("(%p, %p)\n", hIMC, lpptPos);
 
     if (!data || !lpptPos)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
+    }
 
-    TRACE("\t(%i,%i)\n", lpptPos->x, lpptPos->y);
+    if (IMM_IsCrossThreadAccess(NULL, hIMC))
+        return FALSE;
+
+    TRACE("\t%s\n", wine_dbgstr_point(lpptPos));
 
     data->IMC.ptStatusWndPos = *lpptPos;
     ImmNotifyIME( hIMC, NI_CONTEXTUPDATED, 0, IMC_SETSTATUSWINDOWPOS);
@@ -2489,9 +2760,16 @@ DWORD WINAPI ImmGetImeMenuItemsA( HIMC hIMC, DWORD dwFlags, DWORD dwType,
    LPIMEMENUITEMINFOA lpImeParentMenu, LPIMEMENUITEMINFOA lpImeMenu,
     DWORD dwSize)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
         lpImeParentMenu, lpImeMenu, dwSize);
+
+    if (!data)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return 0;
+    }
+
     if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
     {
         if (!is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
@@ -2553,9 +2831,16 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC hIMC, DWORD dwFlags, DWORD dwType,
    LPIMEMENUITEMINFOW lpImeParentMenu, LPIMEMENUITEMINFOW lpImeMenu,
    DWORD dwSize)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
     TRACE("(%p, %i, %i, %p, %p, %i):\n", hIMC, dwFlags, dwType,
         lpImeParentMenu, lpImeMenu, dwSize);
+
+    if (!data)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return 0;
+    }
+
     if (data->immKbd->hIME && data->immKbd->pImeGetImeMenuItems)
     {
         if (is_himc_ime_unicode(data) || (!lpImeParentMenu && !lpImeMenu))
@@ -2613,7 +2898,7 @@ DWORD WINAPI ImmGetImeMenuItemsW( HIMC hIMC, DWORD dwFlags, DWORD dwType,
 */
 LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
 
     if (!data)
         return NULL;
@@ -2626,7 +2911,10 @@ LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
 */
 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
+
+    if (!data)
+        return FALSE;
     if (data->dwLock)
         data->dwLock--;
     return TRUE;
@@ -2637,7 +2925,9 @@ BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
 */
 DWORD WINAPI ImmGetIMCLockCount(HIMC hIMC)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
+    if (!data)
+        return 0;
     return data->dwLock;
 }
 
@@ -2702,21 +2992,35 @@ DWORD WINAPI ImmGetIMCCSize(HIMCC imcc)
 */
 BOOL WINAPI ImmGenerateMessage(HIMC hIMC)
 {
-    InputContextData *data = hIMC;
+    InputContextData *data = get_imc_data(hIMC);
+
+    if (!data)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
 
     TRACE("%i messages queued\n",data->IMC.dwNumMsgBuf);
     if (data->IMC.dwNumMsgBuf > 0)
     {
         LPTRANSMSG lpTransMsg;
-        DWORD i;
-
-        lpTransMsg = ImmLockIMCC(data->IMC.hMsgBuf);
-        for (i = 0; i < data->IMC.dwNumMsgBuf; i++)
-            ImmInternalPostIMEMessage(data, lpTransMsg[i].message, lpTransMsg[i].wParam, lpTransMsg[i].lParam);
+        HIMCC hMsgBuf;
+        DWORD i, dwNumMsgBuf;
 
-        ImmUnlockIMCC(data->IMC.hMsgBuf);
+        /* We are going to detach our hMsgBuff so that if processing messages
+           generates new messages they go into a new buffer */
+        hMsgBuf = data->IMC.hMsgBuf;
+        dwNumMsgBuf = data->IMC.dwNumMsgBuf;
 
+        data->IMC.hMsgBuf = ImmCreateIMCC(0);
         data->IMC.dwNumMsgBuf = 0;
+
+        lpTransMsg = ImmLockIMCC(hMsgBuf);
+        for (i = 0; i < dwNumMsgBuf; i++)
+            ImmInternalSendIMEMessage(data, lpTransMsg[i].message, lpTransMsg[i].wParam, lpTransMsg[i].lParam);
+
+        ImmUnlockIMCC(hMsgBuf);
+        ImmDestroyIMCC(hMsgBuf);
     }
 
     return TRUE;
@@ -2803,6 +3107,22 @@ BOOL WINAPI ImmProcessKey(HWND hwnd, HKL hKL, UINT vKey, LPARAM lKeyData, DWORD
     else
         return FALSE;
 
+    /* Make sure we are inputting to the correct keyboard */
+    if (data->immKbd->hkl != hKL)
+    {
+        ImmHkl *new_hkl = IMM_GetImmHkl(hKL);
+        if (new_hkl)
+        {
+            data->immKbd->pImeSelect(imc, FALSE);
+            data->immKbd->uSelected--;
+            data->immKbd = new_hkl;
+            data->immKbd->pImeSelect(imc, TRUE);
+            data->immKbd->uSelected++;
+        }
+        else
+            return FALSE;
+    }
+
     if (!data->immKbd->hIME || !data->immKbd->pImeProcessKey)
         return FALSE;
 
@@ -2846,52 +3166,11 @@ BOOL WINAPI ImmGetHotKey(DWORD hotkey, UINT *modifiers, UINT *key, HKL hkl)
     return FALSE;
 }
 
-
-/*
- * Window Proc for the Default IME window class
- */
-static LRESULT WINAPI DefIME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
-                                        LPARAM lParam)
-{
-    switch (uMsg)
-    {
-        case WM_CREATE:
-        case WM_NCCREATE:
-            return TRUE;
-        case WM_IME_STARTCOMPOSITION:
-        case WM_IME_ENDCOMPOSITION:
-        case WM_IME_COMPOSITION:
-        case WM_IME_SETCONTEXT:
-        case WM_IME_NOTIFY:
-        case WM_IME_CONTROL:
-        case WM_IME_COMPOSITIONFULL:
-        case WM_IME_SELECT:
-        case WM_IME_CHAR:
-        case WM_IME_REQUEST:
-        case WM_IME_KEYDOWN:
-        case WM_IME_KEYUP:
-        {
-           ImmHkl *immHkl = IMM_GetImmHkl(GetKeyboardLayout(0));
-           if (immHkl->UIWnd)
-                return SendMessageW(immHkl->UIWnd,uMsg,wParam,lParam);
-           else
-                return FALSE;
-        }
-        default:
-            if ((uMsg == WM_MSIME_RECONVERTOPTIONS) ||
-                (uMsg == WM_MSIME_SERVICE) ||
-                (uMsg == WM_MSIME_MOUSE) ||
-                (uMsg == WM_MSIME_RECONVERTREQUEST) ||
-                (uMsg == WM_MSIME_RECONVERT) ||
-                (uMsg == WM_MSIME_QUERYPOSITION) ||
-                (uMsg == WM_MSIME_DOCUMENTFEED))
-            {
-               ImmHkl *immHkl = IMM_GetImmHkl(GetKeyboardLayout(0));
-               if (immHkl->UIWnd)
-                   return SendMessageW(immHkl->UIWnd,uMsg,wParam,lParam);
-               else
-                   return FALSE;
-            }
-            return DefWindowProcW(hwnd, uMsg, wParam, lParam);
-    }
+/***********************************************************************
+ *              ImmDisableLegacyIME(IMM32.@)
+ */
+BOOL WINAPI ImmDisableLegacyIME(void)
+{
+    FIXME("stub\n");
+    return TRUE;
 }