一个困扰我半年多的问题,关于输入法的,有网友能指点一下吗?

screen12 2014-10-20 09:00:15
我做了一个输入法,已经完工,即将发布,但有一个问题,从半年前发现,到现在一直没有解决,我费尽心思查阅各种资料,反复进行调试、尝试,都不能解决问题。

这个问题是:这个输入法在别的应用程序里工作正常,但是到了像傲游浏览器,或者Chrome浏览器(这两个浏览器好象都是基于webkit引擎的),在地址栏里用输入法输入汉字的时候,它的UIWndproc,也就是UI窗口函数,收不到需要的消息。

我们知道,在输入法的ImeToAsciiEx函数中,我们可以根据用户输入的英文,进行转换。如果有结果就发消息输出,没有结果,也要消息通过UI窗口更新写作字符串。通常发消息的顺序是:WM_IME_STARTCOMPOSITION、WM_IME_COMPOSITION、WM_IME_ENDCOMPOSITION。第一个消息指示开始输入编码,最后一个消息是指示结束输入编码,第二个消息是告诉UI窗口,需要显示写作字符串。

在别的应用程序中,一切正常,UI窗口正常收到这三个消息,然后我在WM_IME_COMPOSITION消息中,显示候选窗口(我的写作窗口和候选窗口是合二为一的)。

但是,在傲游以及chrome等基本webkit的浏览器的地址栏中(包括搜索栏),却只能收到最后一个WM_IME_ENDCOMPOSITION消息,却收不到前两个消息。但是,它却比正常的应用程序多收到数个WM_IME_NOTIFY(其中wParam = IMN_SETCANDIDATEPOS)。

可是我的程序是在WM_IME_COMPOSITION消息处理分支中显示写作窗口(包括候选窗口)的,现在收不到这个消息,当然结果就是窗口无法显示,能打字,但却看不到候选窗口。

为了解决这个问题,我不得不在WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS分支里,加上显示窗口的代码,这样窗口虽然能显示了,但是光标跟随却工作不正常。因为在收到WM_IME_COMPOSITION,才能在相应的IMC的数据结构里找到当前光标位置,从而实现光标跟随功能。而且这样做还有一个问题,通常击打一个键,会收到4到6个WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS分支,这使得窗口的显示不正常,比如你关闭应用程序的时候,最后窗口消息前,你的输入法窗口还会闪现一下。

我试了一下别的输入法,凡是广泛应用的,象极点五笔、小鸭五笔、百度五笔、QQ五笔、搜狗拼音等等。均工作正常,我也不知道它们是怎么做到的。

我又试了一下网上流传的开源输入法:自由拼音,以及启程输入之星的作者写的一个例子输入法(很简单的输入法例子),发现这两种输入法,在傲游游览器的地址栏,干脆窗口不显示,也就是说,和我的输入法存在一样的问题。

有人提醒我是不是发送了WM_IME_STARTCOMPOSITION消息,只有发这个消息,应用程序才能认为开始输入法的输入,我确实发了,我后来急了,尝试再发一次,也没用,发现每多发一次WM_IME_STARTCOMPOSITION,UI窗口就多收到两个WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS分支。再多发一个WM_IME_COMPOSITION消息,同样UI窗口再多收到WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS分支。

不知道是怎么回事,有网友能指点一下吗?
...全文
349 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2014-10-21
  • 打赏
  • 举报
回复
参考MSDN98\SAMPLES\VC98\SDK\WINUI\INPUT\IME\IMEAPPS\COMP.C
赵4老师 2014-10-21
  • 打赏
  • 举报
回复
Status, Composition, and Candidates Windows The status, composition, and candidates windows form the user interface for the IME. The status window indicates that the IME is open and provides the user the means to set the conversion modes. The composition window appears when the user enters text and, depending on the conversion mode, either displays the text as entered or displays converted text. The candidates window appears in conjunction with the composition window. It contains a list of "candidates" (alternative characters) for the selected character or characters in the composition window. The user can scroll through the candidates list and select the desired character(s), then return to the composition window. The user can compose the desired text in this way until the composition string is finalized and the window is closed. The IME sends the composed characters to the application in the form of WM_IME_CHAR or WM_IME_COMPOSITION/GCS_RESULT messages. If the application does not process these messages, theDefWindowProc function translates them into one or moreWM_CHAR messages. By default, the system automatically creates and manages status, composition, and candidates windows for all windows that require text input. For many applications, this default processing is sufficient. These applications rely entirely on the system for IME support and are said to be IME-unaware because they are unaware of the many tasks the system carries out to manage the IME windows. An IME-aware application, on the other hand, participates in the creation and management of IME windows. Such applications control the operation, position, and appearance of the default windows by sending messages to and by intercepting and processing messages intended for these windows. In some cases, applications create their own IME windows and provide complete processing for their custom status, composition and candidates windows.
screen12 2014-10-21
  • 打赏
  • 举报
回复
今天继续进行艰苦的研究工作,取得了一些进展: 极点五笔不是在傲游浏览器的地址栏里工作正常吗?我用SPY++找到极点五笔的UI窗口,然后看到收到什么消息。 结果发现:它和我的输入法一样,收不到WM_IME_STARTCOMPOSITION消息和WM_IME_COMPOSITION消息,也和我的输入法一样,每打一个字符,会收到6个WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS子消息。这样一来,我发现我主贴中需要寻找的目标根本是不可能的:傲游浏览器中在地址栏输入时,输入法UI窗口确实收不到WM_IME_STARTCOMPOSITION消息和WM_IME_COMPOSITION消息,即使那些正常工作的输入法也是收不到的。 但是,我发现了最重要的一点:就是极点五笔输入法,除了收到我的输入法同样收到的4到6个WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS子消息以外,它在开始输入一个编码时,会收到WM_IME_NOTIFY消息IMN_OPENCANDIDATE子消息,在继续输入编码时,会收到WM_IME_NOTIFY消息IMN_CHANGECANDIDATE子消息,在结束一个编码时,会收到WM_IME_NOTIFY消息IMN_CLOSECANDIDATE子消息。看来它就是在这样三个消息中,处理候选窗口的显示和隐藏的。 可是:我自己的输入法,为什么收不到这三个WM_IME_NOTIFY消息的子消息呢? 请问:WM_IME_NOTIFY消息的三个子消息:IMN_OPENCANDIDATE、IMN_CHANGECANDIDATE、IMN_CLOSECANDIDATE,在什么条件下发送?
赵4老师 2014-10-20
  • 打赏
  • 举报
回复
多线程访问同一个全局变量需要加锁。参考下面:
//循环向a函数每次发送200个字节长度(这个是固定的)的buffer,
//a函数中需要将循环传进来的buffer,组成240字节(也是固定的)的新buffer进行处理,
//在处理的时候每次从新buffer中取两个字节打印
#ifdef WIN32
    #pragma warning(disable:4996)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
    #include <windows.h>
    #include <process.h>
    #include <io.h>
    #define  MYVOID             void
    #define  vsnprintf          _vsnprintf
#else
    #include <unistd.h>
    #include <sys/time.h>
    #include <pthread.h>
    #define  CRITICAL_SECTION   pthread_mutex_t
    #define  MYVOID             void *
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
void sleep_ms(int ms) {
    Sleep(ms);
}
#else
void Lock(CRITICAL_SECTION *l) {
    pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
    pthread_mutex_unlock(l);
}
void sleep_ms(int ms) {
    usleep(ms*1000);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;

    if (NULL==pszFmt||0==pszFmt[0]) return;
    vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
        } else {
            fclose(flog);
        }
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
#define ASIZE    200
#define BSIZE    240
#define CSIZE      2
char Abuf[ASIZE];
char Cbuf[CSIZE];
CRITICAL_SECTION cs_HEX ;
CRITICAL_SECTION cs_BBB ;
struct FIFO_BUFFER {
    int  head;
    int  tail;
    int  size;
    char data[BSIZE];
} BBB;
int No_Loop=0;
void HexDump(int cn,char *buf,int len) {
    int i,j,k;
    char binstr[80];

    Lock(&cs_HEX);
    for (i=0;i<len;i++) {
        if (0==(i%16)) {
            sprintf(binstr,"%03d %04x -",cn,i);
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        } else if (15==(i%16)) {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
            sprintf(binstr,"%s  ",binstr);
            for (j=i-15;j<=i;j++) {
                sprintf(binstr,"%s%c",binstr,('!'<buf[j]&&buf[j]<='~')?buf[j]:'.');
            }
            Log("%s\n",binstr);
        } else {
            sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]);
        }
    }
    if (0!=(i%16)) {
        k=16-(i%16);
        for (j=0;j<k;j++) {
            sprintf(binstr,"%s   ",binstr);
        }
        sprintf(binstr,"%s  ",binstr);
        k=16-k;
        for (j=i-k;j<i;j++) {
            sprintf(binstr,"%s%c",binstr,('!'<buf[j]&&buf[j]<='~')?buf[j]:'.');
        }
        Log("%s\n",binstr);
    }
    Unlock(&cs_HEX);
}
int GetFromRBuf(int cn,CRITICAL_SECTION *cs,FIFO_BUFFER *fbuf,char *buf,int len) {
    int lent,len1,len2;

    lent=0;
    Lock(cs);
    if (fbuf->size>=len) {
        lent=len;
        if (fbuf->head+lent>BSIZE) {
            len1=BSIZE-fbuf->head;
            memcpy(buf     ,fbuf->data+fbuf->head,len1);
            len2=lent-len1;
            memcpy(buf+len1,fbuf->data           ,len2);
            fbuf->head=len2;
        } else {
            memcpy(buf     ,fbuf->data+fbuf->head,lent);
            fbuf->head+=lent;
        }
        fbuf->size-=lent;
    }
    Unlock(cs);
    return lent;
}
MYVOID thdB(void *pcn) {
    char        *recv_buf;
    int          recv_nbytes;
    int          cn;
    int          wc;
    int          pb;

    cn=(int)pcn;
    Log("%03d thdB              thread begin...\n",cn);
    while (1) {
        sleep_ms(10);
        recv_buf=(char *)Cbuf;
        recv_nbytes=CSIZE;
        wc=0;
        while (1) {
            pb=GetFromRBuf(cn,&cs_BBB,&BBB,recv_buf,recv_nbytes);
            if (pb) {
                Log("%03d recv %d bytes\n",cn,pb);
                HexDump(cn,recv_buf,pb);
                sleep_ms(1);
            } else {
                sleep_ms(1000);
            }
            if (No_Loop) break;//
            wc++;
            if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc);
        }
        if (No_Loop) break;//
    }
#ifndef WIN32
    pthread_exit(NULL);
#endif
}
int PutToRBuf(int cn,CRITICAL_SECTION *cs,FIFO_BUFFER *fbuf,char *buf,int len) {
    int lent,len1,len2;

    Lock(cs);
    lent=len;
    if (fbuf->size+lent>BSIZE) {
        lent=BSIZE-fbuf->size;
    }
    if (fbuf->tail+lent>BSIZE) {
        len1=BSIZE-fbuf->tail;
        memcpy(fbuf->data+fbuf->tail,buf     ,len1);
        len2=lent-len1;
        memcpy(fbuf->data           ,buf+len1,len2);
        fbuf->tail=len2;
    } else {
        memcpy(fbuf->data+fbuf->tail,buf     ,lent);
        fbuf->tail+=lent;
    }
    fbuf->size+=lent;
    Unlock(cs);
    return lent;
}
MYVOID thdA(void *pcn) {
    char        *send_buf;
    int          send_nbytes;
    int          cn;
    int          wc;
    int           a;
    int          pa;

    cn=(int)pcn;
    Log("%03d thdA              thread begin...\n",cn);
    a=0;
    while (1) {
        sleep_ms(100);
        memset(Abuf,a,ASIZE);
        a=(a+1)%256;
        if (16==a) {No_Loop=1;break;}//去掉这句可以让程序一直循环直到按Ctrl+C或Ctrl+Break或当前目录下存在文件No_Loop
        send_buf=(char *)Abuf;
        send_nbytes=ASIZE;
        Log("%03d sending %d bytes\n",cn,send_nbytes);
        HexDump(cn,send_buf,send_nbytes);
        wc=0;
        while (1) {
            pa=PutToRBuf(cn,&cs_BBB,&BBB,send_buf,send_nbytes);
            Log("%03d sent %d bytes\n",cn,pa);
            HexDump(cn,send_buf,pa);
            send_buf+=pa;
            send_nbytes-=pa;
            if (send_nbytes<=0) break;//
            sleep_ms(1000);
            if (No_Loop) break;//
            wc++;
            if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc);
        }
        if (No_Loop) break;//
    }
#ifndef WIN32
    pthread_exit(NULL);
#endif
}
int main() {
#ifdef WIN32
    InitializeCriticalSection(&cs_log);
    InitializeCriticalSection(&cs_HEX );
    InitializeCriticalSection(&cs_BBB );
#else
    pthread_t threads[2];
    int threadsN;
    int rc;
    pthread_mutex_init(&cs_log,NULL);
    pthread_mutex_init(&cs_HEX,NULL);
    pthread_mutex_init(&cs_BBB,NULL);
#endif
    Log("Start===========================================================\n");

    BBB.head=0;
    BBB.tail=0;
    BBB.size=0;

#ifdef WIN32
    _beginthread((void(__cdecl *)(void *))thdA,0,(void *)1);
    _beginthread((void(__cdecl *)(void *))thdB,0,(void *)2);
#else
    threadsN=0;
    rc=pthread_create(&(threads[threadsN++]),NULL,thdA,(void *)1);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1);
    rc=pthread_create(&(threads[threadsN++]),NULL,thdB,(void *)2);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1);
#endif

    if (!access("No_Loop",0)) {
        remove("No_Loop");
        if (!access("No_Loop",0)) {
            No_Loop=1;
        }
    }
    while (1) {
        sleep_ms(1000);
        if (No_Loop) break;//
        if (!access("No_Loop",0)) {
            No_Loop=1;
        }
    }
    sleep_ms(3000);
    Log("End=============================================================\n");
#ifdef WIN32
    DeleteCriticalSection(&cs_BBB );
    DeleteCriticalSection(&cs_HEX );
    DeleteCriticalSection(&cs_log);
#else
    pthread_mutex_destroy(&cs_BBB);
    pthread_mutex_destroy(&cs_HEX);
    pthread_mutex_destroy(&cs_log);
#endif
    return 0;
}
screen12 2014-10-20
  • 打赏
  • 举报
回复
我还有一点我需要说一下: 按照IME开发的规范。应该是把写作字符串放到IMC中的一个组件lpIMC->hCompStr中,把候选项放到另一个组件lpIMC->hCandInfo中,可是我没有,我全部是放在我自己定义的全局变量中的,对于IMC中的内容,基本上没怎么操作,不知这是不是造成这个问题的根源? 另外,在创建写作窗口和候选窗口的时候,我把窗口句柄也是保存在全局变量中的,按照道理应该放在什么地方?我嫌IMC中的数据结构过于复杂,而且不合我的要求,就自己用全局变量来代替了。 这是不是造成问题的根源呢?如果是,应该怎么修改?填写IMC的哪些部分内容才能让它工作正常?
sichuanwww 2014-10-20
  • 打赏
  • 举报
回复
screen12 2014-10-20
  • 打赏
  • 举报
回复
在调试的时候,我发现:如果开始打第一个英文,比如“a",我在ImeToAsciiEx函数中只发了两个消息:WM_IME_STARTCOMPOSITION、WM_IME_COMPOSITION,但在UI窗口的窗口函数UIWndProc中,却收一对对6个WM_IME_NOTIFY消息(其中wParam = IMN_SETCANDIDATEPOS),再打第二个英文“b“,又收到4个WM_IME_NOTIFY消息(其中wParam = IMN_SETCANDIDATEPOS),经过尝试发现:打第一个英文的时候,前两个WM_IME_NOTIFY消息好晚服是WM_IME_STARTCOMPOSITION转换而来的,也就是说:我发的WM_IME_STARTCOMPOSITION转换成了两个WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS子消息,我发的WM_IME_COMPOSITION消息转换成了4个WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS子消息。 而我最后发的WM_IME_ENDCOMPOSITION,UI窗口能正常收到。 虽然在WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS子消息里显示窗口也未尝不可。也能勉强让输入法看起来正常。但毕竟打一个英文,窗口显示了4到6次,是不好的。其实也不能完全正常显示,因为这样一来,只要是收到WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS子消息,窗口就显示一次。会造成一些不舒服的现象。比如你要关闭应用程序,它会提示你”要保存当前文件吗?“,当你选择”否“,返回继续编辑时,这个对话框消失的时候,输入法候选窗口会在显示和隐藏之间转换几次,造成闪烁。 而且还会有一个问题,光标跟随工作不正常。收到的4个WM_IME_NOTIFY消息的IMN_SETCANDIDATEPOS子消息,我发现前两个消息,它的lpIMC->cfCandForm[0].ptCurrentPos指示的是前一个光标位置。后2个消息,lpIMC->cfCandForm[0].ptCurrentPos指示的是当前正确的光标位置。这样的话,输入法窗口会先显示在原来光标位置,再向右移到当前光标位置。让用户不舒服。 这个问题怎么解决?

16,473

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧