You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1000 lines
35 KiB
1000 lines
35 KiB
//////////////////////////////////////////////////////////////////////////
|
|
// imemenu.c - IME Menu APIs
|
|
//
|
|
// handles IME specific menu retrieval
|
|
//
|
|
// Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
//
|
|
// History:
|
|
// 23-Mar-1997 hiroyama Created
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "precomp.h"
|
|
|
|
#ifdef HIRO_DEBUG
|
|
#define D(x) x
|
|
#else
|
|
#define D(x)
|
|
#endif
|
|
|
|
#define IME_MENU_FILE_NAME L"ImmMenuInfo"
|
|
#define IME_MENU_MAXMEM (128 * 1024) // maximum size of mapped file
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// private structurs for inter process communication
|
|
//
|
|
// NOTE for NT50: these are dedicated to internat.exe
|
|
//
|
|
// uses memory mapped file as shared buffer
|
|
// all strings expect UNICODE
|
|
// HBITMAP is de-compiled and then compiled again using
|
|
// internat.exe's context
|
|
//
|
|
////////////////////////////////////////////////////////////
|
|
|
|
typedef struct _IMEMENU_BMP_HEADER {
|
|
struct _IMEMENU_BMP_HEADER* lpNext;
|
|
HBITMAP hBitmap;
|
|
LPBYTE pBits;
|
|
BITMAPINFO bmi;
|
|
} IMEMENU_BMP_HEADER;
|
|
|
|
typedef struct {
|
|
UINT cbSize;
|
|
UINT fType;
|
|
UINT fState;
|
|
UINT wID;
|
|
IMEMENU_BMP_HEADER* lpBmpChecked;
|
|
IMEMENU_BMP_HEADER* lpBmpUnchecked;
|
|
DWORD dwItemData;
|
|
WCHAR szString[IMEMENUITEM_STRING_SIZE]; // menu string: always UNICODE.
|
|
IMEMENU_BMP_HEADER* lpBmpItem; // NULL means no bitmap in this menu
|
|
} IMEMENU_ITEM;
|
|
|
|
typedef struct {
|
|
DWORD dwVersion; // holds version of this memory chunk
|
|
DWORD dwMemSize; // size of memory buffer allocated
|
|
DWORD dwFlags; // flags returned from IME
|
|
DWORD dwType;
|
|
IMEMENU_ITEM* lpImeParentMenu; // parent menu's offset (if any) passed from requester
|
|
IMEMENU_ITEM* lpImeMenu; // offset to first menu item (will be set by IME side)
|
|
DWORD dwSize; // number of menus to fill (not byte count)
|
|
IMEMENU_BMP_HEADER* lpBmp; // offset to first bitmap header
|
|
IMEMENU_BMP_HEADER* lpBmpNext; // points next available location for bmp buffer
|
|
} IMEMENU_HEADER;
|
|
|
|
|
|
// address conversion
|
|
#define CONVTO_OFFSET(x) ((x) = (LPVOID)((x) ? ((LPBYTE)(x) - offset) : NULL))
|
|
#define CONVTO_PTR(x) ((x) = (LPVOID)((x) ? ((LPBYTE)(x) + offset) : NULL))
|
|
|
|
#if DBG
|
|
#define CHK_OFFSET(x) if ((ULONG_PTR)(x) >= pHeader->dwMemSize) { \
|
|
RIPMSG2(RIP_WARNING, "CHK_OFFSET(%s=%lx) is out of range.", #x, (ULONG_PTR)(x)); \
|
|
}
|
|
#define CHK_PTR(x) if ((LPVOID)(x) < (LPVOID)pHeader || (LPBYTE)(x) > (LPBYTE)pHeader + pHeader->dwMemSize) { \
|
|
if ((x) != NULL) { \
|
|
RIPMSG2(RIP_WARNING, "CHK_PTR(%s=%lx) is out of range.", #x, (ULONG_PTR)(x)); \
|
|
DebugBreak(); \
|
|
} \
|
|
}
|
|
#else
|
|
#define CHK_OFFSET(x)
|
|
#define CHK_PTR(x) if ((x) != NULL) { \
|
|
if ((LPVOID)(x) < (LPVOID)pHeader || (LPBYTE)(x) > (LPBYTE)pHeader + pHeader->dwMemSize) { \
|
|
goto cleanup; \
|
|
} \
|
|
}
|
|
#endif
|
|
|
|
int ConvertImeMenuItemInfoAtoW(LPIMEMENUITEMINFOA lpA, LPIMEMENUITEMINFOW lpW, int nCP, BOOL copyBmp)
|
|
{
|
|
int i;
|
|
|
|
lpW->cbSize = lpA->cbSize;
|
|
lpW->fType = lpA->fType;
|
|
lpW->fState = lpA->fState;
|
|
lpW->wID = lpA->wID;
|
|
if (copyBmp) {
|
|
lpW->hbmpChecked = lpA->hbmpChecked;
|
|
lpW->hbmpUnchecked = lpA->hbmpUnchecked;
|
|
lpW->hbmpItem = lpA->hbmpItem;
|
|
}
|
|
lpW->dwItemData = lpA->dwItemData;
|
|
|
|
i = MultiByteToWideChar(nCP,
|
|
0,
|
|
lpA->szString,
|
|
lstrlenA(lpA->szString),
|
|
lpW->szString,
|
|
IMEMENUITEM_STRING_SIZE);
|
|
|
|
if (i >= IMEMENUITEM_STRING_SIZE) {
|
|
return 0;
|
|
}
|
|
else {
|
|
lpW->szString[i] = L'\0';
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int ConvertImeMenuItemInfoWtoA(LPIMEMENUITEMINFOW lpW, LPIMEMENUITEMINFOA lpA, int nCP)
|
|
{
|
|
int i;
|
|
BOOL bUDC;
|
|
|
|
lpA->cbSize = lpW->cbSize;
|
|
lpA->fType = lpW->fType;
|
|
lpA->fState = lpW->fState;
|
|
lpA->wID = lpW->wID;
|
|
lpA->hbmpChecked = lpW->hbmpChecked;
|
|
lpA->hbmpUnchecked = lpW->hbmpUnchecked;
|
|
lpA->dwItemData = lpW->dwItemData;
|
|
lpA->hbmpItem = lpW->hbmpItem;
|
|
|
|
|
|
i = WideCharToMultiByte(nCP,
|
|
0,
|
|
lpW->szString,
|
|
wcslen(lpW->szString),
|
|
lpA->szString,
|
|
IMEMENUITEM_STRING_SIZE,
|
|
(LPSTR)NULL,
|
|
&bUDC);
|
|
|
|
if (i >= IMEMENUITEM_STRING_SIZE) {
|
|
return 0;
|
|
}
|
|
else {
|
|
lpA->szString[i] = '\0';
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
void DumpBytes(LPBYTE pb, UINT size)
|
|
{
|
|
UINT i;
|
|
TRACE(("\npbmi dump:"));
|
|
for (i = 0; i < size; ++i) {
|
|
TRACE(("%02X ", pb[i] & 0xff));
|
|
}
|
|
TRACE(("\n"));
|
|
UNREFERENCED_PARAMETER(pb); // just in case
|
|
}
|
|
#else
|
|
#define DumpBytes(a,b)
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// SaveBitmapToMemory
|
|
|
|
IMEMENU_BMP_HEADER* SaveBitmapToMemory(HDC hDC, HBITMAP hBmp, IMEMENU_BMP_HEADER* lpBH, IMEMENU_HEADER* pHeader)
|
|
{
|
|
HBITMAP hTmpBmp, hBmpOld;
|
|
IMEMENU_BMP_HEADER* lpNext = NULL;
|
|
PBITMAPINFO pbmi = &lpBH->bmi;
|
|
ULONG sizBMI;
|
|
|
|
|
|
if (!hBmp) {
|
|
RIPMSG0(RIP_WARNING, "SaveBitmapToMemory: hBmp == NULL");
|
|
return NULL;
|
|
}
|
|
UserAssert(lpBH != NULL);
|
|
|
|
//
|
|
// Let the graphics engine to retrieve the dimension of the bitmap for us
|
|
// GetDIBits uses the size to determine if it's BITMAPCOREINFO or BITMAPINFO
|
|
// if BitCount != 0, color table will be retrieved
|
|
//
|
|
pbmi->bmiHeader.biSize = sizeof pbmi->bmiHeader;
|
|
pbmi->bmiHeader.biBitCount = 0; // don't get the color table
|
|
if ((GetDIBits(hDC, hBmp, 0, 0, (LPSTR)NULL, pbmi, DIB_RGB_COLORS)) == 0) {
|
|
RIPMSG0(RIP_WARNING, "SaveBitmapToMemory: failed to GetDIBits(NULL)");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Note: 24 bits per pixel has no color table. So, we don't have to
|
|
// allocate memory for retrieving that. Otherwise, we do.
|
|
//
|
|
switch (pbmi->bmiHeader.biBitCount) {
|
|
case 24: // has color table
|
|
sizBMI = sizeof(BITMAPINFOHEADER);
|
|
break;
|
|
case 16:
|
|
case 32:
|
|
sizBMI = sizeof(BITMAPINFOHEADER) + sizeof(DWORD) * 3;
|
|
break;
|
|
default:
|
|
sizBMI = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << pbmi->bmiHeader.biBitCount);
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// check if the buffer has enough space to put bitmap
|
|
//
|
|
if ((LPBYTE)pHeader + pHeader->dwMemSize < (LPBYTE)lpBH + sizeof lpBH + sizBMI + pbmi->bmiHeader.biSizeImage) {
|
|
RIPMSG0(RIP_WARNING, "SaveBitmapToMemory: size of bmp image(s) exceed limit ");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now that we know the size of the image, let pBits point the given buffer
|
|
//
|
|
lpBH->pBits = (LPBYTE)pbmi + sizBMI;
|
|
|
|
//
|
|
// Bitmap can't be selected into a DC when calling GetDIBits
|
|
// Assume that the hDC is the DC where the bitmap would have been selected
|
|
// if indeed it has been selected
|
|
//
|
|
if (hTmpBmp = CreateCompatibleBitmap(hDC, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight)) {
|
|
hBmpOld = SelectObject(hDC, hTmpBmp);
|
|
if (GetDIBits(hDC, hBmp, 0, pbmi->bmiHeader.biHeight, (LPSTR)lpBH->pBits, pbmi, DIB_RGB_COLORS) == 0){
|
|
SelectObject(hDC, hBmpOld);
|
|
RIPMSG0(RIP_WARNING, "SaveBitmapToMemory: GetDIBits() failed.");
|
|
return NULL;
|
|
}
|
|
lpNext = (IMEMENU_BMP_HEADER*)((LPBYTE)pbmi + sizBMI + pbmi->bmiHeader.biSizeImage);
|
|
|
|
DumpBytes((LPBYTE)pbmi, sizeof *pbmi);
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "SaveBitmapToMemory: CreateCompatibleBitmap() failed.");
|
|
return NULL;
|
|
}
|
|
|
|
SelectObject(hDC, hBmpOld);
|
|
DeleteObject(hTmpBmp);
|
|
return lpNext;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// DecompileBitmap()
|
|
//
|
|
// decompile given hBitmap into IMEMENU_BMP_HEADER
|
|
// manupilate IMEMENU_BMP_HEADER links in IMEMENU_HEADER
|
|
//
|
|
// History:
|
|
// 23-Mar-1997 HiroYama Created
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
IMEMENU_BMP_HEADER* DecompileBitmap(IMEMENU_HEADER* pHeader, HBITMAP hBitmap)
|
|
{
|
|
IMEMENU_BMP_HEADER* pBmp = pHeader->lpBmp;
|
|
HDC hDC;
|
|
|
|
// first search handled bitmap
|
|
while (pBmp) {
|
|
if (pBmp->hBitmap == hBitmap) {
|
|
// if hBitmap is already de-compiled, return it
|
|
return pBmp;
|
|
}
|
|
pBmp = pBmp->lpNext;
|
|
}
|
|
|
|
// not yet allocated, so prepare memory buffer
|
|
pBmp = pHeader->lpBmpNext;
|
|
UserAssert(pBmp != NULL);
|
|
CHK_PTR(pBmp);
|
|
if (pBmp == NULL) {
|
|
RIPMSG1(RIP_WARNING, "DecompileBitmap: pBmp == NULL in L%d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
// use desktop's DC
|
|
hDC = GetDC(GetDesktopWindow());
|
|
if (hDC == NULL) {
|
|
RIPMSG1(RIP_WARNING, "DecompileBitmap: hDC == NULL in L%d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// decompile hBitmap
|
|
//
|
|
pBmp->lpNext = pHeader->lpBmp;
|
|
pHeader->lpBmpNext = SaveBitmapToMemory(hDC, hBitmap, pBmp, pHeader);
|
|
if (pHeader->lpBmpNext == NULL) {
|
|
RIPMSG1(RIP_WARNING, "DecompileBitmap: pHeader->lpBmpNext == NULL in L%d", __LINE__);
|
|
// error case. restore bmp link, then returns NULL
|
|
pHeader->lpBmpNext = pBmp;
|
|
pHeader->lpBmp = pBmp->lpNext;
|
|
pBmp = NULL;
|
|
goto cleanup;
|
|
}
|
|
|
|
// if succeeded, mark this BITMAP_HEADER with hBitmap
|
|
pBmp->hBitmap = hBitmap;
|
|
|
|
//
|
|
// put this BITMAP_HEADER in linked list
|
|
//
|
|
pHeader->lpBmp = pBmp;
|
|
|
|
cleanup:
|
|
if (hDC)
|
|
ReleaseDC(GetDesktopWindow(), hDC);
|
|
return pBmp;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// ImmPutImeMenuItemsIntoMappedFile()
|
|
//
|
|
// Interprocess IME Menu handler
|
|
//
|
|
// called from ImeSystemHandler() in user32.dll
|
|
//
|
|
// handler of WM_IME_SYSTEM:IMS_MENU_ITEM
|
|
//
|
|
// History:
|
|
// 23-Mar-1997 HiroYama Created
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
LRESULT ImmPutImeMenuItemsIntoMappedFile(HIMC hImc)
|
|
{
|
|
HANDLE hMap = NULL;
|
|
LPVOID lpMap = NULL;
|
|
IMEMENU_HEADER* pHeader;
|
|
LPIMEMENUITEMINFO lpBuf = NULL;
|
|
IMEMENU_ITEM* pMenu;
|
|
IMEMENU_BMP_HEADER* pBmp;
|
|
LRESULT lRet = 0;
|
|
ULONG_PTR offset;
|
|
DWORD i;
|
|
|
|
// Open memory mapped file
|
|
hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, IME_MENU_FILE_NAME);
|
|
if (hMap == NULL) {
|
|
RIPMSG0(RIP_WARNING, "ImmPutImeMenuItemsIntoMappedFile: cannot open mapped file.");
|
|
return 0L;
|
|
}
|
|
|
|
// Map entire view of the file into the process memory space
|
|
lpMap = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
|
if (lpMap == NULL) {
|
|
RIPMSG0(RIP_WARNING, "ImmPutImeMenuItemsIntoMappedFile: cannot map view of file.");
|
|
goto cleanup;
|
|
// I wish if I could use C++...
|
|
}
|
|
|
|
pHeader = (IMEMENU_HEADER*)lpMap;
|
|
|
|
///////////////////
|
|
// Version check
|
|
///////////////////
|
|
if (pHeader->dwVersion != 1) {
|
|
RIPMSG1(RIP_WARNING, "ImmPutImeMenuItemsIntoMappedFile: dwVersion(%d) does not match.",
|
|
pHeader->dwVersion);
|
|
goto cleanup;
|
|
}
|
|
|
|
//////////////////////////////
|
|
// convert offset to pointer
|
|
offset = (ULONG_PTR)pHeader;
|
|
CONVTO_PTR(pHeader->lpImeParentMenu);
|
|
CHK_PTR(pHeader->lpImeParentMenu);
|
|
pMenu = CONVTO_PTR(pHeader->lpImeMenu);
|
|
CHK_PTR(pHeader->lpImeMenu);
|
|
if (pHeader->dwSize) {
|
|
UserAssert(pHeader->lpImeMenu); // if dwSize is specified, we need real buffer here
|
|
lpBuf = ImmLocalAlloc(HEAP_ZERO_MEMORY, pHeader->dwSize * sizeof(IMEMENUITEMINFOW));
|
|
if (lpBuf == NULL) {
|
|
RIPMSG0(RIP_WARNING, "ImmPutImeMenuItemsIntoMappedFile: not enough memory for receiver's buffer.");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
// preparation
|
|
#if DBG
|
|
if (pHeader->lpImeParentMenu) {
|
|
UserAssert(!pHeader->lpImeParentMenu->lpBmpChecked &&
|
|
!pHeader->lpImeParentMenu->lpBmpUnchecked &&
|
|
!pHeader->lpImeParentMenu->lpBmpItem);
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////
|
|
// Get IME menus
|
|
pHeader->dwSize = ImmGetImeMenuItemsW(hImc, pHeader->dwFlags, pHeader->dwType,
|
|
(LPIMEMENUITEMINFOW)pHeader->lpImeParentMenu, lpBuf,
|
|
pHeader->dwSize * sizeof(IMEMENUITEMINFOW));
|
|
// now, pHeader->dwSize contains number of menu items rather than byte size
|
|
if (pHeader->dwSize == 0) {
|
|
goto cleanup;
|
|
}
|
|
//////////////////////////////////
|
|
|
|
//
|
|
// Copy back the information
|
|
//
|
|
// if lpBuf != NULL, we need to copy back information
|
|
//
|
|
if (lpBuf) {
|
|
LPIMEMENUITEMINFO lpMenuW = lpBuf;
|
|
|
|
pHeader->lpBmp = NULL;
|
|
// lpBmpNext will point first possible memory for bmp de-compile
|
|
pHeader->lpBmpNext = (LPVOID)((LPBYTE)pHeader + (pHeader->dwSize + 1) * sizeof(IMEMENUITEMINFOW));
|
|
|
|
// copy menuinfo
|
|
for (i = 0; i < pHeader->dwSize; ++i, ++pMenu, ++lpMenuW) {
|
|
RtlCopyMemory(pMenu, lpMenuW, sizeof *lpMenuW);
|
|
// decompile hbitmap
|
|
if (lpMenuW->hbmpChecked) {
|
|
if ((pMenu->lpBmpChecked = DecompileBitmap(pHeader, lpMenuW->hbmpChecked)) == NULL) {
|
|
RIPMSG1(RIP_WARNING, "ImmPutImeMenuItemsIntoMappedFile: DecompileBitmap Failed in L%d", __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (lpMenuW->hbmpUnchecked) {
|
|
if ((pMenu->lpBmpUnchecked = DecompileBitmap(pHeader, lpMenuW->hbmpUnchecked)) == NULL) {
|
|
RIPMSG1(RIP_WARNING, "ImmPutImeMenuItemsIntoMappedFile: DecompileBitmap Failed in L%d", __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (lpMenuW->hbmpItem) {
|
|
if ((pMenu->lpBmpItem = DecompileBitmap(pHeader, lpMenuW->hbmpItem)) == NULL) {
|
|
RIPMSG1(RIP_WARNING, "ImmPutImeMenuItemsIntoMappedFile: DecompileBitmap Failed in L%d", __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// convert pointer to offset
|
|
//
|
|
|
|
pMenu = pHeader->lpImeMenu;
|
|
CONVTO_OFFSET(pHeader->lpImeMenu);
|
|
// no need to convert parent menu, so let it be NULL
|
|
D(pHeader->lpImeParentMenu = NULL);
|
|
|
|
// pointer to BITMAP_HEADER in each menu
|
|
for (i = 0; i < pHeader->dwSize; ++i, ++pMenu) {
|
|
TRACE(("ImmPutImeMenuItemsIntoMappedFile: convertiong '%S'\n", pMenu->szString));
|
|
CONVTO_OFFSET(pMenu->lpBmpChecked);
|
|
CONVTO_OFFSET(pMenu->lpBmpUnchecked);
|
|
TRACE(("ImmPutImeMenuItemsIntoMappedFile: before conversion (%#lx)\n", pMenu->lpBmpItem));
|
|
CONVTO_OFFSET(pMenu->lpBmpItem);
|
|
TRACE(("ImmPutImeMenuItemsIntoMappedFile: after conversion (%#lx)\n", pMenu->lpBmpItem));
|
|
|
|
// check them
|
|
CHK_OFFSET(pMenu->lpBmpChecked);
|
|
CHK_OFFSET(pMenu->lpBmpChecked);
|
|
CHK_OFFSET(pMenu->lpBmpItem);
|
|
}
|
|
|
|
//
|
|
// first pointer to BITMAP_HEADER linked list
|
|
//
|
|
pBmp = pHeader->lpBmp;
|
|
CONVTO_OFFSET(pHeader->lpBmp);
|
|
CHK_OFFSET(pHeader->lpBmp);
|
|
// pHeader->lpBmpNext will not be used, so let it be NULL
|
|
D(pHeader->lpBmpNext = NULL);
|
|
|
|
//
|
|
// pointers in BITMAP_HEADER linked list
|
|
//
|
|
while (pBmp) {
|
|
IMEMENU_BMP_HEADER* ptBmp = pBmp->lpNext;
|
|
CONVTO_OFFSET(pBmp->pBits);
|
|
CONVTO_OFFSET(pBmp->lpNext);
|
|
CHK_OFFSET(pBmp->lpNext);
|
|
pBmp = ptBmp;
|
|
}
|
|
//
|
|
// pointer conversion finished
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
} // end if (lpBuf)
|
|
|
|
//
|
|
// everything went OK
|
|
//
|
|
lRet = 1;
|
|
|
|
cleanup:
|
|
if (lpBuf)
|
|
ImmLocalFree(lpBuf);
|
|
if (lpMap)
|
|
UnmapViewOfFile(lpMap);
|
|
if (hMap)
|
|
CloseHandle(hMap);
|
|
return lRet;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// InternalImeMenuCreateBitmap()
|
|
//
|
|
// create bitmap from IMEMENU_BMP_HEADER
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
HBITMAP InternalImeMenuCreateBitmap(IMEMENU_BMP_HEADER* lpBH)
|
|
{
|
|
HDC hDC;
|
|
|
|
if (lpBH == NULL) {
|
|
RIPMSG1(RIP_WARNING, "InternalImeMenuCreateBitmap: lpBH == NULL in L%d", __LINE__);
|
|
return NULL;
|
|
}
|
|
if (lpBH->pBits == NULL) {
|
|
RIPMSG1(RIP_WARNING, "InternalImeMenuCreateBitmap: lpBH->pBits == NULL in L%d", __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
if (lpBH->hBitmap) {
|
|
TRACE(("InternalImeMenuCreateBitmap: lpBH->hBitmap != NULL. will return it.\n"));
|
|
return lpBH->hBitmap;
|
|
}
|
|
|
|
if (hDC = GetDC(GetDesktopWindow())) {
|
|
HDC hMyDC = CreateCompatibleDC(hDC);
|
|
if (hMyDC) {
|
|
// (select palette) needed ?
|
|
lpBH->hBitmap = CreateDIBitmap(hDC, &lpBH->bmi.bmiHeader, CBM_INIT,
|
|
lpBH->pBits, &lpBH->bmi, DIB_RGB_COLORS);
|
|
if (lpBH->hBitmap == NULL) {
|
|
DWORD dwErr = GetLastError();
|
|
RIPMSG1(RIP_WARNING, "InternalImeMenuCreateBitmap: CreateDIBitmap Failed. Last error=%#x\n", dwErr);
|
|
}
|
|
DeleteDC(hMyDC);
|
|
}
|
|
else {
|
|
RIPMSG0(RIP_WARNING, "InternalImeMenuCreateBitmap: CreateCompatibleDC failed.");
|
|
}
|
|
|
|
ReleaseDC(GetDesktopWindow(), hDC);
|
|
}
|
|
else {
|
|
RIPMSG0(RIP_WARNING, "InternalImeMenuCreateBitmap: couldn't get Desktop DC.");
|
|
}
|
|
return lpBH->hBitmap;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// ImmGetImeMenuItemsInterProcess()
|
|
//
|
|
// Inter process IME Menu handler
|
|
// sends WM_IME_SYSTEM:IMS_GETIMEMENU
|
|
//
|
|
// History:
|
|
// 23-Mar-1997 HiroYama Created
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD ImmGetImeMenuItemsInterProcess(HIMC hImc,
|
|
DWORD dwFlags,
|
|
DWORD dwType,
|
|
LPIMEMENUITEMINFOW lpParentMenu,
|
|
LPIMEMENUITEMINFOW lpMenu,
|
|
DWORD dwSize)
|
|
{
|
|
HWND hwnd;
|
|
HANDLE hMemFile = NULL;
|
|
DWORD dwRet = 0;
|
|
LPBYTE lpMap = NULL;
|
|
IMEMENU_HEADER* pHeader;
|
|
IMEMENU_ITEM* pMenuItem;
|
|
IMEMENU_BMP_HEADER* pBmpHeader;
|
|
DWORD i;
|
|
ULONG_PTR offset;
|
|
|
|
// Get default IME window
|
|
//
|
|
// Note: We do not consider user created HIMC here, because this inter-process call is intended to
|
|
// support only internat.exe, and this message is passed as just a kick to IMM's def WinProc.
|
|
hwnd = (HWND)NtUserQueryInputContext(hImc, InputContextDefaultImeWindow);
|
|
if (hwnd == NULL || !IsWindow(hwnd)) {
|
|
RIPMSG1(RIP_WARNING, "ImmGetImeMenuItemsInterProcess: hwnd(%lx) is not a valid window.", hwnd);
|
|
return 0;
|
|
}
|
|
|
|
RtlEnterCriticalSection(&gcsImeDpi);
|
|
|
|
// first, create memory mapped file
|
|
hMemFile = CreateFileMapping((HANDLE)~0, NULL, PAGE_READWRITE,
|
|
0, IME_MENU_MAXMEM, IME_MENU_FILE_NAME);
|
|
if (hMemFile == NULL) {
|
|
RIPMSG0(RIP_WARNING, "ImmGetImeMenuItemsInterProcess: cannot allocate memory mapped file.");
|
|
goto cleanup;
|
|
}
|
|
// then get a view of the mapped file
|
|
lpMap = (LPBYTE)MapViewOfFile(hMemFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
|
|
if (lpMap == NULL) {
|
|
RIPMSG0(RIP_WARNING, "ImmGetImeMenuItemsInterProcess: cannot map view of memory mapped file.");
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// shared buffer (memory mapped file) initialization
|
|
//
|
|
pHeader = (IMEMENU_HEADER*)lpMap;
|
|
RtlZeroMemory(pHeader, sizeof *pHeader);
|
|
pHeader->dwVersion = 1;
|
|
pHeader->dwMemSize = IME_MENU_MAXMEM;
|
|
pHeader->dwSize = dwSize / sizeof(IMEMENUITEMINFOW); // CAUTION: dwSize could be 0.
|
|
RIPMSG1(RIP_WARNING, "ImmGetImeMenuItemsInterProcess: pHeader->dwSize=%ld", pHeader->dwSize);
|
|
pHeader->dwFlags = dwFlags;
|
|
pHeader->dwType = dwType;
|
|
|
|
//
|
|
// 1) dwSize != 0 and lpMenu != NULL means, caller requests the given buffer filled
|
|
// 2) if lpParentMenu is passed, we need to put its information in shared buffer
|
|
//
|
|
if ((dwSize && lpMenu) || lpParentMenu) {
|
|
// if parent menu is specified, copy it here
|
|
if (lpParentMenu) {
|
|
IMEMENU_ITEM* pPMenu =
|
|
pHeader->lpImeParentMenu = (IMEMENU_ITEM*)&pHeader[1];
|
|
|
|
RtlCopyMemory(pPMenu, lpParentMenu, sizeof(IMEMENUITEMINFOW));
|
|
|
|
// by design, IME will receive NULL hbmpItem in parent menu.
|
|
// there is no way to guarantee the same hbmpItem is returned, thus NULL is passed.
|
|
pPMenu->lpBmpChecked = pPMenu->lpBmpUnchecked = pPMenu->lpBmpItem = NULL;
|
|
pHeader->lpImeMenu = pHeader->lpImeParentMenu + 1;
|
|
}
|
|
else {
|
|
pHeader->lpImeParentMenu = NULL;
|
|
pHeader->lpImeMenu = (LPVOID)&pHeader[1];
|
|
}
|
|
// convert pointer to offset
|
|
offset = (ULONG_PTR)lpMap;
|
|
CONVTO_OFFSET(pHeader->lpImeParentMenu);
|
|
CONVTO_OFFSET(pHeader->lpImeMenu);
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
if (!SendMessage(hwnd, WM_IME_SYSTEM, IMS_GETIMEMENU, (LPARAM)hImc)) {
|
|
// if it fails
|
|
goto cleanup;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
// NOTE: dwSize is maximum index of menu array. not a total byte size of array.
|
|
dwSize = pHeader->dwSize;
|
|
|
|
if (lpMenu) {
|
|
///////////////////////////////
|
|
// convert offset to pointer
|
|
///////////////////////////////
|
|
pMenuItem = CONVTO_PTR(pHeader->lpImeMenu);
|
|
CHK_PTR(pMenuItem);
|
|
// NOTE: we don't have to handle parent menu
|
|
|
|
//
|
|
// pointers to BITMAP_HEADER in each menu structure
|
|
//
|
|
for (i = 0; i < dwSize; ++i, ++pMenuItem) {
|
|
CONVTO_PTR(pMenuItem->lpBmpChecked);
|
|
CONVTO_PTR(pMenuItem->lpBmpUnchecked);
|
|
CONVTO_PTR(pMenuItem->lpBmpItem);
|
|
//
|
|
// check the pointers
|
|
//
|
|
CHK_PTR(pMenuItem->lpBmpChecked);
|
|
CHK_PTR(pMenuItem->lpBmpUnchecked);
|
|
CHK_PTR(pMenuItem->lpBmpItem);
|
|
}
|
|
|
|
//
|
|
// pointer to first BITMAP_HEADER
|
|
//
|
|
pBmpHeader = CONVTO_PTR(pHeader->lpBmp);
|
|
|
|
//
|
|
// each BITMAP_HEADER
|
|
//
|
|
while (pBmpHeader) {
|
|
pBmpHeader->hBitmap = NULL; // clear
|
|
// pBits
|
|
CONVTO_PTR(pBmpHeader->pBits);
|
|
CHK_PTR(pBmpHeader->pBits);
|
|
|
|
// next BITMAP_HEADER
|
|
pBmpHeader = CONVTO_PTR(pBmpHeader->lpNext);
|
|
CHK_PTR(pBmpHeader);
|
|
}
|
|
|
|
//
|
|
// copy back the results
|
|
//
|
|
pMenuItem = pHeader->lpImeMenu;
|
|
for (i = 0; i < dwSize; ++i, ++pMenuItem, ++lpMenu) {
|
|
lpMenu->cbSize = pMenuItem->cbSize;
|
|
lpMenu->fType = pMenuItem->fType;
|
|
lpMenu->fState = pMenuItem->fState;
|
|
lpMenu->wID = pMenuItem->wID;
|
|
lpMenu->dwItemData = pMenuItem->dwItemData;
|
|
wcsncpy(lpMenu->szString, pMenuItem->szString, ARRAY_SIZE(lpMenu->szString));
|
|
|
|
// Create bitmap from memory buffer
|
|
// hbmp will be NULL if no bmp is specified.
|
|
if (pMenuItem->lpBmpChecked) {
|
|
lpMenu->hbmpChecked = InternalImeMenuCreateBitmap(pMenuItem->lpBmpChecked);
|
|
}
|
|
else {
|
|
lpMenu->hbmpChecked = NULL;
|
|
}
|
|
if (pMenuItem->lpBmpUnchecked) {
|
|
lpMenu->hbmpUnchecked = InternalImeMenuCreateBitmap(pMenuItem->lpBmpUnchecked);
|
|
}
|
|
else {
|
|
lpMenu->hbmpUnchecked = NULL;
|
|
}
|
|
if (pMenuItem->lpBmpItem) {
|
|
lpMenu->hbmpItem = InternalImeMenuCreateBitmap(pMenuItem->lpBmpItem);
|
|
}
|
|
else {
|
|
lpMenu->hbmpItem = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
cleanup:
|
|
if (lpMap) {
|
|
UnmapViewOfFile(lpMap);
|
|
}
|
|
RtlLeaveCriticalSection(&gcsImeDpi);
|
|
// destroy memory mapped file
|
|
if (hMemFile) {
|
|
CloseHandle(hMemFile);
|
|
}
|
|
|
|
return dwSize;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// ImmGetImeMenuItemsWorker()
|
|
//
|
|
// Handler of IME Menu
|
|
//
|
|
// if specified HIMC belongs to other process, it calls
|
|
// ImmGetImeMenuItemsInterProcess()
|
|
//
|
|
// History:
|
|
// 23-Mar-1997 HiroYama Created
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
DWORD ImmGetImeMenuItemsWorker(HIMC hIMC,
|
|
DWORD dwFlags,
|
|
DWORD dwType,
|
|
LPVOID lpImeParentMenu,
|
|
LPVOID lpImeMenu,
|
|
DWORD dwSize,
|
|
BOOL bAnsiOrigin)
|
|
{
|
|
BOOL bAnsiIme = IsAnsiIMC(hIMC);
|
|
DWORD dwRet = 0;
|
|
LPINPUTCONTEXT lpInputContext;
|
|
DWORD dwThreadId;
|
|
PIMEDPI pImeDpi = NULL;
|
|
LPVOID lpImePTemp = lpImeParentMenu; // keeps parent menu
|
|
LPVOID lpImeTemp = lpImeMenu; // points menu buffer
|
|
IMEMENUITEMINFOA imiiParentA;
|
|
IMEMENUITEMINFOW imiiParentW;
|
|
|
|
//
|
|
// check if the call will be inter process
|
|
//
|
|
{
|
|
DWORD dwProcessId = GetInputContextProcess(hIMC);
|
|
if (dwProcessId == 0) {
|
|
RIPMSG0(RIP_WARNING, "ImmGetImeMenuItemsWorker: dwProcessId == 0");
|
|
return 0;
|
|
}
|
|
if (dwProcessId != GetCurrentProcessId()) {
|
|
//
|
|
// going to call another process' IME
|
|
//
|
|
TRACE(("ImmGetImeMenuItemsWorker: Inter process.\n"));
|
|
if (bAnsiOrigin) {
|
|
//
|
|
// this inter-process thing is only allowed to internat.exe or equivalent
|
|
//
|
|
RIPMSG0(RIP_WARNING, "ImmGetImeMenuItemsWorker: interprocess getmenu is not allowed for ANSI caller.");
|
|
return 0;
|
|
}
|
|
return ImmGetImeMenuItemsInterProcess(hIMC, dwFlags, dwType, lpImeParentMenu,
|
|
lpImeMenu, dwSize);
|
|
}
|
|
}
|
|
|
|
//
|
|
// within process
|
|
//
|
|
|
|
if (hIMC == NULL || (lpInputContext = ImmLockIMC(hIMC)) == NULL) {
|
|
RIPMSG2(RIP_WARNING, "ImmGetImeMenuItemsWorker: illegal hIMC(%#lx) in L%d", hIMC, __LINE__);
|
|
return 0;
|
|
}
|
|
|
|
dwThreadId = GetInputContextThread(hIMC);
|
|
if (dwThreadId == 0) {
|
|
RIPMSG1(RIP_WARNING, "ImmGetImeMenuItemsWorker: dwThreadId = 0 in L%d", __LINE__);
|
|
goto cleanup;
|
|
}
|
|
if ((pImeDpi = ImmLockImeDpi(GetKeyboardLayout(dwThreadId))) == NULL) {
|
|
RIPMSG1(RIP_WARNING, "ImmGetImeMenuItemWorker: pImeDpi == NULL in L%d.", __LINE__);
|
|
goto cleanup;
|
|
}
|
|
|
|
#if 0 // NT: we don't keep version info in ImeDpi
|
|
if (pImeDpi->dwWinVersion <= IMEVER_0310) {
|
|
RIPMSG1(RIP_WARNING, "GetImeMenuItems: OldIME does not support this. %lx", hIMC);
|
|
goto cleanup;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// if IME does not support IME Menu, do nothing
|
|
//
|
|
if (pImeDpi->pfn.ImeGetImeMenuItems) {
|
|
LPVOID lpNewBuf = NULL;
|
|
|
|
TRACE(("ImmGetImeMenuItemsWorker: IME has menu callback.\n"));
|
|
|
|
if (bAnsiIme != bAnsiOrigin) {
|
|
//
|
|
// we need A/W translation before calling IME
|
|
//
|
|
if (bAnsiOrigin) {
|
|
// ANSI API and UNICODE IME.
|
|
// A to W conversion needed here
|
|
if (lpImeParentMenu) {
|
|
// parent menu is specified. need conversion
|
|
lpImePTemp = (LPVOID)&imiiParentW;
|
|
if (! ConvertImeMenuItemInfoAtoW((LPIMEMENUITEMINFOA)lpImeParentMenu,
|
|
(LPIMEMENUITEMINFOW)lpImePTemp,
|
|
CP_ACP, TRUE)) { // ANSI app, UNICODE IME: let's use CP_ACP
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (lpImeMenu) {
|
|
// allocate memory block for temporary storage
|
|
DWORD dwNumBuffer = dwSize / sizeof(IMEMENUITEMINFOA);
|
|
dwSize = dwNumBuffer * sizeof(IMEMENUITEMINFOW);
|
|
if (dwSize == 0) {
|
|
RIPMSG0(RIP_WARNING, "ImmGetImeMenuItemsWorker: (AtoW) dwSize is 0.");
|
|
goto cleanup;
|
|
}
|
|
lpImeTemp = lpNewBuf = ImmLocalAlloc(0, dwSize);
|
|
TRACE(("ImmGetImeMenuItemsWorker: for UNICODE IME memory allocated %d bytes. lpNewBuf=%#x\n", dwSize, lpNewBuf));
|
|
if (lpNewBuf == NULL) {
|
|
RIPMSG1(RIP_WARNING, "ImmGetImeMenuItemsWorker: cannot alloc lpNewBuf in L%d", __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// UNICODE API and ANSI IME.
|
|
// W to A conversion needed here
|
|
if (lpImeParentMenu) {
|
|
// parent menu is speicified. need conversion
|
|
lpImePTemp = (LPVOID)&imiiParentA;
|
|
if (! ConvertImeMenuItemInfoWtoA((LPIMEMENUITEMINFOW)lpImeParentMenu,
|
|
(LPIMEMENUITEMINFOA)lpImePTemp,
|
|
pImeDpi->dwCodePage)) { // Note: hopefully in the future, this can be changed to IMECodePage(pImeDpi)
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (lpImeMenu) {
|
|
// allocate memory block for temporary storage
|
|
DWORD dwNumBuffer = dwSize / sizeof(IMEMENUITEMINFOW);
|
|
dwSize = dwNumBuffer / sizeof(IMEMENUITEMINFOA);
|
|
if (dwSize == 0) {
|
|
RIPMSG0(RIP_WARNING, "ImmGetImeMenuItemsWorker: (WtoA) dwSize is 0.");
|
|
goto cleanup;
|
|
}
|
|
lpImeTemp = lpNewBuf = ImmLocalAlloc(0, dwSize);
|
|
RIPMSG2(RIP_WARNING, "ImmGetImeMenuItemsWorker: for ANSI IME memory allocated %d bytes. lpNewBuf=%#x", dwSize, lpNewBuf);
|
|
if (lpNewBuf == NULL) {
|
|
RIPMSG1(RIP_WARNING, "ImmGetImeMenuItemsWorker: cannot alloc lpNewBuf in L%d", __LINE__);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
dwRet = pImeDpi->pfn.ImeGetImeMenuItems(hIMC, dwFlags, dwType, lpImePTemp, lpImeTemp, dwSize);
|
|
////////////////////////////////////////
|
|
|
|
//
|
|
// back-conversion needed if:
|
|
// 1) IME returns menus, and
|
|
// 2) A/W is different between caller and IME, and
|
|
// 3) caller wants the buffer to be filled
|
|
//
|
|
if (dwRet && bAnsiIme != bAnsiOrigin && lpImeTemp) {
|
|
if (bAnsiOrigin) {
|
|
// ANSI API and UNICODE IME.
|
|
// W to A conversion needed here
|
|
LPIMEMENUITEMINFOW lpW = (LPIMEMENUITEMINFOW)lpImeTemp;
|
|
LPIMEMENUITEMINFOA lpA = (LPIMEMENUITEMINFOA)lpImeMenu;
|
|
DWORD i;
|
|
|
|
for (i = 0; i < dwRet; ++i) {
|
|
if (! ConvertImeMenuItemInfoWtoA((LPIMEMENUITEMINFOW)lpW++,
|
|
(LPIMEMENUITEMINFOA)lpA++,
|
|
CP_ACP)) { // ANSI app and UNICODE IME: let's use CP_ACP
|
|
dwRet = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// UNICODE API and ANSI IME.
|
|
// A to W conversion needed here
|
|
LPIMEMENUITEMINFOA lpA = (LPIMEMENUITEMINFOA)lpImeTemp;
|
|
LPIMEMENUITEMINFOW lpW = (LPIMEMENUITEMINFOW)lpImeMenu;
|
|
DWORD i;
|
|
|
|
for (i = 0; i < dwSize; i++) {
|
|
if (! ConvertImeMenuItemInfoAtoW((LPIMEMENUITEMINFOA)lpA++,
|
|
(LPIMEMENUITEMINFOW)lpW++,
|
|
pImeDpi->dwCodePage, // Note: hopefully in the future, this can be changed to IMECodePage(pImeDpi)
|
|
TRUE)) { // copy hbitmap also
|
|
dwRet = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// free temporary buffer if we've allocated it
|
|
if (lpNewBuf)
|
|
ImmLocalFree(lpNewBuf);
|
|
} // end if IME has menu callback
|
|
|
|
cleanup:
|
|
if (pImeDpi) {
|
|
ImmUnlockImeDpi(pImeDpi);
|
|
}
|
|
|
|
if (hIMC != NULL) {
|
|
ImmUnlockIMC(hIMC);
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
DWORD WINAPI ImmGetImeMenuItemsA(
|
|
HIMC hIMC,
|
|
DWORD dwFlags,
|
|
DWORD dwType,
|
|
LPIMEMENUITEMINFOA lpImeParentMenu,
|
|
LPIMEMENUITEMINFOA lpImeMenu,
|
|
DWORD dwSize)
|
|
{
|
|
return ImmGetImeMenuItemsWorker(hIMC, dwFlags, dwType,
|
|
(LPVOID)lpImeParentMenu,
|
|
(LPVOID)lpImeMenu, dwSize, TRUE /* ANSI origin */);
|
|
}
|
|
|
|
|
|
DWORD WINAPI ImmGetImeMenuItemsW(
|
|
HIMC hIMC,
|
|
DWORD dwFlags,
|
|
DWORD dwType,
|
|
LPIMEMENUITEMINFOW lpImeParentMenu,
|
|
LPIMEMENUITEMINFOW lpImeMenu,
|
|
DWORD dwSize)
|
|
{
|
|
return ImmGetImeMenuItemsWorker(hIMC, dwFlags, dwType,
|
|
(LPVOID)lpImeParentMenu,
|
|
(LPVOID)lpImeMenu, dwSize, FALSE /* UNICODE origin */);
|
|
}
|