xref: /reactos/dll/win32/imm32/compstr.c (revision 1734f297)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing composition strings of IMM32
5  * COPYRIGHT:   Copyright 1998 Patrik Stridvall
6  *              Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
7  *              Copyright 2017 James Tabor <james.tabor@reactos.org>
8  *              Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
9  *              Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  */
11 
12 #include "precomp.h"
13 
14 WINE_DEFAULT_DEBUG_CHANNEL(imm);
15 
16 BOOL APIENTRY
17 Imm32OpenICAndCS(HIMC hIMC, LPINPUTCONTEXT *ppIC, LPCOMPOSITIONSTRING *ppCS)
18 {
19     LPINPUTCONTEXT pIC;
20     LPCOMPOSITIONSTRING pCS;
21 
22     *ppIC = NULL;
23     *ppCS = NULL;
24 
25     pIC = ImmLockIMC(hIMC);
26     if (IS_NULL_UNEXPECTEDLY(pIC))
27         return FALSE;
28 
29     pCS = ImmLockIMCC(pIC->hCompStr);
30     if (IS_NULL_UNEXPECTEDLY(pCS))
31     {
32         ImmUnlockIMC(hIMC);
33         return FALSE;
34     }
35 
36     *ppIC = pIC;
37     *ppCS = pCS;
38     return TRUE;
39 }
40 
41 static inline LONG APIENTRY
42 Imm32CompStrAnsiToWide(LPCSTR psz, DWORD cb, LPWSTR lpBuf, DWORD dwBufLen, UINT uCodePage)
43 {
44     DWORD ret = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, psz, cb / sizeof(CHAR),
45                                     lpBuf, dwBufLen / sizeof(WCHAR));
46     if (lpBuf && (ret + 1) * sizeof(WCHAR) <= dwBufLen)
47         lpBuf[ret] = 0;
48     return ret * sizeof(WCHAR);
49 }
50 
51 static inline LONG APIENTRY
52 Imm32CompStrWideToAnsi(LPCWSTR psz, DWORD cb, LPSTR lpBuf, DWORD dwBufLen, UINT uCodePage)
53 {
54     DWORD ret = WideCharToMultiByte(uCodePage, 0, psz, cb / sizeof(WCHAR),
55                                     lpBuf, dwBufLen / sizeof(CHAR), NULL, NULL);
56     if (lpBuf && (ret + 1) * sizeof(CHAR) <= dwBufLen)
57         lpBuf[ret] = 0;
58     return ret * sizeof(CHAR);
59 }
60 
61 static INT APIENTRY
62 Imm32CompAttrWideToAnsi(const BYTE *src, INT src_len, LPCWSTR text,
63                         INT str_len, LPBYTE dst, INT dst_len, UINT uCodePage)
64 {
65     INT rc;
66     INT i, j = 0, k = 0, len;
67 
68     if (!src_len)
69         return 0;
70 
71     str_len /= sizeof(WCHAR);
72     rc = WideCharToMultiByte(uCodePage, 0, text, str_len, NULL, 0, NULL, NULL);
73 
74     if (dst_len)
75     {
76         if (dst_len > rc)
77             dst_len = rc;
78 
79         for (i = 0; i < str_len; ++i, ++k)
80         {
81             len = WideCharToMultiByte(uCodePage, 0, &text[i], 1, NULL, 0, NULL, NULL);
82             for (; len > 0; --len)
83             {
84                 dst[j++] = src[k];
85 
86                 if (dst_len <= j)
87                     goto end;
88             }
89         }
90 end:
91         rc = j;
92     }
93 
94     return rc * sizeof(BYTE);
95 }
96 
97 static INT APIENTRY
98 Imm32CompAttrAnsiToWide(const BYTE *src, INT src_len, LPCSTR text,
99                         INT str_len, LPBYTE dst, INT dst_len, UINT uCodePage)
100 {
101     INT rc;
102     INT i, j = 0;
103 
104     if (!src_len)
105         return 0;
106 
107     str_len /= sizeof(CHAR);
108     rc = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, text, str_len, NULL, 0);
109 
110     if (dst_len)
111     {
112         if (dst_len > rc)
113             dst_len = rc;
114 
115         for (i = 0; i < str_len; ++i)
116         {
117             if (IsDBCSLeadByteEx(uCodePage, text[i]) && text[i + 1])
118                 continue;
119 
120             dst[j++] = src[i];
121 
122             if (dst_len <= j)
123                 break;
124         }
125 
126         rc = j;
127     }
128 
129     return rc * sizeof(BYTE);
130 }
131 
132 static INT APIENTRY
133 Imm32CompClauseAnsiToWide(const DWORD *source, INT slen, LPCSTR text,
134                           LPDWORD target, INT tlen, UINT uCodePage)
135 {
136     INT rc, i;
137 
138     if (!slen)
139         return 0;
140 
141     if (tlen)
142     {
143         if (tlen > slen)
144             tlen = slen;
145 
146         tlen /= sizeof(DWORD);
147 
148         for (i = 0; i < tlen; ++i)
149         {
150             target[i] = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, text, source[i], NULL, 0);
151         }
152 
153         rc = sizeof(DWORD) * i;
154     }
155     else
156     {
157         rc = slen;
158     }
159 
160     return rc;
161 }
162 
163 static INT APIENTRY
164 Imm32CompClauseWideToAnsi(const DWORD *source, INT slen, LPCWSTR text,
165                           LPDWORD target, INT tlen, UINT uCodePage)
166 {
167     INT rc, i;
168 
169     if (!slen)
170         return 0;
171 
172     if (tlen)
173     {
174         if (tlen > slen)
175             tlen = slen;
176 
177         tlen /= sizeof(DWORD);
178 
179         for (i = 0; i < tlen; ++i)
180         {
181             target[i] = WideCharToMultiByte(uCodePage, 0, text, source[i], NULL, 0, NULL, NULL);
182         }
183 
184         rc = sizeof(DWORD) * i;
185     }
186     else
187     {
188         rc = slen;
189     }
190 
191     return rc;
192 }
193 
194 #define CS_StrA(pCS, Name)      ((LPCSTR)(pCS) + (pCS)->dw##Name##Offset)
195 #define CS_StrW(pCS, Name)      ((LPCWSTR)CS_StrA(pCS, Name))
196 #define CS_Attr(pCS, Name)      ((const BYTE *)CS_StrA(pCS, Name))
197 #define CS_Clause(pCS, Name)    ((const DWORD *)CS_StrA(pCS, Name))
198 #define CS_Size(pCS, Name)      ((pCS)->dw##Name##Len)
199 #define CS_SizeA(pCS, Name)     (CS_Size(pCS, Name) * sizeof(CHAR))
200 #define CS_SizeW(pCS, Name)     (CS_Size(pCS, Name) * sizeof(WCHAR))
201 
202 #define CS_DoStr(pCS, Name, AorW) do { \
203     if (dwBufLen == 0) { \
204         dwBufLen = CS_Size##AorW((pCS), Name); \
205     } else { \
206         if (dwBufLen > CS_Size##AorW((pCS), Name)) \
207             dwBufLen = CS_Size##AorW((pCS), Name); \
208         RtlCopyMemory(lpBuf, CS_Str##AorW((pCS), Name), dwBufLen); \
209     } \
210 } while (0)
211 
212 #define CS_DoStrA(pCS, Name) CS_DoStr(pCS, Name, A)
213 #define CS_DoStrW(pCS, Name) CS_DoStr(pCS, Name, W)
214 #define CS_DoAttr CS_DoStrA
215 #define CS_DoClause CS_DoStrA
216 
217 // Win: InternalGetCompositionStringA
218 LONG APIENTRY
219 Imm32GetCompStrA(HIMC hIMC, const COMPOSITIONSTRING *pCS, DWORD dwIndex,
220                  LPVOID lpBuf, DWORD dwBufLen, BOOL bAnsiClient, UINT uCodePage)
221 {
222     if (bAnsiClient)
223     {
224         switch (dwIndex)
225         {
226             case GCS_COMPREADSTR:
227                 CS_DoStrA(pCS, CompReadStr);
228                 break;
229 
230             case GCS_COMPREADATTR:
231                 CS_DoAttr(pCS, CompReadAttr);
232                 break;
233 
234             case GCS_COMPREADCLAUSE:
235                 CS_DoClause(pCS, CompReadClause);
236                 break;
237 
238             case GCS_COMPSTR:
239                 CS_DoStrA(pCS, CompStr);
240                 break;
241 
242             case GCS_COMPATTR:
243                 CS_DoAttr(pCS, CompAttr);
244                 break;
245 
246             case GCS_COMPCLAUSE:
247                 CS_DoClause(pCS, CompClause);
248                 break;
249 
250             case GCS_CURSORPOS:
251                 dwBufLen = pCS->dwCursorPos;
252                 break;
253 
254             case GCS_DELTASTART:
255                 dwBufLen = pCS->dwDeltaStart;
256                 break;
257 
258             case GCS_RESULTREADSTR:
259                 CS_DoStrA(pCS, ResultReadStr);
260                 break;
261 
262             case GCS_RESULTREADCLAUSE:
263                 CS_DoClause(pCS, ResultReadClause);
264                 break;
265 
266             case GCS_RESULTSTR:
267                 CS_DoStrA(pCS, ResultStr);
268                 break;
269 
270             case GCS_RESULTCLAUSE:
271                 CS_DoClause(pCS, ResultClause);
272                 break;
273 
274             default:
275                 FIXME("0x%X\n", dwIndex);
276                 return IMM_ERROR_GENERAL;
277         }
278     }
279     else /* !bAnsiClient */
280     {
281         switch (dwIndex)
282         {
283             case GCS_COMPREADSTR:
284                 dwBufLen = Imm32CompStrWideToAnsi(CS_StrW(pCS, CompReadStr),
285                                                   CS_SizeW(pCS, CompReadStr),
286                                                   lpBuf, dwBufLen, uCodePage);
287                 break;
288 
289             case GCS_COMPREADATTR:
290                 dwBufLen = Imm32CompAttrWideToAnsi(CS_Attr(pCS, CompReadAttr),
291                                                    CS_Size(pCS, CompReadAttr),
292                                                    CS_StrW(pCS, CompStr),
293                                                    CS_SizeW(pCS, CompStr),
294                                                    lpBuf, dwBufLen, uCodePage);
295                 break;
296 
297             case GCS_COMPREADCLAUSE:
298                 dwBufLen = Imm32CompClauseWideToAnsi(CS_Clause(pCS, CompReadClause),
299                                                      CS_Size(pCS, CompReadClause),
300                                                      CS_StrW(pCS, CompStr),
301                                                      lpBuf, dwBufLen, uCodePage);
302                 break;
303 
304             case GCS_COMPSTR:
305                 dwBufLen = Imm32CompStrWideToAnsi(CS_StrW(pCS, CompStr),
306                                                   CS_SizeW(pCS, CompStr),
307                                                   lpBuf, dwBufLen, uCodePage);
308                 break;
309 
310             case GCS_COMPATTR:
311                 dwBufLen = Imm32CompAttrWideToAnsi(CS_Attr(pCS, CompAttr),
312                                                    CS_Size(pCS, CompAttr),
313                                                    CS_StrW(pCS, CompStr),
314                                                    CS_SizeW(pCS, CompStr),
315                                                    lpBuf, dwBufLen, uCodePage);
316                 break;
317 
318             case GCS_COMPCLAUSE:
319                 dwBufLen = Imm32CompClauseWideToAnsi(CS_Clause(pCS, CompClause),
320                                                      CS_Size(pCS, CompClause),
321                                                      CS_StrW(pCS, CompStr),
322                                                      lpBuf, dwBufLen, uCodePage);
323                 break;
324 
325             case GCS_CURSORPOS:
326                 dwBufLen = IchAnsiFromWide(pCS->dwCursorPos, CS_StrW(pCS, CompStr), uCodePage);
327                 break;
328 
329             case GCS_DELTASTART:
330                 dwBufLen = IchAnsiFromWide(pCS->dwDeltaStart, CS_StrW(pCS, CompStr), uCodePage);
331                 break;
332 
333             case GCS_RESULTREADSTR:
334                 dwBufLen = Imm32CompStrWideToAnsi(CS_StrW(pCS, ResultReadStr),
335                                                   CS_SizeW(pCS, ResultReadStr),
336                                                   lpBuf, dwBufLen, uCodePage);
337                 break;
338 
339             case GCS_RESULTREADCLAUSE:
340                 dwBufLen = Imm32CompClauseWideToAnsi(CS_Clause(pCS, ResultReadClause),
341                                                      CS_Size(pCS, ResultReadClause),
342                                                      CS_StrW(pCS, CompStr),
343                                                      lpBuf, dwBufLen, uCodePage);
344                 break;
345 
346             case GCS_RESULTSTR:
347                 dwBufLen = Imm32CompStrWideToAnsi(CS_StrW(pCS, ResultStr),
348                                                   CS_SizeW(pCS, ResultStr),
349                                                   lpBuf, dwBufLen, uCodePage);
350                 break;
351 
352             case GCS_RESULTCLAUSE:
353                 dwBufLen = Imm32CompClauseWideToAnsi(CS_Clause(pCS, ResultClause),
354                                                      CS_Size(pCS, ResultClause),
355                                                      CS_StrW(pCS, CompStr),
356                                                      lpBuf, dwBufLen, uCodePage);
357                 break;
358 
359             default:
360                 FIXME("0x%X\n", dwIndex);
361                 return IMM_ERROR_GENERAL;
362         }
363     }
364 
365     return dwBufLen;
366 }
367 
368 // Win: InternalGetCompositionStringW
369 LONG APIENTRY
370 Imm32GetCompStrW(HIMC hIMC, const COMPOSITIONSTRING *pCS, DWORD dwIndex,
371                  LPVOID lpBuf, DWORD dwBufLen, BOOL bAnsiClient, UINT uCodePage)
372 {
373     if (bAnsiClient)
374     {
375         switch (dwIndex)
376         {
377             case GCS_COMPREADSTR:
378                 dwBufLen = Imm32CompStrAnsiToWide(CS_StrA(pCS, CompReadStr),
379                                                   CS_SizeA(pCS, CompReadStr),
380                                                   lpBuf, dwBufLen, uCodePage);
381                 break;
382 
383             case GCS_COMPREADATTR:
384                 dwBufLen = Imm32CompAttrAnsiToWide(CS_Attr(pCS, CompReadAttr),
385                                                    CS_Size(pCS, CompReadAttr),
386                                                    CS_StrA(pCS, CompStr), CS_SizeA(pCS, CompStr),
387                                                    lpBuf, dwBufLen, uCodePage);
388                 break;
389 
390             case GCS_COMPREADCLAUSE:
391                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, CompReadClause),
392                                                      CS_Size(pCS, CompReadClause),
393                                                      CS_StrA(pCS, CompStr),
394                                                      lpBuf, dwBufLen, uCodePage);
395                 break;
396 
397             case GCS_COMPSTR:
398                 dwBufLen = Imm32CompStrAnsiToWide(CS_StrA(pCS, CompStr),
399                                                   CS_SizeA(pCS, CompStr),
400                                                   lpBuf, dwBufLen, uCodePage);
401                 break;
402 
403             case GCS_COMPATTR:
404                 dwBufLen = Imm32CompAttrAnsiToWide(CS_Attr(pCS, CompAttr),
405                                                    CS_Size(pCS, CompAttr),
406                                                    CS_StrA(pCS, CompStr), CS_SizeA(pCS, CompStr),
407                                                    lpBuf, dwBufLen, uCodePage);
408                 break;
409 
410             case GCS_COMPCLAUSE:
411                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, CompClause),
412                                                      CS_Size(pCS, CompClause),
413                                                      CS_StrA(pCS, CompStr),
414                                                      lpBuf, dwBufLen, uCodePage);
415                 break;
416 
417             case GCS_CURSORPOS:
418                 dwBufLen = IchWideFromAnsi(pCS->dwCursorPos, CS_StrA(pCS, CompStr), uCodePage);
419                 break;
420 
421             case GCS_DELTASTART:
422                 dwBufLen = IchWideFromAnsi(pCS->dwDeltaStart, CS_StrA(pCS, CompStr), uCodePage);
423                 break;
424 
425             case GCS_RESULTREADSTR:
426                 dwBufLen = Imm32CompStrAnsiToWide(CS_StrA(pCS, ResultReadStr),
427                                                   CS_SizeA(pCS, ResultReadStr),
428                                                   lpBuf, dwBufLen, uCodePage);
429                 break;
430 
431             case GCS_RESULTREADCLAUSE:
432                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, ResultReadClause),
433                                                      CS_Size(pCS, ResultReadClause),
434                                                      CS_StrA(pCS, CompStr),
435                                                      lpBuf, dwBufLen, uCodePage);
436                 break;
437 
438             case GCS_RESULTSTR:
439                 dwBufLen = Imm32CompStrAnsiToWide(CS_StrA(pCS, ResultStr),
440                                                   CS_SizeA(pCS, ResultStr),
441                                                   lpBuf, dwBufLen, uCodePage);
442                 break;
443 
444             case GCS_RESULTCLAUSE:
445                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, ResultClause),
446                                                      CS_Size(pCS, ResultClause),
447                                                      CS_StrA(pCS, CompStr),
448                                                      lpBuf, dwBufLen, uCodePage);
449                 break;
450 
451             default:
452                 FIXME("0x%X\n", dwIndex);
453                 return IMM_ERROR_GENERAL;
454         }
455     }
456     else /* !bAnsiClient */
457     {
458         switch (dwIndex)
459         {
460             case GCS_COMPREADSTR:
461                 CS_DoStrW(pCS, CompReadStr);
462                 break;
463 
464             case GCS_COMPREADATTR:
465                 CS_DoAttr(pCS, CompReadAttr);
466                 break;
467 
468             case GCS_COMPREADCLAUSE:
469                 CS_DoClause(pCS, CompReadClause);
470                 break;
471 
472             case GCS_COMPSTR:
473                 CS_DoStrW(pCS, CompStr);
474                 break;
475 
476             case GCS_COMPATTR:
477                 CS_DoAttr(pCS, CompAttr);
478                 break;
479 
480             case GCS_COMPCLAUSE:
481                 CS_DoClause(pCS, CompClause);
482                 break;
483 
484             case GCS_CURSORPOS:
485                 dwBufLen = pCS->dwCursorPos;
486                 break;
487 
488             case GCS_DELTASTART:
489                 dwBufLen = pCS->dwDeltaStart;
490                 break;
491 
492             case GCS_RESULTREADSTR:
493                 CS_DoStrW(pCS, ResultReadStr);
494                 break;
495 
496             case GCS_RESULTREADCLAUSE:
497                 CS_DoClause(pCS, ResultReadClause);
498                 break;
499 
500             case GCS_RESULTSTR:
501                 CS_DoStrW(pCS, ResultStr);
502                 break;
503 
504             case GCS_RESULTCLAUSE:
505                 CS_DoClause(pCS, ResultClause);
506                 break;
507 
508             default:
509                 FIXME("0x%X\n", dwIndex);
510                 return IMM_ERROR_GENERAL;
511         }
512     }
513 
514     return dwBufLen;
515 }
516 
517 // Win: ImmSetCompositionStringWorker
518 BOOL APIENTRY
519 ImmSetCompositionStringAW(HIMC hIMC, DWORD dwIndex, LPVOID pComp, DWORD dwCompLen,
520                           LPVOID pRead, DWORD dwReadLen, BOOL bAnsiAPI)
521 {
522     BOOL ret = FALSE, bAnsiClient;
523     LPVOID pCompNew = NULL, pReadNew = NULL;
524     DWORD dwThreadId, cbCompNew = 0, cbReadNew = 0;
525     LPINPUTCONTEXT pIC;
526     LPCOMPOSITIONSTRING pCS;
527     HKL hKL;
528     PIMEDPI pImeDpi;
529     UINT uCodePage;
530     LPRECONVERTSTRING pRS;
531 
532     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
533     if (dwThreadId != GetCurrentThreadId())
534     {
535         ERR("Thread mismatch\n");
536         return FALSE;
537     }
538 
539     hKL = GetKeyboardLayout(dwThreadId);
540     pImeDpi = ImmLockImeDpi(hKL);
541     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
542         return FALSE;
543 
544     uCodePage = pImeDpi->uCodePage;
545     bAnsiClient = !ImeDpi_IsUnicode(pImeDpi);
546 
547     switch (dwIndex)
548     {
549         case SCS_SETSTR: case SCS_CHANGEATTR: case SCS_CHANGECLAUSE:
550             break;
551 
552         case SCS_SETRECONVERTSTRING: case SCS_QUERYRECONVERTSTRING:
553             if (pImeDpi->ImeInfo.fdwSCSCaps & SCS_CAP_SETRECONVERTSTRING)
554                 break;
555             /* FALL THROUGH */
556         default:
557             ERR("0x%X\n", dwIndex);
558             ImmUnlockImeDpi(pImeDpi);
559             return FALSE;
560     }
561 
562     if (bAnsiAPI == bAnsiClient || (!pComp && !pRead)) /* No conversion needed */
563     {
564         ret = pImeDpi->ImeSetCompositionString(hIMC, dwIndex, pComp, dwCompLen,
565                                                pRead, dwReadLen);
566         ImmUnlockImeDpi(pImeDpi);
567         return ret;
568     }
569 
570     if (!Imm32OpenICAndCS(hIMC, &pIC, &pCS))
571     {
572         ImmUnlockImeDpi(pImeDpi);
573         return FALSE;
574     }
575 
576     /*
577      * This code is really too complicated. But I cannot simplify.
578      * It converts like (pComp, dwCompLen) --> (pCompNew, cbCompNew) and
579      * (pRead, dwReadLen) --> (pReadNew, cbReadNew).
580      * (1) Check bAnsiClient, (2) Get the size, (3) Allocate a buffer for conversion,
581      * (4) Store converted data into the buffer.
582      */
583     switch (dwIndex)
584     {
585         case SCS_SETSTR:
586             if (pComp)
587             {
588                 if (bAnsiClient)
589                 {
590                     cbCompNew = Imm32CompStrWideToAnsi(pComp, dwCompLen, NULL, 0, uCodePage);
591                     pCompNew = ImmLocalAlloc(0, cbCompNew);
592                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
593                         goto Quit;
594 
595                     Imm32CompStrWideToAnsi(pComp, dwCompLen, pCompNew, cbCompNew, uCodePage);
596                 }
597                 else
598                 {
599                     cbCompNew = Imm32CompStrAnsiToWide(pComp, dwCompLen, NULL, 0, uCodePage);
600                     pCompNew = ImmLocalAlloc(0, cbCompNew);
601                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
602                         goto Quit;
603 
604                     Imm32CompStrAnsiToWide(pComp, dwCompLen, pCompNew, cbCompNew, uCodePage);
605                 }
606             }
607 
608             if (pRead)
609             {
610                 if (bAnsiClient)
611                 {
612                     cbReadNew = Imm32CompStrWideToAnsi(pRead, dwReadLen, NULL, 0, uCodePage);
613                     pReadNew = ImmLocalAlloc(0, cbReadNew);
614                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
615                         goto Quit;
616 
617                     Imm32CompStrWideToAnsi(pRead, dwReadLen, pReadNew, cbReadNew, uCodePage);
618                 }
619                 else
620                 {
621                     cbReadNew = Imm32CompStrAnsiToWide(pRead, dwReadLen, NULL, 0, uCodePage);
622                     pReadNew = ImmLocalAlloc(0, cbReadNew);
623                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
624                         goto Quit;
625 
626                     Imm32CompStrAnsiToWide(pRead, dwReadLen, pReadNew, cbReadNew, uCodePage);
627                 }
628             }
629             break;
630 
631         case SCS_CHANGEATTR:
632             if (pComp)
633             {
634                 if (bAnsiClient)
635                 {
636                     cbCompNew = Imm32CompAttrWideToAnsi(pComp, dwCompLen,
637                                                         CS_StrW(pCS, CompStr),
638                                                         CS_SizeW(pCS, CompStr),
639                                                         NULL, 0, uCodePage);
640                     pCompNew = ImmLocalAlloc(0, cbCompNew);
641                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
642                         goto Quit;
643 
644                     Imm32CompAttrWideToAnsi(pComp, dwCompLen,
645                                             CS_StrW(pCS, CompStr), CS_SizeW(pCS, CompStr),
646                                             pCompNew, cbCompNew, uCodePage);
647                 }
648                 else
649                 {
650                     cbCompNew = Imm32CompAttrAnsiToWide(pComp, dwCompLen,
651                                                         CS_StrA(pCS, CompStr),
652                                                         CS_SizeA(pCS, CompStr),
653                                                         NULL, 0, uCodePage);
654                     pCompNew = ImmLocalAlloc(0, cbCompNew);
655                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
656                         goto Quit;
657 
658                     Imm32CompAttrAnsiToWide(pComp, dwCompLen,
659                                             CS_StrA(pCS, CompStr), CS_SizeA(pCS, CompStr),
660                                             pCompNew, cbCompNew, uCodePage);
661                 }
662             }
663 
664             if (pRead)
665             {
666                 if (bAnsiClient)
667                 {
668                     cbReadNew = Imm32CompAttrWideToAnsi(pRead, dwReadLen,
669                                                         CS_StrW(pCS, CompReadStr),
670                                                         CS_SizeW(pCS, CompReadStr),
671                                                         NULL, 0, uCodePage);
672                     pReadNew = ImmLocalAlloc(0, cbReadNew);
673                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
674                         goto Quit;
675 
676                     Imm32CompAttrWideToAnsi(pRead, dwReadLen,
677                                             CS_StrW(pCS, CompReadStr), CS_SizeW(pCS, CompReadStr),
678                                             pReadNew, cbReadNew, uCodePage);
679                 }
680                 else
681                 {
682                     cbReadNew = Imm32CompAttrAnsiToWide(pRead, dwReadLen,
683                                                         CS_StrA(pCS, CompReadStr),
684                                                         CS_SizeA(pCS, CompReadStr),
685                                                         NULL, 0, uCodePage);
686                     pReadNew = ImmLocalAlloc(0, cbReadNew);
687                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
688                         goto Quit;
689 
690                     Imm32CompAttrAnsiToWide(pRead, dwReadLen,
691                                             CS_StrA(pCS, CompReadStr), CS_SizeA(pCS, CompReadStr),
692                                             pReadNew, cbReadNew, uCodePage);
693                 }
694             }
695             break;
696 
697         case SCS_CHANGECLAUSE:
698             if (pComp)
699             {
700                 if (bAnsiClient)
701                 {
702                     cbCompNew = Imm32CompClauseWideToAnsi(pComp, dwCompLen, CS_StrW(pCS, CompStr),
703                                                           NULL, 0, uCodePage);
704                     pCompNew = ImmLocalAlloc(0, cbCompNew);
705                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
706                         goto Quit;
707 
708                     Imm32CompClauseWideToAnsi(pComp, dwCompLen, CS_StrW(pCS, CompStr),
709                                               pCompNew, cbCompNew, uCodePage);
710                 }
711                 else
712                 {
713                     cbCompNew = Imm32CompClauseAnsiToWide(pComp, dwCompLen, CS_StrA(pCS, CompStr),
714                                                           NULL, 0, uCodePage);
715                     pCompNew = ImmLocalAlloc(0, cbCompNew);
716                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
717                         goto Quit;
718 
719                     Imm32CompClauseAnsiToWide(pComp, dwCompLen, CS_StrA(pCS, CompStr),
720                                               pCompNew, cbCompNew, uCodePage);
721                 }
722             }
723 
724             if (pRead)
725             {
726                 if (bAnsiClient)
727                 {
728                     cbReadNew = Imm32CompClauseWideToAnsi(pRead, dwReadLen, CS_StrW(pCS, CompReadStr),
729                                                           NULL, 0, uCodePage);
730                     pReadNew = ImmLocalAlloc(0, cbReadNew);
731                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
732                         goto Quit;
733 
734                     Imm32CompClauseWideToAnsi(pRead, dwReadLen,
735                                               CS_StrW(pCS, CompReadStr),
736                                               pReadNew, cbReadNew, uCodePage);
737                 }
738                 else
739                 {
740                     cbReadNew = Imm32CompClauseAnsiToWide(pRead, dwReadLen, CS_StrA(pCS, CompReadStr),
741                                                           NULL, 0, uCodePage);
742                     pReadNew = ImmLocalAlloc(0, cbReadNew);
743                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
744                         goto Quit;
745 
746                     Imm32CompClauseAnsiToWide(pRead, dwReadLen, CS_StrA(pCS, CompReadStr),
747                                               pReadNew, cbReadNew, uCodePage);
748                 }
749             }
750             break;
751 
752         case SCS_SETRECONVERTSTRING: case SCS_QUERYRECONVERTSTRING:
753         {
754             if (pComp)
755             {
756                 if (bAnsiClient)
757                 {
758                     cbCompNew = Imm32ReconvertAnsiFromWide(NULL, pComp, uCodePage);
759                     pCompNew = ImmLocalAlloc(0, cbCompNew);
760                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
761                         goto Quit;
762 
763                     pRS = pCompNew;
764                     pRS->dwSize = cbCompNew;
765                     pRS->dwVersion = 0;
766                     Imm32ReconvertAnsiFromWide(pRS, pComp, uCodePage);
767                 }
768                 else
769                 {
770                     cbCompNew = Imm32ReconvertWideFromAnsi(NULL, pComp, uCodePage);
771                     pCompNew = ImmLocalAlloc(0, cbCompNew);
772                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
773                         goto Quit;
774 
775                     pRS = pCompNew;
776                     pRS->dwSize = cbCompNew;
777                     pRS->dwVersion = 0;
778                     Imm32ReconvertWideFromAnsi(pRS, pComp, uCodePage);
779                 }
780             }
781 
782             if (pRead)
783             {
784                 if (bAnsiClient)
785                 {
786                     cbReadNew = Imm32ReconvertAnsiFromWide(NULL, pRead, uCodePage);
787                     pReadNew = ImmLocalAlloc(0, cbReadNew);
788                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
789                         goto Quit;
790 
791                     pRS = pReadNew;
792                     pRS->dwSize = cbReadNew;
793                     pRS->dwVersion = 0;
794                     Imm32ReconvertAnsiFromWide(pRS, pRead, uCodePage);
795                 }
796                 else
797                 {
798                     cbReadNew = Imm32ReconvertWideFromAnsi(NULL, pRead, uCodePage);
799                     pReadNew = ImmLocalAlloc(0, cbReadNew);
800                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
801                         goto Quit;
802 
803                     pRS = pReadNew;
804                     pRS->dwSize = cbReadNew;
805                     pRS->dwVersion = 0;
806                     Imm32ReconvertWideFromAnsi(pRS, pRead, uCodePage);
807                 }
808             }
809             break;
810         }
811     }
812 
813     ImmUnlockIMCC(pIC->hCompStr);
814     pCS = NULL;
815     ImmUnlockIMC(hIMC);
816     pIC = NULL;
817 
818     ret = pImeDpi->ImeSetCompositionString(hIMC, dwIndex, pCompNew, cbCompNew,
819                                            pReadNew, cbReadNew);
820 
821     if (dwIndex == SCS_QUERYRECONVERTSTRING)
822     {
823         if (pComp)
824         {
825             if (bAnsiClient)
826                 ret = Imm32ReconvertWideFromAnsi(pComp, pCompNew, uCodePage);
827             else
828                 ret = Imm32ReconvertAnsiFromWide(pComp, pCompNew, uCodePage);
829         }
830 
831         if (pRead)
832         {
833             if (bAnsiClient)
834                 ret = Imm32ReconvertWideFromAnsi(pRead, pReadNew, uCodePage);
835             else
836                 ret = Imm32ReconvertAnsiFromWide(pRead, pReadNew, uCodePage);
837         }
838     }
839 
840 Quit:
841     if (pCS)
842         ImmUnlockIMCC(pIC->hCompStr);
843     if (pIC)
844         ImmUnlockIMC(hIMC);
845     ImmLocalFree(pCompNew);
846     ImmLocalFree(pReadNew);
847     ImmUnlockImeDpi(pImeDpi);
848     TRACE("ret: %d\n", ret);
849     return ret;
850 }
851 
852 /***********************************************************************
853  *		ImmGetCompositionStringA (IMM32.@)
854  */
855 LONG WINAPI ImmGetCompositionStringA(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
856 {
857     LONG ret = 0;
858     LPINPUTCONTEXT pIC;
859     PCLIENTIMC pClientImc;
860     LPCOMPOSITIONSTRING pCS;
861     BOOL bAnsiClient;
862     UINT uCodePage;
863 
864     TRACE("(%p, %lu, %p, %lu)\n", hIMC, dwIndex, lpBuf, dwBufLen);
865 
866     if (dwBufLen && IS_NULL_UNEXPECTEDLY(lpBuf))
867         return 0;
868 
869     pClientImc = ImmLockClientImc(hIMC);
870     if (IS_NULL_UNEXPECTEDLY(pClientImc))
871         return 0;
872 
873     bAnsiClient = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
874     uCodePage = pClientImc->uCodePage;
875     ImmUnlockClientImc(pClientImc);
876 
877     pIC = ImmLockIMC(hIMC);
878     if (IS_NULL_UNEXPECTEDLY(pIC))
879         return 0;
880 
881     pCS = ImmLockIMCC(pIC->hCompStr);
882     if (IS_NULL_UNEXPECTEDLY(pCS))
883     {
884         ImmUnlockIMC(hIMC);
885         return 0;
886     }
887 
888     ret = Imm32GetCompStrA(hIMC, pCS, dwIndex, lpBuf, dwBufLen, bAnsiClient, uCodePage);
889     ImmUnlockIMCC(pIC->hCompStr);
890     ImmUnlockIMC(hIMC);
891     TRACE("ret: %ld\n", ret);
892     return ret;
893 }
894 
895 /***********************************************************************
896  *		ImmGetCompositionStringW (IMM32.@)
897  */
898 LONG WINAPI ImmGetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
899 {
900     LONG ret = 0;
901     LPINPUTCONTEXT pIC;
902     PCLIENTIMC pClientImc;
903     LPCOMPOSITIONSTRING pCS;
904     BOOL bAnsiClient;
905     UINT uCodePage;
906 
907     TRACE("(%p, %lu, %p, %lu)\n", hIMC, dwIndex, lpBuf, dwBufLen);
908 
909     if (dwBufLen && IS_NULL_UNEXPECTEDLY(lpBuf))
910         return 0;
911 
912     pClientImc = ImmLockClientImc(hIMC);
913     if (IS_NULL_UNEXPECTEDLY(pClientImc))
914         return 0;
915 
916     bAnsiClient = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
917     uCodePage = pClientImc->uCodePage;
918     ImmUnlockClientImc(pClientImc);
919 
920     pIC = ImmLockIMC(hIMC);
921     if (IS_NULL_UNEXPECTEDLY(pIC))
922         return 0;
923 
924     pCS = ImmLockIMCC(pIC->hCompStr);
925     if (IS_NULL_UNEXPECTEDLY(pCS))
926     {
927         ImmUnlockIMC(hIMC);
928         return 0;
929     }
930 
931     ret = Imm32GetCompStrW(hIMC, pCS, dwIndex, lpBuf, dwBufLen, bAnsiClient, uCodePage);
932     ImmUnlockIMCC(pIC->hCompStr);
933     ImmUnlockIMC(hIMC);
934     TRACE("ret: %ld\n", ret);
935     return ret;
936 }
937 
938 /***********************************************************************
939  *		ImmSetCompositionStringA (IMM32.@)
940  */
941 BOOL WINAPI
942 ImmSetCompositionStringA(HIMC hIMC, DWORD dwIndex, LPVOID lpComp, DWORD dwCompLen,
943                          LPVOID lpRead, DWORD dwReadLen)
944 {
945     TRACE("(%p, %lu, %p, %lu, %p, %lu)\n",
946           hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
947     return ImmSetCompositionStringAW(hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen, TRUE);
948 }
949 
950 /***********************************************************************
951  *		ImmSetCompositionStringW (IMM32.@)
952  */
953 BOOL WINAPI
954 ImmSetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID lpComp, DWORD dwCompLen,
955                          LPVOID lpRead, DWORD dwReadLen)
956 {
957     TRACE("(%p, %lu, %p, %lu, %p, %lu)\n",
958           hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
959     return ImmSetCompositionStringAW(hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen, FALSE);
960 }
961