include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / imm32 / imm.c
blobcab531e4342dc403015e93301b8b0abdcca4ec4d
1 /*
2 * IMM32 library
4 * Copyright 1998 Patrik Stridvall
5 * Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
23 #include "initguid.h"
24 #include "imm_private.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(imm);
28 #define IMM_INIT_MAGIC 0x19650412
29 BOOL WINAPI User32InitializeImmEntryTable(DWORD);
31 HMODULE imm32_module;
33 /* MSIME messages */
34 UINT WM_MSIME_SERVICE;
35 UINT WM_MSIME_RECONVERTOPTIONS;
36 UINT WM_MSIME_MOUSE;
37 UINT WM_MSIME_RECONVERTREQUEST;
38 UINT WM_MSIME_RECONVERT;
39 UINT WM_MSIME_QUERYPOSITION;
40 UINT WM_MSIME_DOCUMENTFEED;
42 struct imc_entry
44 HIMC himc;
45 INPUTCONTEXT context;
46 struct list entry;
49 struct ime
51 LONG refcount; /* guarded by ime_cs */
53 HKL hkl;
54 HMODULE module;
55 struct list entry;
57 IMEINFO info;
58 WCHAR ui_class[17];
59 struct list input_contexts;
61 BOOL (WINAPI *pImeInquire)(IMEINFO *, void *, DWORD);
62 BOOL (WINAPI *pImeConfigure)(HKL, HWND, DWORD, void *);
63 BOOL (WINAPI *pImeDestroy)(UINT);
64 LRESULT (WINAPI *pImeEscape)(HIMC, UINT, void *);
65 BOOL (WINAPI *pImeSelect)(HIMC, BOOL);
66 BOOL (WINAPI *pImeSetActiveContext)(HIMC, BOOL);
67 UINT (WINAPI *pImeToAsciiEx)(UINT, UINT, const BYTE *, TRANSMSGLIST *, UINT, HIMC);
68 BOOL (WINAPI *pNotifyIME)(HIMC, DWORD, DWORD, DWORD);
69 BOOL (WINAPI *pImeRegisterWord)(const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*);
70 BOOL (WINAPI *pImeUnregisterWord)(const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*);
71 UINT (WINAPI *pImeEnumRegisterWord)(void */*REGISTERWORDENUMPROCW*/, const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*, void *);
72 BOOL (WINAPI *pImeSetCompositionString)(HIMC, DWORD, const void/*TCHAR*/*, DWORD, const void/*TCHAR*/*, DWORD);
73 DWORD (WINAPI *pImeConversionList)(HIMC, const void/*TCHAR*/*, CANDIDATELIST*, DWORD, UINT);
74 UINT (WINAPI *pImeGetRegisterWordStyle)(UINT, void/*STYLEBUFW*/*);
75 BOOL (WINAPI *pImeProcessKey)(HIMC, UINT, LPARAM, const BYTE*);
76 DWORD (WINAPI *pImeGetImeMenuItems)(HIMC, DWORD, DWORD, void/*IMEMENUITEMINFOW*/*, void/*IMEMENUITEMINFOW*/*, DWORD);
79 static HRESULT (WINAPI *pCoRevokeInitializeSpy)(ULARGE_INTEGER cookie);
80 static void (WINAPI *pCoUninitialize)(void);
82 struct imc
84 HIMC handle;
85 DWORD dwLock;
86 INPUTCONTEXT IMC;
88 struct ime *ime;
89 UINT vkey;
91 HWND ui_hwnd; /* IME UI window, on the default input context */
94 #define WINE_IMC_VALID_MAGIC 0x56434D49
96 struct coinit_spy
98 IInitializeSpy IInitializeSpy_iface;
99 LONG ref;
100 ULARGE_INTEGER cookie;
101 enum
103 IMM_APT_INIT = 0x1,
104 IMM_APT_CREATED = 0x2,
105 IMM_APT_CAN_FREE = 0x4,
106 IMM_APT_BROKEN = 0x8
107 } apt_flags;
110 static CRITICAL_SECTION ime_cs;
111 static CRITICAL_SECTION_DEBUG ime_cs_debug =
113 0, 0, &ime_cs,
114 { &ime_cs_debug.ProcessLocksList, &ime_cs_debug.ProcessLocksList },
115 0, 0, { (DWORD_PTR)(__FILE__ ": ime_cs") }
117 static CRITICAL_SECTION ime_cs = { &ime_cs_debug, -1, 0, 0, 0, 0 };
118 static struct list ime_list = LIST_INIT( ime_list );
120 static const WCHAR layouts_formatW[] = L"System\\CurrentControlSet\\Control\\Keyboard Layouts\\%08lx";
122 static const char *debugstr_composition( const COMPOSITIONFORM *composition )
124 if (!composition) return "(null)";
125 return wine_dbg_sprintf( "style %#lx, pos %s, area %s", composition->dwStyle,
126 wine_dbgstr_point( &composition->ptCurrentPos ),
127 wine_dbgstr_rect( &composition->rcArea ) );
130 static const char *debugstr_candidate( const CANDIDATEFORM *candidate )
132 if (!candidate) return "(null)";
133 return wine_dbg_sprintf( "idx %#lx, style %#lx, pos %s, area %s", candidate->dwIndex,
134 candidate->dwStyle, wine_dbgstr_point( &candidate->ptCurrentPos ),
135 wine_dbgstr_rect( &candidate->rcArea ) );
138 static BOOL ime_is_unicode( const struct ime *ime )
140 return !!(ime->info.fdwProperty & IME_PROP_UNICODE);
143 static BOOL input_context_is_unicode( INPUTCONTEXT *ctx )
145 struct imc *imc = CONTAINING_RECORD( ctx, struct imc, IMC );
146 return !imc->ime || ime_is_unicode( imc->ime );
149 static BOOL IMM_DestroyContext(HIMC hIMC);
150 static struct imc *get_imc_data( HIMC hIMC );
152 static inline WCHAR *strdupAtoW( const char *str )
154 WCHAR *ret = NULL;
155 if (str)
157 DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
158 if ((ret = malloc( len * sizeof(WCHAR) ))) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
160 return ret;
163 static inline CHAR *strdupWtoA( const WCHAR *str )
165 CHAR *ret = NULL;
166 if (str)
168 DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
169 if ((ret = malloc( len ))) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
171 return ret;
174 static DWORD convert_candidatelist_WtoA(
175 LPCANDIDATELIST lpSrc, LPCANDIDATELIST lpDst, DWORD dwBufLen)
177 DWORD ret, i, len;
179 ret = FIELD_OFFSET( CANDIDATELIST, dwOffset[lpSrc->dwCount] );
180 if ( lpDst && dwBufLen > 0 )
182 *lpDst = *lpSrc;
183 lpDst->dwOffset[0] = ret;
186 for ( i = 0; i < lpSrc->dwCount; i++)
188 LPBYTE src = (LPBYTE)lpSrc + lpSrc->dwOffset[i];
190 if ( lpDst && dwBufLen > 0 )
192 LPBYTE dest = (LPBYTE)lpDst + lpDst->dwOffset[i];
194 len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1,
195 (LPSTR)dest, dwBufLen, NULL, NULL);
197 if ( i + 1 < lpSrc->dwCount )
198 lpDst->dwOffset[i+1] = lpDst->dwOffset[i] + len * sizeof(char);
199 dwBufLen -= len * sizeof(char);
201 else
202 len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, NULL, 0, NULL, NULL);
204 ret += len * sizeof(char);
207 if ( lpDst )
208 lpDst->dwSize = ret;
210 return ret;
213 static DWORD convert_candidatelist_AtoW(
214 LPCANDIDATELIST lpSrc, LPCANDIDATELIST lpDst, DWORD dwBufLen)
216 DWORD ret, i, len;
218 ret = FIELD_OFFSET( CANDIDATELIST, dwOffset[lpSrc->dwCount] );
219 if ( lpDst && dwBufLen > 0 )
221 *lpDst = *lpSrc;
222 lpDst->dwOffset[0] = ret;
225 for ( i = 0; i < lpSrc->dwCount; i++)
227 LPBYTE src = (LPBYTE)lpSrc + lpSrc->dwOffset[i];
229 if ( lpDst && dwBufLen > 0 )
231 LPBYTE dest = (LPBYTE)lpDst + lpDst->dwOffset[i];
233 len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1,
234 (LPWSTR)dest, dwBufLen);
236 if ( i + 1 < lpSrc->dwCount )
237 lpDst->dwOffset[i+1] = lpDst->dwOffset[i] + len * sizeof(WCHAR);
238 dwBufLen -= len * sizeof(WCHAR);
240 else
241 len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, NULL, 0);
243 ret += len * sizeof(WCHAR);
246 if ( lpDst )
247 lpDst->dwSize = ret;
249 return ret;
252 static struct coinit_spy *get_thread_coinit_spy(void)
254 return (struct coinit_spy *)(UINT_PTR)NtUserGetThreadInfo()->client_imm;
257 static void imm_couninit_thread(BOOL cleanup)
259 struct coinit_spy *spy;
261 TRACE("implicit COM deinitialization\n");
263 if (!(spy = get_thread_coinit_spy()) || (spy->apt_flags & IMM_APT_BROKEN))
264 return;
266 if (cleanup && spy->cookie.QuadPart)
268 pCoRevokeInitializeSpy(spy->cookie);
269 spy->cookie.QuadPart = 0;
272 if (!(spy->apt_flags & IMM_APT_INIT))
273 return;
274 spy->apt_flags &= ~IMM_APT_INIT;
276 if (spy->apt_flags & IMM_APT_CREATED)
278 spy->apt_flags &= ~IMM_APT_CREATED;
279 if (spy->apt_flags & IMM_APT_CAN_FREE)
280 pCoUninitialize();
282 if (cleanup)
283 spy->apt_flags = 0;
286 static inline struct coinit_spy *impl_from_IInitializeSpy(IInitializeSpy *iface)
288 return CONTAINING_RECORD(iface, struct coinit_spy, IInitializeSpy_iface);
291 static HRESULT WINAPI InitializeSpy_QueryInterface(IInitializeSpy *iface, REFIID riid, void **obj)
293 if (IsEqualIID(&IID_IInitializeSpy, riid) ||
294 IsEqualIID(&IID_IUnknown, riid))
296 *obj = iface;
297 IInitializeSpy_AddRef(iface);
298 return S_OK;
301 *obj = NULL;
302 return E_NOINTERFACE;
305 static ULONG WINAPI InitializeSpy_AddRef(IInitializeSpy *iface)
307 struct coinit_spy *spy = impl_from_IInitializeSpy(iface);
308 return InterlockedIncrement(&spy->ref);
311 static ULONG WINAPI InitializeSpy_Release(IInitializeSpy *iface)
313 struct coinit_spy *spy = impl_from_IInitializeSpy(iface);
314 LONG ref = InterlockedDecrement(&spy->ref);
315 if (!ref)
317 free( spy );
318 NtUserGetThreadInfo()->client_imm = 0;
320 return ref;
323 static HRESULT WINAPI InitializeSpy_PreInitialize(IInitializeSpy *iface,
324 DWORD coinit, DWORD refs)
326 struct coinit_spy *spy = impl_from_IInitializeSpy(iface);
328 if ((spy->apt_flags & IMM_APT_CREATED) &&
329 !(coinit & COINIT_APARTMENTTHREADED) && refs == 1)
331 imm_couninit_thread(TRUE);
332 spy->apt_flags |= IMM_APT_BROKEN;
334 return S_OK;
337 static HRESULT WINAPI InitializeSpy_PostInitialize(IInitializeSpy *iface,
338 HRESULT hr, DWORD coinit, DWORD refs)
340 struct coinit_spy *spy = impl_from_IInitializeSpy(iface);
342 if ((spy->apt_flags & IMM_APT_CREATED) && hr == S_FALSE && refs == 2)
343 hr = S_OK;
344 if (SUCCEEDED(hr))
345 spy->apt_flags |= IMM_APT_CAN_FREE;
346 return hr;
349 static HRESULT WINAPI InitializeSpy_PreUninitialize(IInitializeSpy *iface, DWORD refs)
351 return S_OK;
354 static HRESULT WINAPI InitializeSpy_PostUninitialize(IInitializeSpy *iface, DWORD refs)
356 struct coinit_spy *spy = impl_from_IInitializeSpy(iface);
358 TRACE("%lu %p\n", refs, ImmGetDefaultIMEWnd(0));
360 if (refs == 1 && !ImmGetDefaultIMEWnd(0))
361 imm_couninit_thread(FALSE);
362 else if (!refs)
363 spy->apt_flags &= ~IMM_APT_CAN_FREE;
364 return S_OK;
367 static const IInitializeSpyVtbl InitializeSpyVtbl =
369 InitializeSpy_QueryInterface,
370 InitializeSpy_AddRef,
371 InitializeSpy_Release,
372 InitializeSpy_PreInitialize,
373 InitializeSpy_PostInitialize,
374 InitializeSpy_PreUninitialize,
375 InitializeSpy_PostUninitialize,
378 static BOOL WINAPI init_ole32_funcs( INIT_ONCE *once, void *param, void **context )
380 HMODULE module_ole32 = GetModuleHandleA("ole32");
381 pCoRevokeInitializeSpy = (void*)GetProcAddress(module_ole32, "CoRevokeInitializeSpy");
382 pCoUninitialize = (void*)GetProcAddress(module_ole32, "CoUninitialize");
383 return TRUE;
386 static void imm_coinit_thread(void)
388 struct coinit_spy *spy;
389 HRESULT hr;
390 static INIT_ONCE init_ole32_once = INIT_ONCE_STATIC_INIT;
392 TRACE("implicit COM initialization\n");
394 if (!(spy = get_thread_coinit_spy()))
396 if (!(spy = malloc( sizeof(*spy) ))) return;
397 spy->IInitializeSpy_iface.lpVtbl = &InitializeSpyVtbl;
398 spy->ref = 1;
399 spy->cookie.QuadPart = 0;
400 spy->apt_flags = 0;
401 NtUserGetThreadInfo()->client_imm = (UINT_PTR)spy;
405 if (spy->apt_flags & (IMM_APT_INIT | IMM_APT_BROKEN))
406 return;
407 spy->apt_flags |= IMM_APT_INIT;
409 if(!spy->cookie.QuadPart)
411 hr = CoRegisterInitializeSpy(&spy->IInitializeSpy_iface, &spy->cookie);
412 if (FAILED(hr))
413 return;
416 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
417 if (SUCCEEDED(hr))
418 spy->apt_flags |= IMM_APT_CREATED;
420 InitOnceExecuteOnce(&init_ole32_once, init_ole32_funcs, NULL, NULL);
423 static struct imc *query_imc_data( HIMC handle )
425 struct imc *ret;
427 if (!handle) return NULL;
428 ret = (void *)NtUserQueryInputContext(handle, NtUserInputContextClientPtr);
429 return ret && ret->handle == handle ? ret : NULL;
432 /* lookup an IME from a HKL, must hold ime_cs */
433 static struct ime *find_ime_from_hkl( HKL hkl )
435 struct ime *ime = NULL;
436 LIST_FOR_EACH_ENTRY( ime, &ime_list, struct ime, entry )
437 if (ime->hkl == hkl) return ime;
438 return NULL;
441 BOOL WINAPI ImmFreeLayout( HKL hkl )
443 struct imc_entry *imc_entry, *imc_next;
444 struct ime *ime;
446 TRACE( "hkl %p\n", hkl );
448 EnterCriticalSection( &ime_cs );
449 if ((ime = find_ime_from_hkl( hkl )))
451 list_remove( &ime->entry );
452 if (!ime->pImeDestroy( 0 )) WARN( "ImeDestroy failed\n" );
453 LIST_FOR_EACH_ENTRY_SAFE( imc_entry, imc_next, &ime->input_contexts, struct imc_entry, entry )
455 ImmDestroyIMCC( imc_entry->context.hPrivate );
456 free( imc_entry );
459 LeaveCriticalSection( &ime_cs );
460 if (!ime) return TRUE;
462 FreeLibrary( ime->module );
463 free( ime );
464 return TRUE;
467 BOOL WINAPI ImmLoadIME( HKL hkl )
469 WCHAR buffer[MAX_PATH] = {0};
470 BOOL use_default_ime;
471 struct ime *ime;
473 TRACE( "hkl %p\n", hkl );
475 EnterCriticalSection( &ime_cs );
476 if ((ime = find_ime_from_hkl( hkl )) || !(ime = calloc( 1, sizeof(*ime) )))
478 LeaveCriticalSection( &ime_cs );
479 return !!ime;
482 if (!ImmGetIMEFileNameW( hkl, buffer, MAX_PATH )) use_default_ime = TRUE;
483 else if (!(ime->module = LoadLibraryW( buffer ))) use_default_ime = TRUE;
484 else use_default_ime = FALSE;
486 if (use_default_ime)
488 if (*buffer) WARN( "Failed to load %s, falling back to default.\n", debugstr_w(buffer) );
489 ime->module = LoadLibraryW( L"imm32" );
490 ime->pImeInquire = (void *)ImeInquire;
491 ime->pImeDestroy = ImeDestroy;
492 ime->pImeSelect = ImeSelect;
493 ime->pImeConfigure = ImeConfigure;
494 ime->pImeEscape = ImeEscape;
495 ime->pImeSetActiveContext = ImeSetActiveContext;
496 ime->pImeToAsciiEx = (void *)ImeToAsciiEx;
497 ime->pNotifyIME = NotifyIME;
498 ime->pImeRegisterWord = (void *)ImeRegisterWord;
499 ime->pImeUnregisterWord = (void *)ImeUnregisterWord;
500 ime->pImeEnumRegisterWord = (void *)ImeEnumRegisterWord;
501 ime->pImeSetCompositionString = ImeSetCompositionString;
502 ime->pImeConversionList = (void *)ImeConversionList;
503 ime->pImeProcessKey = (void *)ImeProcessKey;
504 ime->pImeGetRegisterWordStyle = (void *)ImeGetRegisterWordStyle;
505 ime->pImeGetImeMenuItems = (void *)ImeGetImeMenuItems;
507 else
509 #define LOAD_FUNCPTR( f ) \
510 if (!(ime->p##f = (void *)GetProcAddress( ime->module, #f ))) \
512 WARN( "Can't find function %s in HKL %p IME\n", #f, hkl ); \
513 goto failed; \
516 LOAD_FUNCPTR( ImeInquire );
517 LOAD_FUNCPTR( ImeDestroy );
518 LOAD_FUNCPTR( ImeSelect );
519 LOAD_FUNCPTR( ImeConfigure );
520 LOAD_FUNCPTR( ImeEscape );
521 LOAD_FUNCPTR( ImeSetActiveContext );
522 LOAD_FUNCPTR( ImeToAsciiEx );
523 LOAD_FUNCPTR( NotifyIME );
524 LOAD_FUNCPTR( ImeRegisterWord );
525 LOAD_FUNCPTR( ImeUnregisterWord );
526 LOAD_FUNCPTR( ImeEnumRegisterWord );
527 LOAD_FUNCPTR( ImeSetCompositionString );
528 LOAD_FUNCPTR( ImeConversionList );
529 LOAD_FUNCPTR( ImeProcessKey );
530 LOAD_FUNCPTR( ImeGetRegisterWordStyle );
531 LOAD_FUNCPTR( ImeGetImeMenuItems );
532 #undef LOAD_FUNCPTR
535 ime->hkl = hkl;
536 if (!ime->pImeInquire( &ime->info, buffer, 0 )) goto failed;
538 if (ime_is_unicode( ime )) lstrcpynW( ime->ui_class, buffer, ARRAY_SIZE(ime->ui_class) );
539 else MultiByteToWideChar( CP_ACP, 0, (char *)buffer, -1, ime->ui_class, ARRAY_SIZE(ime->ui_class) );
540 list_init( &ime->input_contexts );
542 list_add_tail( &ime_list, &ime->entry );
543 LeaveCriticalSection( &ime_cs );
545 TRACE( "Created IME %p for HKL %p\n", ime, hkl );
546 return TRUE;
548 failed:
549 LeaveCriticalSection( &ime_cs );
551 if (ime->module) FreeLibrary( ime->module );
552 free( ime );
553 return FALSE;
556 static struct ime *ime_acquire( HKL hkl )
558 struct ime *ime;
560 EnterCriticalSection( &ime_cs );
562 if (!ImmLoadIME( hkl )) ime = NULL;
563 else ime = find_ime_from_hkl( hkl );
565 if (ime)
567 ULONG ref = ++ime->refcount;
568 TRACE( "ime %p increasing refcount to %lu.\n", ime, ref );
571 LeaveCriticalSection( &ime_cs );
573 return ime;
576 static void ime_release( struct ime *ime )
578 ULONG ref;
580 EnterCriticalSection( &ime_cs );
582 ref = --ime->refcount;
583 TRACE( "ime %p decreasing refcount to %lu.\n", ime, ref );
585 if (!ref && (ime->info.fdwProperty & IME_PROP_END_UNLOAD))
586 ImmFreeLayout( ime->hkl );
588 LeaveCriticalSection( &ime_cs );
591 static void ime_save_input_context( struct ime *ime, HIMC himc, INPUTCONTEXT *ctx )
593 static INPUTCONTEXT default_input_context =
595 .cfCandForm = {{.dwIndex = -1}, {.dwIndex = -1}, {.dwIndex = -1}, {.dwIndex = -1}}
597 const INPUTCONTEXT old = *ctx;
598 struct imc_entry *entry;
600 *ctx = default_input_context;
601 ctx->hWnd = old.hWnd;
602 ctx->hMsgBuf = old.hMsgBuf;
603 ctx->hCompStr = old.hCompStr;
604 ctx->hCandInfo = old.hCandInfo;
605 ctx->hGuideLine = old.hGuideLine;
606 if (!(ctx->hPrivate = ImmCreateIMCC( ime->info.dwPrivateDataSize )))
607 WARN( "Failed to allocate IME private data\n" );
609 if (!(entry = malloc( sizeof(*entry) ))) return;
610 entry->himc = himc;
611 entry->context = *ctx;
613 EnterCriticalSection( &ime_cs );
615 /* reference the IME the first time the input context cache is used
616 * in the same way Windows does it, so it doesn't get destroyed and
617 * INPUTCONTEXT cache lost when keyboard layout is changed
619 if (list_empty( &ime->input_contexts )) ime->refcount++;
621 list_add_tail( &ime->input_contexts, &entry->entry );
622 LeaveCriticalSection( &ime_cs );
625 static INPUTCONTEXT *ime_find_input_context( struct ime *ime, HIMC himc )
627 struct imc_entry *entry;
629 EnterCriticalSection( &ime_cs );
630 LIST_FOR_EACH_ENTRY( entry, &ime->input_contexts, struct imc_entry, entry )
631 if (entry->himc == himc) break;
632 LeaveCriticalSection( &ime_cs );
634 if (&entry->entry == &ime->input_contexts) return NULL;
635 return &entry->context;
638 static void imc_release_ime( struct imc *imc, struct ime *ime )
640 INPUTCONTEXT *ctx;
642 if (imc->ui_hwnd) DestroyWindow( imc->ui_hwnd );
643 imc->ui_hwnd = NULL;
644 ime->pImeSelect( imc->handle, FALSE );
646 if ((ctx = ime_find_input_context( ime, imc->handle ))) *ctx = imc->IMC;
647 ime_release( ime );
650 static struct ime *imc_select_ime( struct imc *imc )
652 HKL hkl = GetKeyboardLayout( 0 );
653 struct ime *ime;
655 if ((ime = imc->ime))
657 if (ime->hkl == hkl) return ime;
658 imc->ime = NULL;
659 imc_release_ime( imc, ime );
662 if (!(imc->ime = ime_acquire( hkl )))
663 WARN( "Failed to acquire IME for HKL %p\n", hkl );
664 else
666 INPUTCONTEXT *ctx;
668 if ((ctx = ime_find_input_context( imc->ime, imc->handle ))) imc->IMC = *ctx;
669 else ime_save_input_context( imc->ime, imc->handle, &imc->IMC );
671 imc->ime->pImeSelect( imc->handle, TRUE );
674 return imc->ime;
677 static BOOL CALLBACK enum_activate_layout( HIMC himc, LPARAM lparam )
679 if (ImmLockIMC( himc )) ImmUnlockIMC( himc );
680 return TRUE;
683 BOOL WINAPI ImmActivateLayout( HKL hkl )
685 TRACE( "hkl %p\n", hkl );
687 if (hkl == GetKeyboardLayout( 0 )) return TRUE;
688 if (!ActivateKeyboardLayout( hkl, 0 )) return FALSE;
690 ImmEnumInputContext( 0, enum_activate_layout, 0 );
692 return TRUE;
695 static BOOL free_input_context_data( HIMC hIMC )
697 struct imc *data = query_imc_data( hIMC );
698 struct ime *ime;
700 if (!data) return FALSE;
702 TRACE( "Destroying %p\n", hIMC );
704 if ((ime = imc_select_ime( data ))) imc_release_ime( data, ime );
706 ImmDestroyIMCC( data->IMC.hCompStr );
707 ImmDestroyIMCC( data->IMC.hCandInfo );
708 ImmDestroyIMCC( data->IMC.hGuideLine );
709 ImmDestroyIMCC( data->IMC.hMsgBuf );
711 free( data );
713 return TRUE;
716 static void input_context_init( INPUTCONTEXT *ctx )
718 COMPOSITIONSTRING *str;
719 CANDIDATEINFO *info;
720 GUIDELINE *line;
721 UINT i;
723 if (!(ctx->hMsgBuf = ImmCreateIMCC( 0 )))
724 WARN( "Failed to allocate %p message buffer\n", ctx );
726 if (!(ctx->hCompStr = ImmCreateIMCC( sizeof(COMPOSITIONSTRING) )))
727 WARN( "Failed to allocate %p COMPOSITIONSTRING\n", ctx );
728 else if (!(str = ImmLockIMCC( ctx->hCompStr )))
729 WARN( "Failed to lock IMCC for COMPOSITIONSTRING\n" );
730 else
732 str->dwSize = sizeof(COMPOSITIONSTRING);
733 ImmUnlockIMCC( ctx->hCompStr );
736 if (!(ctx->hCandInfo = ImmCreateIMCC( sizeof(CANDIDATEINFO) )))
737 WARN( "Failed to allocate %p CANDIDATEINFO\n", ctx );
738 else if (!(info = ImmLockIMCC( ctx->hCandInfo )))
739 WARN( "Failed to lock IMCC for CANDIDATEINFO\n" );
740 else
742 info->dwSize = sizeof(CANDIDATEINFO);
743 ImmUnlockIMCC( ctx->hCandInfo );
746 if (!(ctx->hGuideLine = ImmCreateIMCC( sizeof(GUIDELINE) )))
747 WARN( "Failed to allocate %p GUIDELINE\n", ctx );
748 else if (!(line = ImmLockIMCC( ctx->hGuideLine )))
749 WARN( "Failed to lock IMCC for GUIDELINE\n" );
750 else
752 line->dwSize = sizeof(GUIDELINE);
753 ImmUnlockIMCC( ctx->hGuideLine );
756 for (i = 0; i < ARRAY_SIZE(ctx->cfCandForm); i++)
757 ctx->cfCandForm[i].dwIndex = ~0u;
760 static void IMM_FreeThreadData(void)
762 struct coinit_spy *spy;
764 free_input_context_data( UlongToHandle( NtUserGetThreadInfo()->default_imc ) );
765 if ((spy = get_thread_coinit_spy())) IInitializeSpy_Release( &spy->IInitializeSpy_iface );
768 static void IMM_FreeAllImmHkl(void)
770 struct ime *ime, *ime_next;
772 LIST_FOR_EACH_ENTRY_SAFE( ime, ime_next, &ime_list, struct ime, entry )
774 struct imc_entry *imc_entry, *imc_next;
775 list_remove( &ime->entry );
777 ime->pImeDestroy( 1 );
778 FreeLibrary( ime->module );
779 LIST_FOR_EACH_ENTRY_SAFE( imc_entry, imc_next, &ime->input_contexts, struct imc_entry, entry )
781 ImmDestroyIMCC( imc_entry->context.hPrivate );
782 free( imc_entry );
785 free( ime );
789 BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved )
791 TRACE( "instance %p, reason %lx, reserved %p\n", instance, reason, reserved );
793 switch (reason)
795 case DLL_PROCESS_ATTACH:
796 if (!User32InitializeImmEntryTable( IMM_INIT_MAGIC )) return FALSE;
797 imm32_module = instance;
798 break;
799 case DLL_THREAD_ATTACH:
800 break;
801 case DLL_THREAD_DETACH:
802 IMM_FreeThreadData();
803 break;
804 case DLL_PROCESS_DETACH:
805 if (reserved) break;
806 IMM_FreeThreadData();
807 IMM_FreeAllImmHkl();
808 break;
811 return TRUE;
814 /***********************************************************************
815 * ImmSetActiveContext (IMM32.@)
817 BOOL WINAPI ImmSetActiveContext(HWND hwnd, HIMC himc, BOOL activate)
819 struct imc *data = get_imc_data( himc );
820 struct ime *ime;
822 TRACE("(%p, %p, %x)\n", hwnd, himc, activate);
824 if (himc && !data && activate)
825 return FALSE;
827 imm_coinit_thread();
829 if (data)
831 if (activate) data->IMC.hWnd = hwnd;
832 if ((ime = imc_select_ime( data ))) ime->pImeSetActiveContext( himc, activate );
835 if (IsWindow(hwnd))
837 SendMessageW(hwnd, WM_IME_SETCONTEXT, activate, ISC_SHOWUIALL);
838 /* TODO: send WM_IME_NOTIFY */
840 SetLastError(0);
841 return TRUE;
844 /***********************************************************************
845 * ImmConfigureIMEA (IMM32.@)
847 BOOL WINAPI ImmConfigureIMEA( HKL hkl, HWND hwnd, DWORD mode, void *data )
849 struct ime *ime;
850 BOOL ret;
852 TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data );
854 if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE;
855 if (!(ime = ime_acquire( hkl ))) return FALSE;
857 if (mode != IME_CONFIG_REGISTERWORD || !ime_is_unicode( ime ))
858 ret = ime->pImeConfigure( hkl, hwnd, mode, data );
859 else
861 REGISTERWORDA *wordA = data;
862 REGISTERWORDW wordW;
863 wordW.lpWord = strdupAtoW( wordA->lpWord );
864 wordW.lpReading = strdupAtoW( wordA->lpReading );
865 ret = ime->pImeConfigure( hkl, hwnd, mode, &wordW );
866 free( wordW.lpReading );
867 free( wordW.lpWord );
870 ime_release( ime );
871 return ret;
874 /***********************************************************************
875 * ImmConfigureIMEW (IMM32.@)
877 BOOL WINAPI ImmConfigureIMEW( HKL hkl, HWND hwnd, DWORD mode, void *data )
879 struct ime *ime;
880 BOOL ret;
882 TRACE( "hkl %p, hwnd %p, mode %lu, data %p.\n", hkl, hwnd, mode, data );
884 if (mode == IME_CONFIG_REGISTERWORD && !data) return FALSE;
885 if (!(ime = ime_acquire( hkl ))) return FALSE;
887 if (mode != IME_CONFIG_REGISTERWORD || ime_is_unicode( ime ))
888 ret = ime->pImeConfigure( hkl, hwnd, mode, data );
889 else
891 REGISTERWORDW *wordW = data;
892 REGISTERWORDA wordA;
893 wordA.lpWord = strdupWtoA( wordW->lpWord );
894 wordA.lpReading = strdupWtoA( wordW->lpReading );
895 ret = ime->pImeConfigure( hkl, hwnd, mode, &wordA );
896 free( wordA.lpReading );
897 free( wordA.lpWord );
900 ime_release( ime );
901 return ret;
904 static struct imc *create_input_context( HIMC default_imc )
906 struct imc *new_context;
908 if (!(new_context = calloc( 1, sizeof(*new_context) ))) return NULL;
909 input_context_init( &new_context->IMC );
911 if (!default_imc)
912 new_context->handle = NtUserCreateInputContext((UINT_PTR)new_context);
913 else if (NtUserUpdateInputContext(default_imc, NtUserInputContextClientPtr, (UINT_PTR)new_context))
914 new_context->handle = default_imc;
915 if (!new_context->handle)
917 free_input_context_data(new_context);
918 return 0;
921 TRACE("Created context %p\n", new_context);
922 return new_context;
925 static struct imc *get_imc_data( HIMC handle )
927 struct imc *ret;
929 if ((ret = query_imc_data(handle)) || !handle) return ret;
930 return create_input_context(handle);
933 static struct imc *default_input_context(void)
935 UINT *himc = &NtUserGetThreadInfo()->default_imc;
936 if (!*himc) *himc = (UINT_PTR)NtUserCreateInputContext( 0 );
937 return get_imc_data( (HIMC)(UINT_PTR)*himc );
940 static HWND get_ime_ui_window(void)
942 struct imc *imc = default_input_context();
943 struct ime *ime;
945 if (!(ime = imc_select_ime( imc ))) return 0;
947 if (!imc->ui_hwnd)
949 imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1,
950 ImmGetDefaultIMEWnd( 0 ), 0, ime->module, 0 );
951 SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)NtUserGetWindowInputContext( GetFocus() ) );
953 return imc->ui_hwnd;
956 static void set_ime_ui_window_himc( HIMC himc )
958 HWND hwnd;
959 if (!(hwnd = get_ime_ui_window())) return;
960 SetWindowLongPtrW( hwnd, IMMGWL_IMC, (LONG_PTR)himc );
963 /***********************************************************************
964 * ImmCreateContext (IMM32.@)
966 HIMC WINAPI ImmCreateContext(void)
968 struct imc *new_context;
970 if (!(new_context = create_input_context(0))) return 0;
971 return new_context->handle;
974 static BOOL IMM_DestroyContext(HIMC hIMC)
976 if (!free_input_context_data(hIMC)) return FALSE;
977 NtUserDestroyInputContext(hIMC);
978 return TRUE;
981 /***********************************************************************
982 * ImmDestroyContext (IMM32.@)
984 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
986 if ((UINT_PTR)hIMC == NtUserGetThreadInfo()->default_imc) return FALSE;
987 if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
988 return IMM_DestroyContext(hIMC);
991 /***********************************************************************
992 * ImmAssociateContext (IMM32.@)
994 HIMC WINAPI ImmAssociateContext( HWND hwnd, HIMC new_himc )
996 HIMC old_himc;
997 UINT ret;
999 TRACE( "hwnd %p, new_himc %p\n", hwnd, new_himc );
1001 old_himc = NtUserGetWindowInputContext( hwnd );
1002 ret = NtUserAssociateInputContext( hwnd, new_himc, 0 );
1003 if (ret == AICR_FOCUS_CHANGED)
1005 ImmSetActiveContext( hwnd, old_himc, FALSE );
1006 ImmSetActiveContext( hwnd, new_himc, TRUE );
1007 if (hwnd == GetFocus()) set_ime_ui_window_himc( new_himc );
1010 return ret == AICR_FAILED ? 0 : old_himc;
1013 static BOOL CALLBACK enum_associate_context( HWND hwnd, LPARAM lparam )
1015 ImmAssociateContext( hwnd, (HIMC)lparam );
1016 return TRUE;
1019 /***********************************************************************
1020 * ImmAssociateContextEx (IMM32.@)
1022 BOOL WINAPI ImmAssociateContextEx( HWND hwnd, HIMC new_himc, DWORD flags )
1024 HIMC old_himc;
1025 UINT ret;
1027 TRACE( "hwnd %p, new_himc %p, flags %#lx\n", hwnd, new_himc, flags );
1029 if (!hwnd) return FALSE;
1031 if (flags == IACE_CHILDREN)
1033 EnumChildWindows( hwnd, enum_associate_context, (LPARAM)new_himc );
1034 return TRUE;
1037 old_himc = NtUserGetWindowInputContext( hwnd );
1038 ret = NtUserAssociateInputContext( hwnd, new_himc, flags );
1039 if (ret == AICR_FOCUS_CHANGED)
1041 if (flags == IACE_DEFAULT) new_himc = NtUserGetWindowInputContext( hwnd );
1042 ImmSetActiveContext( hwnd, old_himc, FALSE );
1043 ImmSetActiveContext( hwnd, new_himc, TRUE );
1044 if (hwnd == GetFocus()) set_ime_ui_window_himc( new_himc );
1047 return ret != AICR_FAILED;
1050 struct enum_register_word_params_WtoA
1052 REGISTERWORDENUMPROCA proc;
1053 void *user;
1056 static int CALLBACK enum_register_word_WtoA( const WCHAR *readingW, DWORD style,
1057 const WCHAR *stringW, void *user )
1059 char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW );
1060 struct enum_register_word_params_WtoA *params = user;
1061 int ret = params->proc( readingA, style, stringA, params->user );
1062 free( readingA );
1063 free( stringA );
1064 return ret;
1067 /***********************************************************************
1068 * ImmEnumRegisterWordA (IMM32.@)
1070 UINT WINAPI ImmEnumRegisterWordA( HKL hkl, REGISTERWORDENUMPROCA procA, const char *readingA,
1071 DWORD style, const char *stringA, void *user )
1073 struct ime *ime;
1074 UINT ret;
1076 TRACE( "hkl %p, procA %p, readingA %s, style %lu, stringA %s, user %p.\n", hkl, procA,
1077 debugstr_a(readingA), style, debugstr_a(stringA), user );
1079 if (!(ime = ime_acquire( hkl ))) return 0;
1081 if (!ime_is_unicode( ime ))
1082 ret = ime->pImeEnumRegisterWord( procA, readingA, style, stringA, user );
1083 else
1085 struct enum_register_word_params_WtoA params = {.proc = procA, .user = user};
1086 WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA );
1087 ret = ime->pImeEnumRegisterWord( enum_register_word_WtoA, readingW, style, stringW, &params );
1088 free( readingW );
1089 free( stringW );
1092 ime_release( ime );
1093 return ret;
1096 struct enum_register_word_params_AtoW
1098 REGISTERWORDENUMPROCW proc;
1099 void *user;
1102 static int CALLBACK enum_register_word_AtoW( const char *readingA, DWORD style,
1103 const char *stringA, void *user )
1105 WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA );
1106 struct enum_register_word_params_AtoW *params = user;
1107 int ret = params->proc( readingW, style, stringW, params->user );
1108 free( readingW );
1109 free( stringW );
1110 return ret;
1113 /***********************************************************************
1114 * ImmEnumRegisterWordW (IMM32.@)
1116 UINT WINAPI ImmEnumRegisterWordW( HKL hkl, REGISTERWORDENUMPROCW procW, const WCHAR *readingW,
1117 DWORD style, const WCHAR *stringW, void *user )
1119 struct ime *ime;
1120 UINT ret;
1122 TRACE( "hkl %p, procW %p, readingW %s, style %lu, stringW %s, user %p.\n", hkl, procW,
1123 debugstr_w(readingW), style, debugstr_w(stringW), user );
1125 if (!(ime = ime_acquire( hkl ))) return 0;
1127 if (ime_is_unicode( ime ))
1128 ret = ime->pImeEnumRegisterWord( procW, readingW, style, stringW, user );
1129 else
1131 struct enum_register_word_params_AtoW params = {.proc = procW, .user = user};
1132 char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW );
1133 ret = ime->pImeEnumRegisterWord( enum_register_word_AtoW, readingA, style, stringA, &params );
1134 free( readingA );
1135 free( stringA );
1138 ime_release( ime );
1139 return ret;
1142 static inline BOOL EscapeRequiresWA(UINT uEscape)
1144 if (uEscape == IME_ESC_GET_EUDC_DICTIONARY ||
1145 uEscape == IME_ESC_SET_EUDC_DICTIONARY ||
1146 uEscape == IME_ESC_IME_NAME ||
1147 uEscape == IME_ESC_GETHELPFILENAME)
1148 return TRUE;
1149 return FALSE;
1152 /***********************************************************************
1153 * ImmEscapeA (IMM32.@)
1155 LRESULT WINAPI ImmEscapeA( HKL hkl, HIMC himc, UINT code, void *data )
1157 struct ime *ime;
1158 LRESULT ret;
1160 TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data );
1162 if (!(ime = ime_acquire( hkl ))) return 0;
1164 if (!EscapeRequiresWA( code ) || !ime_is_unicode( ime ) || !data)
1165 ret = ime->pImeEscape( himc, code, data );
1166 else
1168 WCHAR buffer[81]; /* largest required buffer should be 80 */
1169 if (code == IME_ESC_SET_EUDC_DICTIONARY)
1171 MultiByteToWideChar( CP_ACP, 0, data, -1, buffer, 81 );
1172 ret = ime->pImeEscape( himc, code, buffer );
1174 else
1176 ret = ime->pImeEscape( himc, code, buffer );
1177 WideCharToMultiByte( CP_ACP, 0, buffer, -1, data, 80, NULL, NULL );
1181 ime_release( ime );
1182 return ret;
1185 /***********************************************************************
1186 * ImmEscapeW (IMM32.@)
1188 LRESULT WINAPI ImmEscapeW( HKL hkl, HIMC himc, UINT code, void *data )
1190 struct ime *ime;
1191 LRESULT ret;
1193 TRACE( "hkl %p, himc %p, code %u, data %p.\n", hkl, himc, code, data );
1195 if (!(ime = ime_acquire( hkl ))) return 0;
1197 if (!EscapeRequiresWA( code ) || ime_is_unicode( ime ) || !data)
1198 ret = ime->pImeEscape( himc, code, data );
1199 else
1201 char buffer[81]; /* largest required buffer should be 80 */
1202 if (code == IME_ESC_SET_EUDC_DICTIONARY)
1204 WideCharToMultiByte( CP_ACP, 0, data, -1, buffer, 81, NULL, NULL );
1205 ret = ime->pImeEscape( himc, code, buffer );
1207 else
1209 ret = ime->pImeEscape( himc, code, buffer );
1210 MultiByteToWideChar( CP_ACP, 0, buffer, -1, data, 80 );
1214 ime_release( ime );
1215 return ret;
1218 /***********************************************************************
1219 * ImmGetCandidateListA (IMM32.@)
1221 DWORD WINAPI ImmGetCandidateListA(
1222 HIMC hIMC, DWORD dwIndex,
1223 LPCANDIDATELIST lpCandList, DWORD dwBufLen)
1225 struct imc *data = get_imc_data( hIMC );
1226 LPCANDIDATEINFO candinfo;
1227 LPCANDIDATELIST candlist;
1228 struct ime *ime;
1229 DWORD ret = 0;
1231 TRACE("%p, %ld, %p, %ld\n", hIMC, dwIndex, lpCandList, dwBufLen);
1233 if (!data || !data->IMC.hCandInfo)
1234 return 0;
1236 candinfo = ImmLockIMCC(data->IMC.hCandInfo);
1237 if (dwIndex >= candinfo->dwCount || dwIndex >= ARRAY_SIZE(candinfo->dwOffset))
1238 goto done;
1240 candlist = (LPCANDIDATELIST)((LPBYTE)candinfo + candinfo->dwOffset[dwIndex]);
1241 if ( !candlist->dwSize || !candlist->dwCount )
1242 goto done;
1244 if (!(ime = imc_select_ime( data )))
1245 ret = 0;
1246 else if (!ime_is_unicode( ime ))
1248 ret = candlist->dwSize;
1249 if ( lpCandList && dwBufLen >= ret )
1250 memcpy(lpCandList, candlist, ret);
1252 else
1253 ret = convert_candidatelist_WtoA( candlist, lpCandList, dwBufLen);
1255 done:
1256 ImmUnlockIMCC(data->IMC.hCandInfo);
1257 return ret;
1260 /***********************************************************************
1261 * ImmGetCandidateListCountA (IMM32.@)
1263 DWORD WINAPI ImmGetCandidateListCountA(
1264 HIMC hIMC, LPDWORD lpdwListCount)
1266 struct imc *data = get_imc_data( hIMC );
1267 LPCANDIDATEINFO candinfo;
1268 DWORD ret, count;
1269 struct ime *ime;
1271 TRACE("%p, %p\n", hIMC, lpdwListCount);
1273 if (!data || !lpdwListCount || !data->IMC.hCandInfo)
1274 return 0;
1276 candinfo = ImmLockIMCC(data->IMC.hCandInfo);
1278 *lpdwListCount = count = candinfo->dwCount;
1280 if (!(ime = imc_select_ime( data )))
1281 ret = 0;
1282 else if (!ime_is_unicode( ime ))
1283 ret = candinfo->dwSize;
1284 else
1286 ret = sizeof(CANDIDATEINFO);
1287 while ( count-- )
1288 ret += ImmGetCandidateListA(hIMC, count, NULL, 0);
1291 ImmUnlockIMCC(data->IMC.hCandInfo);
1292 return ret;
1295 /***********************************************************************
1296 * ImmGetCandidateListCountW (IMM32.@)
1298 DWORD WINAPI ImmGetCandidateListCountW(
1299 HIMC hIMC, LPDWORD lpdwListCount)
1301 struct imc *data = get_imc_data( hIMC );
1302 LPCANDIDATEINFO candinfo;
1303 DWORD ret, count;
1304 struct ime *ime;
1306 TRACE("%p, %p\n", hIMC, lpdwListCount);
1308 if (!data || !lpdwListCount || !data->IMC.hCandInfo)
1309 return 0;
1311 candinfo = ImmLockIMCC(data->IMC.hCandInfo);
1313 *lpdwListCount = count = candinfo->dwCount;
1315 if (!(ime = imc_select_ime( data )))
1316 ret = 0;
1317 else if (ime_is_unicode( ime ))
1318 ret = candinfo->dwSize;
1319 else
1321 ret = sizeof(CANDIDATEINFO);
1322 while ( count-- )
1323 ret += ImmGetCandidateListW(hIMC, count, NULL, 0);
1326 ImmUnlockIMCC(data->IMC.hCandInfo);
1327 return ret;
1330 /***********************************************************************
1331 * ImmGetCandidateListW (IMM32.@)
1333 DWORD WINAPI ImmGetCandidateListW(
1334 HIMC hIMC, DWORD dwIndex,
1335 LPCANDIDATELIST lpCandList, DWORD dwBufLen)
1337 struct imc *data = get_imc_data( hIMC );
1338 LPCANDIDATEINFO candinfo;
1339 LPCANDIDATELIST candlist;
1340 struct ime *ime;
1341 DWORD ret = 0;
1343 TRACE("%p, %ld, %p, %ld\n", hIMC, dwIndex, lpCandList, dwBufLen);
1345 if (!data || !data->IMC.hCandInfo)
1346 return 0;
1348 candinfo = ImmLockIMCC(data->IMC.hCandInfo);
1349 if (dwIndex >= candinfo->dwCount || dwIndex >= ARRAY_SIZE(candinfo->dwOffset))
1350 goto done;
1352 candlist = (LPCANDIDATELIST)((LPBYTE)candinfo + candinfo->dwOffset[dwIndex]);
1353 if ( !candlist->dwSize || !candlist->dwCount )
1354 goto done;
1356 if (!(ime = imc_select_ime( data )))
1357 ret = 0;
1358 else if (ime_is_unicode( ime ))
1360 ret = candlist->dwSize;
1361 if ( lpCandList && dwBufLen >= ret )
1362 memcpy(lpCandList, candlist, ret);
1364 else
1365 ret = convert_candidatelist_AtoW( candlist, lpCandList, dwBufLen);
1367 done:
1368 ImmUnlockIMCC(data->IMC.hCandInfo);
1369 return ret;
1372 /***********************************************************************
1373 * ImmGetCandidateWindow (IMM32.@)
1375 BOOL WINAPI ImmGetCandidateWindow( HIMC himc, DWORD index, CANDIDATEFORM *candidate )
1377 INPUTCONTEXT *ctx;
1378 BOOL ret = TRUE;
1380 TRACE( "himc %p, index %lu, candidate %p\n", himc, index, candidate );
1382 if (!candidate) return FALSE;
1384 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
1385 if (ctx->cfCandForm[index].dwIndex == -1) ret = FALSE;
1386 else *candidate = ctx->cfCandForm[index];
1387 ImmUnlockIMC( himc );
1389 return ret;
1392 /***********************************************************************
1393 * ImmGetCompositionFontA (IMM32.@)
1395 BOOL WINAPI ImmGetCompositionFontA( HIMC himc, LOGFONTA *fontA )
1397 INPUTCONTEXT *ctx;
1398 LOGFONTW fontW;
1399 BOOL ret = TRUE;
1401 TRACE( "himc %p, fontA %p\n", himc, fontA );
1403 if (!fontA) return FALSE;
1405 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
1406 if (!(ctx->fdwInit & INIT_LOGFONT)) ret = FALSE;
1407 else if (!input_context_is_unicode( ctx )) *fontA = ctx->lfFont.A;
1408 else if ((ret = ImmGetCompositionFontW( himc, &fontW )))
1410 memcpy( fontA, &fontW, offsetof(LOGFONTA, lfFaceName) );
1411 WideCharToMultiByte( CP_ACP, 0, fontW.lfFaceName, -1, fontA->lfFaceName, LF_FACESIZE, NULL, NULL );
1413 ImmUnlockIMC( himc );
1415 return ret;
1418 /***********************************************************************
1419 * ImmGetCompositionFontW (IMM32.@)
1421 BOOL WINAPI ImmGetCompositionFontW( HIMC himc, LOGFONTW *fontW )
1423 INPUTCONTEXT *ctx;
1424 LOGFONTA fontA;
1425 BOOL ret = TRUE;
1427 TRACE( "himc %p, fontW %p\n", himc, fontW );
1429 if (!fontW) return FALSE;
1431 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
1432 if (!(ctx->fdwInit & INIT_LOGFONT)) ret = FALSE;
1433 else if (input_context_is_unicode( ctx )) *fontW = ctx->lfFont.W;
1434 else if ((ret = ImmGetCompositionFontA( himc, &fontA )))
1436 memcpy( fontW, &fontA, offsetof(LOGFONTW, lfFaceName) );
1437 MultiByteToWideChar( CP_ACP, 0, fontA.lfFaceName, -1, fontW->lfFaceName, LF_FACESIZE );
1439 ImmUnlockIMC( himc );
1441 return ret;
1445 /* Helpers for the GetCompositionString functions */
1447 /* Source encoding is defined by context, source length is always given in respective characters. Destination buffer
1448 length is always in bytes. */
1449 static INT CopyCompStringIMEtoClient( BOOL src_unicode, const void *src, INT src_len,
1450 void *dst, INT dst_len, BOOL dst_unicode )
1452 int char_size = dst_unicode ? sizeof(WCHAR) : sizeof(char);
1453 INT ret;
1455 if (src_unicode ^ dst_unicode)
1457 if (dst_unicode)
1458 ret = MultiByteToWideChar(CP_ACP, 0, src, src_len, dst, dst_len / sizeof(WCHAR));
1459 else
1460 ret = WideCharToMultiByte(CP_ACP, 0, src, src_len, dst, dst_len, NULL, NULL);
1461 ret *= char_size;
1463 else
1465 if (dst_len)
1467 ret = min(src_len * char_size, dst_len);
1468 memcpy(dst, src, ret);
1470 else
1471 ret = src_len * char_size;
1474 return ret;
1477 /* Composition string encoding is defined by context, returned attributes correspond to string, converted according to
1478 passed mode. String length is in characters, attributes are in byte arrays. */
1479 static INT CopyCompAttrIMEtoClient( BOOL src_unicode, const BYTE *src, INT src_len, const void *comp_string, INT str_len,
1480 BYTE *dst, INT dst_len, BOOL unicode )
1482 union
1484 const void *str;
1485 const WCHAR *strW;
1486 const char *strA;
1487 } string;
1488 INT rc;
1490 string.str = comp_string;
1492 if (src_unicode && !unicode)
1494 rc = WideCharToMultiByte(CP_ACP, 0, string.strW, str_len, NULL, 0, NULL, NULL);
1495 if (dst_len)
1497 int i, j = 0, k = 0;
1499 if (rc < dst_len)
1500 dst_len = rc;
1501 for (i = 0; i < str_len; ++i)
1503 int len;
1505 len = WideCharToMultiByte(CP_ACP, 0, string.strW + i, 1, NULL, 0, NULL, NULL);
1506 for (; len > 0; --len)
1508 dst[j++] = src[k];
1510 if (j >= dst_len)
1511 goto end;
1513 ++k;
1515 end:
1516 rc = j;
1519 else if (!src_unicode && unicode)
1521 rc = MultiByteToWideChar(CP_ACP, 0, string.strA, str_len, NULL, 0);
1522 if (dst_len)
1524 int i, j = 0;
1526 if (rc < dst_len)
1527 dst_len = rc;
1528 for (i = 0; i < str_len; ++i)
1530 if (IsDBCSLeadByte(string.strA[i]))
1531 continue;
1533 dst[j++] = src[i];
1535 if (j >= dst_len)
1536 break;
1538 rc = j;
1541 else
1543 memcpy(dst, src, min(src_len, dst_len));
1544 rc = src_len;
1547 return rc;
1550 static INT CopyCompClauseIMEtoClient( BOOL src_unicode, LPBYTE source, INT slen, LPBYTE ssource,
1551 LPBYTE target, INT tlen, BOOL unicode )
1553 INT rc;
1555 if (src_unicode && !unicode)
1557 if (tlen)
1559 int i;
1561 if (slen < tlen)
1562 tlen = slen;
1563 tlen /= sizeof (DWORD);
1564 for (i = 0; i < tlen; ++i)
1566 ((DWORD *)target)[i] = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource,
1567 ((DWORD *)source)[i],
1568 NULL, 0,
1569 NULL, NULL);
1571 rc = sizeof (DWORD) * i;
1573 else
1574 rc = slen;
1576 else if (!src_unicode && unicode)
1578 if (tlen)
1580 int i;
1582 if (slen < tlen)
1583 tlen = slen;
1584 tlen /= sizeof (DWORD);
1585 for (i = 0; i < tlen; ++i)
1587 ((DWORD *)target)[i] = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource,
1588 ((DWORD *)source)[i],
1589 NULL, 0);
1591 rc = sizeof (DWORD) * i;
1593 else
1594 rc = slen;
1596 else
1598 memcpy( target, source, min(slen,tlen));
1599 rc = slen;
1602 return rc;
1605 static INT CopyCompOffsetIMEtoClient( BOOL src_unicode, DWORD offset, LPBYTE ssource, BOOL unicode )
1607 int rc;
1609 if (src_unicode && !unicode)
1611 rc = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)ssource, offset, NULL, 0, NULL, NULL);
1613 else if (!src_unicode && unicode)
1615 rc = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ssource, offset, NULL, 0);
1617 else
1618 rc = offset;
1620 return rc;
1623 static LONG ImmGetCompositionStringT( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf,
1624 DWORD dwBufLen, BOOL unicode)
1626 LONG rc = 0;
1627 struct imc *data = get_imc_data( hIMC );
1628 LPCOMPOSITIONSTRING compstr;
1629 BOOL src_unicode;
1630 struct ime *ime;
1631 LPBYTE compdata;
1633 TRACE("(%p, 0x%lx, %p, %ld)\n", hIMC, dwIndex, lpBuf, dwBufLen);
1635 if (!data)
1636 return FALSE;
1638 if (!data->IMC.hCompStr)
1639 return FALSE;
1641 if (!(ime = imc_select_ime( data )))
1642 return FALSE;
1643 src_unicode = ime_is_unicode( ime );
1645 compdata = ImmLockIMCC(data->IMC.hCompStr);
1646 compstr = (LPCOMPOSITIONSTRING)compdata;
1648 switch (dwIndex)
1650 case GCS_RESULTSTR:
1651 TRACE("GCS_RESULTSTR\n");
1652 rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwResultStrOffset, compstr->dwResultStrLen, lpBuf, dwBufLen, unicode);
1653 break;
1654 case GCS_COMPSTR:
1655 TRACE("GCS_COMPSTR\n");
1656 rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen, lpBuf, dwBufLen, unicode);
1657 break;
1658 case GCS_COMPATTR:
1659 TRACE("GCS_COMPATTR\n");
1660 rc = CopyCompAttrIMEtoClient(src_unicode, compdata + compstr->dwCompAttrOffset, compstr->dwCompAttrLen,
1661 compdata + compstr->dwCompStrOffset, compstr->dwCompStrLen,
1662 lpBuf, dwBufLen, unicode);
1663 break;
1664 case GCS_COMPCLAUSE:
1665 TRACE("GCS_COMPCLAUSE\n");
1666 rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwCompClauseOffset,compstr->dwCompClauseLen,
1667 compdata + compstr->dwCompStrOffset,
1668 lpBuf, dwBufLen, unicode);
1669 break;
1670 case GCS_RESULTCLAUSE:
1671 TRACE("GCS_RESULTCLAUSE\n");
1672 rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwResultClauseOffset,compstr->dwResultClauseLen,
1673 compdata + compstr->dwResultStrOffset,
1674 lpBuf, dwBufLen, unicode);
1675 break;
1676 case GCS_RESULTREADSTR:
1677 TRACE("GCS_RESULTREADSTR\n");
1678 rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwResultReadStrOffset, compstr->dwResultReadStrLen, lpBuf, dwBufLen, unicode);
1679 break;
1680 case GCS_RESULTREADCLAUSE:
1681 TRACE("GCS_RESULTREADCLAUSE\n");
1682 rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwResultReadClauseOffset,compstr->dwResultReadClauseLen,
1683 compdata + compstr->dwResultStrOffset,
1684 lpBuf, dwBufLen, unicode);
1685 break;
1686 case GCS_COMPREADSTR:
1687 TRACE("GCS_COMPREADSTR\n");
1688 rc = CopyCompStringIMEtoClient(src_unicode, compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen, lpBuf, dwBufLen, unicode);
1689 break;
1690 case GCS_COMPREADATTR:
1691 TRACE("GCS_COMPREADATTR\n");
1692 rc = CopyCompAttrIMEtoClient(src_unicode, compdata + compstr->dwCompReadAttrOffset, compstr->dwCompReadAttrLen,
1693 compdata + compstr->dwCompReadStrOffset, compstr->dwCompReadStrLen,
1694 lpBuf, dwBufLen, unicode);
1695 break;
1696 case GCS_COMPREADCLAUSE:
1697 TRACE("GCS_COMPREADCLAUSE\n");
1698 rc = CopyCompClauseIMEtoClient(src_unicode, compdata + compstr->dwCompReadClauseOffset,compstr->dwCompReadClauseLen,
1699 compdata + compstr->dwCompStrOffset,
1700 lpBuf, dwBufLen, unicode);
1701 break;
1702 case GCS_CURSORPOS:
1703 TRACE("GCS_CURSORPOS\n");
1704 rc = CopyCompOffsetIMEtoClient(src_unicode, compstr->dwCursorPos, compdata + compstr->dwCompStrOffset, unicode);
1705 break;
1706 case GCS_DELTASTART:
1707 TRACE("GCS_DELTASTART\n");
1708 rc = CopyCompOffsetIMEtoClient(src_unicode, compstr->dwDeltaStart, compdata + compstr->dwCompStrOffset, unicode);
1709 break;
1710 default:
1711 FIXME("Unhandled index 0x%lx\n",dwIndex);
1712 break;
1715 ImmUnlockIMCC(data->IMC.hCompStr);
1717 return rc;
1720 /***********************************************************************
1721 * ImmGetCompositionStringA (IMM32.@)
1723 LONG WINAPI ImmGetCompositionStringA(
1724 HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
1726 return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, FALSE);
1730 /***********************************************************************
1731 * ImmGetCompositionStringW (IMM32.@)
1733 LONG WINAPI ImmGetCompositionStringW(
1734 HIMC hIMC, DWORD dwIndex,
1735 LPVOID lpBuf, DWORD dwBufLen)
1737 return ImmGetCompositionStringT(hIMC, dwIndex, lpBuf, dwBufLen, TRUE);
1740 /***********************************************************************
1741 * ImmGetCompositionWindow (IMM32.@)
1743 BOOL WINAPI ImmGetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition )
1745 INPUTCONTEXT *ctx;
1746 BOOL ret;
1748 TRACE( "himc %p, composition %p\n", himc, composition );
1750 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
1751 if ((ret = !!(ctx->fdwInit & INIT_COMPFORM))) *composition = ctx->cfCompForm;
1752 ImmUnlockIMC( himc );
1754 return ret;
1757 /***********************************************************************
1758 * ImmGetContext (IMM32.@)
1761 HIMC WINAPI ImmGetContext( HWND hwnd )
1763 TRACE( "hwnd %p\n", hwnd );
1764 return NtUserGetWindowInputContext( hwnd );
1767 /***********************************************************************
1768 * ImmGetConversionListA (IMM32.@)
1770 DWORD WINAPI ImmGetConversionListA( HKL hkl, HIMC himc, const char *srcA, CANDIDATELIST *listA,
1771 DWORD lengthA, UINT flags )
1773 struct ime *ime;
1774 DWORD ret;
1776 TRACE( "hkl %p, himc %p, srcA %s, listA %p, lengthA %lu, flags %#x.\n", hkl, himc,
1777 debugstr_a(srcA), listA, lengthA, flags );
1779 if (!(ime = ime_acquire( hkl ))) return 0;
1781 if (!ime_is_unicode( ime ))
1782 ret = ime->pImeConversionList( himc, srcA, listA, lengthA, flags );
1783 else
1785 CANDIDATELIST *listW;
1786 WCHAR *srcW = strdupAtoW( srcA );
1787 DWORD lengthW = ime->pImeConversionList( himc, srcW, NULL, 0, flags );
1789 if (!(listW = malloc( lengthW ))) ret = 0;
1790 else
1792 ime->pImeConversionList( himc, srcW, listW, lengthW, flags );
1793 ret = convert_candidatelist_WtoA( listW, listA, lengthA );
1794 free( listW );
1796 free( srcW );
1799 ime_release( ime );
1800 return ret;
1803 /***********************************************************************
1804 * ImmGetConversionListW (IMM32.@)
1806 DWORD WINAPI ImmGetConversionListW( HKL hkl, HIMC himc, const WCHAR *srcW, CANDIDATELIST *listW,
1807 DWORD lengthW, UINT flags )
1809 struct ime *ime;
1810 DWORD ret;
1812 TRACE( "hkl %p, himc %p, srcW %s, listW %p, lengthW %lu, flags %#x.\n", hkl, himc,
1813 debugstr_w(srcW), listW, lengthW, flags );
1815 if (!(ime = ime_acquire( hkl ))) return 0;
1817 if (ime_is_unicode( ime ))
1818 ret = ime->pImeConversionList( himc, srcW, listW, lengthW, flags );
1819 else
1821 CANDIDATELIST *listA;
1822 char *srcA = strdupWtoA( srcW );
1823 DWORD lengthA = ime->pImeConversionList( himc, srcA, NULL, 0, flags );
1825 if (!(listA = malloc( lengthA ))) ret = 0;
1826 else
1828 ime->pImeConversionList( himc, srcA, listA, lengthA, flags );
1829 ret = convert_candidatelist_AtoW( listA, listW, lengthW );
1830 free( listA );
1832 free( srcA );
1835 ime_release( ime );
1836 return ret;
1839 /***********************************************************************
1840 * ImmGetConversionStatus (IMM32.@)
1842 BOOL WINAPI ImmGetConversionStatus( HIMC himc, DWORD *conversion, DWORD *sentence )
1844 INPUTCONTEXT *ctx;
1846 TRACE( "himc %p, conversion %p, sentence %p\n", himc, conversion, sentence );
1848 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
1849 if (conversion) *conversion = ctx->fdwConversion;
1850 if (sentence) *sentence = ctx->fdwSentence;
1851 ImmUnlockIMC( himc );
1853 return TRUE;
1856 /***********************************************************************
1857 * ImmGetDefaultIMEWnd (IMM32.@)
1859 HWND WINAPI ImmGetDefaultIMEWnd(HWND hWnd)
1861 return NtUserGetDefaultImeWindow(hWnd);
1864 /***********************************************************************
1865 * ImmGetDescriptionA (IMM32.@)
1867 UINT WINAPI ImmGetDescriptionA( HKL hkl, LPSTR bufferA, UINT lengthA )
1869 WCHAR *bufferW;
1870 DWORD lengthW;
1872 TRACE( "hkl %p, bufferA %p, lengthA %d\n", hkl, bufferA, lengthA );
1874 if (!(lengthW = ImmGetDescriptionW( hkl, NULL, 0 ))) return 0;
1875 if (!(bufferW = malloc( (lengthW + 1) * sizeof(WCHAR) ))) return 0;
1876 lengthW = ImmGetDescriptionW( hkl, bufferW, lengthW + 1 );
1877 lengthA = WideCharToMultiByte( CP_ACP, 0, bufferW, lengthW, bufferA,
1878 bufferA ? lengthA : 0, NULL, NULL );
1879 if (bufferA) bufferA[lengthA] = 0;
1880 free( bufferW );
1882 return lengthA;
1885 /***********************************************************************
1886 * ImmGetDescriptionW (IMM32.@)
1888 UINT WINAPI ImmGetDescriptionW( HKL hkl, WCHAR *buffer, UINT length )
1890 WCHAR path[MAX_PATH];
1891 HKEY hkey = 0;
1892 DWORD size;
1894 TRACE( "hkl %p, buffer %p, length %u\n", hkl, buffer, length );
1896 swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG)(ULONG_PTR)hkl );
1897 if (RegOpenKeyW( HKEY_LOCAL_MACHINE, path, &hkey )) return 0;
1899 size = ARRAY_SIZE(path) * sizeof(WCHAR);
1900 if (RegGetValueW( hkey, NULL, L"Layout Text", RRF_RT_REG_SZ, NULL, path, &size )) *path = 0;
1901 RegCloseKey( hkey );
1903 size = wcslen( path );
1904 if (!buffer) return size;
1906 lstrcpynW( buffer, path, length );
1907 return wcslen( buffer );
1910 /***********************************************************************
1911 * ImmGetGuideLineA (IMM32.@)
1913 DWORD WINAPI ImmGetGuideLineA(
1914 HIMC hIMC, DWORD dwIndex, LPSTR lpBuf, DWORD dwBufLen)
1916 FIXME("(%p, %ld, %s, %ld): stub\n",
1917 hIMC, dwIndex, debugstr_a(lpBuf), dwBufLen
1919 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1920 return 0;
1923 /***********************************************************************
1924 * ImmGetGuideLineW (IMM32.@)
1926 DWORD WINAPI ImmGetGuideLineW(HIMC hIMC, DWORD dwIndex, LPWSTR lpBuf, DWORD dwBufLen)
1928 FIXME("(%p, %ld, %s, %ld): stub\n",
1929 hIMC, dwIndex, debugstr_w(lpBuf), dwBufLen
1931 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1932 return 0;
1935 /***********************************************************************
1936 * ImmGetIMEFileNameA (IMM32.@)
1938 UINT WINAPI ImmGetIMEFileNameA( HKL hkl, char *bufferA, UINT lengthA )
1940 WCHAR *bufferW;
1941 DWORD lengthW;
1943 TRACE( "hkl %p, bufferA %p, lengthA %d\n", hkl, bufferA, lengthA );
1945 if (!(lengthW = ImmGetIMEFileNameW( hkl, NULL, 0 ))) return 0;
1946 if (!(bufferW = malloc( (lengthW + 1) * sizeof(WCHAR) ))) return 0;
1947 lengthW = ImmGetIMEFileNameW( hkl, bufferW, lengthW + 1 );
1948 lengthA = WideCharToMultiByte( CP_ACP, 0, bufferW, lengthW, bufferA,
1949 bufferA ? lengthA : 0, NULL, NULL );
1950 if (bufferA) bufferA[lengthA] = 0;
1951 free( bufferW );
1953 return lengthA;
1956 /***********************************************************************
1957 * ImmGetIMEFileNameW (IMM32.@)
1959 UINT WINAPI ImmGetIMEFileNameW( HKL hkl, WCHAR *buffer, UINT length )
1961 WCHAR path[MAX_PATH];
1962 HKEY hkey = 0;
1963 DWORD size;
1965 TRACE( "hkl %p, buffer %p, length %u\n", hkl, buffer, length );
1967 swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG)(ULONG_PTR)hkl );
1968 if (RegOpenKeyW( HKEY_LOCAL_MACHINE, path, &hkey )) return 0;
1970 size = ARRAY_SIZE(path) * sizeof(WCHAR);
1971 if (RegGetValueW( hkey, NULL, L"Ime File", RRF_RT_REG_SZ, NULL, path, &size )) *path = 0;
1972 RegCloseKey( hkey );
1974 size = wcslen( path );
1975 if (!buffer) return size;
1977 lstrcpynW( buffer, path, length );
1978 return wcslen( buffer );
1981 /***********************************************************************
1982 * ImmGetOpenStatus (IMM32.@)
1984 BOOL WINAPI ImmGetOpenStatus( HIMC himc )
1986 INPUTCONTEXT *ctx;
1987 BOOL status;
1989 TRACE( "himc %p\n", himc );
1991 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
1992 status = ctx->fOpen;
1993 ImmUnlockIMC( himc );
1995 return status;
1998 /***********************************************************************
1999 * ImmGetProperty (IMM32.@)
2001 DWORD WINAPI ImmGetProperty( HKL hkl, DWORD index )
2003 struct ime *ime;
2004 DWORD ret;
2006 TRACE( "hkl %p, index %lu.\n", hkl, index );
2008 if (!(ime = ime_acquire( hkl ))) return 0;
2010 switch (index)
2012 case IGP_PROPERTY: ret = ime->info.fdwProperty; break;
2013 case IGP_CONVERSION: ret = ime->info.fdwConversionCaps; break;
2014 case IGP_SENTENCE: ret = ime->info.fdwSentenceCaps; break;
2015 case IGP_SETCOMPSTR: ret = ime->info.fdwSCSCaps; break;
2016 case IGP_SELECT: ret = ime->info.fdwSelectCaps; break;
2017 case IGP_GETIMEVERSION: ret = IMEVER_0400; break;
2018 case IGP_UI: ret = 0; break;
2019 default: ret = 0; break;
2022 ime_release( ime );
2023 return ret;
2026 /***********************************************************************
2027 * ImmGetRegisterWordStyleA (IMM32.@)
2029 UINT WINAPI ImmGetRegisterWordStyleA( HKL hkl, UINT count, STYLEBUFA *styleA )
2031 struct ime *ime;
2032 UINT ret;
2034 TRACE( "hkl %p, count %u, styleA %p.\n", hkl, count, styleA );
2036 if (!(ime = ime_acquire( hkl ))) return 0;
2038 if (!ime_is_unicode( ime ))
2039 ret = ime->pImeGetRegisterWordStyle( count, styleA );
2040 else
2042 STYLEBUFW styleW;
2043 ret = ime->pImeGetRegisterWordStyle( count, &styleW );
2044 WideCharToMultiByte( CP_ACP, 0, styleW.szDescription, -1, styleA->szDescription, 32, NULL, NULL );
2045 styleA->dwStyle = styleW.dwStyle;
2048 ime_release( ime );
2049 return ret;
2052 /***********************************************************************
2053 * ImmGetRegisterWordStyleW (IMM32.@)
2055 UINT WINAPI ImmGetRegisterWordStyleW( HKL hkl, UINT count, STYLEBUFW *styleW )
2057 struct ime *ime;
2058 UINT ret;
2060 TRACE( "hkl %p, count %u, styleW %p.\n", hkl, count, styleW );
2062 if (!(ime = ime_acquire( hkl ))) return 0;
2064 if (ime_is_unicode( ime ))
2065 ret = ime->pImeGetRegisterWordStyle( count, styleW );
2066 else
2068 STYLEBUFA styleA;
2069 ret = ime->pImeGetRegisterWordStyle( count, &styleA );
2070 MultiByteToWideChar( CP_ACP, 0, styleA.szDescription, -1, styleW->szDescription, 32 );
2071 styleW->dwStyle = styleA.dwStyle;
2074 ime_release( ime );
2075 return ret;
2078 /***********************************************************************
2079 * ImmGetStatusWindowPos (IMM32.@)
2081 BOOL WINAPI ImmGetStatusWindowPos( HIMC himc, POINT *pos )
2083 INPUTCONTEXT *ctx;
2084 BOOL ret;
2086 TRACE( "himc %p, pos %p\n", himc, pos );
2088 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2089 if ((ret = !!(ctx->fdwInit & INIT_STATUSWNDPOS))) *pos = ctx->ptStatusWndPos;
2090 ImmUnlockIMC( himc );
2092 return ret;
2095 /***********************************************************************
2096 * ImmGetVirtualKey (IMM32.@)
2098 UINT WINAPI ImmGetVirtualKey( HWND hwnd )
2100 HIMC himc = ImmGetContext( hwnd );
2101 struct imc *imc;
2103 TRACE( "%p\n", hwnd );
2105 if ((imc = get_imc_data( himc ))) return imc->vkey;
2106 return VK_PROCESSKEY;
2109 /***********************************************************************
2110 * ImmInstallIMEA (IMM32.@)
2112 HKL WINAPI ImmInstallIMEA( const char *filenameA, const char *descriptionA )
2114 WCHAR *filenameW = strdupAtoW( filenameA ), *descriptionW = strdupAtoW( descriptionA );
2115 HKL hkl;
2117 TRACE( "filenameA %s, descriptionA %s\n", debugstr_a(filenameA), debugstr_a(descriptionA) );
2119 hkl = ImmInstallIMEW( filenameW, descriptionW );
2120 free( descriptionW );
2121 free( filenameW );
2123 return hkl;
2126 static LCID get_ime_file_lang( const WCHAR *filename )
2128 DWORD *languages;
2129 LCID lcid = 0;
2130 void *info;
2131 UINT len;
2133 if (!(len = GetFileVersionInfoSizeW( filename, NULL ))) return 0;
2134 if (!(info = malloc( len ))) goto done;
2135 if (!GetFileVersionInfoW( filename, 0, len, info )) goto done;
2136 if (!VerQueryValueW( info, L"\\VarFileInfo\\Translation", (void **)&languages, &len ) || !len) goto done;
2137 lcid = languages[0];
2139 done:
2140 free( info );
2141 return lcid;
2144 /***********************************************************************
2145 * ImmInstallIMEW (IMM32.@)
2147 HKL WINAPI ImmInstallIMEW( const WCHAR *filename, const WCHAR *description )
2149 WCHAR path[ARRAY_SIZE(layouts_formatW)+8], buffer[MAX_PATH];
2150 LCID lcid;
2151 WORD count = 0x20;
2152 const WCHAR *tmp;
2153 DWORD length;
2154 HKEY hkey;
2155 HKL hkl;
2157 TRACE( "filename %s, description %s\n", debugstr_w(filename), debugstr_w(description) );
2159 if (!filename || !description || !(lcid = get_ime_file_lang( filename )))
2161 SetLastError( ERROR_INVALID_PARAMETER );
2162 return 0;
2165 while (count < 0xfff)
2167 DWORD disposition = 0;
2169 hkl = (HKL)(UINT_PTR)MAKELONG( lcid, 0xe000 | count );
2170 swprintf( path, ARRAY_SIZE(path), layouts_formatW, (ULONG)(ULONG_PTR)hkl);
2171 if (!RegCreateKeyExW( HKEY_LOCAL_MACHINE, path, 0, NULL, 0,
2172 KEY_WRITE, NULL, &hkey, &disposition ))
2174 if (disposition == REG_CREATED_NEW_KEY) break;
2175 RegCloseKey( hkey );
2178 count++;
2181 if (count == 0xfff)
2183 WARN("Unable to find slot to install IME\n");
2184 return 0;
2187 if ((tmp = wcsrchr( filename, '\\' ))) tmp++;
2188 else tmp = filename;
2190 length = LCMapStringW( LOCALE_USER_DEFAULT, LCMAP_UPPERCASE, tmp, -1, buffer, ARRAY_SIZE(buffer) );
2192 if (RegSetValueExW( hkey, L"Ime File", 0, REG_SZ, (const BYTE *)buffer, length * sizeof(WCHAR) ) ||
2193 RegSetValueExW( hkey, L"Layout Text", 0, REG_SZ, (const BYTE *)description,
2194 (wcslen(description) + 1) * sizeof(WCHAR) ))
2196 WARN( "Unable to write registry to install IME\n");
2197 hkl = 0;
2199 RegCloseKey( hkey );
2201 if (!hkl) RegDeleteKeyW( HKEY_LOCAL_MACHINE, path );
2202 return hkl;
2205 /***********************************************************************
2206 * ImmIsIME (IMM32.@)
2208 BOOL WINAPI ImmIsIME( HKL hkl )
2210 TRACE( "hkl %p\n", hkl );
2211 if (!hkl) return FALSE;
2212 return TRUE;
2215 /***********************************************************************
2216 * ImmIsUIMessageA (IMM32.@)
2218 BOOL WINAPI ImmIsUIMessageA(
2219 HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
2221 TRACE("(%p, %x, %Id, %Id)\n", hWndIME, msg, wParam, lParam);
2222 if ((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
2223 (msg == WM_IME_SETCONTEXT) ||
2224 (msg == WM_IME_NOTIFY) ||
2225 (msg == WM_IME_COMPOSITIONFULL) ||
2226 (msg == WM_IME_SELECT) ||
2227 (msg == 0x287 /* FIXME: WM_IME_SYSTEM */))
2229 if (hWndIME)
2230 SendMessageA(hWndIME, msg, wParam, lParam);
2232 return TRUE;
2234 return FALSE;
2237 /***********************************************************************
2238 * ImmIsUIMessageW (IMM32.@)
2240 BOOL WINAPI ImmIsUIMessageW(
2241 HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
2243 TRACE("(%p, %x, %Id, %Id)\n", hWndIME, msg, wParam, lParam);
2244 if ((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) ||
2245 (msg == WM_IME_SETCONTEXT) ||
2246 (msg == WM_IME_NOTIFY) ||
2247 (msg == WM_IME_COMPOSITIONFULL) ||
2248 (msg == WM_IME_SELECT) ||
2249 (msg == 0x287 /* FIXME: WM_IME_SYSTEM */))
2251 if (hWndIME)
2252 SendMessageW(hWndIME, msg, wParam, lParam);
2254 return TRUE;
2256 return FALSE;
2259 /***********************************************************************
2260 * ImmNotifyIME (IMM32.@)
2262 BOOL WINAPI ImmNotifyIME(
2263 HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
2265 struct imc *data = get_imc_data( hIMC );
2266 struct ime *ime;
2268 TRACE("(%p, %ld, %ld, %ld)\n",
2269 hIMC, dwAction, dwIndex, dwValue);
2271 if (hIMC == NULL)
2273 SetLastError(ERROR_SUCCESS);
2274 return FALSE;
2277 if (!data)
2279 return FALSE;
2282 if (!(ime = imc_select_ime( data ))) return FALSE;
2283 return ime->pNotifyIME( hIMC, dwAction, dwIndex, dwValue );
2286 /***********************************************************************
2287 * ImmRegisterWordA (IMM32.@)
2289 BOOL WINAPI ImmRegisterWordA( HKL hkl, const char *readingA, DWORD style, const char *stringA )
2291 struct ime *ime;
2292 BOOL ret;
2294 TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) );
2296 if (!(ime = ime_acquire( hkl ))) return FALSE;
2298 if (!ime_is_unicode( ime ))
2299 ret = ime->pImeRegisterWord( readingA, style, stringA );
2300 else
2302 WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA );
2303 ret = ime->pImeRegisterWord( readingW, style, stringW );
2304 free( readingW );
2305 free( stringW );
2308 ime_release( ime );
2309 return ret;
2312 /***********************************************************************
2313 * ImmRegisterWordW (IMM32.@)
2315 BOOL WINAPI ImmRegisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const WCHAR *stringW )
2317 struct ime *ime;
2318 BOOL ret;
2320 TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) );
2322 if (!(ime = ime_acquire( hkl ))) return FALSE;
2324 if (ime_is_unicode( ime ))
2325 ret = ime->pImeRegisterWord( readingW, style, stringW );
2326 else
2328 char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW );
2329 ret = ime->pImeRegisterWord( readingA, style, stringA );
2330 free( readingA );
2331 free( stringA );
2334 ime_release( ime );
2335 return ret;
2338 /***********************************************************************
2339 * ImmReleaseContext (IMM32.@)
2341 BOOL WINAPI ImmReleaseContext( HWND hwnd, HIMC himc )
2343 TRACE( "hwnd %p, himc %p\n", hwnd, himc );
2344 return TRUE;
2347 /***********************************************************************
2348 * ImmRequestMessageA(IMM32.@)
2350 LRESULT WINAPI ImmRequestMessageA( HIMC himc, WPARAM wparam, LPARAM lparam )
2352 INPUTCONTEXT *ctx;
2353 LRESULT res;
2355 TRACE( "himc %p, wparam %#Ix, lparam %#Ix\n", himc, wparam, lparam );
2357 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2359 switch (wparam)
2361 case IMR_CANDIDATEWINDOW:
2362 case IMR_COMPOSITIONFONT:
2363 case IMR_COMPOSITIONWINDOW:
2364 case IMR_CONFIRMRECONVERTSTRING:
2365 case IMR_DOCUMENTFEED:
2366 case IMR_QUERYCHARPOSITION:
2367 case IMR_RECONVERTSTRING:
2368 break;
2369 default:
2370 return FALSE;
2373 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2374 res = SendMessageA( ctx->hWnd, WM_IME_REQUEST, wparam, lparam );
2375 ImmUnlockIMC( himc );
2377 return res;
2380 /***********************************************************************
2381 * ImmRequestMessageW(IMM32.@)
2383 LRESULT WINAPI ImmRequestMessageW( HIMC himc, WPARAM wparam, LPARAM lparam )
2385 INPUTCONTEXT *ctx;
2386 LRESULT res;
2388 TRACE( "himc %p, wparam %#Ix, lparam %#Ix\n", himc, wparam, lparam );
2390 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2392 switch (wparam)
2394 case IMR_CANDIDATEWINDOW:
2395 case IMR_COMPOSITIONFONT:
2396 case IMR_COMPOSITIONWINDOW:
2397 case IMR_CONFIRMRECONVERTSTRING:
2398 case IMR_DOCUMENTFEED:
2399 case IMR_QUERYCHARPOSITION:
2400 case IMR_RECONVERTSTRING:
2401 break;
2402 default:
2403 return FALSE;
2406 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2407 res = SendMessageW( ctx->hWnd, WM_IME_REQUEST, wparam, lparam );
2408 ImmUnlockIMC( himc );
2410 return res;
2413 /***********************************************************************
2414 * ImmSetCandidateWindow (IMM32.@)
2416 BOOL WINAPI ImmSetCandidateWindow( HIMC himc, CANDIDATEFORM *candidate )
2418 INPUTCONTEXT *ctx;
2420 TRACE( "hwnd %p, candidate %s\n", himc, debugstr_candidate( candidate ) );
2422 if (!candidate) return FALSE;
2423 if (candidate->dwIndex >= ARRAY_SIZE(ctx->cfCandForm)) return FALSE;
2425 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2426 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2428 ctx->cfCandForm[candidate->dwIndex] = *candidate;
2430 ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCANDIDATEPOS );
2431 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCANDIDATEPOS, 1 << candidate->dwIndex );
2433 ImmUnlockIMC( himc );
2435 return TRUE;
2438 /***********************************************************************
2439 * ImmSetCompositionFontA (IMM32.@)
2441 BOOL WINAPI ImmSetCompositionFontA( HIMC himc, LOGFONTA *fontA )
2443 INPUTCONTEXT *ctx;
2444 BOOL ret = TRUE;
2446 TRACE( "hwnd %p, fontA %p\n", himc, fontA );
2448 if (!fontA) return FALSE;
2450 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2451 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2453 if (input_context_is_unicode( ctx ))
2455 LOGFONTW fontW;
2456 memcpy( &fontW, fontA, offsetof(LOGFONTW, lfFaceName) );
2457 MultiByteToWideChar( CP_ACP, 0, fontA->lfFaceName, -1, fontW.lfFaceName, LF_FACESIZE );
2458 ret = ImmSetCompositionFontW( himc, &fontW );
2460 else
2462 ctx->lfFont.A = *fontA;
2463 ctx->fdwInit |= INIT_LOGFONT;
2465 ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT );
2466 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONFONT, 0 );
2469 ImmUnlockIMC( himc );
2471 return ret;
2474 /***********************************************************************
2475 * ImmSetCompositionFontW (IMM32.@)
2477 BOOL WINAPI ImmSetCompositionFontW( HIMC himc, LOGFONTW *fontW )
2479 INPUTCONTEXT *ctx;
2480 BOOL ret = TRUE;
2482 TRACE( "hwnd %p, fontW %p\n", himc, fontW );
2484 if (!fontW) return FALSE;
2486 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2487 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2489 if (!input_context_is_unicode( ctx ))
2491 LOGFONTA fontA;
2492 memcpy( &fontA, fontW, offsetof(LOGFONTA, lfFaceName) );
2493 WideCharToMultiByte( CP_ACP, 0, fontW->lfFaceName, -1, fontA.lfFaceName, LF_FACESIZE, NULL, NULL );
2494 ret = ImmSetCompositionFontA( himc, &fontA );
2496 else
2498 ctx->lfFont.W = *fontW;
2499 ctx->fdwInit |= INIT_LOGFONT;
2501 ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONFONT );
2502 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONFONT, 0 );
2505 ImmUnlockIMC( himc );
2507 return ret;
2510 /***********************************************************************
2511 * ImmSetCompositionStringA (IMM32.@)
2513 BOOL WINAPI ImmSetCompositionStringA(
2514 HIMC hIMC, DWORD dwIndex,
2515 LPCVOID lpComp, DWORD dwCompLen,
2516 LPCVOID lpRead, DWORD dwReadLen)
2518 DWORD comp_len;
2519 DWORD read_len;
2520 WCHAR *CompBuffer = NULL;
2521 WCHAR *ReadBuffer = NULL;
2522 BOOL rc;
2523 struct imc *data = get_imc_data( hIMC );
2524 struct ime *ime;
2526 TRACE("(%p, %ld, %p, %ld, %p, %ld):\n",
2527 hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
2529 if (!data)
2530 return FALSE;
2532 if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2534 if (!(dwIndex == SCS_SETSTR ||
2535 dwIndex == SCS_CHANGEATTR ||
2536 dwIndex == SCS_CHANGECLAUSE ||
2537 dwIndex == SCS_SETRECONVERTSTRING ||
2538 dwIndex == SCS_QUERYRECONVERTSTRING))
2539 return FALSE;
2541 if (!(ime = imc_select_ime( data ))) return FALSE;
2542 if (!lpComp) dwCompLen = 0;
2543 if (!lpRead) dwReadLen = 0;
2545 if (!ime_is_unicode( ime )) return ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen );
2547 comp_len = MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, NULL, 0);
2548 if (comp_len)
2550 CompBuffer = malloc( comp_len * sizeof(WCHAR) );
2551 MultiByteToWideChar(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len);
2554 read_len = MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, NULL, 0);
2555 if (read_len)
2557 ReadBuffer = malloc( read_len * sizeof(WCHAR) );
2558 MultiByteToWideChar(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len);
2561 rc = ImmSetCompositionStringW(hIMC, dwIndex, CompBuffer, comp_len,
2562 ReadBuffer, read_len);
2564 free( CompBuffer );
2565 free( ReadBuffer );
2567 return rc;
2570 /***********************************************************************
2571 * ImmSetCompositionStringW (IMM32.@)
2573 BOOL WINAPI ImmSetCompositionStringW(
2574 HIMC hIMC, DWORD dwIndex,
2575 LPCVOID lpComp, DWORD dwCompLen,
2576 LPCVOID lpRead, DWORD dwReadLen)
2578 DWORD comp_len;
2579 DWORD read_len;
2580 CHAR *CompBuffer = NULL;
2581 CHAR *ReadBuffer = NULL;
2582 BOOL rc;
2583 struct imc *data = get_imc_data( hIMC );
2584 struct ime *ime;
2586 TRACE("(%p, %ld, %p, %ld, %p, %ld):\n",
2587 hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
2589 if (!data)
2590 return FALSE;
2592 if (NtUserQueryInputContext( hIMC, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2594 if (!(dwIndex == SCS_SETSTR ||
2595 dwIndex == SCS_CHANGEATTR ||
2596 dwIndex == SCS_CHANGECLAUSE ||
2597 dwIndex == SCS_SETRECONVERTSTRING ||
2598 dwIndex == SCS_QUERYRECONVERTSTRING))
2599 return FALSE;
2601 if (!(ime = imc_select_ime( data ))) return FALSE;
2602 if (!lpComp) dwCompLen = 0;
2603 if (!lpRead) dwReadLen = 0;
2605 if (ime_is_unicode( ime )) return ime->pImeSetCompositionString( hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen );
2607 comp_len = WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, NULL, 0, NULL,
2608 NULL);
2609 if (comp_len)
2611 CompBuffer = malloc( comp_len );
2612 WideCharToMultiByte(CP_ACP, 0, lpComp, dwCompLen, CompBuffer, comp_len,
2613 NULL, NULL);
2616 read_len = WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, NULL, 0, NULL,
2617 NULL);
2618 if (read_len)
2620 ReadBuffer = malloc( read_len );
2621 WideCharToMultiByte(CP_ACP, 0, lpRead, dwReadLen, ReadBuffer, read_len,
2622 NULL, NULL);
2625 rc = ImmSetCompositionStringA(hIMC, dwIndex, CompBuffer, comp_len,
2626 ReadBuffer, read_len);
2628 free( CompBuffer );
2629 free( ReadBuffer );
2631 return rc;
2634 /***********************************************************************
2635 * ImmSetCompositionWindow (IMM32.@)
2637 BOOL WINAPI ImmSetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition )
2639 INPUTCONTEXT *ctx;
2641 TRACE( "himc %p, composition %s\n", himc, debugstr_composition( composition ) );
2643 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2644 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2646 ctx->cfCompForm = *composition;
2647 ctx->fdwInit |= INIT_COMPFORM;
2649 ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONWINDOW );
2650 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONWINDOW, 0 );
2652 ImmUnlockIMC( himc );
2654 return TRUE;
2657 /***********************************************************************
2658 * ImmSetConversionStatus (IMM32.@)
2660 BOOL WINAPI ImmSetConversionStatus( HIMC himc, DWORD conversion, DWORD sentence )
2662 DWORD old_conversion, old_sentence;
2663 INPUTCONTEXT *ctx;
2665 TRACE( "himc %p, conversion %#lx, sentence %#lx\n", himc, conversion, sentence );
2667 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2668 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2670 if (conversion != ctx->fdwConversion)
2672 old_conversion = ctx->fdwConversion;
2673 ctx->fdwConversion = conversion;
2674 ImmNotifyIME( himc, NI_CONTEXTUPDATED, old_conversion, IMC_SETCONVERSIONMODE );
2675 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCONVERSIONMODE, 0 );
2678 if (sentence != ctx->fdwSentence)
2680 old_sentence = ctx->fdwSentence;
2681 ctx->fdwSentence = sentence;
2682 ImmNotifyIME( himc, NI_CONTEXTUPDATED, old_sentence, IMC_SETSENTENCEMODE );
2683 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETSENTENCEMODE, 0 );
2686 ImmUnlockIMC( himc );
2688 return TRUE;
2691 /***********************************************************************
2692 * ImmSetOpenStatus (IMM32.@)
2694 BOOL WINAPI ImmSetOpenStatus( HIMC himc, BOOL status )
2696 INPUTCONTEXT *ctx;
2698 TRACE( "himc %p, status %u\n", himc, status );
2700 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2701 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2703 if (status != ctx->fOpen)
2705 ctx->fOpen = status;
2706 ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETOPENSTATUS );
2707 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETOPENSTATUS, 0 );
2710 ImmUnlockIMC( himc );
2712 return TRUE;
2715 /***********************************************************************
2716 * ImmSetStatusWindowPos (IMM32.@)
2718 BOOL WINAPI ImmSetStatusWindowPos( HIMC himc, POINT *pos )
2720 INPUTCONTEXT *ctx;
2722 TRACE( "himc %p, pos %s\n", himc, wine_dbgstr_point( pos ) );
2724 if (!pos)
2726 SetLastError( ERROR_INVALID_HANDLE );
2727 return FALSE;
2730 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
2731 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
2733 ctx->ptStatusWndPos = *pos;
2734 ctx->fdwInit |= INIT_STATUSWNDPOS;
2736 ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETSTATUSWINDOWPOS );
2737 SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETSTATUSWINDOWPOS, 0 );
2739 ImmUnlockIMC( himc );
2741 return TRUE;
2744 /***********************************************************************
2745 * ImmCreateSoftKeyboard(IMM32.@)
2747 HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
2749 FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
2750 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2751 return 0;
2754 /***********************************************************************
2755 * ImmDestroySoftKeyboard(IMM32.@)
2757 BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
2759 FIXME("(%p): stub\n", hSoftWnd);
2760 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2761 return FALSE;
2764 /***********************************************************************
2765 * ImmShowSoftKeyboard(IMM32.@)
2767 BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
2769 FIXME("(%p, %d): stub\n", hSoftWnd, nCmdShow);
2770 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2771 return FALSE;
2774 /***********************************************************************
2775 * ImmSimulateHotKey (IMM32.@)
2777 BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID)
2779 FIXME("(%p, %ld): stub\n", hWnd, dwHotKeyID);
2780 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2781 return FALSE;
2784 /***********************************************************************
2785 * ImmUnregisterWordA (IMM32.@)
2787 BOOL WINAPI ImmUnregisterWordA( HKL hkl, const char *readingA, DWORD style, const char *stringA )
2789 struct ime *ime;
2790 BOOL ret;
2792 TRACE( "hkl %p, readingA %s, style %lu, stringA %s.\n", hkl, debugstr_a(readingA), style, debugstr_a(stringA) );
2794 if (!(ime = ime_acquire( hkl ))) return FALSE;
2796 if (!ime_is_unicode( ime ))
2797 ret = ime->pImeUnregisterWord( readingA, style, stringA );
2798 else
2800 WCHAR *readingW = strdupAtoW( readingA ), *stringW = strdupAtoW( stringA );
2801 ret = ime->pImeUnregisterWord( readingW, style, stringW );
2802 free( readingW );
2803 free( stringW );
2806 ime_release( ime );
2807 return ret;
2810 /***********************************************************************
2811 * ImmUnregisterWordW (IMM32.@)
2813 BOOL WINAPI ImmUnregisterWordW( HKL hkl, const WCHAR *readingW, DWORD style, const WCHAR *stringW )
2815 struct ime *ime;
2816 BOOL ret;
2818 TRACE( "hkl %p, readingW %s, style %lu, stringW %s.\n", hkl, debugstr_w(readingW), style, debugstr_w(stringW) );
2820 if (!(ime = ime_acquire( hkl ))) return FALSE;
2822 if (ime_is_unicode( ime ))
2823 ret = ime->pImeUnregisterWord( readingW, style, stringW );
2824 else
2826 char *readingA = strdupWtoA( readingW ), *stringA = strdupWtoA( stringW );
2827 ret = ime->pImeUnregisterWord( readingA, style, stringA );
2828 free( readingA );
2829 free( stringA );
2832 ime_release( ime );
2833 return ret;
2836 /***********************************************************************
2837 * ImmGetImeMenuItemsA (IMM32.@)
2839 DWORD WINAPI ImmGetImeMenuItemsA( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOA *parentA,
2840 IMEMENUITEMINFOA *menuA, DWORD size )
2842 struct imc *data = get_imc_data( himc );
2843 struct ime *ime;
2844 DWORD ret;
2846 TRACE( "himc %p, flags %#lx, type %lu, parentA %p, menuA %p, size %lu.\n",
2847 himc, flags, type, parentA, menuA, size );
2849 if (!data)
2851 SetLastError( ERROR_INVALID_HANDLE );
2852 return 0;
2855 if (!(ime = imc_select_ime( data ))) return 0;
2856 if (!ime_is_unicode( ime ) || (!parentA && !menuA))
2857 ret = ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size );
2858 else
2860 IMEMENUITEMINFOW tmpW, *menuW, *parentW = parentA ? &tmpW : NULL;
2862 if (!menuA) menuW = NULL;
2863 else
2865 int count = size / sizeof(LPIMEMENUITEMINFOA);
2866 size = count * sizeof(IMEMENUITEMINFOW);
2867 menuW = malloc( size );
2870 ret = ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size );
2872 if (parentA)
2874 memcpy( parentA, parentW, sizeof(IMEMENUITEMINFOA) );
2875 parentA->hbmpItem = parentW->hbmpItem;
2876 WideCharToMultiByte( CP_ACP, 0, parentW->szString, -1, parentA->szString,
2877 IMEMENUITEM_STRING_SIZE, NULL, NULL );
2879 if (menuA && ret)
2881 unsigned int i;
2882 for (i = 0; i < ret; i++)
2884 memcpy( &menuA[i], &menuW[1], sizeof(IMEMENUITEMINFOA) );
2885 menuA[i].hbmpItem = menuW[i].hbmpItem;
2886 WideCharToMultiByte( CP_ACP, 0, menuW[i].szString, -1, menuA[i].szString,
2887 IMEMENUITEM_STRING_SIZE, NULL, NULL );
2890 free( menuW );
2893 return ret;
2896 /***********************************************************************
2897 * ImmGetImeMenuItemsW (IMM32.@)
2899 DWORD WINAPI ImmGetImeMenuItemsW( HIMC himc, DWORD flags, DWORD type, IMEMENUITEMINFOW *parentW,
2900 IMEMENUITEMINFOW *menuW, DWORD size )
2902 struct imc *data = get_imc_data( himc );
2903 struct ime *ime;
2904 DWORD ret;
2906 TRACE( "himc %p, flags %#lx, type %lu, parentW %p, menuW %p, size %lu.\n",
2907 himc, flags, type, parentW, menuW, size );
2909 if (!data)
2911 SetLastError( ERROR_INVALID_HANDLE );
2912 return 0;
2915 if (!(ime = imc_select_ime( data ))) return 0;
2916 if (ime_is_unicode( ime ) || (!parentW && !menuW))
2917 ret = ime->pImeGetImeMenuItems( himc, flags, type, parentW, menuW, size );
2918 else
2920 IMEMENUITEMINFOA tmpA, *menuA, *parentA = parentW ? &tmpA : NULL;
2922 if (!menuW) menuA = NULL;
2923 else
2925 int count = size / sizeof(LPIMEMENUITEMINFOW);
2926 size = count * sizeof(IMEMENUITEMINFOA);
2927 menuA = malloc( size );
2930 ret = ime->pImeGetImeMenuItems( himc, flags, type, parentA, menuA, size );
2932 if (parentW)
2934 memcpy( parentW, parentA, sizeof(IMEMENUITEMINFOA) );
2935 parentW->hbmpItem = parentA->hbmpItem;
2936 MultiByteToWideChar( CP_ACP, 0, parentA->szString, -1, parentW->szString, IMEMENUITEM_STRING_SIZE );
2938 if (menuW && ret)
2940 unsigned int i;
2941 for (i = 0; i < ret; i++)
2943 memcpy( &menuW[i], &menuA[1], sizeof(IMEMENUITEMINFOA) );
2944 menuW[i].hbmpItem = menuA[i].hbmpItem;
2945 MultiByteToWideChar( CP_ACP, 0, menuA[i].szString, -1, menuW[i].szString, IMEMENUITEM_STRING_SIZE );
2948 free( menuA );
2951 return ret;
2954 /***********************************************************************
2955 * ImmLockIMC(IMM32.@)
2957 INPUTCONTEXT *WINAPI ImmLockIMC( HIMC himc )
2959 struct imc *imc = get_imc_data( himc );
2961 TRACE( "himc %p\n", himc );
2963 if (!imc) return NULL;
2964 imc->dwLock++;
2966 imc_select_ime( imc );
2967 return &imc->IMC;
2970 /***********************************************************************
2971 * ImmUnlockIMC(IMM32.@)
2973 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
2975 struct imc *data = get_imc_data( hIMC );
2977 if (!data)
2978 return FALSE;
2979 if (data->dwLock)
2980 data->dwLock--;
2981 return TRUE;
2984 /***********************************************************************
2985 * ImmGetIMCLockCount(IMM32.@)
2987 DWORD WINAPI ImmGetIMCLockCount(HIMC hIMC)
2989 struct imc *data = get_imc_data( hIMC );
2990 if (!data)
2991 return 0;
2992 return data->dwLock;
2995 /***********************************************************************
2996 * ImmCreateIMCC(IMM32.@)
2998 HIMCC WINAPI ImmCreateIMCC(DWORD size)
3000 return GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE, size);
3003 /***********************************************************************
3004 * ImmDestroyIMCC(IMM32.@)
3006 HIMCC WINAPI ImmDestroyIMCC(HIMCC block)
3008 return GlobalFree(block);
3011 /***********************************************************************
3012 * ImmLockIMCC(IMM32.@)
3014 LPVOID WINAPI ImmLockIMCC(HIMCC imcc)
3016 return GlobalLock(imcc);
3019 /***********************************************************************
3020 * ImmUnlockIMCC(IMM32.@)
3022 BOOL WINAPI ImmUnlockIMCC(HIMCC imcc)
3024 return GlobalUnlock(imcc);
3027 /***********************************************************************
3028 * ImmGetIMCCLockCount(IMM32.@)
3030 DWORD WINAPI ImmGetIMCCLockCount(HIMCC imcc)
3032 return GlobalFlags(imcc) & GMEM_LOCKCOUNT;
3035 /***********************************************************************
3036 * ImmReSizeIMCC(IMM32.@)
3038 HIMCC WINAPI ImmReSizeIMCC(HIMCC imcc, DWORD size)
3040 return GlobalReAlloc(imcc, size, GMEM_ZEROINIT | GMEM_MOVEABLE);
3043 /***********************************************************************
3044 * ImmGetIMCCSize(IMM32.@)
3046 DWORD WINAPI ImmGetIMCCSize(HIMCC imcc)
3048 return GlobalSize(imcc);
3051 /***********************************************************************
3052 * ImmGenerateMessage(IMM32.@)
3054 BOOL WINAPI ImmGenerateMessage( HIMC himc )
3056 INPUTCONTEXT *ctx;
3058 TRACE( "himc %p\n", himc );
3060 if (NtUserQueryInputContext( himc, NtUserInputContextThreadId ) != GetCurrentThreadId()) return FALSE;
3061 if (!(ctx = ImmLockIMC( himc ))) return FALSE;
3063 while (ctx->dwNumMsgBuf--)
3065 TRANSMSG *msgs, msg;
3066 if (!(msgs = ImmLockIMCC( ctx->hMsgBuf ))) return FALSE;
3067 msg = msgs[0];
3068 memmove( msgs, msgs + 1, ctx->dwNumMsgBuf * sizeof(*msgs) );
3069 ImmUnlockIMCC( ctx->hMsgBuf );
3070 SendMessageW( ctx->hWnd, msg.message, msg.wParam, msg.lParam );
3072 ctx->dwNumMsgBuf++;
3074 return TRUE;
3077 /***********************************************************************
3078 * ImmTranslateMessage(IMM32.@)
3079 * ( Undocumented, call internally and from user32.dll )
3081 BOOL WINAPI ImmTranslateMessage( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
3083 union
3085 struct
3087 UINT uMsgCount;
3088 TRANSMSG TransMsg[10];
3090 TRANSMSGLIST list;
3091 } buffer = {.uMsgCount = ARRAY_SIZE(buffer.TransMsg)};
3092 TRANSMSG *msgs = buffer.TransMsg;
3093 UINT scan, vkey, count, i;
3094 struct imc *data;
3095 struct ime *ime;
3096 BYTE state[256];
3097 WCHAR chr;
3099 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
3101 if (msg < WM_KEYDOWN || msg > WM_KEYUP) return FALSE;
3102 if (!(data = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE;
3103 if (!(ime = imc_select_ime( data ))) return FALSE;
3105 if ((vkey = data->vkey) == VK_PROCESSKEY) return FALSE;
3106 data->vkey = VK_PROCESSKEY;
3107 GetKeyboardState( state );
3108 scan = (lparam >> 0x10) & 0xffff;
3110 if (ime->info.fdwProperty & IME_PROP_KBD_CHAR_FIRST)
3112 if (!ime_is_unicode( ime )) ToAscii( vkey, scan, state, &chr, 0 );
3113 else ToUnicodeEx( vkey, scan, state, &chr, 1, 0, GetKeyboardLayout( 0 ) );
3114 vkey = MAKELONG( vkey, chr );
3117 count = ime->pImeToAsciiEx( vkey, scan, state, &buffer.list, 0, data->handle );
3118 if (count >= ARRAY_SIZE(buffer.TransMsg)) return 0;
3120 for (i = 0; i < count; i++) PostMessageW( hwnd, msgs[i].message, msgs[i].wParam, msgs[i].lParam );
3121 TRACE( "%u messages generated\n", count );
3123 return count > 0;
3126 /***********************************************************************
3127 * ImmProcessKey(IMM32.@)
3128 * ( Undocumented, called from user32.dll )
3130 BOOL WINAPI ImmProcessKey( HWND hwnd, HKL hkl, UINT vkey, LPARAM lparam, DWORD unknown )
3132 struct imc *imc;
3133 struct ime *ime;
3134 BYTE state[256];
3135 BOOL ret;
3137 TRACE( "hwnd %p, hkl %p, vkey %#x, lparam %#Ix, unknown %#lx\n", hwnd, hkl, vkey, lparam, unknown );
3139 if (hkl != GetKeyboardLayout( 0 )) return FALSE;
3140 if (!(imc = get_imc_data( ImmGetContext( hwnd ) ))) return FALSE;
3141 if (!(ime = imc_select_ime( imc ))) return FALSE;
3143 GetKeyboardState( state );
3145 ret = ime->pImeProcessKey( imc->handle, vkey, lparam, state );
3146 imc->vkey = ret ? vkey : VK_PROCESSKEY;
3148 return ret;
3151 /***********************************************************************
3152 * ImmDisableTextFrameService(IMM32.@)
3154 BOOL WINAPI ImmDisableTextFrameService(DWORD idThread)
3156 FIXME("Stub\n");
3157 return FALSE;
3160 /***********************************************************************
3161 * ImmEnumInputContext(IMM32.@)
3164 BOOL WINAPI ImmEnumInputContext( DWORD thread, IMCENUMPROC callback, LPARAM lparam )
3166 HIMC buffer[256];
3167 NTSTATUS status;
3168 UINT i, size;
3170 TRACE( "thread %lu, callback %p, lparam %#Ix\n", thread, callback, lparam );
3172 if ((status = NtUserBuildHimcList( thread, ARRAY_SIZE(buffer), buffer, &size )))
3174 RtlSetLastWin32Error( RtlNtStatusToDosError( status ) );
3175 WARN( "NtUserBuildHimcList returned %#lx\n", status );
3176 return FALSE;
3179 if (size == ARRAY_SIZE(buffer)) FIXME( "NtUserBuildHimcList returned %u handles\n", size );
3180 for (i = 0; i < size; i++) if (!callback( buffer[i], lparam )) return FALSE;
3182 return TRUE;
3185 /***********************************************************************
3186 * ImmGetHotKey(IMM32.@)
3189 BOOL WINAPI ImmGetHotKey(DWORD hotkey, UINT *modifiers, UINT *key, HKL *hkl)
3191 FIXME("%lx, %p, %p, %p: stub\n", hotkey, modifiers, key, hkl);
3192 return FALSE;
3195 /***********************************************************************
3196 * ImmDisableLegacyIME(IMM32.@)
3198 BOOL WINAPI ImmDisableLegacyIME(void)
3200 FIXME("stub\n");
3201 return TRUE;
3204 static BOOL is_ime_ui_msg(UINT msg)
3206 switch (msg)
3208 case WM_IME_STARTCOMPOSITION:
3209 case WM_IME_ENDCOMPOSITION:
3210 case WM_IME_COMPOSITION:
3211 case WM_IME_SETCONTEXT:
3212 case WM_IME_NOTIFY:
3213 case WM_IME_CONTROL:
3214 case WM_IME_COMPOSITIONFULL:
3215 case WM_IME_SELECT:
3216 case WM_IME_CHAR:
3217 case WM_IME_REQUEST:
3218 case WM_IME_KEYDOWN:
3219 case WM_IME_KEYUP:
3220 return TRUE;
3221 default:
3222 return msg == WM_MSIME_RECONVERTOPTIONS ||
3223 msg == WM_MSIME_SERVICE ||
3224 msg == WM_MSIME_MOUSE ||
3225 msg == WM_MSIME_RECONVERTREQUEST ||
3226 msg == WM_MSIME_RECONVERT ||
3227 msg == WM_MSIME_QUERYPOSITION ||
3228 msg == WM_MSIME_DOCUMENTFEED;
3232 static LRESULT ime_internal_msg( WPARAM wparam, LPARAM lparam)
3234 HWND hwnd;
3235 HIMC himc;
3237 switch (wparam)
3239 case IME_INTERNAL_ACTIVATE:
3240 hwnd = (HWND)lparam;
3241 himc = NtUserGetWindowInputContext( hwnd );
3242 ImmSetActiveContext( hwnd, himc, TRUE );
3243 set_ime_ui_window_himc( himc );
3244 break;
3245 case IME_INTERNAL_DEACTIVATE:
3246 hwnd = (HWND)lparam;
3247 himc = NtUserGetWindowInputContext( hwnd );
3248 ImmSetActiveContext( hwnd, himc, FALSE );
3249 break;
3250 case IME_INTERNAL_HKL_ACTIVATE:
3251 ImmEnumInputContext( 0, enum_activate_layout, 0 );
3252 if (!(hwnd = get_ime_ui_window())) break;
3253 SendMessageW( hwnd, WM_IME_SELECT, TRUE, lparam );
3254 break;
3255 case IME_INTERNAL_HKL_DEACTIVATE:
3256 if (!(hwnd = get_ime_ui_window())) break;
3257 SendMessageW( hwnd, WM_IME_SELECT, FALSE, lparam );
3258 break;
3259 default:
3260 FIXME("wparam = %Ix\n", wparam);
3261 break;
3264 return 0;
3267 static void init_messages(void)
3269 static BOOL initialized;
3271 if (initialized) return;
3273 WM_MSIME_SERVICE = RegisterWindowMessageW(L"MSIMEService");
3274 WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageW(L"MSIMEReconvertOptions");
3275 WM_MSIME_MOUSE = RegisterWindowMessageW(L"MSIMEMouseOperation");
3276 WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageW(L"MSIMEReconvertRequest");
3277 WM_MSIME_RECONVERT = RegisterWindowMessageW(L"MSIMEReconvert");
3278 WM_MSIME_QUERYPOSITION = RegisterWindowMessageW(L"MSIMEQueryPosition");
3279 WM_MSIME_DOCUMENTFEED = RegisterWindowMessageW(L"MSIMEDocumentFeed");
3280 initialized = TRUE;
3283 LRESULT WINAPI __wine_ime_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi)
3285 HWND ui_hwnd;
3287 TRACE( "hwnd %p, msg %s, wparam %#Ix, lparam %#Ix, ansi %u\n",
3288 hwnd, debugstr_wm_ime(msg), wparam, lparam, ansi );
3290 switch (msg)
3292 case WM_CREATE:
3293 init_messages();
3294 return TRUE;
3296 case WM_DESTROY:
3298 HWND default_hwnd = ImmGetDefaultIMEWnd(0);
3299 if (!default_hwnd || hwnd == default_hwnd)
3300 imm_couninit_thread(TRUE);
3302 return TRUE;
3304 case WM_IME_INTERNAL:
3305 return ime_internal_msg(wparam, lparam);
3308 if (is_ime_ui_msg(msg))
3310 if ((ui_hwnd = get_ime_ui_window()))
3312 if (ansi)
3313 return SendMessageA(ui_hwnd, msg, wparam, lparam);
3314 else
3315 return SendMessageW(ui_hwnd, msg, wparam, lparam);
3317 return FALSE;
3320 if (ansi)
3321 return DefWindowProcA(hwnd, msg, wparam, lparam);
3322 else
3323 return DefWindowProcW(hwnd, msg, wparam, lparam);
3326 /***********************************************************************
3327 * CtfImmIsCiceroEnabled (IMM32.@)
3329 BOOL WINAPI CtfImmIsCiceroEnabled(void)
3331 FIXME("(): stub\n");
3332 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3333 return FALSE;