pos机上怎么输拼音([高阶应用]利用微软拼音来实现中文分词)

快鱼网 30 0

其实就是IFELanguage接口的应用

因为VB6里不直接支持这个接口,所以需要调用它,只能自己写tlb、动态调用、自己实现接口

写tlb当然可以,需要一点c基础,但是毕竟相对困难

动态调用,就是使用DispCallFunc或者手撸com的方法来调用

这里借用t.tui写好的应用汉字转拼音类来讲

从相关文档我们可以看到IFELanguage接口的vTable如下:

Enum pIFELanguageVTable pQueryInterface = 0 pAddRef = 4 pRelease = 8 pOpen = 12 pClose = 16 pGetJorphResult = 20 pGetConversationModeCaps = 24 pGetPhonetic = 28 pGetConversion = 32End Enum

动态调用接口最大的问题是内存对齐

在VB6/VBA程序读取内存时,默认是4字节对齐的,在tlb里可以强制修改对齐约定(tlb还有这个好处,哈哈)

但是VB6/VBA代码里是做不到的,所以我们在调用的时候,要特别注意这点

IFELanguage里两个核心的结构体如下:

typedef struct tagMORRSLT { DWORD dwSize; // total size of this block. WCHAR *pwchOutput; // conversion result string. WORD cchOutput; // lengh of result string. union { WCHAR *pwchRead; // reading string WCHAR *pwchComp; }; union { WORD cchRead; // length of reading string. WORD cchComp; }; WORD *pchInputPos; // index array of reading to input character. WORD *pchOutputIdxWDD; // index array of output character to WDD union { WORD *pchReadIdxWDD; // index array of reading character to WDD WORD *pchCompIdxWDD; }; WORD *paMonoRubyPos; // array of position of monoruby WDD *pWDD; // pointer to array of WDD INT cWDD; // number of WDD VOID *pPrivate; // pointer of private data area WCHAR BLKBuff[]; // area for stored above members. // WCHAR wchOutput[cchOutput]; // WCHAR wchRead[cchRead]; // CHAR chInputIdx[cwchInput]; // CHAR chOutputIdx[cchOutput]; // CHAR chReadIndx[cchRead]; // ???? Private // WDD WDDBlk[cWDD];}MORRSLT;

.

typedef struct tagWDD{ WORD wDispPos; // Offset of Output string union { WORD wReadPos; // Offset of Reading string WORD wCompPos; }; WORD cchDisp; //number of ptchDisp union { WORD cchRead; //number of ptchRead WORD cchComp; }; DWORD WDD_nReserve1; //reserved WORD nPos; //part of speech // implementation-defined WORD fPhrase : 1;//start of phrase WORD fAutoCorrect: 1;//auto-corrected WORD fNumericPrefix: 1;//kansu-shi expansion(JPN) WORD fUserRegistered: 1;//from user dictionary WORD fUnknown: 1;//unknown word (duplicated information with nPos.) WORD fRecentUsed: 1; //used recently flag WORD :10; // VOID *pReserved; //points directly to WORDITEM} WDD;

翻译成VB6如下:

Private Type MORRSLT dwSize As Long '4 存储结构长度 pwchOutput As Long '4 转换结果字符串指针,你转拼音就是拼音 cchOutput As Integer '2 转换结果字符串长度 pwchReadComp As Long '4 输入或对比字符串指针 cchReadComp As Integer '2 输入或对比字符串长度 pchInputPos As Long '4 pchOutputIdxWDD As Long '4 pchReadIdxWDD As Long '4 paMonoRubyPos As Long '4 pWDD As Long '4 WDD数组指针 cWDD As Integer '2 WDD结构的个数 pPrivate As Long '4 基本上都是零,这个声明不能删除 'BLKBuff As Long '4 这个没有存在的意义,只是个存储数据的缓冲区End Type

.

Type WDD wDispPos As Integer '输出字串偏移 wReadPos_CompPos As Integer '读取或比较字串的偏移 cchDisp As Integer ' cchRead_Comp As Integer WDD_nReserve1 As Long nPos As Integer ' fFlags As Integer '标志位,意思具体看上面 pReserved As Long '保留指针,指向WORDITEMEnd Type

以上就是正常翻译过来的结构体声明,但是在实际使用中很明显是不能这样使用的

为什么呢,就是前面说的内存对齐,我们来看t.tui是怎么处理的:

Private Type VB_MORRSLT dwSize As Long '4 pwchOutput As Long '4 cchOutput As Integer '2+(2),VBA内存对齐闹得,折腾了好一阵才确认问题所在,唉 Block1 As Long '4 如果要用这个,需要在使用时修正,其直接跳过了一个WORD pchInputPos As Long '4 pchOutputIdxWDD As Long '4 pchReadIdxWDD As Long '4 paMonoRubyPos As Long '4 pWDD As Long '4 cWDD As Integer '2 pPrivate As Long '4 这个虽然没用,但是不能删除 'BLKBuff As Long '4 这个没有存在的意义,只是个存储数据的缓冲区End Type

以上结构,除了第3个元素做了特殊处理以外,其他跟我上面的翻译一毛一样。

但是因为这么处理就干掉了原结构体中的4个元素,虽然我们暂时没用到。

为什么会干掉呢,简单解释一下,如下图:

内存对齐

4字节对齐的意思就是如右侧黄色标记所示,总是一次读取4个字节

每次读取的起点必然是0,4,8,C(16进制)

读取出来后,存到成员里时,成员只有2个字节,就只存储了低位,高位数据就丢失了。

黄色部分,就把左侧的4个元素都干掉了,当然这里问题不大,因为在转拼音的应用里,压根儿不需要这4个成员。

(老实说,目前我并不知道这4个成员是干啥的)

#############我是分割线#############

当然,我就是来提解决方案的,以WDD为例:

我们可以直接把这个结构体简化:

Enum WDDIndex wDispPos = 1 wReadCompPos = 2 cchDisp = 3 cchReadComp = 4 WDD_nReserve1 = 5 'DWORD nPos = 7 fFlags = 8 pReserved = 9 'DWORDEnd EnumPrivate Type VB_WDD Data(1 To 10) As IntegerEnd Type

这样复杂的WDD结构体改成了一个数组成员,这样内存读取的时候就只需要读取一次,然后因为是数组,也不会发生错位的情况,哈哈。

这里数组的成员为什么用Integer,没有为什么,只是在我们如果需要分词的话,那两个Long的成员刚好用不上,而其他成员又都是Integer,我们就偷懒采用了Integer数组了。

实际使用时,根据结构体的实际情况来选定数据数组的类型,如下是使用Byte数组的情况:

Enum myIndex index1 = 1 index2 = 2 index3 = 3 index4 = 5 index5 = 7End EnumPrivate Type MyType Data(1 To 10) As ByteEnd Type

使用第1个元素,直接myType.Data(1)即可

使用第3个元素,需要MakeInteger(myType.Data(3), myType.Data(4))

使用第5个元素,需要MakeLong(……) '有点长,省略了,哈哈

.

Public Function HiByte(ByVal w As Integer) As Byte Dim hi As Integer If w And &H8000 Then hi = &H4000 HiByte = (w And &H7FFE) \ 256 HiByte = (HiByte Or (hi \ 128))End FunctionPublic Function LoByte(w As Integer) As Byte LoByte = w And &HFFEnd FunctionPublic Function HiWord(dw As Long) As Integer If dw And &H Then HiWord = (dw \ ) - 1 Else HiWord = dw \ End IfEnd FunctionPublic Function LoWord(dw As Long) As Integer If dw And &H8000& Then LoWord = &H8000 Or (dw And &H7FFF&) Else LoWord = dw And &HFFFF& End IfEnd FunctionPublic Function MakeInteger(ByVal LoByte As Byte, ByVal HiByte As Byte) As Integer MakeInteger= ((HiByte * &H100) + LoByte)End FunctionPublic Function MakeLong(ByVal LoWord As Integer, ByVal HiWord As Integer) As Long MakeLong = ((HiWord * &H) + LoWord)End Function

当然了,你还可以利用读取规则,来使用多个数组,只是这样就又需要多个偏移枚举了。

标签: Integer

抱歉,评论功能暂时关闭!