Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/imm32/Makefile.in | 2 +- dlls/imm32/imm.c | 129 ++++++++++++++++++- dlls/imm32/imm32.spec | 1 + dlls/imm32/tests/imm32.c | 252 ++++++++++++++++++++++++++++++++++++- dlls/user32/focus.c | 2 + dlls/user32/misc.c | 2 + dlls/user32/user_private.h | 1 + 7 files changed, 386 insertions(+), 3 deletions(-)
diff --git a/dlls/imm32/Makefile.in b/dlls/imm32/Makefile.in index b190888659..ad10fc2fa4 100644 --- a/dlls/imm32/Makefile.in +++ b/dlls/imm32/Makefile.in @@ -1,6 +1,6 @@ MODULE = imm32.dll IMPORTLIB = imm32 -IMPORTS = user32 gdi32 advapi32 +IMPORTS = user32 gdi32 advapi32 ole32
C_SRCS = \ imm.c diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 28eb00f355..129f7e8cb5 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -19,6 +19,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS + #include <stdarg.h> #include <stdio.h>
@@ -32,6 +34,8 @@ #include "ddk/imm.h" #include "winnls.h" #include "winreg.h" +#include "initguid.h" +#include "objbase.h" #include "wine/list.h" #include "wine/unicode.h"
@@ -95,8 +99,16 @@ typedef struct _tagIMMThreadData { HWND hwndDefault; BOOL disableIME; DWORD windowRefs; + IInitializeSpy IInitializeSpy_iface; + ULARGE_INTEGER spy_cookie; + BOOL apt_initialized; } IMMThreadData;
+static inline IMMThreadData *impl_from_IInitializeSpy(IInitializeSpy *iface) +{ + return CONTAINING_RECORD(iface, IMMThreadData, IInitializeSpy_iface); +} + static struct list ImmHklList = LIST_INIT(ImmHklList); static struct list ImmThreadDataList = LIST_INIT(ImmThreadDataList);
@@ -227,6 +239,88 @@ static DWORD convert_candidatelist_AtoW( return ret; }
+static HRESULT WINAPI initializespy_QueryInterface(IInitializeSpy *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(&IID_IInitializeSpy, riid) || + IsEqualIID(&IID_IUnknown, riid)) + { + *obj = iface; + IInitializeSpy_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI initializespy_AddRef(IInitializeSpy *iface) +{ + return 2; +} + +static ULONG WINAPI initializespy_Release(IInitializeSpy *iface) +{ + return 1; +} + +static void imm_couninit_thread(IMMThreadData *thread_data) +{ + if (!thread_data->apt_initialized) + return; + + thread_data->apt_initialized = FALSE; + CoUninitialize(); +} + +static HRESULT WINAPI initializespy_PreInitialize(IInitializeSpy *iface, DWORD coinit, DWORD refs) +{ + IMMThreadData *thread_data = impl_from_IInitializeSpy(iface); + + /* Application requested initialization of different apartment type. */ + if (!(coinit & COINIT_APARTMENTTHREADED)) + imm_couninit_thread(thread_data); + + return S_OK; +} + +static HRESULT WINAPI initializespy_PostInitialize(IInitializeSpy *iface, HRESULT hr, DWORD coinit, DWORD refs) +{ + IMMThreadData *thread_data = impl_from_IInitializeSpy(iface); + + /* Explicit initialization call should return S_OK first time. */ + if (thread_data->apt_initialized && hr == S_FALSE && refs == 2) + hr = S_OK; + + return hr; +} + +static HRESULT WINAPI initializespy_PreUninitialize(IInitializeSpy *iface, DWORD refs) +{ + IMMThreadData *thread_data = impl_from_IInitializeSpy(iface); + + /* Account for explicit uninitialization calls. */ + if (thread_data->apt_initialized && refs == 1) + thread_data->apt_initialized = FALSE; + + return S_OK; +} + +static HRESULT WINAPI initializespy_PostUninitialize(IInitializeSpy *iface, DWORD refs) +{ + return S_OK; +} + +static const IInitializeSpyVtbl initializespyvtbl = +{ + initializespy_QueryInterface, + initializespy_AddRef, + initializespy_Release, + initializespy_PreInitialize, + initializespy_PostInitialize, + initializespy_PreUninitialize, + initializespy_PostUninitialize, +}; + static IMMThreadData *IMM_GetThreadData(HWND hwnd, DWORD thread) { IMMThreadData *data; @@ -253,6 +347,7 @@ static IMMThreadData *IMM_GetThreadData(HWND hwnd, DWORD thread) if (data->threadID == thread) return data;
data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data)); + data->IInitializeSpy_iface.lpVtbl = &initializespyvtbl; data->threadID = thread; list_add_head(&ImmThreadDataList,&data->entry); TRACE("Thread Data Created (%x)\n",thread); @@ -281,6 +376,7 @@ static void IMM_FreeThreadData(void) list_remove(&data->entry); LeaveCriticalSection(&threaddata_cs); IMM_DestroyContext(data->defaultContext); + imm_couninit_thread(data); HeapFree(GetProcessHeap(),0,data); TRACE("Thread Data Destroyed\n"); return; @@ -1627,6 +1723,32 @@ static BOOL needs_ime_window(HWND hwnd) return TRUE; }
+void WINAPI __wine_activate_window(HWND hwnd) +{ + IMMThreadData *thread_data; + + TRACE("(%p)\n", hwnd); + + if (!needs_ime_window(hwnd)) + return; + + thread_data = IMM_GetThreadData(hwnd, 0); + if (!thread_data) + return; + + if (thread_data->disableIME || disable_ime) + { + TRACE("IME for this thread is disabled\n"); + LeaveCriticalSection(&threaddata_cs); + return; + } + + if (!thread_data->apt_initialized) + thread_data->apt_initialized = SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); + + LeaveCriticalSection(&threaddata_cs); +} + /*********************************************************************** * __wine_register_window (IMM32.@) */ @@ -1656,6 +1778,8 @@ BOOL WINAPI __wine_register_window(HWND hwnd) /* Create default IME window */ if (thread_data->windowRefs == 1) { + CoRegisterInitializeSpy(&thread_data->IInitializeSpy_iface, &thread_data->spy_cookie); + /* Do not create the window inside of a critical section */ LeaveCriticalSection(&threaddata_cs); new = CreateWindowExW( 0, szwIME, szwDefaultIME, @@ -1697,8 +1821,11 @@ void WINAPI __wine_unregister_window(HWND hwnd) thread_data->windowRefs, thread_data->hwndDefault);
/* Destroy default IME window */ - if (thread_data->windowRefs == 0 && thread_data->hwndDefault) + if (thread_data->windowRefs == 0) { + CoRevokeInitializeSpy(thread_data->spy_cookie); + thread_data->spy_cookie.QuadPart = 0; + imm_couninit_thread(thread_data); to_destroy = thread_data->hwndDefault; thread_data->hwndDefault = NULL; } diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 4197bb81e2..d9cdc794e9 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -117,3 +117,4 @@ @ stdcall __wine_get_ui_window(ptr) @ stdcall __wine_register_window(long) @ stdcall __wine_unregister_window(long) +@ stdcall __wine_activate_window(long) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index ee1aeb3965..0d1632aa2f 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS + #include <stdio.h>
#include "wine/test.h" @@ -25,10 +27,17 @@ #include "wingdi.h" #include "imm.h" #include "ddk/imm.h" +#include "initguid.h" +#include "objbase.h" +#include "urlmon.h"
static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD); static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM); static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t); +static HRESULT (WINAPI *pCoGetApartmentType)(APTTYPE *, APTTYPEQUALIFIER *); +static HRESULT (WINAPI *pCoInitializeEx)(void *, DWORD); +static void (WINAPI *pCoUninitialize)(void); +static HRESULT (WINAPI *pCoCreateInstance)(REFCLSID, IUnknown *, DWORD, REFIID, void **);
/* * msgspy - record and analyse message traces sent to a certain window @@ -1990,7 +1999,247 @@ static void test_InvalidIMC(void) ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret); }
-START_TEST(imm32) { +static LRESULT CALLBACK com_init_test_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProcA(hwnd, uMsg, wParam, lParam); +} + +#define COM_INIT_TEST_APTTYPE(apttype) com_init_test_apttype(apttype, __LINE__) +static void com_init_test_apttype(APTTYPE expected_type, unsigned int line) +{ + APTTYPEQUALIFIER apttypequal; + HRESULT hr, hr_expected; + APTTYPE apttype; + IUnknown *unk; + + if (expected_type == -1) + hr_expected = CO_E_NOTINITIALIZED; + else + hr_expected = S_OK; + + hr = pCoCreateInstance(&CLSID_InternetZoneManager, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk); + ok_(__FILE__, line)(hr == hr_expected, "Unexpected hr %#x.\n", hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); + + hr = pCoGetApartmentType(&apttype, &apttypequal); + ok_(__FILE__, line)(hr == (expected_type == -1 ? CO_E_NOTINITIALIZED : S_OK), + "Failed to get apartment type, hr %#x.\n", hr); + if (SUCCEEDED(hr)) + ok_(__FILE__, line)(apttype == expected_type && apttypequal == APTTYPEQUALIFIER_NONE, + "Unexpected apartment type %u/%u.\n", apttype, apttypequal); +} + +static HWND test_com_create_window(DWORD style) +{ + WNDCLASSA clsA; + HWND hwnd; + + clsA.style = 0; + clsA.lpfnWndProc = com_init_test_wndproc; + clsA.cbClsExtra = 0; + clsA.cbWndExtra = 0; + clsA.hInstance = GetModuleHandleA(NULL); + clsA.hIcon = 0; + clsA.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + clsA.hbrBackground = NULL; + clsA.lpszMenuName = NULL; + clsA.lpszClassName = "COMInitTest"; + + RegisterClassA(&clsA); + + hwnd = CreateWindowExA(0, "COMInitTest", "Test window", WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_MAXIMIZEBOX | style, 0, 0, 100, 100, GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL); + ok(hwnd != NULL, "Failed to create a test window.\n"); + + return hwnd; +} + +static void test_com_init(const char *testname) +{ + WNDCLASSA clsA; + HMODULE hmod; + HRESULT hr; + HWND hwnd; + + clsA.style = 0; + clsA.lpfnWndProc = com_init_test_wndproc; + clsA.cbClsExtra = 0; + clsA.cbWndExtra = 0; + clsA.hInstance = GetModuleHandleA(NULL); + clsA.hIcon = 0; + clsA.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + clsA.hbrBackground = NULL; + clsA.lpszMenuName = NULL; + clsA.lpszClassName = "COMInitTest"; + + RegisterClassA(&clsA); + + hmod = LoadLibraryA("ole32.dll"); + + pCoGetApartmentType = (void *)GetProcAddress(hmod, "CoGetApartmentType"); + pCoInitializeEx = (void *)GetProcAddress(hmod, "CoInitializeEx"); + pCoUninitialize = (void *)GetProcAddress(hmod, "CoUninitialize"); + pCoCreateInstance = (void *)GetProcAddress(hmod, "CoCreateInstance"); + + if (!strcmp(testname, "visible")) + { + COM_INIT_TEST_APTTYPE(-1); + + hwnd = test_com_create_window(WS_VISIBLE); + + COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA); + + hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED); + ok(hr == S_OK, "Failed to re-initialize, hr %#x.\n", hr); + + COM_INIT_TEST_APTTYPE(APTTYPE_MTA); + + pCoUninitialize(); + + COM_INIT_TEST_APTTYPE(-1); + + DestroyWindow(hwnd); + } + else if (!strcmp(testname, "invisible")) + { + COM_INIT_TEST_APTTYPE(-1); + + hwnd = test_com_create_window(0); + + COM_INIT_TEST_APTTYPE(-1); + + ShowWindow(hwnd, SW_SHOW); + + COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA); + + hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED); + ok(hr == S_OK, "Failed to re-initialize, hr %#x.\n", hr); + + COM_INIT_TEST_APTTYPE(APTTYPE_MTA); + + pCoUninitialize(); + + COM_INIT_TEST_APTTYPE(-1); + + DestroyWindow(hwnd); + } + else if (!strcmp(testname, "imedisabled")) + { + COM_INIT_TEST_APTTYPE(-1); + + ImmDisableIME(-1); + + hwnd = test_com_create_window(WS_VISIBLE); + + COM_INIT_TEST_APTTYPE(-1); + + hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED); + ok(hr == S_OK, "Failed to re-initialize, hr %#x.\n", hr); + + COM_INIT_TEST_APTTYPE(APTTYPE_MTA); + + pCoUninitialize(); + + COM_INIT_TEST_APTTYPE(-1); + + DestroyWindow(hwnd); + } + else if (!strcmp(testname, "sta")) + { + COM_INIT_TEST_APTTYPE(-1); + + hwnd = test_com_create_window(WS_VISIBLE); + + COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA); + + /* Initialize for STA explicitly, S_OK is forced, with incremented counter. */ + hr = pCoInitializeEx(0, COINIT_APARTMENTTHREADED); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA); + + pCoUninitialize(); + + COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA); + + DestroyWindow(hwnd); + } + else if (!strcmp(testname, "uninit")) + { + COM_INIT_TEST_APTTYPE(-1); + + hwnd = test_com_create_window(WS_VISIBLE); + + COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA); + + pCoUninitialize(); + + COM_INIT_TEST_APTTYPE(-1); + + DestroyWindow(hwnd); + + hwnd = test_com_create_window(WS_VISIBLE); + + COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA); + + DestroyWindow(hwnd); + } + else + ok(0, "Unknown test name %s.\n", testname); +} + +static void test_com_initialization(void) +{ + char path_name[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + HMODULE hmod; + char **argv; + int i; + static const char *test_params[] = + { + "imedisabled", + "visible", + "invisible", + "sta", + "uninit", + }; + + hmod = LoadLibraryA("ole32.dll"); + pCoGetApartmentType = (void *)GetProcAddress(hmod, "CoGetApartmentType"); + FreeLibrary(hmod); + if (!pCoGetApartmentType) + { + win_skip("Skipping COM initialization tests on older system.\n"); + return; + } + + winetest_get_mainargs( &argv ); + for (i = 0; i < ARRAY_SIZE(test_params); ++i) + { + memset( &startup, 0, sizeof(startup) ); + startup.cb = sizeof( startup ); + sprintf( path_name, "%s imm32 %s", argv[0], test_params[i] ); + ok( CreateProcessA( NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ), + "CreateProcess failed.\n" ); + winetest_wait_child_process( info.hProcess ); + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + } +} + +START_TEST(imm32) +{ + char **argv; + int argc = winetest_get_mainargs( &argv ); + + if (argc >= 3) + { + test_com_init( argv[2] ); + return; + } + if (init()) { test_ImmNotifyIME(); @@ -2017,6 +2266,7 @@ START_TEST(imm32) { if (pSendInput) test_ime_processkey(); else win_skip("SendInput is not available\n"); + test_com_initialization(); } cleanup(); } diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c index f1c883167e..50b3323ae9 100644 --- a/dlls/user32/focus.c +++ b/dlls/user32/focus.c @@ -156,6 +156,8 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) (LPARAM)previous ); if (GetAncestor( hwnd, GA_PARENT ) == GetDesktopWindow()) PostMessageW( GetDesktopWindow(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd ); + + imm_activate_window( hwnd ); }
/* now change focus if necessary */ diff --git a/dlls/user32/misc.c b/dlls/user32/misc.c index d28cd9fd05..cfaa1b94db 100644 --- a/dlls/user32/misc.c +++ b/dlls/user32/misc.c @@ -43,6 +43,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(win); static HWND (WINAPI *imm_get_ui_window)(HKL); BOOL (WINAPI *imm_register_window)(HWND) = NULL; void (WINAPI *imm_unregister_window)(HWND) = NULL; +void (WINAPI *imm_activate_window)(HWND) = NULL;
/* MSIME messages */ static UINT WM_MSIME_SERVICE; @@ -480,6 +481,7 @@ BOOL WINAPI User32InitializeImmEntryTable(DWORD magic) imm_get_ui_window = (void*)GetProcAddress(imm32, "__wine_get_ui_window"); imm_register_window = (void*)GetProcAddress(imm32, "__wine_register_window"); imm_unregister_window = (void*)GetProcAddress(imm32, "__wine_unregister_window"); + imm_activate_window = (void*)GetProcAddress(imm32, "__wine_activate_window"); if (!imm_get_ui_window) FIXME("native imm32.dll not supported\n"); return TRUE; diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 514cf6753f..b86831d7d9 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -197,6 +197,7 @@ C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) extern INT global_key_state_counter DECLSPEC_HIDDEN; extern BOOL (WINAPI *imm_register_window)(HWND) DECLSPEC_HIDDEN; extern void (WINAPI *imm_unregister_window)(HWND) DECLSPEC_HIDDEN; +extern void (WINAPI *imm_activate_window)(HWND) DECLSPEC_HIDDEN;
struct user_key_state_info {
Hi,
While running your changed tests on Windows, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=46952
Your paranoid android.
=== debian9 (32 bit Chinese:China report) ===
user32: msg.c:8713: Test failed: WaitForSingleObject failed 102 msg.c:8719: Test failed: destroy child on thread exit: 0: the msg 0x0082 was expected, but got msg 0x000f instead msg.c:8719: Test failed: destroy child on thread exit: 1: the msg 0x000f was expected, but got msg 0x0014 instead msg.c:8719: Test failed: destroy child on thread exit: 2: the msg sequence is not complete: expected 0014 - actual 0000
Signed-off-by: Aric Stewart aric@codeweavers.com
I have no information or opinion on the debate on initialization costs and such. I tend to be a person that says if it works and help then go for it. The code itself is just fine to me so it gets my sign-off.
-aric
On 1/30/19 3:25 AM, Nikolay Sivov wrote:
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
dlls/imm32/Makefile.in | 2 +- dlls/imm32/imm.c | 129 ++++++++++++++++++- dlls/imm32/imm32.spec | 1 + dlls/imm32/tests/imm32.c | 252 ++++++++++++++++++++++++++++++++++++- dlls/user32/focus.c | 2 + dlls/user32/misc.c | 2 + dlls/user32/user_private.h | 1 + 7 files changed, 386 insertions(+), 3 deletions(-)
diff --git a/dlls/imm32/Makefile.in b/dlls/imm32/Makefile.in index b190888659..ad10fc2fa4 100644 --- a/dlls/imm32/Makefile.in +++ b/dlls/imm32/Makefile.in @@ -1,6 +1,6 @@ MODULE = imm32.dll IMPORTLIB = imm32 -IMPORTS = user32 gdi32 advapi32 +IMPORTS = user32 gdi32 advapi32 ole32
C_SRCS = \ imm.c diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index 28eb00f355..129f7e8cb5 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -19,6 +19,8 @@
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#define COBJMACROS
- #include <stdarg.h> #include <stdio.h>
@@ -32,6 +34,8 @@ #include "ddk/imm.h" #include "winnls.h" #include "winreg.h" +#include "initguid.h" +#include "objbase.h" #include "wine/list.h" #include "wine/unicode.h"
@@ -95,8 +99,16 @@ typedef struct _tagIMMThreadData { HWND hwndDefault; BOOL disableIME; DWORD windowRefs;
- IInitializeSpy IInitializeSpy_iface;
- ULARGE_INTEGER spy_cookie;
- BOOL apt_initialized; } IMMThreadData;
+static inline IMMThreadData *impl_from_IInitializeSpy(IInitializeSpy *iface) +{
- return CONTAINING_RECORD(iface, IMMThreadData, IInitializeSpy_iface);
+}
- static struct list ImmHklList = LIST_INIT(ImmHklList); static struct list ImmThreadDataList = LIST_INIT(ImmThreadDataList);
@@ -227,6 +239,88 @@ static DWORD convert_candidatelist_AtoW( return ret; }
+static HRESULT WINAPI initializespy_QueryInterface(IInitializeSpy *iface, REFIID riid, void **obj) +{
- if (IsEqualIID(&IID_IInitializeSpy, riid) ||
IsEqualIID(&IID_IUnknown, riid))
- {
*obj = iface;
IInitializeSpy_AddRef(iface);
return S_OK;
- }
- *obj = NULL;
- return E_NOINTERFACE;
+}
+static ULONG WINAPI initializespy_AddRef(IInitializeSpy *iface) +{
- return 2;
+}
+static ULONG WINAPI initializespy_Release(IInitializeSpy *iface) +{
- return 1;
+}
+static void imm_couninit_thread(IMMThreadData *thread_data) +{
- if (!thread_data->apt_initialized)
return;
- thread_data->apt_initialized = FALSE;
- CoUninitialize();
+}
+static HRESULT WINAPI initializespy_PreInitialize(IInitializeSpy *iface, DWORD coinit, DWORD refs) +{
- IMMThreadData *thread_data = impl_from_IInitializeSpy(iface);
- /* Application requested initialization of different apartment type. */
- if (!(coinit & COINIT_APARTMENTTHREADED))
imm_couninit_thread(thread_data);
- return S_OK;
+}
+static HRESULT WINAPI initializespy_PostInitialize(IInitializeSpy *iface, HRESULT hr, DWORD coinit, DWORD refs) +{
- IMMThreadData *thread_data = impl_from_IInitializeSpy(iface);
- /* Explicit initialization call should return S_OK first time. */
- if (thread_data->apt_initialized && hr == S_FALSE && refs == 2)
hr = S_OK;
- return hr;
+}
+static HRESULT WINAPI initializespy_PreUninitialize(IInitializeSpy *iface, DWORD refs) +{
- IMMThreadData *thread_data = impl_from_IInitializeSpy(iface);
- /* Account for explicit uninitialization calls. */
- if (thread_data->apt_initialized && refs == 1)
thread_data->apt_initialized = FALSE;
- return S_OK;
+}
+static HRESULT WINAPI initializespy_PostUninitialize(IInitializeSpy *iface, DWORD refs) +{
- return S_OK;
+}
+static const IInitializeSpyVtbl initializespyvtbl = +{
- initializespy_QueryInterface,
- initializespy_AddRef,
- initializespy_Release,
- initializespy_PreInitialize,
- initializespy_PostInitialize,
- initializespy_PreUninitialize,
- initializespy_PostUninitialize,
+};
- static IMMThreadData *IMM_GetThreadData(HWND hwnd, DWORD thread) { IMMThreadData *data;
@@ -253,6 +347,7 @@ static IMMThreadData *IMM_GetThreadData(HWND hwnd, DWORD thread) if (data->threadID == thread) return data;
data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data));
- data->IInitializeSpy_iface.lpVtbl = &initializespyvtbl; data->threadID = thread; list_add_head(&ImmThreadDataList,&data->entry); TRACE("Thread Data Created (%x)\n",thread);
@@ -281,6 +376,7 @@ static void IMM_FreeThreadData(void) list_remove(&data->entry); LeaveCriticalSection(&threaddata_cs); IMM_DestroyContext(data->defaultContext);
imm_couninit_thread(data); HeapFree(GetProcessHeap(),0,data); TRACE("Thread Data Destroyed\n"); return;
@@ -1627,6 +1723,32 @@ static BOOL needs_ime_window(HWND hwnd) return TRUE; }
+void WINAPI __wine_activate_window(HWND hwnd) +{
- IMMThreadData *thread_data;
- TRACE("(%p)\n", hwnd);
- if (!needs_ime_window(hwnd))
return;
- thread_data = IMM_GetThreadData(hwnd, 0);
- if (!thread_data)
return;
- if (thread_data->disableIME || disable_ime)
- {
TRACE("IME for this thread is disabled\n");
LeaveCriticalSection(&threaddata_cs);
return;
- }
- if (!thread_data->apt_initialized)
thread_data->apt_initialized = SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
- LeaveCriticalSection(&threaddata_cs);
+}
- /***********************************************************************
*/
__wine_register_window (IMM32.@)
@@ -1656,6 +1778,8 @@ BOOL WINAPI __wine_register_window(HWND hwnd) /* Create default IME window */ if (thread_data->windowRefs == 1) {
CoRegisterInitializeSpy(&thread_data->IInitializeSpy_iface, &thread_data->spy_cookie);
/* Do not create the window inside of a critical section */ LeaveCriticalSection(&threaddata_cs); new = CreateWindowExW( 0, szwIME, szwDefaultIME,
@@ -1697,8 +1821,11 @@ void WINAPI __wine_unregister_window(HWND hwnd) thread_data->windowRefs, thread_data->hwndDefault);
/* Destroy default IME window */
- if (thread_data->windowRefs == 0 && thread_data->hwndDefault)
- if (thread_data->windowRefs == 0) {
CoRevokeInitializeSpy(thread_data->spy_cookie);
thread_data->spy_cookie.QuadPart = 0;
imm_couninit_thread(thread_data); to_destroy = thread_data->hwndDefault; thread_data->hwndDefault = NULL; }
diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 4197bb81e2..d9cdc794e9 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -117,3 +117,4 @@ @ stdcall __wine_get_ui_window(ptr) @ stdcall __wine_register_window(long) @ stdcall __wine_unregister_window(long) +@ stdcall __wine_activate_window(long) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index ee1aeb3965..0d1632aa2f 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -18,6 +18,8 @@
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#define COBJMACROS
#include <stdio.h>
#include "wine/test.h"
@@ -25,10 +27,17 @@ #include "wingdi.h" #include "imm.h" #include "ddk/imm.h" +#include "initguid.h" +#include "objbase.h" +#include "urlmon.h"
static BOOL (WINAPI *pImmAssociateContextEx)(HWND,HIMC,DWORD); static BOOL (WINAPI *pImmIsUIMessageA)(HWND,UINT,WPARAM,LPARAM); static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t); +static HRESULT (WINAPI *pCoGetApartmentType)(APTTYPE *, APTTYPEQUALIFIER *); +static HRESULT (WINAPI *pCoInitializeEx)(void *, DWORD); +static void (WINAPI *pCoUninitialize)(void); +static HRESULT (WINAPI *pCoCreateInstance)(REFCLSID, IUnknown *, DWORD, REFIID, void **);
/*
- msgspy - record and analyse message traces sent to a certain window
@@ -1990,7 +1999,247 @@ static void test_InvalidIMC(void) ok(ret == ERROR_INVALID_HANDLE, "wrong last error %08x!\n", ret); }
-START_TEST(imm32) { +static LRESULT CALLBACK com_init_test_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{
- return DefWindowProcA(hwnd, uMsg, wParam, lParam);
+}
+#define COM_INIT_TEST_APTTYPE(apttype) com_init_test_apttype(apttype, __LINE__) +static void com_init_test_apttype(APTTYPE expected_type, unsigned int line) +{
- APTTYPEQUALIFIER apttypequal;
- HRESULT hr, hr_expected;
- APTTYPE apttype;
- IUnknown *unk;
- if (expected_type == -1)
hr_expected = CO_E_NOTINITIALIZED;
- else
hr_expected = S_OK;
- hr = pCoCreateInstance(&CLSID_InternetZoneManager, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk);
- ok_(__FILE__, line)(hr == hr_expected, "Unexpected hr %#x.\n", hr);
- if (SUCCEEDED(hr))
IUnknown_Release(unk);
- hr = pCoGetApartmentType(&apttype, &apttypequal);
- ok_(__FILE__, line)(hr == (expected_type == -1 ? CO_E_NOTINITIALIZED : S_OK),
"Failed to get apartment type, hr %#x.\n", hr);
- if (SUCCEEDED(hr))
ok_(__FILE__, line)(apttype == expected_type && apttypequal == APTTYPEQUALIFIER_NONE,
"Unexpected apartment type %u/%u.\n", apttype, apttypequal);
+}
+static HWND test_com_create_window(DWORD style) +{
- WNDCLASSA clsA;
- HWND hwnd;
- clsA.style = 0;
- clsA.lpfnWndProc = com_init_test_wndproc;
- clsA.cbClsExtra = 0;
- clsA.cbWndExtra = 0;
- clsA.hInstance = GetModuleHandleA(NULL);
- clsA.hIcon = 0;
- clsA.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
- clsA.hbrBackground = NULL;
- clsA.lpszMenuName = NULL;
- clsA.lpszClassName = "COMInitTest";
- RegisterClassA(&clsA);
- hwnd = CreateWindowExA(0, "COMInitTest", "Test window", WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
WS_MAXIMIZEBOX | style, 0, 0, 100, 100, GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
- ok(hwnd != NULL, "Failed to create a test window.\n");
- return hwnd;
+}
+static void test_com_init(const char *testname) +{
- WNDCLASSA clsA;
- HMODULE hmod;
- HRESULT hr;
- HWND hwnd;
- clsA.style = 0;
- clsA.lpfnWndProc = com_init_test_wndproc;
- clsA.cbClsExtra = 0;
- clsA.cbWndExtra = 0;
- clsA.hInstance = GetModuleHandleA(NULL);
- clsA.hIcon = 0;
- clsA.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
- clsA.hbrBackground = NULL;
- clsA.lpszMenuName = NULL;
- clsA.lpszClassName = "COMInitTest";
- RegisterClassA(&clsA);
- hmod = LoadLibraryA("ole32.dll");
- pCoGetApartmentType = (void *)GetProcAddress(hmod, "CoGetApartmentType");
- pCoInitializeEx = (void *)GetProcAddress(hmod, "CoInitializeEx");
- pCoUninitialize = (void *)GetProcAddress(hmod, "CoUninitialize");
- pCoCreateInstance = (void *)GetProcAddress(hmod, "CoCreateInstance");
- if (!strcmp(testname, "visible"))
- {
COM_INIT_TEST_APTTYPE(-1);
hwnd = test_com_create_window(WS_VISIBLE);
COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA);
hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED);
ok(hr == S_OK, "Failed to re-initialize, hr %#x.\n", hr);
COM_INIT_TEST_APTTYPE(APTTYPE_MTA);
pCoUninitialize();
COM_INIT_TEST_APTTYPE(-1);
DestroyWindow(hwnd);
- }
- else if (!strcmp(testname, "invisible"))
- {
COM_INIT_TEST_APTTYPE(-1);
hwnd = test_com_create_window(0);
COM_INIT_TEST_APTTYPE(-1);
ShowWindow(hwnd, SW_SHOW);
COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA);
hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED);
ok(hr == S_OK, "Failed to re-initialize, hr %#x.\n", hr);
COM_INIT_TEST_APTTYPE(APTTYPE_MTA);
pCoUninitialize();
COM_INIT_TEST_APTTYPE(-1);
DestroyWindow(hwnd);
- }
- else if (!strcmp(testname, "imedisabled"))
- {
COM_INIT_TEST_APTTYPE(-1);
ImmDisableIME(-1);
hwnd = test_com_create_window(WS_VISIBLE);
COM_INIT_TEST_APTTYPE(-1);
hr = pCoInitializeEx(NULL, COINIT_MULTITHREADED);
ok(hr == S_OK, "Failed to re-initialize, hr %#x.\n", hr);
COM_INIT_TEST_APTTYPE(APTTYPE_MTA);
pCoUninitialize();
COM_INIT_TEST_APTTYPE(-1);
DestroyWindow(hwnd);
- }
- else if (!strcmp(testname, "sta"))
- {
COM_INIT_TEST_APTTYPE(-1);
hwnd = test_com_create_window(WS_VISIBLE);
COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA);
/* Initialize for STA explicitly, S_OK is forced, with incremented counter. */
hr = pCoInitializeEx(0, COINIT_APARTMENTTHREADED);
ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA);
pCoUninitialize();
COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA);
DestroyWindow(hwnd);
- }
- else if (!strcmp(testname, "uninit"))
- {
COM_INIT_TEST_APTTYPE(-1);
hwnd = test_com_create_window(WS_VISIBLE);
COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA);
pCoUninitialize();
COM_INIT_TEST_APTTYPE(-1);
DestroyWindow(hwnd);
hwnd = test_com_create_window(WS_VISIBLE);
COM_INIT_TEST_APTTYPE(APTTYPE_MAINSTA);
DestroyWindow(hwnd);
- }
- else
ok(0, "Unknown test name %s.\n", testname);
+}
+static void test_com_initialization(void) +{
- char path_name[MAX_PATH];
- PROCESS_INFORMATION info;
- STARTUPINFOA startup;
- HMODULE hmod;
- char **argv;
- int i;
- static const char *test_params[] =
- {
"imedisabled",
"visible",
"invisible",
"sta",
"uninit",
- };
- hmod = LoadLibraryA("ole32.dll");
- pCoGetApartmentType = (void *)GetProcAddress(hmod, "CoGetApartmentType");
- FreeLibrary(hmod);
- if (!pCoGetApartmentType)
- {
win_skip("Skipping COM initialization tests on older system.\n");
return;
- }
- winetest_get_mainargs( &argv );
- for (i = 0; i < ARRAY_SIZE(test_params); ++i)
- {
memset( &startup, 0, sizeof(startup) );
startup.cb = sizeof( startup );
sprintf( path_name, "%s imm32 %s", argv[0], test_params[i] );
ok( CreateProcessA( NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ),
"CreateProcess failed.\n" );
winetest_wait_child_process( info.hProcess );
CloseHandle( info.hProcess );
CloseHandle( info.hThread );
- }
+}
+START_TEST(imm32) +{
- char **argv;
- int argc = winetest_get_mainargs( &argv );
- if (argc >= 3)
- {
test_com_init( argv[2] );
return;
- }
if (init()) { test_ImmNotifyIME();
@@ -2017,6 +2266,7 @@ START_TEST(imm32) { if (pSendInput) test_ime_processkey(); else win_skip("SendInput is not available\n");
}test_com_initialization(); } cleanup();
diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c index f1c883167e..50b3323ae9 100644 --- a/dlls/user32/focus.c +++ b/dlls/user32/focus.c @@ -156,6 +156,8 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) (LPARAM)previous ); if (GetAncestor( hwnd, GA_PARENT ) == GetDesktopWindow()) PostMessageW( GetDesktopWindow(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
imm_activate_window( hwnd ); } /* now change focus if necessary */
diff --git a/dlls/user32/misc.c b/dlls/user32/misc.c index d28cd9fd05..cfaa1b94db 100644 --- a/dlls/user32/misc.c +++ b/dlls/user32/misc.c @@ -43,6 +43,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(win); static HWND (WINAPI *imm_get_ui_window)(HKL); BOOL (WINAPI *imm_register_window)(HWND) = NULL; void (WINAPI *imm_unregister_window)(HWND) = NULL; +void (WINAPI *imm_activate_window)(HWND) = NULL;
/* MSIME messages */ static UINT WM_MSIME_SERVICE; @@ -480,6 +481,7 @@ BOOL WINAPI User32InitializeImmEntryTable(DWORD magic) imm_get_ui_window = (void*)GetProcAddress(imm32, "__wine_get_ui_window"); imm_register_window = (void*)GetProcAddress(imm32, "__wine_register_window"); imm_unregister_window = (void*)GetProcAddress(imm32, "__wine_unregister_window");
- imm_activate_window = (void*)GetProcAddress(imm32, "__wine_activate_window"); if (!imm_get_ui_window) FIXME("native imm32.dll not supported\n"); return TRUE;
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 514cf6753f..b86831d7d9 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -197,6 +197,7 @@ C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) extern INT global_key_state_counter DECLSPEC_HIDDEN; extern BOOL (WINAPI *imm_register_window)(HWND) DECLSPEC_HIDDEN; extern void (WINAPI *imm_unregister_window)(HWND) DECLSPEC_HIDDEN; +extern void (WINAPI *imm_activate_window)(HWND) DECLSPEC_HIDDEN;
struct user_key_state_info {
Nikolay Sivov nsivov@codeweavers.com writes:
diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 4197bb81e2..d9cdc794e9 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -117,3 +117,4 @@ @ stdcall __wine_get_ui_window(ptr) @ stdcall __wine_register_window(long) @ stdcall __wine_unregister_window(long) +@ stdcall __wine_activate_window(long)
It shouldn't be necessary to add a custom entry point for this, it should be part of the normal IME flow. Judging from the message traces, it should probably happen by sending WM_IME_SETCONTEXT on focus changes.
On 2/6/19 11:38 AM, Alexandre Julliard wrote:
Nikolay Sivov nsivov@codeweavers.com writes:
diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 4197bb81e2..d9cdc794e9 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -117,3 +117,4 @@ @ stdcall __wine_get_ui_window(ptr) @ stdcall __wine_register_window(long) @ stdcall __wine_unregister_window(long) +@ stdcall __wine_activate_window(long)
It shouldn't be necessary to add a custom entry point for this, it should be part of the normal IME flow. Judging from the message traces, it should probably happen by sending WM_IME_SETCONTEXT on focus changes.
The problem is, it will have even larger impact if I start to mess with IME message handling which as I remember took some time to stabilize.
Right now I see traces of what I think you're suggesting in winex11 xim integration only, that calls ImmAssociateContext() to send this IME message. However initialization should happen regardless of windowing driver we use, so imm32 has to respond to input focus changes.
So to be clear, you're talking about making general improvements in this area first?
Nikolay Sivov nsivov@codeweavers.com writes:
On 2/6/19 11:38 AM, Alexandre Julliard wrote:
Nikolay Sivov nsivov@codeweavers.com writes:
diff --git a/dlls/imm32/imm32.spec b/dlls/imm32/imm32.spec index 4197bb81e2..d9cdc794e9 100644 --- a/dlls/imm32/imm32.spec +++ b/dlls/imm32/imm32.spec @@ -117,3 +117,4 @@ @ stdcall __wine_get_ui_window(ptr) @ stdcall __wine_register_window(long) @ stdcall __wine_unregister_window(long) +@ stdcall __wine_activate_window(long)
It shouldn't be necessary to add a custom entry point for this, it should be part of the normal IME flow. Judging from the message traces, it should probably happen by sending WM_IME_SETCONTEXT on focus changes.
The problem is, it will have even larger impact if I start to mess with IME message handling which as I remember took some time to stabilize.
Right now I see traces of what I think you're suggesting in winex11 xim integration only, that calls ImmAssociateContext() to send this IME message. However initialization should happen regardless of windowing driver we use, so imm32 has to respond to input focus changes.
So to be clear, you're talking about making general improvements in this area first?
It may require some code to be moved to user32, but it could also be left to the windowing driver, if that's easier. If it works with winex11 but not with the other drivers they can be fixed.