[v2 PATCH] imm32: Automatically initialize COM on window activation.
Aric Stewart
aric at codeweavers.com
Thu Jan 31 08:06:41 CST 2019
Signed-off-by: Aric Stewart <aric at 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 at 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
> {
>
More information about the wine-devel
mailing list