xref: /reactos/dll/win32/imm32/compstr.c (revision ccef43f3)
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, CompReadStr),
293                                                    CS_SizeW(pCS, CompReadStr),
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, CompReadStr),
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, ResultReadStr),
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, ResultStr),
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, CompReadStr),
387                                                    CS_SizeA(pCS, CompReadStr),
388                                                    lpBuf, dwBufLen, uCodePage);
389                 break;
390 
391             case GCS_COMPREADCLAUSE:
392                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, CompReadClause),
393                                                      CS_Size(pCS, CompReadClause),
394                                                      CS_StrA(pCS, CompReadStr),
395                                                      lpBuf, dwBufLen, uCodePage);
396                 break;
397 
398             case GCS_COMPSTR:
399                 dwBufLen = Imm32CompStrAnsiToWide(CS_StrA(pCS, CompStr),
400                                                   CS_SizeA(pCS, CompStr),
401                                                   lpBuf, dwBufLen, uCodePage);
402                 break;
403 
404             case GCS_COMPATTR:
405                 dwBufLen = Imm32CompAttrAnsiToWide(CS_Attr(pCS, CompAttr),
406                                                    CS_Size(pCS, CompAttr),
407                                                    CS_StrA(pCS, CompStr), CS_SizeA(pCS, CompStr),
408                                                    lpBuf, dwBufLen, uCodePage);
409                 break;
410 
411             case GCS_COMPCLAUSE:
412                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, CompClause),
413                                                      CS_Size(pCS, CompClause),
414                                                      CS_StrA(pCS, CompStr),
415                                                      lpBuf, dwBufLen, uCodePage);
416                 break;
417 
418             case GCS_CURSORPOS:
419                 dwBufLen = IchWideFromAnsi(pCS->dwCursorPos, CS_StrA(pCS, CompStr), uCodePage);
420                 break;
421 
422             case GCS_DELTASTART:
423                 dwBufLen = IchWideFromAnsi(pCS->dwDeltaStart, CS_StrA(pCS, CompStr), uCodePage);
424                 break;
425 
426             case GCS_RESULTREADSTR:
427                 dwBufLen = Imm32CompStrAnsiToWide(CS_StrA(pCS, ResultReadStr),
428                                                   CS_SizeA(pCS, ResultReadStr),
429                                                   lpBuf, dwBufLen, uCodePage);
430                 break;
431 
432             case GCS_RESULTREADCLAUSE:
433                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, ResultReadClause),
434                                                      CS_Size(pCS, ResultReadClause),
435                                                      CS_StrA(pCS, ResultReadStr),
436                                                      lpBuf, dwBufLen, uCodePage);
437                 break;
438 
439             case GCS_RESULTSTR:
440                 dwBufLen = Imm32CompStrAnsiToWide(CS_StrA(pCS, ResultStr),
441                                                   CS_SizeA(pCS, ResultStr),
442                                                   lpBuf, dwBufLen, uCodePage);
443                 break;
444 
445             case GCS_RESULTCLAUSE:
446                 dwBufLen = Imm32CompClauseAnsiToWide(CS_Clause(pCS, ResultClause),
447                                                      CS_Size(pCS, ResultClause),
448                                                      CS_StrA(pCS, ResultStr),
449                                                      lpBuf, dwBufLen, uCodePage);
450                 break;
451 
452             default:
453                 FIXME("0x%X\n", dwIndex);
454                 return IMM_ERROR_GENERAL;
455         }
456     }
457     else /* !bAnsiClient */
458     {
459         switch (dwIndex)
460         {
461             case GCS_COMPREADSTR:
462                 CS_DoStrW(pCS, CompReadStr);
463                 break;
464 
465             case GCS_COMPREADATTR:
466                 CS_DoAttr(pCS, CompReadAttr);
467                 break;
468 
469             case GCS_COMPREADCLAUSE:
470                 CS_DoClause(pCS, CompReadClause);
471                 break;
472 
473             case GCS_COMPSTR:
474                 CS_DoStrW(pCS, CompStr);
475                 break;
476 
477             case GCS_COMPATTR:
478                 CS_DoAttr(pCS, CompAttr);
479                 break;
480 
481             case GCS_COMPCLAUSE:
482                 CS_DoClause(pCS, CompClause);
483                 break;
484 
485             case GCS_CURSORPOS:
486                 dwBufLen = pCS->dwCursorPos;
487                 break;
488 
489             case GCS_DELTASTART:
490                 dwBufLen = pCS->dwDeltaStart;
491                 break;
492 
493             case GCS_RESULTREADSTR:
494                 CS_DoStrW(pCS, ResultReadStr);
495                 break;
496 
497             case GCS_RESULTREADCLAUSE:
498                 CS_DoClause(pCS, ResultReadClause);
499                 break;
500 
501             case GCS_RESULTSTR:
502                 CS_DoStrW(pCS, ResultStr);
503                 break;
504 
505             case GCS_RESULTCLAUSE:
506                 CS_DoClause(pCS, ResultClause);
507                 break;
508 
509             default:
510                 FIXME("0x%X\n", dwIndex);
511                 return IMM_ERROR_GENERAL;
512         }
513     }
514 
515     return dwBufLen;
516 }
517 
518 // Win: ImmSetCompositionStringWorker
519 BOOL APIENTRY
520 ImmSetCompositionStringAW(HIMC hIMC, DWORD dwIndex, LPVOID pComp, DWORD dwCompLen,
521                           LPVOID pRead, DWORD dwReadLen, BOOL bAnsiAPI)
522 {
523     BOOL ret = FALSE, bAnsiClient;
524     LPVOID pCompNew = NULL, pReadNew = NULL;
525     DWORD dwThreadId, cbCompNew = 0, cbReadNew = 0;
526     LPINPUTCONTEXT pIC;
527     LPCOMPOSITIONSTRING pCS;
528     HKL hKL;
529     PIMEDPI pImeDpi;
530     UINT uCodePage;
531     LPRECONVERTSTRING pRS;
532 
533     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
534     if (dwThreadId != GetCurrentThreadId())
535     {
536         ERR("Thread mismatch\n");
537         return FALSE;
538     }
539 
540     hKL = GetKeyboardLayout(dwThreadId);
541     pImeDpi = ImmLockImeDpi(hKL);
542     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
543         return FALSE;
544 
545     uCodePage = pImeDpi->uCodePage;
546     bAnsiClient = !ImeDpi_IsUnicode(pImeDpi);
547 
548     switch (dwIndex)
549     {
550         case SCS_SETSTR: case SCS_CHANGEATTR: case SCS_CHANGECLAUSE:
551             break;
552 
553         case SCS_SETRECONVERTSTRING: case SCS_QUERYRECONVERTSTRING:
554             if (pImeDpi->ImeInfo.fdwSCSCaps & SCS_CAP_SETRECONVERTSTRING)
555                 break;
556             /* FALL THROUGH */
557         default:
558             ERR("0x%X\n", dwIndex);
559             ImmUnlockImeDpi(pImeDpi);
560             return FALSE;
561     }
562 
563     if (bAnsiAPI == bAnsiClient || (!pComp && !pRead)) /* No conversion needed */
564     {
565         ret = pImeDpi->ImeSetCompositionString(hIMC, dwIndex, pComp, dwCompLen,
566                                                pRead, dwReadLen);
567         ImmUnlockImeDpi(pImeDpi);
568         return ret;
569     }
570 
571     if (!Imm32OpenICAndCS(hIMC, &pIC, &pCS))
572     {
573         ImmUnlockImeDpi(pImeDpi);
574         return FALSE;
575     }
576 
577     /*
578      * This code is really too complicated. But I cannot simplify.
579      * It converts like (pComp, dwCompLen) --> (pCompNew, cbCompNew) and
580      * (pRead, dwReadLen) --> (pReadNew, cbReadNew).
581      * (1) Check bAnsiClient, (2) Get the size, (3) Allocate a buffer for conversion,
582      * (4) Store converted data into the buffer.
583      */
584     switch (dwIndex)
585     {
586         case SCS_SETSTR:
587             if (pComp)
588             {
589                 if (bAnsiClient)
590                 {
591                     cbCompNew = Imm32CompStrWideToAnsi(pComp, dwCompLen, NULL, 0, uCodePage);
592                     pCompNew = ImmLocalAlloc(0, cbCompNew);
593                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
594                         goto Quit;
595 
596                     Imm32CompStrWideToAnsi(pComp, dwCompLen, pCompNew, cbCompNew, uCodePage);
597                 }
598                 else
599                 {
600                     cbCompNew = Imm32CompStrAnsiToWide(pComp, dwCompLen, NULL, 0, uCodePage);
601                     pCompNew = ImmLocalAlloc(0, cbCompNew);
602                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
603                         goto Quit;
604 
605                     Imm32CompStrAnsiToWide(pComp, dwCompLen, pCompNew, cbCompNew, uCodePage);
606                 }
607             }
608 
609             if (pRead)
610             {
611                 if (bAnsiClient)
612                 {
613                     cbReadNew = Imm32CompStrWideToAnsi(pRead, dwReadLen, NULL, 0, uCodePage);
614                     pReadNew = ImmLocalAlloc(0, cbReadNew);
615                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
616                         goto Quit;
617 
618                     Imm32CompStrWideToAnsi(pRead, dwReadLen, pReadNew, cbReadNew, uCodePage);
619                 }
620                 else
621                 {
622                     cbReadNew = Imm32CompStrAnsiToWide(pRead, dwReadLen, NULL, 0, uCodePage);
623                     pReadNew = ImmLocalAlloc(0, cbReadNew);
624                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
625                         goto Quit;
626 
627                     Imm32CompStrAnsiToWide(pRead, dwReadLen, pReadNew, cbReadNew, uCodePage);
628                 }
629             }
630             break;
631 
632         case SCS_CHANGEATTR:
633             if (pComp)
634             {
635                 if (bAnsiClient)
636                 {
637                     cbCompNew = Imm32CompAttrWideToAnsi(pComp, dwCompLen,
638                                                         CS_StrW(pCS, CompStr),
639                                                         CS_SizeW(pCS, CompStr),
640                                                         NULL, 0, uCodePage);
641                     pCompNew = ImmLocalAlloc(0, cbCompNew);
642                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
643                         goto Quit;
644 
645                     Imm32CompAttrWideToAnsi(pComp, dwCompLen,
646                                             CS_StrW(pCS, CompStr), CS_SizeW(pCS, CompStr),
647                                             pCompNew, cbCompNew, uCodePage);
648                 }
649                 else
650                 {
651                     cbCompNew = Imm32CompAttrAnsiToWide(pComp, dwCompLen,
652                                                         CS_StrA(pCS, CompStr),
653                                                         CS_SizeA(pCS, CompStr),
654                                                         NULL, 0, uCodePage);
655                     pCompNew = ImmLocalAlloc(0, cbCompNew);
656                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
657                         goto Quit;
658 
659                     Imm32CompAttrAnsiToWide(pComp, dwCompLen,
660                                             CS_StrA(pCS, CompStr), CS_SizeA(pCS, CompStr),
661                                             pCompNew, cbCompNew, uCodePage);
662                 }
663             }
664 
665             if (pRead)
666             {
667                 if (bAnsiClient)
668                 {
669                     cbReadNew = Imm32CompAttrWideToAnsi(pRead, dwReadLen,
670                                                         CS_StrW(pCS, CompReadStr),
671                                                         CS_SizeW(pCS, CompReadStr),
672                                                         NULL, 0, uCodePage);
673                     pReadNew = ImmLocalAlloc(0, cbReadNew);
674                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
675                         goto Quit;
676 
677                     Imm32CompAttrWideToAnsi(pRead, dwReadLen,
678                                             CS_StrW(pCS, CompReadStr), CS_SizeW(pCS, CompReadStr),
679                                             pReadNew, cbReadNew, uCodePage);
680                 }
681                 else
682                 {
683                     cbReadNew = Imm32CompAttrAnsiToWide(pRead, dwReadLen,
684                                                         CS_StrA(pCS, CompReadStr),
685                                                         CS_SizeA(pCS, CompReadStr),
686                                                         NULL, 0, uCodePage);
687                     pReadNew = ImmLocalAlloc(0, cbReadNew);
688                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
689                         goto Quit;
690 
691                     Imm32CompAttrAnsiToWide(pRead, dwReadLen,
692                                             CS_StrA(pCS, CompReadStr), CS_SizeA(pCS, CompReadStr),
693                                             pReadNew, cbReadNew, uCodePage);
694                 }
695             }
696             break;
697 
698         case SCS_CHANGECLAUSE:
699             if (pComp)
700             {
701                 if (bAnsiClient)
702                 {
703                     cbCompNew = Imm32CompClauseWideToAnsi(pComp, dwCompLen, CS_StrW(pCS, CompStr),
704                                                           NULL, 0, uCodePage);
705                     pCompNew = ImmLocalAlloc(0, cbCompNew);
706                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
707                         goto Quit;
708 
709                     Imm32CompClauseWideToAnsi(pComp, dwCompLen, CS_StrW(pCS, CompStr),
710                                               pCompNew, cbCompNew, uCodePage);
711                 }
712                 else
713                 {
714                     cbCompNew = Imm32CompClauseAnsiToWide(pComp, dwCompLen, CS_StrA(pCS, CompStr),
715                                                           NULL, 0, uCodePage);
716                     pCompNew = ImmLocalAlloc(0, cbCompNew);
717                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
718                         goto Quit;
719 
720                     Imm32CompClauseAnsiToWide(pComp, dwCompLen, CS_StrA(pCS, CompStr),
721                                               pCompNew, cbCompNew, uCodePage);
722                 }
723             }
724 
725             if (pRead)
726             {
727                 if (bAnsiClient)
728                 {
729                     cbReadNew = Imm32CompClauseWideToAnsi(pRead, dwReadLen, CS_StrW(pCS, CompReadStr),
730                                                           NULL, 0, uCodePage);
731                     pReadNew = ImmLocalAlloc(0, cbReadNew);
732                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
733                         goto Quit;
734 
735                     Imm32CompClauseWideToAnsi(pRead, dwReadLen,
736                                               CS_StrW(pCS, CompReadStr),
737                                               pReadNew, cbReadNew, uCodePage);
738                 }
739                 else
740                 {
741                     cbReadNew = Imm32CompClauseAnsiToWide(pRead, dwReadLen, CS_StrA(pCS, CompReadStr),
742                                                           NULL, 0, uCodePage);
743                     pReadNew = ImmLocalAlloc(0, cbReadNew);
744                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
745                         goto Quit;
746 
747                     Imm32CompClauseAnsiToWide(pRead, dwReadLen, CS_StrA(pCS, CompReadStr),
748                                               pReadNew, cbReadNew, uCodePage);
749                 }
750             }
751             break;
752 
753         case SCS_SETRECONVERTSTRING: case SCS_QUERYRECONVERTSTRING:
754         {
755             if (pComp)
756             {
757                 if (bAnsiClient)
758                 {
759                     cbCompNew = Imm32ReconvertAnsiFromWide(NULL, pComp, uCodePage);
760                     pCompNew = ImmLocalAlloc(0, cbCompNew);
761                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
762                         goto Quit;
763 
764                     pRS = pCompNew;
765                     pRS->dwSize = cbCompNew;
766                     pRS->dwVersion = 0;
767                     Imm32ReconvertAnsiFromWide(pRS, pComp, uCodePage);
768                 }
769                 else
770                 {
771                     cbCompNew = Imm32ReconvertWideFromAnsi(NULL, pComp, uCodePage);
772                     pCompNew = ImmLocalAlloc(0, cbCompNew);
773                     if (IS_NULL_UNEXPECTEDLY(pCompNew))
774                         goto Quit;
775 
776                     pRS = pCompNew;
777                     pRS->dwSize = cbCompNew;
778                     pRS->dwVersion = 0;
779                     Imm32ReconvertWideFromAnsi(pRS, pComp, uCodePage);
780                 }
781             }
782 
783             if (pRead)
784             {
785                 if (bAnsiClient)
786                 {
787                     cbReadNew = Imm32ReconvertAnsiFromWide(NULL, pRead, uCodePage);
788                     pReadNew = ImmLocalAlloc(0, cbReadNew);
789                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
790                         goto Quit;
791 
792                     pRS = pReadNew;
793                     pRS->dwSize = cbReadNew;
794                     pRS->dwVersion = 0;
795                     Imm32ReconvertAnsiFromWide(pRS, pRead, uCodePage);
796                 }
797                 else
798                 {
799                     cbReadNew = Imm32ReconvertWideFromAnsi(NULL, pRead, uCodePage);
800                     pReadNew = ImmLocalAlloc(0, cbReadNew);
801                     if (IS_NULL_UNEXPECTEDLY(pReadNew))
802                         goto Quit;
803 
804                     pRS = pReadNew;
805                     pRS->dwSize = cbReadNew;
806                     pRS->dwVersion = 0;
807                     Imm32ReconvertWideFromAnsi(pRS, pRead, uCodePage);
808                 }
809             }
810             break;
811         }
812     }
813 
814     ImmUnlockIMCC(pIC->hCompStr);
815     pCS = NULL;
816     ImmUnlockIMC(hIMC);
817     pIC = NULL;
818 
819     ret = pImeDpi->ImeSetCompositionString(hIMC, dwIndex, pCompNew, cbCompNew,
820                                            pReadNew, cbReadNew);
821 
822     if (dwIndex == SCS_QUERYRECONVERTSTRING)
823     {
824         if (pComp)
825         {
826             if (bAnsiClient)
827                 ret = Imm32ReconvertWideFromAnsi(pComp, pCompNew, uCodePage);
828             else
829                 ret = Imm32ReconvertAnsiFromWide(pComp, pCompNew, uCodePage);
830         }
831 
832         if (pRead)
833         {
834             if (bAnsiClient)
835                 ret = Imm32ReconvertWideFromAnsi(pRead, pReadNew, uCodePage);
836             else
837                 ret = Imm32ReconvertAnsiFromWide(pRead, pReadNew, uCodePage);
838         }
839     }
840 
841 Quit:
842     if (pCS)
843         ImmUnlockIMCC(pIC->hCompStr);
844     if (pIC)
845         ImmUnlockIMC(hIMC);
846     ImmLocalFree(pCompNew);
847     ImmLocalFree(pReadNew);
848     ImmUnlockImeDpi(pImeDpi);
849     TRACE("ret: %d\n", ret);
850     return ret;
851 }
852 
853 /***********************************************************************
854  *		ImmGetCompositionStringA (IMM32.@)
855  */
856 LONG WINAPI ImmGetCompositionStringA(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
857 {
858     LONG ret = 0;
859     LPINPUTCONTEXT pIC;
860     PCLIENTIMC pClientImc;
861     LPCOMPOSITIONSTRING pCS;
862     BOOL bAnsiClient;
863     UINT uCodePage;
864 
865     TRACE("(%p, %lu, %p, %lu)\n", hIMC, dwIndex, lpBuf, dwBufLen);
866 
867     if (dwBufLen && IS_NULL_UNEXPECTEDLY(lpBuf))
868         return 0;
869 
870     pClientImc = ImmLockClientImc(hIMC);
871     if (IS_NULL_UNEXPECTEDLY(pClientImc))
872         return 0;
873 
874     bAnsiClient = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
875     uCodePage = pClientImc->uCodePage;
876     ImmUnlockClientImc(pClientImc);
877 
878     pIC = ImmLockIMC(hIMC);
879     if (IS_NULL_UNEXPECTEDLY(pIC))
880         return 0;
881 
882     pCS = ImmLockIMCC(pIC->hCompStr);
883     if (IS_NULL_UNEXPECTEDLY(pCS))
884     {
885         ImmUnlockIMC(hIMC);
886         return 0;
887     }
888 
889     ret = Imm32GetCompStrA(hIMC, pCS, dwIndex, lpBuf, dwBufLen, bAnsiClient, uCodePage);
890     ImmUnlockIMCC(pIC->hCompStr);
891     ImmUnlockIMC(hIMC);
892     TRACE("ret: %ld\n", ret);
893     return ret;
894 }
895 
896 /***********************************************************************
897  *		ImmGetCompositionStringW (IMM32.@)
898  */
899 LONG WINAPI ImmGetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen)
900 {
901     LONG ret = 0;
902     LPINPUTCONTEXT pIC;
903     PCLIENTIMC pClientImc;
904     LPCOMPOSITIONSTRING pCS;
905     BOOL bAnsiClient;
906     UINT uCodePage;
907 
908     TRACE("(%p, %lu, %p, %lu)\n", hIMC, dwIndex, lpBuf, dwBufLen);
909 
910     if (dwBufLen && IS_NULL_UNEXPECTEDLY(lpBuf))
911         return 0;
912 
913     pClientImc = ImmLockClientImc(hIMC);
914     if (IS_NULL_UNEXPECTEDLY(pClientImc))
915         return 0;
916 
917     bAnsiClient = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
918     uCodePage = pClientImc->uCodePage;
919     ImmUnlockClientImc(pClientImc);
920 
921     pIC = ImmLockIMC(hIMC);
922     if (IS_NULL_UNEXPECTEDLY(pIC))
923         return 0;
924 
925     pCS = ImmLockIMCC(pIC->hCompStr);
926     if (IS_NULL_UNEXPECTEDLY(pCS))
927     {
928         ImmUnlockIMC(hIMC);
929         return 0;
930     }
931 
932     ret = Imm32GetCompStrW(hIMC, pCS, dwIndex, lpBuf, dwBufLen, bAnsiClient, uCodePage);
933     ImmUnlockIMCC(pIC->hCompStr);
934     ImmUnlockIMC(hIMC);
935     TRACE("ret: %ld\n", ret);
936     return ret;
937 }
938 
939 /***********************************************************************
940  *		ImmSetCompositionStringA (IMM32.@)
941  */
942 BOOL WINAPI
943 ImmSetCompositionStringA(HIMC hIMC, DWORD dwIndex, LPVOID lpComp, DWORD dwCompLen,
944                          LPVOID lpRead, DWORD dwReadLen)
945 {
946     TRACE("(%p, %lu, %p, %lu, %p, %lu)\n",
947           hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
948     return ImmSetCompositionStringAW(hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen, TRUE);
949 }
950 
951 /***********************************************************************
952  *		ImmSetCompositionStringW (IMM32.@)
953  */
954 BOOL WINAPI
955 ImmSetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID lpComp, DWORD dwCompLen,
956                          LPVOID lpRead, DWORD dwReadLen)
957 {
958     TRACE("(%p, %lu, %p, %lu, %p, %lu)\n",
959           hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen);
960     return ImmSetCompositionStringAW(hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen, FALSE);
961 }
962