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