[IMM32] Rewrite ImmRequestMessageA/W (#4002)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Fri, 8 Oct 2021 22:40:56 +0000 (07:40 +0900)
committerGitHub <noreply@github.com>
Fri, 8 Oct 2021 22:40:56 +0000 (07:40 +0900)
- Re-implement ImmRequestMessageA and ImmRequestMessageW functions.
- Add IchWideFromAnsi, IchAnsiFromWide, Imm32RequestError, Imm32ReconvertSize, Imm32ConvertReconvert, and Imm32ProcessRequest helper functions.
CORE-11700

dll/win32/imm32/imm.c
dll/win32/imm32/imm32.spec
dll/win32/imm32/keymsg.c
dll/win32/imm32/precomp.h
dll/win32/imm32/utils.c

index db537ad..69cd428 100644 (file)
@@ -1483,36 +1483,6 @@ BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
     return TRUE;
 }
 
-/***********************************************************************
- *              ImmRequestMessageA(IMM32.@)
- */
-LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
-{
-    InputContextData *data = get_imc_data(hIMC);
-
-    TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
-
-    if (data) return SendMessageA(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
-
-    SetLastError(ERROR_INVALID_HANDLE);
-    return 0;
-}
-
-/***********************************************************************
- *              ImmRequestMessageW(IMM32.@)
- */
-LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
-{
-    InputContextData *data = get_imc_data(hIMC);
-
-    TRACE("%p %ld %ld\n", hIMC, wParam, wParam);
-
-    if (data) return SendMessageW(data->IMC.hWnd, WM_IME_REQUEST, wParam, lParam);
-
-    SetLastError(ERROR_INVALID_HANDLE);
-    return 0;
-}
-
 /***********************************************************************
  *             ImmReleaseContext (IMM32.@)
  */
index 09ca424..0221701 100644 (file)
@@ -87,8 +87,8 @@
 @ stdcall ImmRegisterWordA(long str long str)
 @ stdcall ImmRegisterWordW(long wstr long wstr)
 @ stdcall ImmReleaseContext(ptr ptr)
-@ stdcall ImmRequestMessageA(ptr long long)
-@ stdcall ImmRequestMessageW(ptr long long)
+@ stdcall ImmRequestMessageA(ptr ptr ptr)
+@ stdcall ImmRequestMessageW(ptr ptr ptr)
 @ stdcall ImmSendIMEMessageExA(ptr ptr)
 @ stdcall ImmSendIMEMessageExW(ptr ptr)
 @ stub ImmSendMessageToActiveDefImeWndW
index c8dc867..1683825 100644 (file)
@@ -349,6 +349,323 @@ BOOL APIENTRY Imm32SendChange(BOOL bProcess)
     return ImmEnumInputContext((bProcess ? -1 : 0), Imm32SendChangeProc, 0);
 }
 
+VOID APIENTRY Imm32RequestError(DWORD dwError)
+{
+    FIXME("()\n");
+    SetLastError(dwError);
+}
+
+DWORD APIENTRY Imm32ReconvertSize(DWORD dwSize, BOOL bAnsi, BOOL bConvert)
+{
+    DWORD dwOffset;
+    if (dwSize < sizeof(RECONVERTSTRING))
+        return 0;
+    if (!bConvert)
+        return dwSize;
+    dwOffset = dwSize - sizeof(RECONVERTSTRING);
+    if (bAnsi)
+        dwOffset /= sizeof(WCHAR);
+    else
+        dwOffset *= sizeof(WCHAR);
+    return sizeof(RECONVERTSTRING) + dwOffset;
+}
+
+DWORD APIENTRY
+Imm32ConvertReconvert(LPRECONVERTSTRING pDest, const RECONVERTSTRING *pSrc, BOOL bAnsi,
+                      UINT uCodePage)
+{
+    DWORD ret = sizeof(RECONVERTSTRING), cch0, cch1, cchDest;
+
+    if ((pSrc->dwVersion != 0) || (pDest->dwVersion != 0))
+        return 0;
+
+    pDest->dwStrOffset = sizeof(RECONVERTSTRING);
+
+    /*
+     * See RECONVERTSTRING structure:
+     * https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/RECONVERTSTRING.html
+     *
+     * The dwCompStrOffset and dwTargetOffset members are the relative position of dwStrOffset.
+     * dwStrLen, dwCompStrLen, and dwTargetStrLen are the TCHAR count. dwStrOffset,
+     * dwCompStrOffset, and dwTargetStrOffset are the byte offset.
+     */
+    if (bAnsi) /* Ansi <-- Wide */
+    {
+        LPCWSTR pchSrc = (LPCWSTR)((LPCSTR)pSrc + pSrc->dwStrOffset);
+        LPSTR pchDest = (LPSTR)pDest + pDest->dwStrOffset;
+
+        cchDest = WideCharToMultiByte(uCodePage, 0, pchSrc, pSrc->dwStrLen,
+                                      pchDest, pSrc->dwStrLen, NULL, NULL);
+
+        /* dwCompStrOffset */
+        cch1 = pSrc->dwCompStrOffset / sizeof(WCHAR);
+        cch0 = IchAnsiFromWide(cch1, pchSrc, uCodePage);
+        pDest->dwCompStrOffset = cch0 * sizeof(CHAR);
+
+        /* dwCompStrLen */
+        cch0 = IchAnsiFromWide(cch1 + pSrc->dwCompStrLen, pchSrc, uCodePage);
+        pDest->dwCompStrLen = cch0 * sizeof(CHAR) - pDest->dwCompStrOffset;
+
+        /* dwTargetStrOffset */
+        cch1 = pSrc->dwTargetStrOffset / sizeof(WCHAR);
+        cch0 = IchAnsiFromWide(cch1, pchSrc, uCodePage);
+        pDest->dwTargetStrOffset = cch0 * sizeof(CHAR);
+
+        /* dwTargetStrLen */
+        cch0 = IchAnsiFromWide(cch1 + pSrc->dwTargetStrLen, pchSrc, uCodePage);
+        pDest->dwTargetStrLen = cch0 * sizeof(CHAR) - pDest->dwTargetStrOffset;
+
+        /* dwStrLen */
+        pDest->dwStrLen = cchDest;
+        pchDest[cchDest] = 0;
+
+        ret += (cchDest + 1) * sizeof(CHAR);
+    }
+    else /* Wide <-- Ansi */
+    {
+        LPCSTR pchSrc = (LPCSTR)pSrc + pSrc->dwStrOffset;
+        LPWSTR pchDest = (LPWSTR)((LPBYTE)pDest + pDest->dwStrOffset);
+
+        cchDest = MultiByteToWideChar(uCodePage, MB_PRECOMPOSED, pchSrc, pSrc->dwStrLen,
+                                      pchDest, pSrc->dwStrLen);
+
+        /* dwCompStrOffset */
+        cch0 = IchWideFromAnsi(pSrc->dwCompStrOffset, pchSrc, uCodePage);
+        pDest->dwCompStrOffset = cch0 * sizeof(WCHAR);
+
+        /* dwCompStrLen */
+        cch0 = IchWideFromAnsi(pSrc->dwCompStrOffset + pSrc->dwCompStrLen, pchSrc, uCodePage);
+        pDest->dwCompStrLen = (cch0 * sizeof(WCHAR) - pDest->dwCompStrOffset) / sizeof(WCHAR);
+
+        /* dwTargetStrOffset */
+        cch0 = IchWideFromAnsi(pSrc->dwTargetStrOffset, pchSrc, uCodePage);
+        pDest->dwTargetStrOffset = cch0 * sizeof(WCHAR);
+
+        /* dwTargetStrLen */
+        cch0 = IchWideFromAnsi(pSrc->dwTargetStrOffset + pSrc->dwTargetStrLen, pchSrc, uCodePage);
+        pDest->dwTargetStrLen = (cch0 * sizeof(WCHAR) - pSrc->dwTargetStrOffset) / sizeof(WCHAR);
+
+        /* dwStrLen */
+        pDest->dwStrLen = cchDest;
+        pchDest[cchDest] = 0;
+
+        ret += (cchDest + 1) * sizeof(WCHAR);
+    }
+
+    return ret;
+}
+
+LRESULT APIENTRY
+Imm32ProcessRequest(HIMC hIMC, PWND pWnd, DWORD dwCommand, LPVOID pData, BOOL bAnsiAPI)
+{
+    HWND hWnd;
+    DWORD ret = 0, dwCharPos, cchCompStr;
+    LPVOID pCS, pTempData = pData;
+    LPRECONVERTSTRING pRS;
+    LPIMECHARPOSITION pICP;
+    PCLIENTIMC pClientImc;
+    UINT uCodePage = CP_ACP;
+    BOOL bAnsiWnd = !!(pWnd->state & WNDS_ANSIWINDOWPROC);
+    static const size_t acbData[7 * 2] =
+    {
+        /* UNICODE */
+        sizeof(COMPOSITIONFORM), sizeof(CANDIDATEFORM), sizeof(LOGFONTW),
+        sizeof(RECONVERTSTRING), sizeof(RECONVERTSTRING),
+        sizeof(IMECHARPOSITION), sizeof(RECONVERTSTRING),
+        /* ANSI */
+        sizeof(COMPOSITIONFORM), sizeof(CANDIDATEFORM), sizeof(LOGFONTA),
+        sizeof(RECONVERTSTRING), sizeof(RECONVERTSTRING),
+        sizeof(IMECHARPOSITION), sizeof(RECONVERTSTRING),
+    };
+
+    if (dwCommand == 0 || dwCommand > IMR_DOCUMENTFEED)
+        return 0; /* Out of range */
+
+    if (pData && IsBadWritePtr(pData, acbData[bAnsiAPI * 7 + dwCommand - 1]))
+        return 0; /* Invalid pointer */
+
+    /* Sanity check */
+    switch (dwCommand)
+    {
+        case IMR_RECONVERTSTRING: case IMR_DOCUMENTFEED:
+            pRS = pData;
+            if (pRS && (pRS->dwVersion != 0 || pRS->dwSize < sizeof(RECONVERTSTRING)))
+            {
+                Imm32RequestError(ERROR_INVALID_PARAMETER);
+                return 0;
+            }
+            break;
+
+        case IMR_CONFIRMRECONVERTSTRING:
+            pRS = pData;
+            if (!pRS || pRS->dwVersion != 0)
+            {
+                Imm32RequestError(ERROR_INVALID_PARAMETER);
+                return 0;
+            }
+            break;
+
+        default:
+            if (!pData)
+            {
+                Imm32RequestError(ERROR_INVALID_PARAMETER);
+                return 0;
+            }
+            break;
+    }
+
+    pClientImc = ImmLockClientImc(hIMC);
+    if (pClientImc)
+    {
+        uCodePage = pClientImc->uCodePage;
+        ImmUnlockClientImc(pClientImc);
+    }
+
+    /* Prepare */
+    switch (dwCommand)
+    {
+        case IMR_COMPOSITIONFONT:
+            if (bAnsiAPI == bAnsiWnd)
+                goto DoIt;
+            if (bAnsiWnd)
+                pTempData = Imm32HeapAlloc(0, sizeof(LOGFONTA));
+            else
+                pTempData = Imm32HeapAlloc(0, sizeof(LOGFONTW));
+            if (!pTempData)
+                return 0;
+            break;
+
+        case IMR_RECONVERTSTRING: case IMR_CONFIRMRECONVERTSTRING: case IMR_DOCUMENTFEED:
+            if (bAnsiAPI == bAnsiWnd || !pData)
+                goto DoIt;
+
+            pRS = pData;
+            ret = Imm32ReconvertSize(pRS->dwSize, FALSE, bAnsiWnd);
+            pTempData = Imm32HeapAlloc(0, ret + sizeof(WCHAR));
+            if (!pTempData)
+                return 0;
+
+            pRS = pTempData;
+            pRS->dwSize = ret;
+            pRS->dwVersion = 0;
+
+            if (dwCommand == IMR_CONFIRMRECONVERTSTRING)
+                Imm32ConvertReconvert(pData, pTempData, bAnsiWnd, uCodePage);
+            break;
+
+        case IMR_QUERYCHARPOSITION:
+            if (bAnsiAPI == bAnsiWnd)
+                goto DoIt;
+
+            pICP = pData;
+            dwCharPos = pICP->dwCharPos;
+
+            if (bAnsiAPI)
+            {
+                cchCompStr = ImmGetCompositionStringA(hIMC, GCS_COMPSTR, NULL, 0);
+                if (!cchCompStr)
+                    return 0;
+
+                pCS = Imm32HeapAlloc(0, (cchCompStr + 1) * sizeof(CHAR));
+                if (!pCS)
+                    return 0;
+
+                ImmGetCompositionStringA(hIMC, GCS_COMPSTR, pCS, cchCompStr);
+                pICP->dwCharPos = IchWideFromAnsi(pICP->dwCharPos, pCS, uCodePage);
+            }
+            else
+            {
+                cchCompStr = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
+                if (!cchCompStr)
+                    return 0;
+
+                pCS = Imm32HeapAlloc(0, (cchCompStr + 1) * sizeof(WCHAR));
+                if (!pCS)
+                    return 0;
+
+                ImmGetCompositionStringW(hIMC, GCS_COMPSTR, pCS, cchCompStr);
+                pICP->dwCharPos = IchAnsiFromWide(pICP->dwCharPos, pCS, uCodePage);
+            }
+
+            Imm32HeapFree(pCS);
+            break;
+
+        default:
+            break;
+    }
+
+DoIt:
+    /* The main task */
+    hWnd = pWnd->head.h;
+    if (bAnsiWnd)
+        ret = SendMessageA(hWnd, WM_IME_REQUEST, dwCommand, (LPARAM)pTempData);
+    else
+        ret = SendMessageW(hWnd, WM_IME_REQUEST, dwCommand, (LPARAM)pTempData);
+
+    if (bAnsiAPI == bAnsiWnd)
+        goto Quit;
+
+    /* Get back to caller */
+    switch (dwCommand)
+    {
+        case IMR_COMPOSITIONFONT:
+            if (bAnsiAPI)
+                LogFontWideToAnsi(pTempData, pData);
+            else
+                LogFontAnsiToWide(pTempData, pData);
+            break;
+
+        case IMR_RECONVERTSTRING: case IMR_DOCUMENTFEED:
+            if (!ret)
+                goto Quit;
+
+            ret = Imm32ReconvertSize(ret, TRUE, bAnsiWnd);
+            if (ret < sizeof(RECONVERTSTRING) ||
+                (pTempData && !Imm32ConvertReconvert(pData, pTempData, bAnsiAPI, uCodePage)))
+            {
+                ret = 0;
+            }
+            break;
+
+        case IMR_QUERYCHARPOSITION:
+            pICP->dwCharPos = dwCharPos;
+            break;
+
+        default:
+            break;
+    }
+
+Quit:
+    if (pTempData != pData)
+        Imm32HeapFree(pTempData);
+    return ret;
+}
+
+LRESULT APIENTRY Imm32RequestMessageAW(HIMC hIMC, WPARAM wParam, LPARAM lParam, BOOL bAnsi)
+{
+    LRESULT ret = 0;
+    LPINPUTCONTEXT pIC;
+    HWND hWnd;
+    PWND pWnd = NULL;
+
+    if (!hIMC || Imm32IsCrossThreadAccess(hIMC))
+        return FALSE;
+
+    pIC = ImmLockIMC(hIMC);
+    if (!pIC)
+        return FALSE;
+
+    hWnd = pIC->hWnd;
+    if (hWnd)
+        pWnd = ValidateHwndNoErr(hWnd);
+
+    if (pWnd && pWnd->head.pti == NtCurrentTeb()->Win32ThreadInfo)
+        ret = Imm32ProcessRequest(hIMC, pWnd, (DWORD)wParam, (LPVOID)lParam, bAnsi);
+
+    ImmUnlockIMC(hIMC);
+    return ret;
+}
+
 /***********************************************************************
  *             ImmIsUIMessageA (IMM32.@)
  */
@@ -798,3 +1115,21 @@ Quit:
     return ret;
 #undef MSG_COUNT
 }
+
+/***********************************************************************
+ *              ImmRequestMessageA(IMM32.@)
+ */
+LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
+{
+    TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
+    return Imm32RequestMessageAW(hIMC, wParam, lParam, TRUE);
+}
+
+/***********************************************************************
+ *              ImmRequestMessageW(IMM32.@)
+ */
+LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
+{
+    TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
+    return Imm32RequestMessageAW(hIMC, wParam, lParam, FALSE);
+}
index 4ca77e1..17f4649 100644 (file)
@@ -81,6 +81,8 @@ LPVOID APIENTRY Imm32HeapAlloc(DWORD dwFlags, DWORD dwBytes);
 
 LPWSTR APIENTRY Imm32WideFromAnsi(LPCSTR pszA);
 LPSTR APIENTRY Imm32AnsiFromWide(LPCWSTR pszW);
+DWORD APIENTRY IchWideFromAnsi(DWORD cchAnsi, LPCSTR pchAnsi, UINT uCodePage);
+DWORD APIENTRY IchAnsiFromWide(DWORD cchWide, LPCWSTR pchWide, UINT uCodePage);
 PIMEDPI APIENTRY ImmLockOrLoadImeDpi(HKL hKL);
 LPINPUTCONTEXT APIENTRY Imm32LockIMCEx(HIMC hIMC, BOOL fSelect);
 BOOL APIENTRY Imm32ReleaseIME(HKL hKL);
index 7024ec8..20790f7 100644 (file)
@@ -50,6 +50,45 @@ LPSTR APIENTRY Imm32AnsiFromWide(LPCWSTR pszW)
     return pszA;
 }
 
+DWORD APIENTRY IchWideFromAnsi(DWORD cchAnsi, LPCSTR pchAnsi, UINT uCodePage)
+{
+    DWORD cchWide;
+    for (cchWide = 0; cchAnsi; ++cchWide)
+    {
+        if (IsDBCSLeadByteEx(uCodePage, *pchAnsi))
+        {
+            if (cchAnsi <= 1)
+            {
+                ++cchWide;
+                break;
+            }
+            else
+            {
+                cchAnsi -= 2;
+                pchAnsi += 2;
+            }
+        }
+        else
+        {
+            --cchAnsi;
+            ++pchAnsi;
+        }
+    }
+    return cchWide;
+}
+
+DWORD APIENTRY IchAnsiFromWide(DWORD cchWide, LPCWSTR pchWide, UINT uCodePage)
+{
+    DWORD cb, cchAnsi;
+    for (cchAnsi = 0; cchWide; ++cchAnsi, ++pchWide, --cchWide)
+    {
+        cb = WideCharToMultiByte(uCodePage, 0, pchWide, 1, NULL, 0, NULL, NULL);
+        if (cb > 1)
+            ++cchAnsi;
+    }
+    return cchAnsi;
+}
+
 BOOL Imm32GetSystemLibraryPath(LPWSTR pszPath, DWORD cchPath, LPCWSTR pszFileName)
 {
     if (!pszFileName[0] || !GetSystemDirectoryW(pszPath, cchPath))