From: Aric Stewart Subject: (try 7)[4/5] imm32: Restrict crossthread Association and destruction Message-Id: <540E608B.3070802@codeweavers.com> Date: Mon, 08 Sep 2014 21:06:03 -0500 --- dlls/imm32/imm.c | 28 ++++++++++++++++++++++- dlls/imm32/tests/imm32.c | 59 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 9b70237..85e3052 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -71,6 +71,7 @@ typedef struct tagInputContextData { DWORD dwLock; INPUTCONTEXT IMC; + DWORD threadID; ImmHkl *immKbd; UINT lastVK; @@ -556,6 +557,27 @@ static IMMThreadData* IMM_GetInitializedThreadData(HWND hWnd) return thread_data; } +static BOOL IMM_IsCrossThreadAccess(HWND hWnd, HIMC hIMC) +{ + DWORD ctid = GetCurrentThreadId(); + InputContextData *data; + + if (hWnd) + { + DWORD process; + DWORD thread = 0; + + thread = GetWindowThreadProcessId(hWnd, &process); + if (process != GetCurrentProcessId() || thread != ctid) + return TRUE; + } + data = get_imc_data(hIMC); + if (data && data->threadID != ctid) + return TRUE; + + return FALSE; +} + /*********************************************************************** * ImmAssociateContext (IMM32.@) */ @@ -576,6 +598,9 @@ HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC) if (hIMC && data->IMC.hWnd == hWnd) return hIMC; + if (hIMC && IMM_IsCrossThreadAccess(hWnd, hIMC)) + return NULL; + thread_data = IMM_GetInitializedThreadData(hWnd); if (!thread_data) return NULL; @@ -803,6 +828,7 @@ HIMC WINAPI ImmCreateContext(void) IMM_DestroyContext(new_context); return 0; } + new_context->threadID = GetCurrentThreadId(); SendMessageW(GetFocus(), WM_IME_SELECT, TRUE, (LPARAM)GetKeyboardLayout(0)); new_context->immKbd->uSelected++; @@ -842,7 +868,7 @@ static BOOL IMM_DestroyContext(HIMC hIMC) */ BOOL WINAPI ImmDestroyContext(HIMC hIMC) { - if (!IMM_IsDefaultContext(hIMC)) + if (!IMM_IsDefaultContext(hIMC) && !IMM_IsCrossThreadAccess(NULL, hIMC)) return IMM_DestroyContext(hIMC); else return FALSE; diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 2471634..a64f9da 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -416,6 +416,7 @@ typedef struct _igc_threadinfo { HWND hwnd; HANDLE event; HIMC himc; + HIMC u_himc; } igc_threadinfo; @@ -424,6 +425,7 @@ static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam) HIMC h1,h2; HWND hwnd2; COMPOSITIONFORM cf; + CANDIDATEFORM cdf; POINT pt; MSG msg; @@ -452,6 +454,13 @@ static DWORD WINAPI ImmGetContextThreadFunc( LPVOID lpParam) /* priming for later tests */ ImmSetCompositionWindow(h1, &cf); ImmSetStatusWindowPos(h1, &pt); + info->u_himc = ImmCreateContext(); + ImmSetOpenStatus(info->u_himc, TRUE); + cdf.dwIndex = 0; + cdf.dwStyle = CFS_CANDIDATEPOS; + cdf.ptCurrentPos.x = 0; + cdf.ptCurrentPos.y = 0; + ImmSetCandidateWindow(info->u_himc, &cdf); SetEvent(info->event); @@ -487,15 +496,23 @@ static void test_ImmThreads(void) ok(himc != otherHimc, "Windows from other threads should have different himc\n"); ok(otherHimc == threadinfo.himc, "Context from other thread should not change in main thread\n"); - if (0) /* FIXME: Causes wine to hang */ - { h1 = ImmAssociateContext(hwnd,otherHimc); ok(h1 == NULL, "Should fail to be able to Associate a default context from a different thread\n"); h1 = ImmGetContext(hwnd); ok(h1 == himc, "Context for window should remain unchanged\n"); ImmReleaseContext(hwnd,h1); - } + h1 = ImmAssociateContext(hwnd, threadinfo.u_himc); + ok (h1 == NULL, "Should fail to associate a context from a different thread\n"); + h1 = ImmGetContext(hwnd); + ok(h1 == himc, "Context for window should remain unchanged\n"); + ImmReleaseContext(hwnd,h1); + + h1 = ImmAssociateContext(threadinfo.hwnd, threadinfo.u_himc); + ok (h1 == NULL, "Should fail to associate a context from a different thread into a window from that thread.\n"); + h1 = ImmGetContext(threadinfo.hwnd); + ok(h1 == threadinfo.himc, "Context for window should remain unchanged\n"); + ImmReleaseContext(threadinfo.hwnd,h1); /* OpenStatus */ rc = ImmSetOpenStatus(himc, TRUE); @@ -509,8 +526,12 @@ static void test_ImmThreads(void) rc = ImmSetOpenStatus(otherHimc, TRUE); todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n"); + rc = ImmSetOpenStatus(threadinfo.u_himc, TRUE); + todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n"); rc = ImmGetOpenStatus(otherHimc); todo_wine ok(rc == 0, "ImmGetOpenStatus failed\n"); + rc = ImmGetOpenStatus(threadinfo.u_himc); + ok (rc == 1 || broken(rc == 0), "ImmGetOpenStatus should return 1\n"); rc = ImmSetOpenStatus(otherHimc, FALSE); todo_wine ok(rc == 0, "ImmSetOpenStatus should fail\n"); rc = ImmGetOpenStatus(otherHimc); @@ -524,8 +545,12 @@ static void test_ImmThreads(void) rc = ImmGetCompositionFontA(otherHimc, &lf); ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont failed\n"); + rc = ImmGetCompositionFontA(threadinfo.u_himc, &lf); + ok(rc != 0 || broken(rc == 0), "ImmGetCompositionFont user himc failed\n"); rc = ImmSetCompositionFontA(otherHimc, &lf); todo_wine ok(rc == 0, "ImmSetCompositionFont should fail\n"); + rc = ImmSetCompositionFontA(threadinfo.u_himc, &lf); + todo_wine ok(rc == 0, "ImmSetCompositionFont should fail\n"); /* CompositionWindow */ rc = ImmSetCompositionWindow(himc, &cf); @@ -535,8 +560,12 @@ static void test_ImmThreads(void) rc = ImmSetCompositionWindow(otherHimc, &cf); todo_wine ok(rc == 0, "ImmSetCompositionWindow should fail\n"); + rc = ImmSetCompositionWindow(threadinfo.u_himc, &cf); + todo_wine ok(rc == 0, "ImmSetCompositionWindow should fail\n"); rc = ImmGetCompositionWindow(otherHimc, &cf); ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n"); + rc = ImmGetCompositionWindow(threadinfo.u_himc, &cf); + ok(rc != 0 || broken(rc == 0), "ImmGetCompositionWindow failed\n"); /* ConversionStatus */ rc = ImmGetConversionStatus(himc, &status, &sentence); @@ -546,8 +575,12 @@ static void test_ImmThreads(void) rc = ImmGetConversionStatus(otherHimc, &status, &sentence); ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n"); + rc = ImmGetConversionStatus(threadinfo.u_himc, &status, &sentence); + ok(rc != 0 || broken(rc == 0), "ImmGetConversionStatus failed\n"); rc = ImmSetConversionStatus(otherHimc, status, sentence); todo_wine ok(rc == 0, "ImmSetConversionStatus should fail\n"); + rc = ImmSetConversionStatus(threadinfo.u_himc, status, sentence); + todo_wine ok(rc == 0, "ImmSetConversionStatus should fail\n"); /* StatusWindowPos */ rc = ImmSetStatusWindowPos(himc, &pt); @@ -557,8 +590,24 @@ static void test_ImmThreads(void) rc = ImmSetStatusWindowPos(otherHimc, &pt); todo_wine ok(rc == 0, "ImmSetStatusWindowPos should fail\n"); + rc = ImmSetStatusWindowPos(threadinfo.u_himc, &pt); + todo_wine ok(rc == 0, "ImmSetStatusWindowPos should fail\n"); rc = ImmGetStatusWindowPos(otherHimc, &pt); ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n"); + rc = ImmGetStatusWindowPos(threadinfo.u_himc, &pt); + ok(rc != 0 || broken(rc == 0), "ImmGetStatusWindowPos failed\n"); + + h1 = ImmAssociateContext(threadinfo.hwnd, NULL); + ok (h1 == otherHimc, "ImmAssociateContext cross thread with NULL should work\n"); + h1 = ImmGetContext(threadinfo.hwnd); + ok (h1 == NULL, "CrossThread window context should be NULL\n"); + h1 = ImmAssociateContext(threadinfo.hwnd, h1); + ok (h1 == NULL, "Resetting cross thread context should fail\n"); + h1 = ImmGetContext(threadinfo.hwnd); + ok (h1 == NULL, "CrossThread window context should still be NULL\n"); + + rc = ImmDestroyContext(threadinfo.u_himc); + ok (rc == 0, "ImmDestroyContext Cross Thread should fail\n"); /* Candidate Window */ rc = ImmGetCandidateWindow(himc, 0, &cdf); @@ -576,6 +625,10 @@ static void test_ImmThreads(void) ok (rc == 0, "ImmGetCandidateWindow should fail\n"); rc = ImmSetCandidateWindow(otherHimc, &cdf); todo_wine ok (rc == 0, "ImmSetCandidateWindow should fail\n"); + rc = ImmGetCandidateWindow(threadinfo.u_himc, 0, &cdf); + ok (rc == 1 || broken( rc == 0), "ImmGetCandidateWindow should succeed\n"); + rc = ImmSetCandidateWindow(threadinfo.u_himc, &cdf); + todo_wine ok (rc == 0, "ImmSetCandidateWindow should fail\n"); ImmReleaseContext(threadinfo.hwnd,otherHimc); ImmReleaseContext(hwnd,himc);