diff --git a/dll/win32/imm32/CMakeLists.txt b/dll/win32/imm32/CMakeLists.txt index 269d42486a9..d580fab3d78 100644 --- a/dll/win32/imm32/CMakeLists.txt +++ b/dll/win32/imm32/CMakeLists.txt @@ -13,6 +13,7 @@ list(APPEND SOURCE ctf.c guideline.c ime.c + imemenu.c imm.c keymsg.c regword.c diff --git a/dll/win32/imm32/ime.c b/dll/win32/imm32/ime.c index 9db576cee7a..41821b637ab 100644 --- a/dll/win32/imm32/ime.c +++ b/dll/win32/imm32/ime.c @@ -415,333 +415,6 @@ Quit: return ret; } -// We will transport the IME menu items by using a flat memory block via -// a file mapping object beyond the boundary of a process. - -#define MAX_IMEMENU_BITMAP_BYTES 0xF00 - -typedef struct tagIMEMENUITEM -{ - IMEMENUITEMINFOW Info; - BYTE abChecked[MAX_IMEMENU_BITMAP_BYTES]; - BYTE abUnchecked[MAX_IMEMENU_BITMAP_BYTES]; - BYTE abItem[MAX_IMEMENU_BITMAP_BYTES]; -} IMEMENUITEM, *PIMEMENUITEM; - -typedef struct tagIMEMENU -{ - DWORD dwVersion; - DWORD dwFlags; - DWORD dwType; - DWORD dwItemCount; - IMEMENUITEMINFOW Parent; - IMEMENUITEM Items[ANYSIZE_ARRAY]; -} IMEMENU, *PIMEMENU; - -/*********************************************************************** - * ImmPutImeMenuItemsIntoMappedFile (IMM32.@) - * - * Called from user32.dll to transport the IME menu items by using a - * file mapping object. This function is provided for WM_IME_SYSTEM:IMS_GETIMEMENU - * handling. - */ -LRESULT WINAPI ImmPutImeMenuItemsIntoMappedFile(HIMC hIMC) -{ - LRESULT ret = FALSE; - HANDLE hMapping; - PIMEMENU pView; - LPIMEMENUITEMINFOW pParent = NULL, pItems = NULL; - DWORD i, cItems, cbItems = 0; - - hMapping = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"ImmMenuInfo"); - pView = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); - if (IS_NULL_UNEXPECTEDLY(pView)) - goto Quit; - - if (pView->dwVersion != 1) - { - ERR("\n"); - goto Quit; - } - - if (pView->Parent.cbSize > 0) - pParent = &pView->Parent; - - if (pView->dwItemCount > 0) - { - cbItems = pView->dwItemCount * sizeof(IMEMENUITEMINFOW); - pItems = ImmLocalAlloc(HEAP_ZERO_MEMORY, cbItems); - if (IS_NULL_UNEXPECTEDLY(pItems)) - goto Quit; - } - - cItems = ImmGetImeMenuItemsW(hIMC, pView->dwFlags, pView->dwType, pParent, pItems, cbItems); - pView->dwItemCount = cItems; - if (IS_ZERO_UNEXPECTEDLY(cItems)) - goto Quit; - - if (pItems) - { - for (i = 0; i < cItems; ++i) - { - pView->Items[i].Info = pItems[i]; - - // store bitmaps to bytes - if (pItems[i].hbmpChecked) - { - Imm32StoreBitmapToBytes(pItems[i].hbmpChecked, pView->Items[i].abChecked, - MAX_IMEMENU_BITMAP_BYTES); - DeleteObject(pItems[i].hbmpChecked); - } - if (pItems[i].hbmpUnchecked) - { - Imm32StoreBitmapToBytes(pItems[i].hbmpUnchecked, pView->Items[i].abUnchecked, - MAX_IMEMENU_BITMAP_BYTES); - DeleteObject(pItems[i].hbmpUnchecked); - } - if (pItems[i].hbmpItem) - { - Imm32StoreBitmapToBytes(pItems[i].hbmpItem, pView->Items[i].abItem, - MAX_IMEMENU_BITMAP_BYTES); - DeleteObject(pItems[i].hbmpItem); - } - } - } - - ret = TRUE; - -Quit: - if (pItems) - ImmLocalFree(pItems); - if (pView) - UnmapViewOfFile(pView); - if (hMapping) - CloseHandle(hMapping); - return ret; -} - -// Win: ImmGetImeMenuItemsInterProcess -DWORD APIENTRY -Imm32GetImeMenuItemWInterProcess(HIMC hIMC, DWORD dwFlags, DWORD dwType, LPVOID lpImeParentMenu, - LPVOID lpImeMenu, DWORD dwSize) -{ - HANDLE hMapping; - PIMEMENU pView; - DWORD i, cbView, dwItemCount, ret = 0; - HWND hImeWnd; - PIMEMENUITEM pGotItem; - LPIMEMENUITEMINFOW pSetInfo; - - hImeWnd = (HWND)NtUserQueryInputContext(hIMC, QIC_DEFAULTWINDOWIME); - if (!hImeWnd || !IsWindow(hImeWnd)) - { - ERR("\n"); - return 0; - } - - dwItemCount = (lpImeMenu ? (dwSize / sizeof(IMEMENUITEMINFOW)) : 0); - cbView = sizeof(IMEMENU) + ((size_t)dwItemCount - 1) * sizeof(IMEMENUITEM); - - RtlEnterCriticalSection(&gcsImeDpi); - - // create a file mapping - hMapping = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, - 0, cbView, L"ImmMenuInfo"); - pView = MapViewOfFile(hMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); - if (IS_NULL_UNEXPECTEDLY(pView)) - goto Quit; - - ZeroMemory(pView, cbView); - pView->dwVersion = 1; - pView->dwFlags = dwFlags; - pView->dwType = dwType; - pView->dwItemCount = dwItemCount; - if (lpImeParentMenu) - { - pView->Parent = *(LPIMEMENUITEMINFOW)lpImeParentMenu; - pView->Parent.cbSize = sizeof(IMEMENUITEMINFOW); - } - - if (!SendMessageW(hImeWnd, WM_IME_SYSTEM, IMS_GETIMEMENU, (LPARAM)hIMC)) - { - ERR("\n"); - goto Quit; - } - - ret = pView->dwItemCount; - - if (!lpImeMenu) - goto Quit; - - for (i = 0; i < ret; ++i) - { - pGotItem = &(pView->Items[i]); - pSetInfo = &((LPIMEMENUITEMINFOW)lpImeMenu)[i]; - - *pSetInfo = pGotItem->Info; - - // load bitmaps from bytes - if (pSetInfo->hbmpChecked) - { - pSetInfo->hbmpChecked = Imm32LoadBitmapFromBytes(pGotItem->abChecked); - } - if (pSetInfo->hbmpUnchecked) - { - pSetInfo->hbmpUnchecked = Imm32LoadBitmapFromBytes(pGotItem->abUnchecked); - } - if (pSetInfo->hbmpItem) - { - pSetInfo->hbmpItem = Imm32LoadBitmapFromBytes(pGotItem->abItem); - } - } - -Quit: - RtlLeaveCriticalSection(&gcsImeDpi); - if (pView) - UnmapViewOfFile(pView); - if (hMapping) - CloseHandle(hMapping); - return ret; -} - -// Win: ImmGetImeMenuItemsWorker -DWORD APIENTRY -ImmGetImeMenuItemsAW(HIMC hIMC, DWORD dwFlags, DWORD dwType, LPVOID lpImeParentMenu, - LPVOID lpImeMenu, DWORD dwSize, BOOL bTargetIsAnsi) -{ - DWORD ret = 0, cbTotal, dwProcessId, dwThreadId, iItem; - LPINPUTCONTEXT pIC; - PIMEDPI pImeDpi = NULL; - IMEMENUITEMINFOA ParentA; - IMEMENUITEMINFOW ParentW; - LPIMEMENUITEMINFOA pItemA; - LPIMEMENUITEMINFOW pItemW; - LPVOID pNewItems = NULL, pNewParent = NULL; - BOOL bImcIsAnsi; - HKL hKL; - - if (IS_NULL_UNEXPECTEDLY(hIMC)) - return 0; - - dwProcessId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTPROCESSID); - if (IS_ZERO_UNEXPECTEDLY(dwProcessId)) - return 0; - - if (dwProcessId != GetCurrentProcessId()) - { - if (bTargetIsAnsi) - return 0; - return Imm32GetImeMenuItemWInterProcess(hIMC, dwFlags, dwType, lpImeParentMenu, - lpImeMenu, dwSize); - } - - pIC = ImmLockIMC(hIMC); - if (IS_NULL_UNEXPECTEDLY(pIC)) - return 0; - - dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID); - if (IS_ZERO_UNEXPECTEDLY(dwThreadId)) - { - ImmUnlockIMC(hIMC); - return 0; - } - - hKL = GetKeyboardLayout(dwThreadId); - pImeDpi = ImmLockImeDpi(hKL); - if (IS_NULL_UNEXPECTEDLY(pImeDpi)) - { - ImmUnlockIMC(hIMC); - return 0; - } - - bImcIsAnsi = Imm32IsImcAnsi(hIMC); - - if (bImcIsAnsi != bTargetIsAnsi) - { - if (bTargetIsAnsi) - { - if (lpImeParentMenu) - pNewParent = &ParentW; - - if (lpImeMenu) - { - cbTotal = ((dwSize / sizeof(IMEMENUITEMINFOA)) * sizeof(IMEMENUITEMINFOW)); - pNewItems = ImmLocalAlloc(0, cbTotal); - if (IS_NULL_UNEXPECTEDLY(pNewItems)) - goto Quit; - } - } - else - { - if (lpImeParentMenu) - pNewParent = &ParentA; - - if (lpImeMenu) - { - cbTotal = ((dwSize / sizeof(IMEMENUITEMINFOW)) * sizeof(IMEMENUITEMINFOA)); - pNewItems = ImmLocalAlloc(0, cbTotal); - if (IS_NULL_UNEXPECTEDLY(pNewItems)) - goto Quit; - } - } - } - else - { - pNewItems = lpImeMenu; - pNewParent = lpImeParentMenu; - } - - ret = pImeDpi->ImeGetImeMenuItems(hIMC, dwFlags, dwType, pNewParent, pNewItems, dwSize); - if (IS_ZERO_UNEXPECTEDLY(ret) || !lpImeMenu) - goto Quit; - - if (bImcIsAnsi != bTargetIsAnsi) - { - if (bTargetIsAnsi) - { - if (pNewParent) - Imm32ImeMenuWideToAnsi(pNewParent, lpImeParentMenu, pImeDpi->uCodePage); - - pItemW = pNewItems; - pItemA = lpImeMenu; - for (iItem = 0; iItem < ret; ++iItem, ++pItemW, ++pItemA) - { - if (!Imm32ImeMenuWideToAnsi(pItemW, pItemA, pImeDpi->uCodePage)) - { - ERR("\n"); - ret = 0; - break; - } - } - } - else - { - if (pNewParent) - Imm32ImeMenuAnsiToWide(pNewParent, lpImeParentMenu, pImeDpi->uCodePage, TRUE); - - pItemA = pNewItems; - pItemW = lpImeMenu; - for (iItem = 0; iItem < dwSize; ++iItem, ++pItemA, ++pItemW) - { - if (!Imm32ImeMenuAnsiToWide(pItemA, pItemW, pImeDpi->uCodePage, TRUE)) - { - ERR("\n"); - ret = 0; - break; - } - } - } - } - -Quit: - if (pNewItems != lpImeMenu) - ImmLocalFree(pNewItems); - ImmUnlockImeDpi(pImeDpi); - ImmUnlockIMC(hIMC); - TRACE("ret: 0x%X\n", ret); - return ret; -} - /*********************************************************************** * ImmInstallIMEA (IMM32.@) */ @@ -2102,32 +1775,6 @@ Quit: return ret; } -/*********************************************************************** - * ImmGetImeMenuItemsA (IMM32.@) - */ -DWORD WINAPI -ImmGetImeMenuItemsA(HIMC hIMC, DWORD dwFlags, DWORD dwType, - LPIMEMENUITEMINFOA lpImeParentMenu, - LPIMEMENUITEMINFOA lpImeMenu, DWORD dwSize) -{ - TRACE("(%p, 0x%lX, 0x%lX, %p, %p, 0x%lX)\n", - hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize); - return ImmGetImeMenuItemsAW(hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize, TRUE); -} - -/*********************************************************************** - * ImmGetImeMenuItemsW (IMM32.@) - */ -DWORD WINAPI -ImmGetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType, - LPIMEMENUITEMINFOW lpImeParentMenu, - LPIMEMENUITEMINFOW lpImeMenu, DWORD dwSize) -{ - TRACE("(%p, 0x%lX, 0x%lX, %p, %p, 0x%lX)\n", - hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize); - return ImmGetImeMenuItemsAW(hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize, FALSE); -} - /*********************************************************************** * ImmWINNLSEnableIME (IMM32.@) */ diff --git a/dll/win32/imm32/imemenu.c b/dll/win32/imm32/imemenu.c new file mode 100644 index 00000000000..9570eced1ea --- /dev/null +++ b/dll/win32/imm32/imemenu.c @@ -0,0 +1,682 @@ +/* + * PROJECT: ReactOS IMM32 + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Implementing IME menus + * COPYRIGHT: Copyright 1998 Patrik Stridvall + * Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart + * Copyright 2017 James Tabor + * Copyright 2018 Amine Khaldi + * Copyright 2020-2025 Katayama Hirofumi MZ + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(imm); + +#define IMEMENUINFO_BUFFER_SIZE 0x20000 +#define IMEMENUINFO_MAGIC 0xBABEF00D /* ReactOS-specific */ + +/* ReactOS-specific */ +typedef struct tagIMEMENUINFO +{ + DWORD cbSize; + DWORD cbCapacity; /* IMEMENUINFO_BUFFER_SIZE */ + DWORD dwMagic; /* IMEMENUINFO_MAGIC */ + DWORD dwFlags; /* ImmGetImeMenuItems.dwFlags */ + DWORD dwType; /* ImmGetImeMenuItems.dwType */ + DWORD dwItemCount; + DWORD dwParentOffset; + DWORD dwItemsOffset; + DWORD dwBitmapsOffset; +} IMEMENUINFO, *PIMEMENUINFO; + +typedef struct tagBITMAPINFOMAX +{ + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[256]; +} BITMAPINFOMAX, *PBITMAPINFOMAX; + +/* ReactOS-specific */ +typedef struct tagIMEMENUBITMAPHEADER +{ + DWORD cbSize; + DWORD dwBitsOffset; + HBITMAP hbm; +} IMEMENUBITMAPHEADER, *PIMEMENUBITMAPHEADER; + +#define PTR_FROM_OFFSET(head, offset) (PVOID)((PBYTE)(head) + (SIZE_T)(offset)) + +/* Convert ANSI IME menu to Wide */ +static BOOL +Imm32ImeMenuAnsiToWide( + _In_ const IMEMENUITEMINFOA *pItemA, + _Out_ PIMEMENUITEMINFOW pItemW, + _In_ UINT uCodePage, + _In_ BOOL bBitmap) +{ + INT ret; + pItemW->cbSize = pItemA->cbSize; + pItemW->fType = pItemA->fType; + pItemW->fState = pItemA->fState; + pItemW->wID = pItemA->wID; + if (bBitmap) + { + pItemW->hbmpChecked = pItemA->hbmpChecked; + pItemW->hbmpUnchecked = pItemA->hbmpUnchecked; + pItemW->hbmpItem = pItemA->hbmpItem; + } + pItemW->dwItemData = pItemA->dwItemData; + ret = MultiByteToWideChar(uCodePage, 0, pItemA->szString, -1, + pItemW->szString, _countof(pItemW->szString)); + pItemW->szString[_countof(pItemW->szString) - 1] = UNICODE_NULL; + return !!ret; +} + +/* Convert Wide IME menu to ANSI */ +static BOOL +Imm32ImeMenuWideToAnsi( + _In_ const IMEMENUITEMINFOW *pItemW, + _Out_ PIMEMENUITEMINFOA pItemA, + _In_ UINT uCodePage) +{ + INT ret; + pItemA->cbSize = pItemW->cbSize; + pItemA->fType = pItemW->fType; + pItemA->fState = pItemW->fState; + pItemA->wID = pItemW->wID; + pItemA->hbmpChecked = pItemW->hbmpChecked; + pItemA->hbmpUnchecked = pItemW->hbmpUnchecked; + pItemA->dwItemData = pItemW->dwItemData; + pItemA->hbmpItem = pItemW->hbmpItem; + ret = WideCharToMultiByte(uCodePage, 0, pItemW->szString, -1, + pItemA->szString, _countof(pItemA->szString), NULL, NULL); + pItemA->szString[_countof(pItemA->szString) - 1] = ANSI_NULL; + return !!ret; +} + +static DWORD +Imm32FindImeMenuBitmap( + _In_ const IMEMENUINFO *pView, + _In_ HBITMAP hbm) +{ + const BYTE *pb = (const BYTE *)pView; + pb += pView->dwBitmapsOffset; + const IMEMENUBITMAPHEADER *pBitmap = (const IMEMENUBITMAPHEADER *)pb; + while (pBitmap->cbSize) + { + if (pBitmap->hbm == hbm) + return (PBYTE)pBitmap - (PBYTE)pView; /* Byte offset from pView */ + pBitmap = PTR_FROM_OFFSET(pBitmap, pBitmap->cbSize); + } + return 0; +} + +static VOID +Imm32DeleteImeMenuBitmaps(_Inout_ PIMEMENUINFO pView) +{ + PBYTE pb = (PBYTE)pView; + pb += pView->dwBitmapsOffset; + PIMEMENUBITMAPHEADER pBitmap = (PIMEMENUBITMAPHEADER)pb; + while (pBitmap->cbSize) + { + if (pBitmap->hbm) + { + DeleteObject(pBitmap->hbm); + pBitmap->hbm = NULL; + } + pBitmap = PTR_FROM_OFFSET(pBitmap, pBitmap->cbSize); + } +} + +static DWORD +Imm32SerializeImeMenuBitmap( + _In_ HDC hDC, + _Inout_ PIMEMENUINFO pView, + _In_ HBITMAP hbm) +{ + if (hbm == NULL) + return 0; + + DWORD dwOffset = Imm32FindImeMenuBitmap(pView, hbm); + if (dwOffset) + return dwOffset; /* Already serialized */ + + /* Get DIB info */ + BITMAPINFOMAX bmi; + ZeroMemory(&bmi, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + if (!GetDIBits(hDC, hbm, 0, 0, NULL, (PBITMAPINFO)&bmi, DIB_RGB_COLORS)) + { + ERR("!GetDIBits\n"); + return 0; + } + + /* Calculate the possible color table size */ + DWORD colorTableSize = 0; + if (bmi.bmiHeader.biBitCount <= 8) + colorTableSize = (1 << bmi.bmiHeader.biBitCount) * sizeof(RGBQUAD); + else if (bmi.bmiHeader.biBitCount == 16 || bmi.bmiHeader.biBitCount == 32) + colorTableSize = 3 * sizeof(DWORD); + + /* Calculate the data sizes and validate them */ + DWORD cbBitmapHeader = sizeof(IMEMENUBITMAPHEADER); + DWORD dibHeaderSize = sizeof(BITMAPINFOHEADER) + colorTableSize; + DWORD cbData = cbBitmapHeader + dibHeaderSize + bmi.bmiHeader.biSizeImage; + if (pView->cbSize + cbData + sizeof(DWORD) > pView->cbCapacity) + { + ERR("Too large IME menu (0x%X, 0x%X)\n", pView->cbSize, cbData); + return 0; + } + + /* Create a bitmap for getting bits */ + HBITMAP hbmTmp = CreateCompatibleBitmap(hDC, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight); + if (!hbmTmp) + { + ERR("Out of memory\n"); + return 0; + } + + /* Store the IMEMENUBITMAPHEADER */ + PBYTE pb = (PBYTE)pView + pView->cbSize; + PIMEMENUBITMAPHEADER pBitmap = (PIMEMENUBITMAPHEADER)pb; + pBitmap->cbSize = cbData; + pBitmap->dwBitsOffset = cbBitmapHeader + dibHeaderSize; + pBitmap->hbm = hbm; + pb += cbBitmapHeader; + + /* Store the BITMAPINFO */ + PBITMAPINFO pbmi = (PBITMAPINFO)pb; + CopyMemory(pbmi, &bmi, dibHeaderSize); + pb += dibHeaderSize; + + /* Get the bits */ + HGDIOBJ hbmOld = SelectObject(hDC, hbmTmp); + BOOL ret = GetDIBits(hDC, hbm, 0, bmi.bmiHeader.biHeight, pb, pbmi, DIB_RGB_COLORS); + SelectObject(hDC, hbmOld); + + DeleteObject(hbmTmp); + + if (!ret) + { + ERR("!GetDIBits\n"); + pBitmap->cbSize = 0; + return 0; + } + + pView->cbSize += cbData; + return (PBYTE)pBitmap - (PBYTE)pView; /* Byte offset from pView */ +} + +static HBITMAP +Imm32DeserializeImeMenuBitmap(_Inout_ const IMEMENUBITMAPHEADER *pBitmap) +{ + const BYTE *pb = (const BYTE *)pBitmap; + const BITMAPINFO *pbmi = (const BITMAPINFO *)(pb + sizeof(*pBitmap)); + + HDC hDC = GetDC(NULL); + HBITMAP hbm = CreateDIBitmap(hDC, + &pbmi->bmiHeader, + CBM_INIT, + pb + pBitmap->dwBitsOffset, + pbmi, + DIB_RGB_COLORS); + if (!hbm) + ERR("!hbm\n"); + ReleaseDC(NULL, hDC); + return hbm; +} + +/* + * We transport the IME menu items by using a flat memory block via + * a file mapping object beyond boundary of process. + */ +static DWORD +Imm32SerializeImeMenu( + _Inout_ PIMEMENUINFO pView, + _In_ HIMC hIMC, + _Inout_opt_ PIMEMENUITEMINFOW lpImeParentMenu, + _In_ BOOL bCountOnly) +{ + /* Sanity check */ + if (pView->dwMagic != IMEMENUINFO_MAGIC || pView->cbSize > pView->cbCapacity) + { + ERR("Invalid pView\n"); + return 0; + } + + /* Get the count of menu items */ + DWORD dwFlags = pView->dwFlags; + DWORD dwType = pView->dwType; + DWORD dwItemCount = ImmGetImeMenuItemsW(hIMC, dwFlags, dwType, lpImeParentMenu, NULL, 0); + pView->dwItemCount = dwItemCount; + if (bCountOnly) + return dwItemCount; + + if (!dwItemCount) + return 0; + + /* Start of serialization */ + PBYTE pb = (PBYTE)pView; + pb += sizeof(*pView); + + /* Store the parent menu data */ + if (lpImeParentMenu) + { + pView->dwParentOffset = pb - (PBYTE)pView; + pView->cbSize += sizeof(*lpImeParentMenu); + CopyMemory(pb, lpImeParentMenu, sizeof(*lpImeParentMenu)); + pb += sizeof(*lpImeParentMenu); + } + + /* The byte size of items */ + SIZE_T cbItems = dwItemCount * sizeof(IMEMENUITEMINFOW); + + /* Update the offset info */ + pView->dwItemsOffset = pb - (PBYTE)pView; + pView->dwBitmapsOffset = pView->dwItemsOffset + cbItems; + if (pView->dwItemsOffset + sizeof(DWORD) > pView->cbCapacity || + pView->dwBitmapsOffset + sizeof(DWORD) > pView->cbCapacity) + { + ERR("Too large IME menu (0x%X, 0x%X)\n", pView->dwItemsOffset, pView->dwBitmapsOffset); + return 0; + } + + /* Actually get the items */ + PIMEMENUITEMINFOW pItems = (PIMEMENUITEMINFOW)pb; + dwItemCount = ImmGetImeMenuItemsW(hIMC, dwFlags, dwType, lpImeParentMenu, pItems, cbItems); + pView->dwItemCount = dwItemCount; + pView->cbSize += cbItems; + + /* Serialize the bitmaps */ + DWORD dwOffset; + HDC hDC = CreateCompatibleDC(NULL); + for (DWORD iItem = 0; iItem < dwItemCount; ++iItem) + { + PIMEMENUITEMINFOW pItem = &pItems[iItem]; + + dwOffset = Imm32SerializeImeMenuBitmap(hDC, pView, pItem->hbmpChecked); + if (dwOffset) + pItem->hbmpChecked = UlongToHandle(dwOffset); + + dwOffset = Imm32SerializeImeMenuBitmap(hDC, pView, pItem->hbmpUnchecked); + if (dwOffset) + pItem->hbmpUnchecked = UlongToHandle(dwOffset); + + dwOffset = Imm32SerializeImeMenuBitmap(hDC, pView, pItem->hbmpItem); + if (dwOffset) + pItem->hbmpItem = UlongToHandle(dwOffset); + } + DeleteDC(hDC); + + TRACE("pView->cbSize: 0x%X\n", pView->cbSize); + + /* Clean up */ + Imm32DeleteImeMenuBitmaps(pView); + + return dwItemCount; +} + +static DWORD +Imm32DeserializeImeMenu( + _Inout_ PIMEMENUINFO pView, + _Out_writes_bytes_opt_(dwSize) PIMEMENUITEMINFOW lpImeMenuItems, + _In_ DWORD dwSize) +{ + /* Sanity check */ + if (pView->dwMagic != IMEMENUINFO_MAGIC || pView->cbSize > pView->cbCapacity) + { + ERR("Invalid pView\n"); + return 0; + } + + DWORD dwItemCount = pView->dwItemCount; + if (lpImeMenuItems == NULL) + return dwItemCount; /* Count only */ + + /* Limit the item count for dwSize */ + if (dwItemCount > dwSize / sizeof(IMEMENUITEMINFOW)) + dwItemCount = dwSize / sizeof(IMEMENUITEMINFOW); + + /* Get the items pointer */ + PIMEMENUITEMINFOW pItems = PTR_FROM_OFFSET(pView, pView->dwItemsOffset); + + /* Copy the items and de-serialize the bitmaps */ + PIMEMENUBITMAPHEADER pBitmap; + for (DWORD iItem = 0; iItem < dwItemCount; ++iItem) + { + PIMEMENUITEMINFOW pItem = &pItems[iItem]; + if (pItem->hbmpChecked) + { + pBitmap = PTR_FROM_OFFSET(pView, pItem->hbmpChecked); + pItem->hbmpChecked = Imm32DeserializeImeMenuBitmap(pBitmap); + } + if (pItem->hbmpUnchecked) + { + pBitmap = PTR_FROM_OFFSET(pView, pItem->hbmpUnchecked); + pItem->hbmpUnchecked = Imm32DeserializeImeMenuBitmap(pBitmap); + } + if (pItem->hbmpItem) + { + pBitmap = PTR_FROM_OFFSET(pView, pItem->hbmpItem); + pItem->hbmpItem = Imm32DeserializeImeMenuBitmap(pBitmap); + } + lpImeMenuItems[iItem] = *pItem; + } + + return dwItemCount; +} + +/*********************************************************************** + * ImmPutImeMenuItemsIntoMappedFile (IMM32.@) + * + * Called from user32.dll to transport the IME menu items by using a + * file mapping object. This function is provided for WM_IME_SYSTEM:IMS_GETIMEMENU + * handling. + */ +LRESULT WINAPI +ImmPutImeMenuItemsIntoMappedFile(_In_ HIMC hIMC) +{ + /* Open the existing file mapping */ + HANDLE hMapping = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"ImmMenuInfo"); + if (!hMapping) + { + ERR("!hMapping\n"); + return 0; + } + + /* Map the view */ + PIMEMENUINFO pView = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (!pView) + { + ERR("!pView\n"); + CloseHandle(hMapping); + return 0; + } + + /* Get parent menu info */ + PVOID lpImeParentMenu = NULL; + if (pView->dwParentOffset) + lpImeParentMenu = PTR_FROM_OFFSET(pView, pView->dwParentOffset); + + /* Serialize the IME menu */ + DWORD dwItemCount = Imm32SerializeImeMenu(pView, hIMC, lpImeParentMenu, !pView->dwItemCount); + + /* Clean up */ + UnmapViewOfFile(pView); + CloseHandle(hMapping); + + return dwItemCount; +} + +static DWORD +Imm32GetImeMenuItemWInterProcess( + _In_ HIMC hIMC, + _In_ DWORD dwFlags, + _In_ DWORD dwType, + _Inout_opt_ PVOID lpImeParentMenu, + _Out_writes_bytes_opt_(dwSize) PVOID lpImeMenuItems, + _In_ DWORD dwSize) +{ + /* Get IME window */ + HWND hwndIme = (HWND)NtUserQueryInputContext(hIMC, QIC_DEFAULTWINDOWIME); + if (!hwndIme || !IsWindow(hwndIme)) + { + ERR("!hwndIme\n"); + return 0; + } + + /* Lock */ + RtlEnterCriticalSection(&gcsImeDpi); + + /* Create a file mapping */ + HANDLE hMapping = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + 0, IMEMENUINFO_BUFFER_SIZE, L"ImmMenuInfo"); + if (!hMapping) + { + ERR("!pView\n"); + RtlLeaveCriticalSection(&gcsImeDpi); + return 0; + } + + /* Map the view */ + PIMEMENUINFO pView = MapViewOfFile(hMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); + if (!pView) + { + ERR("!pView\n"); + CloseHandle(hMapping); + RtlLeaveCriticalSection(&gcsImeDpi); + return 0; + } + + /* Initialize view header */ + ZeroMemory(pView, IMEMENUINFO_BUFFER_SIZE); + pView->cbSize = sizeof(*pView); + pView->cbCapacity = IMEMENUINFO_BUFFER_SIZE; + pView->dwMagic = IMEMENUINFO_MAGIC; + pView->dwFlags = dwFlags; + pView->dwType = dwType; + pView->dwItemCount = lpImeMenuItems ? (dwSize / sizeof(IMEMENUITEMINFOW)) : 0; + + /* Send WM_IME_SYSTEM.IMS_GETIMEMENU message. It will call ImmPutImeMenuItemsIntoMappedFile */ + DWORD ret = 0; + if (SendMessageW(hwndIme, WM_IME_SYSTEM, IMS_GETIMEMENU, (LPARAM)hIMC)) + { + /* De-serialize the IME menu */ + ret = Imm32DeserializeImeMenu(pView, lpImeMenuItems, dwSize); + } + + /* Clean up */ + UnmapViewOfFile(pView); /* Unmap */ + CloseHandle(hMapping); /* Close the file mapping */ + RtlLeaveCriticalSection(&gcsImeDpi); /* Unlock */ + return ret; +} + +/* Absorbs the differences between ANSI and Wide */ +static DWORD +ImmGetImeMenuItemsAW( + _In_ HIMC hIMC, + _In_ DWORD dwFlags, + _In_ DWORD dwType, + _Inout_opt_ PVOID lpImeParentMenu, + _Out_writes_bytes_opt_(dwSize) PVOID lpImeMenuItems, + _In_ DWORD dwSize, + _In_ BOOL bTargetIsAnsi) +{ + DWORD ret = 0, iItem; + + if (!hIMC) + { + ERR("!hIMC\n"); + return 0; + } + + /* Get input process ID */ + DWORD dwProcessId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTPROCESSID); + if (!dwProcessId) + { + ERR("!dwProcessId\n"); + return 0; + } + + if (dwProcessId != GetCurrentProcessId()) /* Cross process? */ + { + if (bTargetIsAnsi) + { + ERR("ImmGetImeMenuItemsA cannot cross process boundary\n"); + return 0; + } + + /* Transport the IME menu items, using file mapping */ + return Imm32GetImeMenuItemWInterProcess(hIMC, dwFlags, dwType, lpImeParentMenu, + lpImeMenuItems, dwSize); + } + + PINPUTCONTEXT pIC = ImmLockIMC(hIMC); + if (!pIC) + { + ERR("!pIC\n"); + return 0; + } + + /* Get input thread ID */ + DWORD dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID); + if (!dwThreadId) + { + ERR("!dwThreadId\n"); + ImmUnlockIMC(hIMC); + return 0; + } + + /* Get IME interface */ + HKL hKL = GetKeyboardLayout(dwThreadId); + PIMEDPI pImeDpi = ImmLockImeDpi(hKL); + if (!pImeDpi) + { + ERR("!pImeDpi\n"); + ImmUnlockIMC(hIMC); + return 0; + } + + /* Is the IME ANSI? */ + BOOL bImcIsAnsi = Imm32IsImcAnsi(hIMC); + + IMEMENUITEMINFOA ParentA, *pItemA; + IMEMENUITEMINFOW ParentW, *pItemW; + PVOID pNewItems = NULL, pNewParent = NULL; + + /* Are text types (ANSI/Wide) different between IME and target? */ + if (bImcIsAnsi != bTargetIsAnsi) + { + DWORD cbTotal; + if (bTargetIsAnsi) + { + /* Convert the parent */ + if (lpImeParentMenu) + { + Imm32ImeMenuAnsiToWide(lpImeParentMenu, &ParentW, pImeDpi->uCodePage, TRUE); + pNewParent = &ParentW; + } + + /* Allocate buffer for new items */ + if (lpImeMenuItems) + { + cbTotal = ((dwSize / sizeof(IMEMENUITEMINFOA)) * sizeof(IMEMENUITEMINFOW)); + pNewItems = ImmLocalAlloc(LPTR, cbTotal); + if (!pNewItems) + { + ERR("!pNewItems\n"); + goto Quit; + } + } + } + else + { + /* Convert the parent */ + if (lpImeParentMenu) + { + Imm32ImeMenuWideToAnsi(lpImeParentMenu, &ParentA, pImeDpi->uCodePage); + pNewParent = &ParentA; + } + + /* Allocate buffer for new items */ + if (lpImeMenuItems) + { + cbTotal = ((dwSize / sizeof(IMEMENUITEMINFOW)) * sizeof(IMEMENUITEMINFOA)); + pNewItems = ImmLocalAlloc(LPTR, cbTotal); + if (!pNewItems) + { + ERR("!pNewItems\n"); + goto Quit; + } + } + } + } + else + { + /* Get the items directly */ + pNewItems = lpImeMenuItems; + pNewParent = lpImeParentMenu; + } + + /* Get IME menu items from the IME */ + ret = pImeDpi->ImeGetImeMenuItems(hIMC, dwFlags, dwType, pNewParent, pNewItems, dwSize); + if (!ret || !lpImeMenuItems) + { + ERR("%d, %p\n", ret, lpImeMenuItems); + goto Quit; + } + + if (bImcIsAnsi != bTargetIsAnsi) /* Are text types different? */ + { + if (bTargetIsAnsi) + { + /* Convert the parent */ + if (pNewParent) + Imm32ImeMenuWideToAnsi(pNewParent, lpImeParentMenu, pImeDpi->uCodePage); + + /* Convert the items */ + pItemW = pNewItems; + pItemA = lpImeMenuItems; + for (iItem = 0; iItem < ret; ++iItem, ++pItemW, ++pItemA) + { + Imm32ImeMenuWideToAnsi(pItemW, pItemA, pImeDpi->uCodePage); + } + } + else + { + /* Convert the parent */ + if (pNewParent) + Imm32ImeMenuAnsiToWide(pNewParent, lpImeParentMenu, pImeDpi->uCodePage, TRUE); + + /* Convert the items */ + pItemA = pNewItems; + pItemW = lpImeMenuItems; + for (iItem = 0; iItem < dwSize; ++iItem, ++pItemA, ++pItemW) + { + Imm32ImeMenuAnsiToWide(pItemA, pItemW, pImeDpi->uCodePage, TRUE); + } + } + } + +Quit: + if (pNewItems != lpImeMenuItems) + ImmLocalFree(pNewItems); + ImmUnlockImeDpi(pImeDpi); + ImmUnlockIMC(hIMC); + return ret; +} + +/*********************************************************************** + * ImmGetImeMenuItemsA (IMM32.@) + */ +DWORD WINAPI +ImmGetImeMenuItemsA( + _In_ HIMC hIMC, + _In_ DWORD dwFlags, + _In_ DWORD dwType, + _Inout_opt_ PIMEMENUITEMINFOA lpImeParentMenu, + _Out_writes_bytes_opt_(dwSize) PIMEMENUITEMINFOA lpImeMenu, + _In_ DWORD dwSize) +{ + TRACE("(%p, 0x%lX, 0x%lX, %p, %p, 0x%lX)\n", + hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize); + return ImmGetImeMenuItemsAW(hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize, TRUE); +} + +/*********************************************************************** + * ImmGetImeMenuItemsW (IMM32.@) + */ +DWORD WINAPI +ImmGetImeMenuItemsW( + _In_ HIMC hIMC, + _In_ DWORD dwFlags, + _In_ DWORD dwType, + _Inout_opt_ PIMEMENUITEMINFOW lpImeParentMenu, + _Out_writes_bytes_opt_(dwSize) PIMEMENUITEMINFOW lpImeMenu, + _In_ DWORD dwSize) +{ + TRACE("(%p, 0x%lX, 0x%lX, %p, %p, 0x%lX)\n", + hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize); + return ImmGetImeMenuItemsAW(hIMC, dwFlags, dwType, lpImeParentMenu, lpImeMenu, dwSize, FALSE); +} diff --git a/dll/win32/imm32/precomp.h b/dll/win32/imm32/precomp.h index ee9fa237b6b..f7fbec0ab5a 100644 --- a/dll/win32/imm32/precomp.h +++ b/dll/win32/imm32/precomp.h @@ -140,13 +140,6 @@ Imm32MakeIMENotify(HIMC hIMC, HWND hwnd, DWORD dwAction, DWORD_PTR dwIndex, DWOR DWORD APIENTRY Imm32BuildHimcList(DWORD dwThreadId, HIMC **pphList); -INT APIENTRY -Imm32ImeMenuAnsiToWide(const IMEMENUITEMINFOA *pItemA, LPIMEMENUITEMINFOW pItemW, - UINT uCodePage, BOOL bBitmap); -INT APIENTRY -Imm32ImeMenuWideToAnsi(const IMEMENUITEMINFOW *pItemW, LPIMEMENUITEMINFOA pItemA, - UINT uCodePage); - PIME_STATE APIENTRY Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL); PIME_SUBSTATE APIENTRY Imm32FetchImeSubState(PIME_STATE pState, HKL hKL); diff --git a/dll/win32/imm32/utils.c b/dll/win32/imm32/utils.c index 9fc0375b64d..4ddb5b1b22f 100644 --- a/dll/win32/imm32/utils.c +++ b/dll/win32/imm32/utils.c @@ -496,57 +496,6 @@ DWORD APIENTRY Imm32BuildHimcList(DWORD dwThreadId, HIMC **pphList) #undef MAX_RETRY } -// Win: ConvertImeMenuItemInfoAtoW -INT APIENTRY -Imm32ImeMenuAnsiToWide(const IMEMENUITEMINFOA *pItemA, LPIMEMENUITEMINFOW pItemW, - UINT uCodePage, BOOL bBitmap) -{ - INT ret; - pItemW->cbSize = pItemA->cbSize; - pItemW->fType = pItemA->fType; - pItemW->fState = pItemA->fState; - pItemW->wID = pItemA->wID; - if (bBitmap) - { - pItemW->hbmpChecked = pItemA->hbmpChecked; - pItemW->hbmpUnchecked = pItemA->hbmpUnchecked; - pItemW->hbmpItem = pItemA->hbmpItem; - } - pItemW->dwItemData = pItemA->dwItemData; - ret = MultiByteToWideChar(uCodePage, 0, pItemA->szString, -1, - pItemW->szString, _countof(pItemW->szString)); - if (ret >= _countof(pItemW->szString)) - { - ret = 0; - pItemW->szString[0] = 0; - } - return ret; -} - -// Win: ConvertImeMenuItemInfoWtoA -INT APIENTRY -Imm32ImeMenuWideToAnsi(const IMEMENUITEMINFOW *pItemW, LPIMEMENUITEMINFOA pItemA, - UINT uCodePage) -{ - INT ret; - pItemA->cbSize = pItemW->cbSize; - pItemA->fType = pItemW->fType; - pItemA->fState = pItemW->fState; - pItemA->wID = pItemW->wID; - pItemA->hbmpChecked = pItemW->hbmpChecked; - pItemA->hbmpUnchecked = pItemW->hbmpUnchecked; - pItemA->dwItemData = pItemW->dwItemData; - pItemA->hbmpItem = pItemW->hbmpItem; - ret = WideCharToMultiByte(uCodePage, 0, pItemW->szString, -1, - pItemA->szString, _countof(pItemA->szString), NULL, NULL); - if (ret >= _countof(pItemA->szString)) - { - ret = 0; - pItemA->szString[0] = 0; - } - return ret; -} - // Win: GetImeModeSaver PIME_STATE APIENTRY Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL) diff --git a/sdk/include/psdk/imm.h b/sdk/include/psdk/imm.h index 9c2e8df039a..26589e8ab05 100644 --- a/sdk/include/psdk/imm.h +++ b/sdk/include/psdk/imm.h @@ -658,7 +658,7 @@ ImmGetImeMenuItemsA( _In_ HIMC hIMC, _In_ DWORD dwFlags, _In_ DWORD dwType, - _Out_opt_ LPIMEMENUITEMINFOA lpImeParentMenu, + _Inout_opt_ LPIMEMENUITEMINFOA lpImeParentMenu, _Out_writes_bytes_opt_(dwSize) LPIMEMENUITEMINFOA lpImeMenu, _In_ DWORD dwSize); @@ -668,7 +668,7 @@ ImmGetImeMenuItemsW( _In_ HIMC hIMC, _In_ DWORD dwFlags, _In_ DWORD dwType, - _Out_opt_ LPIMEMENUITEMINFOW lpImeParentMenu, + _Inout_opt_ LPIMEMENUITEMINFOW lpImeParentMenu, _Out_writes_bytes_opt_(dwSize) LPIMEMENUITEMINFOW lpImeMenu, _In_ DWORD dwSize); diff --git a/sdk/include/reactos/imm32_undoc.h b/sdk/include/reactos/imm32_undoc.h index eea17b6841c..a1cfe4ce02c 100644 --- a/sdk/include/reactos/imm32_undoc.h +++ b/sdk/include/reactos/imm32_undoc.h @@ -228,6 +228,7 @@ DWORD WINAPI ImmGetAppCompatFlags(_In_ HIMC hIMC); BOOL WINAPI ImmSetActiveContext(_In_ HWND hwnd, _In_ HIMC hIMC, _In_ BOOL fFlag); BOOL WINAPI ImmLoadIME(_In_ HKL hKL); DWORD WINAPI ImmProcessKey(_In_ HWND, _In_ HKL, _In_ UINT, _In_ LPARAM, _In_ DWORD); +LRESULT WINAPI ImmPutImeMenuItemsIntoMappedFile(_In_ HIMC hIMC); HRESULT WINAPI CtfAImmActivate(_Out_opt_ HINSTANCE *phinstCtfIme); HRESULT WINAPI CtfAImmDeactivate(_In_ BOOL bDestroy);