Windows 95 输入法编辑器 翻译:戴石麟译自微软的MSDN DDK

关于Windows 95的多语言IME(输入法编辑器)

在Windows 95中,IME以动态连接库(DLL)的形式提供,与Windows 3.1东方版的IME不同,每个IME作为一个多语言键盘布局来运行。与Windows 3.1 IME相比,Windows 95多语言IME具有下述优势:

  • 作为多语言环境的一个组件来运行
  • 为每个应用程序任务提供多个输入场景
  • 为每个应用程序线程提供一个活动IME
  • 通过应用程序的消息循环(消息顺序不变)为其提供信息
  • 为对IME不敏感和对IME敏感的应用程序均提供强大的支持

为了充分利用IME的优势,应用程序需要支持Windows 95 IME应用程序I/F。

本文描述Windows 95 IME体系的应用程序I/F。

IME的结构

Windows 95 IME必须提供两个组件:IME转换接口和IME用户界面。IME转换接口以一套从IME模块输出的函数提供。这些函数由IMM调用。IME用户界面以一套窗口提供。这些窗口接收消息并提供IME的用户界面。

IME敏感的应用程序

有以下类型应用程序:

  • IME不敏感的应用程序:这种类型的应用程序绝不打算控制IME。但是,如果应用程序接受DBCS字符,用户可以用IME向应用程序输入任何DBCS字符。
  • IME半敏感的应用程序:这种类型的应用程序通常控制IME的各种场景,如打开和关闭、工作窗口等,但是它不为IME显示任何用户界面。
  • IME全敏感的应用程序:这种类型的应用程序通常显示IME给它的任何信息。

在Windows 95中,一个对IME不敏感的应用程序由一个默认的IME窗口和一个默认的输入场景支持。

IME半敏感的应用程序用预定义的"IME"类创建自己的IME窗口,可以处理也可以不处理自己的输入场景。

IME全敏感的应用程序自己处理输入场景。它显示输入场景给出的任何必要的信息。它不使用IME窗口。

IME用户界面

IME用户界面包括IME窗口、用户界面(UI)窗口和UI窗口的组件。

特点

IME类是预定义的全局窗口类,它实现IME的用户界面部分。IME类有许多与预定义的公共控件窗口相同的特征。IME窗口实例可用CreateWindowEx函数来创建。象静态控件一样,IME类窗口自身不对用户输入作出反应;代之以接收各类控件消息来实现整个IME用户界面。应用程序可以用IME类创建自己的IME窗口,也可以用ImmGetDefaultIMEWnd函数来获得默认的IME窗口。创建自己的IME窗口或使用默认的IME窗口的应用程序称为IME敏感的应用程序,可以获得下述益处(与Windows 3.1中类似应用程序相比):

  • 包含候选列表窗口,每个应用程序可有自己的用户界面窗口实例,所以用户可以在任何操作的中途停下来,将输入焦点改变到另一应用程序。在Windows 3.1日文版中,在改变到另一应用程序时用户必须放弃操作。
  • 因为IME用户界面窗口拥有应用程序的窗口句柄,因此它可以为应用程序提供默认的行为。例如,它可以随应用程序移动而自动移动,自动跟踪窗口的光标位置,指出每一应用程序的状态等。

虽然系统仅提供一个IME类,但仍有两种IME窗口。一种类型由系统为IME不敏感的应用程序创建。DefWindowsProc函数为这种窗口处理消息。DefWindowsProc函数的IME用户界面为一个线程中所有IME不敏感的窗口所共享。在本文中,它叫做"默认IME窗口"。其它窗口由IME敏感的应用程序创建。在本文中,由IME敏感的应用程序创建的窗口叫做"应用程序IME窗口"。

默认与应用程序IME窗口

线程初始化时,系统创建默认IME窗口;也就是说,默认IME窗口自动赋予线程。默认IME窗口为IME不敏感的应用程序提供IME用户界面。当IME或IMM生成一条IME消息(WM_IME_*)时,对IME不敏感的应用程序传递一条消息到DefWindowProc函数。DefWindowProc将所需的消息送到默认IME窗口以为应用程序提供默认IME用户界面。IME敏感的应用程序在不从IME挂接消息时也使用默认IME窗口。应用程序仅在需要的时候使用自己的应用程序IME窗口。

IME类

IME类是Windows 95-FE格式的预定义窗口类,就象Edit是预定义类一样。预定义IME类处理IME的整个用户界面,处理来自IME和应用程序(包含IMM函数)的所有控制消息。应用程序用IME类创建自己的IME用户界面。系统IME类不会被任何IME替换。

与IME类相联系的窗口类处理WM_IME_SELECT消息。这条消息包括新选择的IME的键盘布局。IME类使用键盘布局来获取由每个IME定义的类名。IME类使用类名为当前活动IME创建用户界面窗口。

来自IME的UI类

每个IME都必须向系统注册自己的用户界面(UI)类。UI类提供特定的IME功能。IME在附着进程时注册自己的类;也就是说,用DLL_PROCESS_ATTACH调用DllEntry函数时。IME在调用ImeInquire函数时必须指明UI类名。UI类应当以CS_IME风格注册,以便每个应用程序都能使用该UI类。UI类名(包括空结束符)可达16个TCHAR字符。此限制可能会在Windows的未来版本中得到扩展。

当注册用户界面类时,你应当指明八个字节的额外窗口数据(即,将WNDCLASSEX结构的cbWndExtra成员设置为2*sizeof(LONG)。这额外的窗口数据由系统使用。

IME在为应用程序完成任务时可注册任何类和创建任何窗口。

以下例子显示如何注册IME窗口类:

BOOL WINAPI DLLEntry (

HINSTANCE    hInstDLL,

DWORD        dwFunction,

LPVOID       lpNot)

{

switch (dwFunction) {

case DLL_PROCESS_ATTACH:

hInst= hInstDLL;

wc.style          = CS_MYCLASSFLAG | CS_IME;

wc.lpfnWndProc    = MyUIServerWndProc;

wc.cbClsExtra     = 0;

wc.cbWndExtra     = 2 * sizeof(LONG);

wc.hInstance      = hInst;

wc.hCursor        = LoadCursor( NULL, IDC_ARROW);

wc.hIcon          = NULL;

wc.lpszMenuName   = (LPSTR) NULL;

wc.lpszClassName  = (LPSTR) szUIClassName;

wc.hbrBackground  = NULL;

if(!RegisterClass((LPWNDCLASS)&wc))

return FALSE;

wc.style          = CS_MYCLASSFLAG | CS_IME;

wc.lpfnWndProc    = MyCompStringWndProc;

wc.cbClsExtra     = 0;

wc.cbWndExtra     = cbMyWndExtra;

wc.hInstance      = hInst;

wc.hCursor        = LoadCursor(NULL, IDC_ARROW);

wc.hIcon          = NULL;

wc.lpszMenuName   = (LPSTR) NULL;

wc.lpszClassName  = (LPSTR) szUICompStringClassName;

wc.hbrBackground  = NULL;

if(!RegisterClass((LPWNDCLASS)&wc))

return FALSE;

break;

case DLL_PROCESS_DETACH:

UnregisterClass(szUIClassName,hInst);

UnregisterClass(szUICompStringClassName,hInst);

break;

}

return TRUE;

}

UI窗口

IME类的IME窗口由应用程序或由系统创建。当IME窗口创建时,由IME本身提供的UI窗口被IME窗口创建并拥有。每个UI窗口都有一个当前输入场景。你可以在UI窗口收到一条IME消息(WM_IME_*)时,通过调用GetWindowLong函数并指明IMMGWL_IMC索引值来获取输入场景。UI窗口可以参照此输入场景并处理消息。UI窗口可以在除对WM_CREATE消息作出反应时以外的任何时候获取输入场景。

IME决不能更改UI窗口的额外窗口数据。如果你的窗口实例需要额外窗口数据,你可以用SetWindowLong和GetWindowLong函数带IMMGWL_PRIVATE参数。IMMGWL_PRIVATE参数提供对UI窗口实例额外数据的一个LONG值的访问,你可将一内存块的句柄存入IMMGWL_PRIVATE区域。

UI窗口过程可以使用DefWindowProc函数,但是UI窗口不能将IME消息传递给DefWindowProc。即使一条IME消息未被UI窗口过程处理,UI窗口也不能将它传递给DefWindowProc。

LRESULT UIWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

HIMC hIMC;

HGLOBAL hMyExtra;

switch(msg){

case WM_CREATE:

// Allocate the memory bloack for the window instance.

hMyExtra = GlobalAlloc(GHND,size_of_MyExtra);

if (!hMyExtra)

MyError();

// Set the memory handle into IMMGWL_PRIVATE

SetWindowLong(hWnd, IMMGWL_PRIVATE, (LONG)hMyExtra);

.

.

.

break;

case WM_IME_xxxx:

// Get IMC;

hIMC = GetWindowLong(hWnd,IMMGWL_IMC);

// Get the memory handle for the window instance.

hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);

lpMyExtra = GlobalLock(hMyExtra);

.

.

.

GlobalUnlock(hMyExtra);

break;

.

.

.

case WM_DESTROY:

// Get the memory handle for the window instance.

hMyExtra = GetWindowLong(hWnd, IMMGWL_PRIVATE);

// Free the memory block for the window instance.

GlobalFree(hMyExtra);

break;

default:

return DefWindowProc(hWnd, msg, wParam, lParam);

}

}

UI窗口在当前选定的输入场景中完成动作。当窗口激活时,UI窗口收到一条提供当前输入场景的消息。在此之后,UI窗口在当前选定的输入场景中运行。输入场景必须提供UI窗口显示工作窗口、状态窗口等所需的所有信息。

UI窗口参照输入场景,但它不必自己刷新该场景。当UI窗口需要刷新输入场景时,它应当调用IMM函数,因为输入场景是由IMM管理的。当输入场景改变时,IMM和IME都收到通知。

例如,有时当鼠标点击时,UI窗口需要改变输入场景的转换模式。要设置转换模式,UI窗口调用ImmSetConversionMode函数来为NotifyIME生成一条通知并将WM_IME_NOTIFY消息送至UI窗口。如果UI窗口要改变转换模式的显示,UI窗口应当等待WM_IME_NOTIFY消息。

UI窗口的组件

UI窗口可以参照当前输入场景来注册和显示工作窗口和状态窗口。UI窗口组件的类风格必须包含CS_IME。UI窗口实例从当前输入场景接收诸如工作字串、字型、位置之类的信息。上应用程序的一个窗口获得焦点时,系统收到为窗口所拥有的输入场景并将当前输入场景设置给UI窗口。系统将带有输入场景句柄的WM_IME_SETCONTEXT消息送到应用程序,应用程序将此消息传递到UI窗口。如果当前输入场景被替换,UI窗口应当重画工作窗口。每当当前场景变化,UI窗口都会显示正确的工作窗口,确保IME的状态。

UI窗口可以创建自己的字窗口或弹出窗口来显示其状态、工作字串或候选名单。这些窗口必须为UI窗口所拥有,并创建为失效窗口。IME创建的任何窗口都不应当获得焦点。

输入场景

默认输入场景

系统通过默认给每个线程一个输入场景。该场景为该线程所有对IME不敏感的窗口共享。

将输入场景与窗口相关联

应用程序的窗口将其窗口句柄与输入场景相关联来维持IME状态,包括当前工作字串。一旦应用程序将输入场景与窗口句柄相关联,系统在窗口激活时将自动选择该场景。利用此特征,应用程序将不需象在Windows 3.1下那样进行复杂的获得、放弃焦点的处理。

使用输入场景

当应用程序或系统创建新的输入场景时,系统准备新的输入场景。新的输入场景已经包括IMCC,即由hCompStr、hCandInfo、hGuideLine、hPrivate和hMsgBuf组成的IMC的组件。IME基本上不需创建输入场景和输入场景的组件。IME可以通过锁定它们来改变其大小并能获得指向组件的指针。

访问HIMC

要访问输入场景,IME必须调用ImmLockIMC来获得指向输入场景的指针。ImmLockIMC递增IMC的IMM锁定记数,而ImmUnlockIMC则递减它。

访问HIMCC

要访问输入场景的一个组件,IME必须调用ImmLockIMCC来获得指向IMCC的指针。ImmLockIMCC递增IMCC的IMM锁定记数,而ImmUnlockIMC则递减它。ImmReSizeIMCC可以将IMCC调整至特定的大小。

有时,IME可能需要自己创建输入场景的一个组件。在这种情况下,IME可以调用ImmCreateIMCC函数并获得IMCC的句柄。该IMCC可以是INPUTCONTEXT结构的成员(hCompStr、hCandInfo、hGuideLine、hPrivate或hMsgBuf)。

ImmDestroyIMCC销毁输入场景的一个组件。

怎样使用输入场景

以下例子显示如何使用输入场景:

LPINPUTCONTEXT lpIMC;

LPCOMOSITIONSTRING lpCompStr;

HIMCC hMyCompStr;

if (hIMC) { // It is not NULL context.

lpIMC = ImmLockIMC(hIMC);

if (!lpIMC) {

MyError( "Can not lock hIMC");

return FALSE;

}

// Use lpIMC->hCompStr.

lpCompStr = (LPCOMPOSITIONSTRING) ImmLockIMCC(lpIMC->hCompStr);

// Access lpCompStr.

ImmUnlockIMCC(lpIMC->hCompStr);

// ReSize lpIMC->hCompStr.

if (!(hMyCompStr = ImmReSizeIMCC(lpIMC->hCompStr,dwNewSize)) {

MyError("Can not resize hCompStr");

ImmUnlockIMC(hIMC);

return FALSE;

}

lpIMC->hCompStr = hMyCompStr;

ImmUnlockIMC(hIMC);

}

生成消息

IME需要生成IME消息。当IME开始一次回话时,IME必须生成WM_IME_STARTCOMPOSITION消息。如果IME改变工作字符串,IME必须生成WM_IME_COMPOSITION消息。从IME来的事件是通过向与输入场景相关联的窗口生成消息来实现的。IME基本上是利用ImeToAsciiEx函数的参数所提供的lpdwTransKey缓冲区来生成消息的。当ImeToAsciiEx被调用时,IME将消息放进lpdwTransKey缓冲区。即使ImeToAsciiEx未被调用,IME还是能够利用输入场景的消息缓冲区向与输入场景相关联的窗口生成消息。输入场景以内存块句柄的形式拥有消息缓冲区。IME将消息放进由消息缓冲区句柄提供的内存块中。然后,IME调用ImmGenerateMessage。ImmGenerateMessage函数将存储于消息缓冲区里的消息发送到适当的窗口。

(注:我没有翻译完,在北大中文论坛上有更全的,请参见http://www.pkucn.com/thread-189364-1-1.html

Windows 95 输入法编辑器的更多相关文章

  1. 【教程】16岁黑客如何把Windows 95装进智能手表?【转】

    来自美国佐治亚州的16岁黑客Corbin Davenport十分喜欢摆弄电子产品,最近他刚到手了一台三星Gear Live,并开始把玩起来.他发现Android Wear作为Android系统的改版并 ...

  2. Windows 10输入法已禁用IME无法输入中文怎么办

    Windows 10输入法已禁用IME无法输入中文怎么办 | 浏览:10453 | 更新:2015-03-01 14:46 | 标签:windows 1 2 3 4 5 分步阅读 Windows10系 ...

  3. Windows平台Atom编辑器的常用快捷键小抄Cheat Sheet

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:Windows平台Atom编辑器的常用快捷键小抄Cheat Sheet.

  4. Windows 98 二十岁了,这些功能都是从它开始的(虽然 Windows 98 不如 Windows 95 那样具有革命性,但完成度更高,更加成熟。到最后还是:相见不如怀念。)

    1998 年 6 月 25 日午夜,美国著名连锁零售店 CompUSA 门外挤满了狂热的消费者和媒体,他们在等待一款软件发售:Windows 98,即使明知它要到当天早上才正式上市. ▲ 在 Comp ...

  5. 【Key】 Windows 95 到 Windows10所有KEY 包括OEM系统

    部分可能不准确,请见谅,数据源自Baidu,由noogai00整理,数据为Microsoft.所有 Windows 95 02398-OEM-0030022-5886409297-OEM-002128 ...

  6. 破解 Windows 下Markdown 编辑器 MarkdownPad 2

    MarkdownPad 是 Windows 平台下一款优秀的 Markdown 编辑器,本文简单介绍 Markdown 以及使用一种方法破解 MarkdownPad 使其升级到专业版.该方法仅限于教育 ...

  7. windows下vim编辑器,字符编码设置。

    在windows下的vim默认字符集修改 之前使用vim编辑器的时候碰到乱码的问题,后来在网上看了记下了:在vim编辑器中按esc进入命令模式 1.修改vim内部编码    set encoding= ...

  8. Ubuntu 下使用Remmina Remote Desktop client 连接windows server输入法的问题

    Ubuntu 自带的Remmina Remote  Desktop 用来连接windows,vnc,ssh等非常方便好用,   但我在连接windows 2008 r2 server时遇到一个问题: ...

  9. 切换windows系统输入法的中英文,可以忽视是哪种打字法

    调用windows的API //用户获取当前输入法句柄 [DllImport("imm32.dll")] public static extern IntPtr ImmGetCon ...

随机推荐

  1. (轉)JSON.stringify 语法实例讲解

    作用:这个函数的作用主要是为了系列化对象的. 可能有些人对系列化这个词过敏,我的理解很简单.就是说把原来是对象的类型转换成字符串类型(或者更确切的说是json类型的).就这么简单.打个比方说,你有一个 ...

  2. 继承 (js原型链)

    原型链是实现继承的主要方法.基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法. 1.构造函数.原型.实例的关系: 每个构造函数都有原型属性(Prototype),指向一个原型对象(函数创 ...

  3. RDMA的原理、传输与Verbs

    RDMA的原理.传输与Verbs   RDMA最早专属于infiniband架构.在网络融合的大趋势下出现的RoCE,使高速.超低延时.极低cpu使用率的RDMA得以部署在目前使用最广泛的以太网上.  ...

  4. 洛谷1462(重题1951) 通往奥格瑞玛的道路(收费站_NOI导刊2009提高(2))

    1462原题链接 1951原题链接 显然答案有单调性,所以可以二分答案,用\(SPFA\)或\(dijkstra\)跑最短路来判断是否可行即可. 注意起点也要收费,\(1462\)数据较水,我一开始没 ...

  5. merge_节点

    (1)CREATE可以创建相同节点,merge若节点已存在,则不会重新添加. CREATE (gp1:GoogleProfile1 {Id: 201401, Name:"Apple" ...

  6. ios微信打开网页键盘弹起后页面上滑,导致弹框里的按钮响应区域错位

    input失去焦点,页面被顶起没有还原,所以就有以下的方法来处理 $("input").blur(function(){   setTimeout(() => {    co ...

  7. vue 获取跳转上一页组件信息

    项目中有一需求,需要根据不同的页面路径url跳转进行不同的操作,首先需要获得上一页面的url,利用 beforeRouteEnter 这个钩子中的from参数获得之前url的信息,然后给 next 传 ...

  8. Armadillo installation

    1.dependencies sudo apt-get install libopenblas-devsudo apt-get install liblapack-devsudo apt-get in ...

  9. DNA计算机及DNA存储

    傅里叶变换到量子水平,可编程元素到原子分子核能,都可以极大的改变有机体(高级有机体都是有寿命的,例如人类),如果可以出现机械体,核能提供能量:并结合类似高级生物大脑的有机体大脑,不断学习进化,甚至优化 ...

  10. 【搜索】C - Catch That Cow

    #include<stdio.h> #include<string.h> struct A{ int state; int step; }queue[]; // 结构体数组用来 ...