[IMM32][SDK] Support IME menus Part 1 (#7969)

Supporting IME menus will improve
Asian users' usability. IME menu is
used on the IME System Pen icon.
JIRA issue: CORE-20142
- Add dll/win32/imm32/imemenu.c.
- Move some code to imemenu.c.
- Implement IME menu transporting
  correctly by using file mapping and
  serialization.
This commit is contained in:
Katayama Hirofumi MZ 2025-05-14 11:02:57 +09:00 committed by GitHub
parent 189623614d
commit 4f84ffa118
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 686 additions and 413 deletions

View file

@ -13,6 +13,7 @@ list(APPEND SOURCE
ctf.c ctf.c
guideline.c guideline.c
ime.c ime.c
imemenu.c
imm.c imm.c
keymsg.c keymsg.c
regword.c regword.c

View file

@ -415,333 +415,6 @@ Quit:
return ret; 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.@) * ImmInstallIMEA (IMM32.@)
*/ */
@ -2102,32 +1775,6 @@ Quit:
return ret; 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.@) * ImmWINNLSEnableIME (IMM32.@)
*/ */

682
dll/win32/imm32/imemenu.c Normal file
View file

@ -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 <james.tabor@reactos.org>
* Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
* Copyright 2020-2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
*/
#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);
}

View file

@ -140,13 +140,6 @@ Imm32MakeIMENotify(HIMC hIMC, HWND hwnd, DWORD dwAction, DWORD_PTR dwIndex, DWOR
DWORD APIENTRY Imm32BuildHimcList(DWORD dwThreadId, HIMC **pphList); 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_STATE APIENTRY Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL);
PIME_SUBSTATE APIENTRY Imm32FetchImeSubState(PIME_STATE pState, HKL hKL); PIME_SUBSTATE APIENTRY Imm32FetchImeSubState(PIME_STATE pState, HKL hKL);

View file

@ -496,57 +496,6 @@ DWORD APIENTRY Imm32BuildHimcList(DWORD dwThreadId, HIMC **pphList)
#undef MAX_RETRY #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 // Win: GetImeModeSaver
PIME_STATE APIENTRY PIME_STATE APIENTRY
Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL) Imm32FetchImeState(LPINPUTCONTEXTDX pIC, HKL hKL)

View file

@ -658,7 +658,7 @@ ImmGetImeMenuItemsA(
_In_ HIMC hIMC, _In_ HIMC hIMC,
_In_ DWORD dwFlags, _In_ DWORD dwFlags,
_In_ DWORD dwType, _In_ DWORD dwType,
_Out_opt_ LPIMEMENUITEMINFOA lpImeParentMenu, _Inout_opt_ LPIMEMENUITEMINFOA lpImeParentMenu,
_Out_writes_bytes_opt_(dwSize) LPIMEMENUITEMINFOA lpImeMenu, _Out_writes_bytes_opt_(dwSize) LPIMEMENUITEMINFOA lpImeMenu,
_In_ DWORD dwSize); _In_ DWORD dwSize);
@ -668,7 +668,7 @@ ImmGetImeMenuItemsW(
_In_ HIMC hIMC, _In_ HIMC hIMC,
_In_ DWORD dwFlags, _In_ DWORD dwFlags,
_In_ DWORD dwType, _In_ DWORD dwType,
_Out_opt_ LPIMEMENUITEMINFOW lpImeParentMenu, _Inout_opt_ LPIMEMENUITEMINFOW lpImeParentMenu,
_Out_writes_bytes_opt_(dwSize) LPIMEMENUITEMINFOW lpImeMenu, _Out_writes_bytes_opt_(dwSize) LPIMEMENUITEMINFOW lpImeMenu,
_In_ DWORD dwSize); _In_ DWORD dwSize);

View file

@ -228,6 +228,7 @@ DWORD WINAPI ImmGetAppCompatFlags(_In_ HIMC hIMC);
BOOL WINAPI ImmSetActiveContext(_In_ HWND hwnd, _In_ HIMC hIMC, _In_ BOOL fFlag); BOOL WINAPI ImmSetActiveContext(_In_ HWND hwnd, _In_ HIMC hIMC, _In_ BOOL fFlag);
BOOL WINAPI ImmLoadIME(_In_ HKL hKL); BOOL WINAPI ImmLoadIME(_In_ HKL hKL);
DWORD WINAPI ImmProcessKey(_In_ HWND, _In_ HKL, _In_ UINT, _In_ LPARAM, _In_ DWORD); 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 CtfAImmActivate(_Out_opt_ HINSTANCE *phinstCtfIme);
HRESULT WINAPI CtfAImmDeactivate(_In_ BOOL bDestroy); HRESULT WINAPI CtfAImmDeactivate(_In_ BOOL bDestroy);