输入法开发日记

2020年7月26日

C++11(及现代C++风格)和快速迭代式开发 – 刘未鹏 | Mind Hacks

《C++ Primer Plus》(第6版)

2020年7月27日

IME输入法编程心得 - FreedomShe - 博客园
https://www.cnblogs.com/freedomshe/archive/2012/11/30/ime_learning.html

“/ZI”和“/Gy-”命令行选项不兼容

解决方案:

  1. 右键项目,打开属性对话框。
  2. 配置属性 > C/C++ > 常规
  3. 调试信息格式改为 程序数据库 (/Zi)
    image-20200727151936248

"链接器工具错误 LNK2026 XXX模块对于 SAFESEH 映像是不安全的

解决方法:

1.打开该项目的“属性页”对话框。

2.单击“链接器”文件夹。

3.单击“命令行”属性页。

4.将 /SAFESEH:NO 键入“附加选项”框中,然后点击应用。
image-20200727151936248

Requirements for IME development (Windows Store apps) - Windows app development | Microsoft Docs
https://docs.microsoft.com/zh-cn/previous-versions/windows/apps/hh967425(v=win.10)

Input Method Manager - Win32 apps | Microsoft Docs
https://docs.microsoft.com/zh-cn/windows/win32/intl/input-method-manager

2020年7月28日

输入法(IME)实现原理_LANSINE_新浪博客
http://blog.sina.com.cn/s/blog_56a388c20100004u.html

Google Code Archive - Long-term storage for Google Code Project Hosting.
https://code.google.com/archive/p/windows-config/wikis/Win32IME.wiki

微软新一代输入法框架 TSF - Text Service Framework 小小的研究_PunCha (PCH)-CSDN博客_tsf框架输入法 https://blog.csdn.net/puncha/article/details/13293665

copyliu/YIME: 一个输入法
https://github.com/copyliu/YIME

2020年7月29日

NyaRuRu/TSF-TypeLib: Type Library of Text Services Framework for .NET
https://github.com/NyaRuRu/TSF-TypeLib

TSF自定义候选词列表界面 - ShengM - 博客园 https://www.cnblogs.com/ShengM/p/5620814.html

输入法的调试方法 | 学步园 https://www.xuebuyuan.com/691828.html

基于文本服务框架的拼音输入法研究与实现_fishmai的专栏-CSDN博客_基于文本服务框架的拼音输入法研究与实现 https://blog.csdn.net/fishmai/article/details/60756753

2020年7月30日

Text Services Framework (Text Services Framework) - Win32 apps | Microsoft Docs
https://docs.microsoft.com/en-us/windows/win32/tsf/text-services-framework

Google Translate
https://translate.google.co.uk/translate?hl=en&sl=ja&tl=zh-CN&u=https%3A%2F%2Fnyaruru.hatenablog.com%2Fentry%2F20070325%2Fp1&prev=search&sandbox=1

2020年8月4日

win10自带的notepad不能用于输入法的调试,会不加载dll,原因未知。使用notepad++可以直接单步进入代码调试。

要开启启动本地代码调试,加载符号。

这里留个效果图。

image-20200804153901986

2020年8月10日

容器

以下GUID用于标识预定义语言栏项目。这些数值通过GetItem函数,用来 获取特殊的的语言栏项。

  • GUID_LBI_SAPILAYR_CFGMENUBUTTON
    语音工具栏菜单项。
  • GUID_TFCAT_TIP_HANDWRITING
    手写输入项
  • GUID_TFCAT_TIP_KEYBOARD
    键盘输入项

MSDN

  • GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER
    如果Text Service需要支持显示属性信息(display attribute infomation)需要注册这个容器。
  • GUID_TFCAT_TIPCAP_SECUREMODE
    Text Service 支持安全模式。
  • TF_IPP_CAPS_UIELEMENTENABLED
    Text Service支持UI元素。
  • TF_IPP_CAPS_COMLESSSUPPORT
    Text Service不需要COM也可以激活。
  • TF_IPP_CAPS_WOW16SUPPORT
    Text Service可以在16位的任务上激活。
  • TF_IPP_CAPS_IMMERSIVESUPPORT
    从Win8开始:Text Service已经通过测试,可以正常运行在Windows应用商店程序中。
  • GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT
    从win8开发:Text Service支持在系统托盘中。这个使于那些未设置TF_IPP_CAPS_IMMERSIVESUPPORT标志,但是仍然与系统托盘兼容的Text Service。

2020年8月11日

访问注册表被拒绝

在 项目属性(Properties)上右键,添加=>新建项 选择“应用程序清单文件(仅限Windows)” 然后单击 添加 按钮
添加后,默认打开app.manifest文件,将:

<requestedExecutionLevel level="asInvoker" uiAccess="false" />

修改为:

<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

重新生成项目。调试运行时就会提示需要以管理员权限运行。

COM接口转换失败

添加[STAThread]在Main上。

MSDN上是这样解释的:
在主入口上应用这个特性(在C#和VB中为Main函数)。在其他方法上应用没有任何效果。为了在你代码中启动的线程中设置单元状态,请在线程开始前调用Thread.SetApartmentState 或者 Thread.TrySetApartmentState

语言支持列表:
[MS-LCID]: Appendix A: Product Behavior | Microsoft Docs
https://docs.microsoft.com/zh-cn/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c

为服务器应用选择 .NET Core 或 .NET Framework | Microsoft Docs
https://docs.microsoft.com/zh-cn/dotnet/standard/choosing-core-framework-server

在 Windows 上安装 .NET Core | Microsoft Docs
https://docs.microsoft.com/zh-cn/dotnet/core/install/windows?tabs=netcore22

.NET Framework 系统要求 | Microsoft Docs
https://docs.microsoft.com/zh-cn/dotnet/framework/get-started/system-requirements

TIP:好像有时候调试的用的编辑器会被玩坏掉,换一款试试看。

2020年8月12日

事件需要在Active内注册监听。
注册服务均可以从ITfThreadMgr中获得。获得到的对象需要使用Marshal.ReleaseComObject函数来释放。
键盘事件需要通过 ITfKeystrokeMgr.AdviseKeyEventSink 注册。
ITfSource.AdviseSink可以注册通知监听。包含:
IID_ITfActiveLanguageProfileNotifySink
IID_ITfDisplayAttributeNotifySink
IID_ITfKeyTraceEventSink
IID_ITfPreservedKeyNotifySink
IID_ITfThreadFocusSink
IID_ITfThreadMgrEventSink

2020年8月14日

输入法算法实现:隐马尔可夫模型、2-Gram的模型、云端3-Gram的语言模型

2020年8月18日

System.Runtime.InteropServices.COMException (0x80040201): 事件无法调用任何订户 (异常来自 HRESULT:0x80040201)
很是疑惑,待解决。

此外系统自带的记事本只触发OnTestKeyDown、OnTestKeyUp这两个函数,而用Typora测试的时候,就只触发OnKeyDown、OnKeyUp这两个函数,暂且不知道这俩对区别。

【更正】2020年8月19日 OnTestKeyDown OnTestKeyUp 下pfEaten返回true,才会调用对应的OnKeyDown。

2020年8月19日

异常转换为ManagerReturnValues 对象以后,显示的错误为TF_E_NOLOCK:The cookie in ec is invalid.

重要】 EditSession 的EditCookie仅仅只在DoEditSession函数体内有效,出了该函数体范围以后,文档将解除锁定,session已经失效且无法再使用。

COM 对象与其基础 RCW 分开后就不能再使用。错误解决

var keystrokeMgr = _threadMgr as ITfKeystrokeMgr;
            if (keystrokeMgr == null)
                return false;

if (!(_threadMgr is ITfKeystrokeMgr keystrokeMgr))
                return false;

等价,且均为同一个实例。不需要使用Marshal.ReleaseComObject(keystrokeMgr);释放,否则会出现System.Runtime.InteropServices.InvalidComObjectException:“COM 对象与其基础 RCW 分开后就不能再使用。”错误。

public HRESULT OnTestKeyDown(ITfContext pic, UIntPtr wParam, IntPtr lParam, out bool pfEaten)传入的ITfContext实例 与 var res = _threadMgr.GetFocus(out var documentMgr);res = documentMgr.GetTop(out var context); 返回context地址一样,均指向同一个实例。

2020年8月26日

感觉之前设计的候选词引擎不太对,这次用暴力实现实现,查询就用LINQ语句暴力查。

2020年9月5日

这边对Key的int数值使用System.Windows.Forms.Keys的复制,避免引入过度无用类。

2020年9月18日

关于VS项目循环引用问题

      写完才发现Core和Gui项目存在循环引用。现在改为Core引用Gui,然后生成的DLL,由Gui引用。以解决循环引用问题。
       后期肯定要对这俩个强耦合的项目,分离开来。现在先主要以实现功能为目的。

2020年9月21日

获取屏幕位置可以由GetCaretPos这个函数得到,该函数定于为

BOOL GetCaretPos(
  LPPOINT lpPoint
);

参考:
MSDN:https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getcaretpos

C# GetCaretPos() works improperly? - CodeProject
https://www.codeproject.com/Questions/559082/GetCaretPos-plusworksplusimproperly-3f

Point需要使用TSF.TypeLib中的POINT,需要与C++的结构体对齐。

2020年9月22日

Core和Gui项目的循环引用可以采用消息队列来解耦。参考进程间使用缓冲通讯。

2020年9月24日

关于Core和Gui解耦问题:

Core --> Gui
Core依赖于Gui,但是Gui不依赖Core。
Gui发出事件,Core注册即可。
Gui事件:

  • 选择候选词 CandidateSelectEvent
  • 翻页 PageChangeEvent
  • 模式改变 InputModeChangeEvent

问题:在开启候选词输入界面以后,原有的窗口失去焦点,造成无法输入问题。参考:https://zhidao.baidu.com/question/688007159565399804.html

2020年9月27日

关于移植到其他平台上输入法不可用问题:

我之前以为是不兼容Win7造成了,于是在虚拟机里面安装了Win8.1和Win10发现故障照样。之后加入错误日记才发现,是路径问题。VS调试的时候默认把路径指向Debug目录,于是可以加载到字典文件。默认指向ApplicationData。这里打算使用注册表来保存程序目录。

此外发现了一个很有趣的现象:当当前CMD为管理员模式的时候,启动Install.exe程序就是输出到当前命令行,如果不是管理员模式,则会新开一个命令行窗口输出。

注册表存放位置为计算机\HKEY_LOCAL_MACHINE\SOFTWARE\TibetInput
包含InstallLocation(安装位置),Version(版本)

发现Debug的可以写入注册表,Release写入以后没反应。发现Release输出为x86的程序。

32位程序在64位系统读注册表问题_Keep Moving~-CSDN博客 https://blog.csdn.net/c_base_jin/article/details/80947204?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.add_param_isCf

.net - 如何使用.NET判断我是在x64还是x86上? - IT工具网
https://www.coder.work/article/6903606

2020年10月20日

context.GetSelection一直报错System.StackOverflowException 原因不明

TF_DEFAULT_SELECTION在此电脑上为4294967295,不知道其他地方时候一样。

猜测是C#的数组没对齐,和C++数组不一样。

原来是TSF.TypeLib中的错误

接口ITfContext中函数需要改为HRESULT GetSelection([In] TfEditCookie ec, [In] uint ulIndex, [In] uint ulCount, [Out] TF_SELECTION[] pSelection, [NullAllowed] out uint pcFetched);函数需要改为HRESULT GetSelection([In] TfEditCookie ec, [In] uint ulIndex, [In] uint ulCount, [Out,MarshalAs(UnmanagedType.LPArray)] TF_SELECTION[] pSelection, [NullAllowed] out uint pcFetched);

考虑到是7年前的项目了,我打算Fork一下,对其BUG修复。

2020年10月21日

逗比的输入法实现 – Yang Yuan – 一个正常的攻城狮
https://yangyuan.github.io/post/2015-01-08-zh-meow-ime/

2020年10月30日

How Do I Rime with the Code | RIME | 中州韻輸入法引擎 https://rime.im/code/

rime/weasel: 【小狼毫】Rime for Windows
https://github.com/rime/weasel

关于输入文字后,光标不移动问题解决:

SetText后,使用range.Collapse(ec, TfAnchor.TF_ANCHOR_END);闭合range。之后创建一个ITfselection,并且给当前ITfcontext设置ITfselection即可。

关键代码如下:

_selections = new TF_SELECTION[1];
            var res = _context.GetSelection(ec, 4294967295, 1, _selections, out var pcFetched);
if (pcFetched == 1)
{
     var range = _selections[0].range;
    res = range.SetText(ec, 0, _words, _words.Length);
    range.Collapse(ec, TfAnchor.TF_ANCHOR_END);
    var selection = new TF_SELECTION
    {
        range = range,
        style = new TF_SELECTIONSTYLE
        {
            ase = TfActiveSelEnd.TF_AE_END,
            fInterimChar = false
        }
    };
    _context.SetSelection(ec, 1, selection);
}

2020年11月2日

TF_DEFAULT_SELECTIONmsctf.h中定义,为TS_DEFAULT_SELECTION的别名,TS_DEFAULT_SELECTIONTextStor.h中定义,为( ( ULONG )-1 )。在minwindef.h中,ULONGtypedef unsigned long ULONG

修改库的函数GetSelectionHRESULT GetSelection([In] TfEditCookie ec, [In] TF_DEFAULT ulIndex, [In] ulong ulCount, [Out, MarshalAs(UnmanagedType.LPArray)] TF_SELECTION[] pSelection, [NullAllowed] out ulong pcFetched);

修改库的函数GetSelectionHRESULT GetSelection([In] TfEditCookie ec, [In] TF_DEFAULT ulIndex, [In] uint ulCount, [Out, MarshalAs(UnmanagedType.LPArray)] TF_SELECTION[] pSelection, [NullAllowed] out uint pcFetched);

注意:X86不支持ulong,但是在x64使用uint也是可以的。因此这边只能定义为uint。

在虚拟机上调试输入法

在自己电脑(win10 2004 专业版)上可以运行,但是到了虚拟机(win7 x86 sp1专业版)上不能运行。

参考《VS远程调试虚拟机中的程序》,在虚拟机上调试输入法步骤如下:

  1. 虚拟机设置为桥接模式,复制物理机网络状态。
    image-20201102110812684
  2. 然后选择连接。(这里主要是为了避免win10在虚拟机中自动更新的问题。)
    image-20201102111035107
  3. 查看网络共享 => 本地连接 =>状态 => 详细信息,获取虚拟机的IP地址。
  4. 设置VS项目属性 => 调试页:
    image-20201102112241268
  5. 将VS所在目录下的远程调试工具复制到虚拟机, 我的是D:\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Remote Debugger
    目录下有3个文件夹,复制对应操作系统位数的文件夹即可。比如我这里复制x86这个文件夹。
  6. 运行虚拟机中 Remote Debugger目录下的msvsmon.exe 然后点击选项 => 无身份验证,运行任何用户进行调试。
    最长空闲时间可以改大点: 100000000000000000
  7. 右键项目 => 调试 => 启动新实例调试。
    之后就可以看见,在虚拟机里面启动了一个记事本程序。

* 目前在IE下有问题。
原因为失去焦点后重返焦点时候,输入框不会被选中问题。

2020年11月3日

阅读小狼毫输入法发现

ITfKeyEventSink::OnTestKeyDown

Some apps sends strange OnTestKeyDown/OnKeyDown combinations:
Some sends OnKeyDown() only. (QQ2012)
Some sends multiple OnTestKeyDown() for a single key event. (MS WORD 2010 x64)
We assume every key event will eventually cause a OnKeyDown() call.
We use _fTestKeyDownPending to omit multiple OnTestKeyDown() calls, and for OnKeyDown() to check if the key has already been sent to the server.

一些应用在发送OnTestKeyDown/OnKeyDown存在古怪的组合。一些应用只会发送OnKeyDown()(QQ2012);一些应用对于一个单独的按键事件,发送多次的OnTestKeyDown()(MS WORD 2010 x64)。我们假定所有的按键事件都会调用OnKeyDown() 函数。我们使用_fTestKeyDownPending标识来忽略多次调用OnTestKeyDown(),这可以确保OnKeyDown()的按键是否已经被发送到了服务端。

IE焦点的问题:

给WPF的Windows窗口设置GetActiveWindow属性为false。属性说明为:获取或者设置一个值,只是首次显示时候,将激活窗口。
然而,新的问题又产生了,在网页输入的时候,IE的候选框位置不在文本输入光标下面的。此外还有窗口移动问题,这里需要实时更新候选框的位置。

2020年11月6日

之前还有遗留的BUG:

  1. 点击任意UI,原有的文本输入框失去焦点问题。
  2. 点击候选词UI,不会选中候选词问题。
1. 第一个问题的解决:

.NET/C# 使窗口永不激活(No Activate 永不获得焦点) - 云+社区 - 腾讯云
https://cloud.tencent.com/developer/article/1341125

pinvoke.net: the interop wiki!
https://www.pinvoke.net/

但是问题还是没有解决。目测输入法的焦点属于进程内交换。思考。

2020年11月8日

智能蒙古文输入法的设计与实现 - 中国知网 https://kns.cnki.net/kcms/detail/detail.aspx?dbcode=CMFD&dbname=CMFD201902&filename=1019884573.nh&v=IpjW3sgiQW3uYl5Ozq3rR6Q3IEqyLhHzE5e0KW7T57TvN4mSr%25mmd2FoM7rwMMqIy7h1U

定制化安卓输入法设计及实现 - 中国知网 https://kns.cnki.net/kcms/detail/detail.aspx?dbcode=CJFD&dbname=CJFDLAST2020&filename=WDZC202006034&v=JA2CxKc59OkUa%25mmd2BppFhZXzA0tXYslouxg7U2W3Au8stO8iDgYdRIHdVhNmwWBaaP7

2020年11月17日

设计字典的时候,必须注意大端小段问题,详细见:https://blog.csdn.net/z736248591/article/details/109458524#_96

2020年11月20日

ITfUIElementMgr::BeginUIElement:

The ITfUIElementMgr::BeginUIElement method is called by a text service before showing UI. The value returned determines whether the UI for the text service should be shown or not.

文字服务在显示UI之前,需要先调用ITfUIElementMgr::BeginUIElement。根据返回的数值来判断UI界面时候需要显示。

2020年11月28日

ComRegisterFunctionAttribute 使你能够添加任意注册代码以满足 COM 客户端的要求。 例如,你可以使用命名空间中的注册函数更新注册表 Microsoft.Win32 。 如果提供了注册方法,还应将应用 System.Runtime.InteropServices.ComUnregisterFunctionAttribute 于注销方法,这会反转注册方法中完成的操作。

.NET Framework: 公共语言运行时使用此属性调用方法,方法是将其包含的程序集注册 (直接或间接) 使用 Regasm.exe (程序集注册) 工具) 或通过 RegistrationServices.RegisterAssembly 方法。

.Net Core: 当公共语言运行时通过 RegSvr32.exe 工具注册了包含程序集的 COM 主机时,公共语言运行时将调用具有此特性的方法。

此属性只能应用于具有以下特征的方法:

  • 范围:任何 (public、private 等) 。
  • 键入:static
  • 参数:接受单个 Type 参数或 String 参数类型。
  • 返回类型: void

2020年11月29日

CA1416警告:平台兼容性问题

来源:https://docs.microsoft.com/zh-cn/dotnet/core/compatibility/code-analysis/5.0/ca1416-platform-compatibility-analyzer

.NET code analyzer rule CA1416 is enabled, by default, starting in .NET 5.0. It produces a build warning for calls to platform-specific APIs from call sites that don’t verify the operating system.

在.NET5上,开始默认启动CA1416警告。

你可以在IF语句中,使用 任意一个Is<Platform> 方法来判断:

public void PlayCMajor()
{
    if (OperatingSystem.IsWindows())
    {
        Console.Beep(261, 1000);
    }
}

或者你不喜欢在运行的时候使用一个IF语句来判断,你可以调用Debug.Assert(Boolean) 来代替:

public void PlayCMajor()
{
    Debug.Assert(OperatingSystem.IsWindows());
    Console.Beep(261, 1000);
}

如果你是一个库的作者,你可以给你的API添加特殊平台标记。在这种情况下,确认平台的任务就交给了调用者。你可以将其标记在一个方法或者一个整个程序集上。

[SupportedOSPlatform("windows")]
public void PlayCMajor()
{
    Console.Beep(261, 1000);
}

2020年12月2日

ITfInputProcessorProfileMgr interface (msctf.h)

The ITfInputProcessorProfileMgr interface is implemented by the TSF manager and used by an application or text service to manipulate the language profile of one or more text services.

不同于 ITfInputProcessorProfiles, ITfInputProcessorProfileMgr可以同时管理按键布局和文字服务程序集。在 Windows Vista下, it is recommended to use this interface instead of using the following methods:

  • ITfInputProcessorProfiles::Register
  • ITfInputProcessorProfiles::Unregister
  • ITfInputProcessorProfiles::AddLanguageProfile
  • ITfInputProcessorProfiles::RemoveLanguageProfile
  • ITfInputProcessorProfiles::EnumInputProcessorInfo
  • ITfInputProcessorProfiles::ActivateLanguageProfile
  • ITfInputProcessorProfiles::GetActiveLanguageProfile
  • ITfInputProcessorProfiles::EnumLanguageProfiles

ITfInputProcessorProfileMgr::RegisterProfile method (msctf.h)

The ITfInputProcessorProfileMgr::RegisterProfile method registers the text service and the profile.

Syntax

HRESULT RegisterProfile(
  REFCLSID    rclsid,
  LANGID      langid,
  REFGUID     guidProfile,
  const WCHAR *pchDesc,
  ULONG       cchDesc,
  const WCHAR *pchIconFile,
  ULONG       cchFile,
  ULONG       uIconIndex,
  HKL         hklsubstitute,
  DWORD       dwPreferredLayout,
  BOOL        bEnabledByDefault,
  DWORD       dwFlags
);

Parameters

rclsid

[in] CLSID of the text service.

langid

[in] The language id of the profile.

guidProfile

[in] The GUID to identify the profile.

pchDesc

[in, size_is(cchDesc)] The description of the profile.

cchDesc

[in] The length of pchDesc.

pchIconFile

[in, size_is(cchFile] The full path of the icon file.

cchFile

[in] The length of pchIconFile.

uIconIndex

[in] The icon index of the icon file for this profile.

hklsubstitute

[in] The substitute hkl of this profile.

dwPreferredLayout

[in] Unused. this must be 0.

bEnabledByDefault

[in] True if this profile is enabled by default.

dwFlags

[in] The combination of the following bits:

ValueMeaning
TF_RP_HIDDENINSETTINGUIThis profile will not appear in the setting UI.
TF_RP_LOCALPROCESSThis profile is available only on the local process.
TF_RP_LOCALTHREADThis profile is available only on the local thread.

其中有个HKL代替不太懂。上网找了一下:参考:https://www.geek-share.com/detail/2680786863.html

HKL:键盘布局,最初的含义就是单纯的键盘布局,能够将键盘的扫描码转换成设备无关的虚键码。现在HKL的含义更广泛,表示本地化标识符。

HKL名字:键盘布局编号,设备码和语言代码组成。前四位是设备码,后四位是语言码。注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts中列出了所有键盘布局。项名就是HKL名称。

“00000804” 表示 中文(简体) - 默认键盘
“E0220804” 表示 中文(简体) - 搜狗输入法
“E0230804” 表示 中文(简体) – 必应Bing输入法

思考:是不是能挂载在藏语下使用中文键盘?

2020年12月3日

https://docs.microsoft.com/en-us/globalization/windows-keyboard-layouts

Windows Keyboard Layouts

Choose a keyboard below to view its layouts. To see different keyboard states, move the mouse over state keys such as Shift, Caps or AltGr. You can also lock or unlock those keys by clicking them.

If you use a pop-up blocker, please update your allowable list to include this Web site. Use the browser magnification feature to increase the size of the keyboards.

http://systemmanager.ru/win2k_regestry.en/58553.htm

DosKeybCode

HKLM\SYSTEM\CurrentControlSet\Control\Keyboard Layout\DosKeybCodes

Description

Represents a keyboard layout.

These entries are named by 8-digit hexadecimal Windows 2000 keyboard layout codes. The first four digits of the codes indicate whether the [keyboard layout](javascript:startpopups(‘keyboardlayout’)) or [Input Method Editor](javascript:startpopups(‘inputmethodeditor’)) is the default layout for the language (indicated by all zeros) or a variation of the default (other than zeros). The last four digits are the standard [locale](javascript:startpopups(‘locale’)) ID. For example, the 0001040A entry represents variation 0x0001 of language 0x040A (Spanish.)

All entries have the following format:

Layout code number REG_SZ Two-character MS-DOS keyboard layout name

For example,

0001040A REG_SZ sp

This entry associates Windows 2000 keyboard layout 0x0001040A with MS-DOS keyboard layout sp.

Note ImageNote

The entry is a variable representing the entries under the HKLM\SYSTEM\CurrentControlSet\Control\DosKeybCodes subkey. It does not actually appear in the registry. This variable subkey displays the entries of the DosKeybCodes subkey.

0x00000409对应US键盘

ITfInputProcessorProfiles::SubstituteKeyboardLayout method (msctf.h)

Contains an HKL value that specifies the input locale identifier for the substitute keyboard. Obtain this value by calling LoadKeyboardLayout.

LoadKeyboardLayout

函数功能:该函数给系统中装入一种新的键盘布局,可以同时装入几种不同的键盘布局,任一时刻仅有一个进程是活动的,装入多个键盘布局使得在多种布局间快速切换。
  函数原型:HKL LoadKeyboardLayout(LPCTSTR pwszKLID,UINT Flags);
  参数:
  pwszKLID:缓冲区中的存放装入的键盘布局名称,名称是由语言标识符(低位字)和设备标识符(高位字)组成的十六进制值串,例如 U.S.英语对应的语言标识符为DX0409,则基本的U.S.英语键盘布局命名为“0000409”。U.S.英语键盘布局的变种(例如Dvorak布局)命名为“00010409”,“00020409”等。
  Flags:指定如何装入键盘布局,该参数可以是如下的值。
  KLF_ACTIVATE:若指定布局尚未装入,该函数为当前线程装入并激活它。
  KLF_NOTELLSHELL:当装入新的键盘布局时,禁止一个ShellProe过程接收一个HSHELL_LANGUAGE代码。
  当应用程序依次装入多个键盘布局时,对除最后一个键盘布局外的所有键盘布局使用该值,将会延迟Shell的处理直到所有的键盘布局均己被装入。
  KLF_RECOROER:将指定键盘布局移动到布局表的头部,使得对于当前线程,该布局的活动的。若不提供DLF_ACTIVATE值,则该值记录键盘布局表。
  KLF_REPLACE_LANG:Windows NT 4.0或Windows 95以上支持,若新布局与当前布局有同样的语言标识符,那么新布局替代当前布局作为那种语言的键盘布局,若未提供该值,而键盘布局又有同样的标识符,则当前布局不被替换,函数返回NULL值。
  KLF_SUBSTITUTE_OK:用用户喜欢的键盘布局来替换给定布局,系统初始时设置该标志,并且建议始终设置该标志,仅当在注册HKEY_CURRENT_USER/Keyboard Layout/Substitate下定义了一个替代布局时,才发生替换。例如,在名为00000409的部分中有一个多于00010409的值,则设置该标志装入U.S.英语键盘布局会导致Dvorak US.英语键盘布局的装入。系统引导时使用该参数,建议在所有应用程序装入键盘布局时使用该值,以确保用户喜欢的键盘布局被选取。
  KLF_SETFORPROCESS:Windows NT 5.0该位仅法与KLF_ACTIVATE一起使用时才有效,为整个进程激活指定键盘布局,且发送WM_INPUTLANGCHANGE消息以当前进程的所有线程。典型的LoadKeyboardLayWut仅为当前线程激活一个键盘布局。
  KLF_UNLOADPREVIOS:WindowsNT5.0,Windows95,Windows98都不支持,仅当与KLF_ACTIVATE一起使用时才有效,仅当装入且激活指定键盘布局成功,先前的布局才能被卸载,建议使用unLoadKeyboardLayout函数。
  返回值:若函数调用成功,返回与要求的名字匹配的键盘布局句柄。若没有匹配的布局,则返回NULL。
  备注:应用程序可以通过仅定义语言标识符的串来装入该语言的IME向缺省键盘布局。若应用程序想装入IME的指定键盘布局,就必须读注册信息以确定传递给LoadKeyboardLayout返回的键盘布局句柄来激活。
  Windows 95和Windows 98:若装载与原先键盘布局使用同种语言的布局,且KLF_REPLACELANG标志未被设置,则函数调用失败,仅有一个键盘布局可与给定语言相关联。(对于装载与同一语言相关的多IME也是可接受的)。
  速查:Windows NT:3.1及以上版本;Windows:95及以上版本;Windows CE:不支持;头文件:winuser.h;库文件:user32.lib;Unicode:在Windows NT上实现为Unicode和ANSI两种版本。

2020年12月4日

net5 failed to load the dll from [C:\Users\ZMK\Desktop\publish\coreclr.dll] HRESULT 0x80070057

解决方案:https://developercommunity.visualstudio.com/content/problem/805039/net-core-30-failed-to-load-dll.html

需要下载KB2533623 补丁。下载地址:http://www.3h3.com/soft/119473.html

Windows 7 / Vista / 8.1 / Server 2008 R2 / Server 2012 R2

如果您在以下系统安装.NET SDK或者运行环境的话,需要添加依赖::

  • Windows 7 SP1 ESU
  • Windows Vista SP 2
  • Windows 8.1
  • Windows Server 2008 R2
  • Windows Server 2012 R2

需要安装以下依赖:

2021年1月21日

查询只有7个的问题

原语句为var q = _list.AsParallel().WithCancellation(tokenSource.Token).Where(item => item.IsBeginWith(syllables)).OrderByDescending(item => item.Syllables.Length).ThenByDescending(item => item.Frequency).Take(num).Skip(skip);
linq的Take和Skip存在先后顺序。如果这样子查询,则第二页的数目就不正确。
Skip必须要在Take前面。
修改完以后为var q = _list.AsParallel().WithCancellation(tokenSource.Token).Where(item => item.IsBeginWith(syllables)).OrderByDescending(item => item.Syllables.Length).ThenByDescending(item => item.Frequency).Skip(skip).Take(num);
翻页后就正确了。

候选词采用带权重的函数

p(x)为候选词x的优先度。候选词列表按照从主序为匹配长度从高到低,次序为优先度从高到低排序。
p ( x ) = k ( x ) ∗ f ( x ) p(x) = k(x) * f(x) p(x)=k(x)f(x)
其中: f ( x ) f(x) f(x)代表使用频率, k ( x ) = { 17 i n p u t = x 2 x 为 用 户 自 己 造 词 1 其 他 情 况 k(x)=\begin{cases} 17 & input=x\\2 & x为用户自己造词\\ 1 & 其他情况\end{cases} k(x)=1721input=xx

2021年1月28日

句柄查看工具

  • LookHandles是一款非常实用的句柄查看软件,可以帮助用户对任何程序句柄进行捕捉,还可以清晰地显示窗口信息,根据相应的逻辑获取句柄信息。

  • Handle(句柄小助手)是一款功能强大的句柄小助手,常常被程序员用于获取打开窗口的信息毛病进行整理加工。

使用WND给WPF设置父窗口:

这个试了一下,也是没用的。最后只能放弃C#的WPF,转用C++的MFC。

.NET/C# 使窗口永不激活(No Activate 永不获得焦点) - 云+社区 - 腾讯云 https://cloud.tencent.com/developer/article/1341125

这个发现,必须要保证handle已经创建。

var interop = new WindowInteropHelper(this);
interop.EnsureHandle();
var handle = interop.Handle;

2021年2月3日

查看DLL是X86还是X64

使用VS开发环境

dumpbin.exe /headers D:\CraneSimulation\x64\Debug\UaClientApi.dll

Windows下SysWow64和System32

参考:https://blog.csdn.net/oncealong/article/details/50477997

Windows作为一个操作系统,自然希望用户在运行64位操作系统时,也能像以前一样,运行各种32位应用程序。Wow64指的是64位Windows上的32位Windows,SysWow64里放的是32位的系统文件。

输入法有时候加载不出来的问题解决(半年了终于找到问题所在)

默认运行在X86上,X64的程序调用不了。

注意生成目标改为Any CPU,记住别勾选上首选32位,不然会运行在x86环境下。

2021年2月12日

试了一下在新建的线程上创建WPF窗口,提示错误,必须要在STA线程上创建。这里只能启动一个新进程。

调用线程必须为 STA,因为许多 UI 组件都需要 - 金虹巴巴 - 博客园 https://www.cnblogs.com/furenjian/articles/3224493.html

SerializationException无法找到程序集

System.Runtime.Serialization.SerializationException:“无法找到程序集“Global, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”。”

新建一个类UBinder

public class UBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            // 从E:\TibetGlobal.dll读取
            var path = Path.Combine("E:\\", "TibetGlobal.dll");
            var assembly = Assembly.LoadFrom(globalPath);
            return assembly.GetType(typeName);
            }
        }
    }

使用这个类

var formatter = new BinaryFormatter
{
    Binder = new UBinder()
};
var obj = formatter.Deserialize(pipeServer);

2021年2月15日

C# file not found in System32 when running in 32 bit - Stack Overflow
https://stackoverflow.com/questions/30533924/c-sharp-file-not-found-in-system32-when-running-in-32-bit

2021年2月16日

发现在x86和x64环境下注册输入法的环境不一样。要在x86和x64环境下都注册一下。而x86和x64下,C#生成的DLL均为Any CPU,均可以通用。

注册的内容为

通过RegAsm.exe /regfile TibetCore.dll查看。

2021年2月18日

Inno Setup 检测已安装的.NET Framework 版本

;.NET Framework 安装文件
#define DotNetSetupFile "ndp48-web.exe"
; 上述消息放置到脚本的顶部

[Code]
const CDotNetSetupFile = '{#DotNetSetupFile}';
var VNeedRestart: boolean;
function IsDotNetDetected(version: string; service: cardinal): boolean;
// Indicates whether the specified version and service pack of the .NET Framework is installed.
//
// version -- Specify one of these strings for the required .NET Framework version:
//    'v1.1'          .NET Framework 1.1
//    'v2.0'          .NET Framework 2.0
//    'v3.0'          .NET Framework 3.0
//    'v3.5'          .NET Framework 3.5
//    'v4\Client'     .NET Framework 4.0 Client Profile
//    'v4\Full'       .NET Framework 4.0 Full Installation
//    'v4.5'          .NET Framework 4.5
//    'v4.5.1'        .NET Framework 4.5.1
//    'v4.5.2'        .NET Framework 4.5.2
//    'v4.6'          .NET Framework 4.6
//    'v4.6.1'        .NET Framework 4.6.1
//    'v4.6.2'        .NET Framework 4.6.2
//    'v4.7'          .NET Framework 4.7
//    'v4.7.1'        .NET Framework 4.7.1
//    'v4.7.2'        .NET Framework 4.7.2
//    'v4.8'          .NET Framework 4.8
//
// service -- Specify any non-negative integer for the required service pack level:
//    0               No service packs required
//    1, 2, etc.      Service pack 1, 2, etc. required
var
    key, versionKey: string;
    install, release, serviceCount, versionRelease: cardinal;
    success: boolean;
begin
    versionKey := version;
    versionRelease := 0;

    // .NET 1.1 and 2.0 embed release number in version key
    if version = 'v1.1' then begin
        versionKey := 'v1.1.4322';
    end
    else if version = 'v2.0' then begin
        versionKey := 'v2.0.50727';
    end

    // .NET 4.5 and newer install as update to .NET 4.0 Full
    else if Pos('v4.', version) = 1 then begin
        versionKey := 'v4\Full';
        case version of
        // from url https://docs.microsoft.com/zh-cn/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed
          'v4.5':   versionRelease := 378389;
          'v4.5.1': versionRelease := 378675; // 378758 on Windows 8 and older
          'v4.5.2': versionRelease := 379893;
          'v4.6':   versionRelease := 393295; // 393297 on Windows 8.1 and older
          'v4.6.1': versionRelease := 394254; // 394271 on Windows 8.1 and older
          'v4.6.2': versionRelease := 394802; // 394806 on Windows 8.1 and older
          'v4.7':   versionRelease := 460798; // 460805 On all other Windows operating systems (including other Windows 10 operating systems)
          'v4.7.1': versionRelease := 461308; // 461310 On all other Windows operating systems (including other Windows 10 operating systems)
          'v4.7.2': versionRelease := 461808; // 461814 On all Windows operating systems other than Windows 10 April 2018 Update and Windows Server, version 1803
          'v4.8'  : versionRelease := 528040; // On Windows 10 May 2019 Update and Windows 10 November 2019 Update: 528040; On Windows 10 May 2020 Update and Windows 10 October 2020 Update: 528372; On all other Windows operating systems (including other Windows 10 operating systems): 528049
        end;
    end;

    // installation key group for all .NET versions
    key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + versionKey;

    // .NET 3.0 uses value InstallSuccess in subkey Setup
    if Pos('v3.0', version) = 1 then begin
        success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
    end else begin
        success := RegQueryDWordValue(HKLM, key, 'Install', install);
    end;

    // .NET 4.0 and newer use value Servicing instead of SP
    if Pos('v4', version) = 1 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
    end else begin
        success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
    end;

    // .NET 4.5 and newer use additional value Release
    if versionRelease > 0 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
        success := success and (release >= versionRelease);
    end;

    result := success and (install = 1) and (serviceCount >= service);
end;

function InitializeSetup(): Boolean;
var IEPath, NetV2DownUrl, DotNetPath:string;
var ResultCode:Integer;
begin
    VNeedRestart := false;

    if not IsDotNetDetected('v4.8', 0) then begin
      
      if MsgBox('系统缺少程序运行组件.Net Framework 4.8,是否立刻下载并安装?', mbConfirmation, MB_YESNO) = idYes then begin
          // 临时解压安装文件
          ExtractTemporaryFile(CDotNetSetupFile);
          DotNetPath := ExpandConstant('{tmp}\'+CDotNetSetupFile);
          // 运行
          if Exec(DotNetPath,'/norestart /passive /showfinalerror','',SW_SHOWNORMAL,ewWaitUntilTerminated,ResultCode) then begin
              if ResultCode = 0 then begin
                  result := true;
              end else if (ResultCode = 1641) or (ResultCode = 3010) then begin
                  // 需要重新启动才能完成安装。 此消息指示安装成功。
                  VNeedRestart := true;
                  result := true;
              end else begin
                  result := false;
              end
          end else begin
              MsgBox('运行.Net Framework 4.8安装程序失败!',mbError,MB_OK);
              result := false;
          end
      end
    end else begin
        result := true;
    end
end;

function NeedRestart(): Boolean;
begin
  result := VNeedRestart;
end;

安装程序可以从微软官方下载:
https://dotnet.microsoft.com/download/dotnet-framework

参考:

确定已安装的 .NET Framework 版本 | Microsoft Docs
https://docs.microsoft.com/zh-cn/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed

Inno Setup 判断.NET是否安装_tcmtang的博客-CSDN博客 https://blog.csdn.net/tcmtang/article/details/49932205

Inno Setup 检测已安装的.NET Framework 版本 - 知乎
https://zhuanlan.zhihu.com/p/51658788

2021年2月20日

Inno Setup覆盖安装几次后,UninstallRun就会执行几次问题。

  1. 使用[RunOnceId]:

仅在 [UninstallRun] 段有效。如果已经安装了相同的应用程序,卸载日志文件中的“run”条目将被复制一个副本。通过分配一个字符给 RunOnceId,可以确保在卸载期间特殊的 [UninstallRun] 条目只执行一次。例如,如果卸载日志中有两个或更多“run”条目用“DelService”的 RunOnceId 设置,只执行最后一个用“DelService”的 RunOnceId 设置的条目;其它的将被忽略。注意 RunOnceId 比校是区分大小写的。

  1. 安装前删除旧版本。

参考:

installation - Inno Setup: How to automatically uninstall previous installed version? - Stack Overflow https://stackoverflow.com/questions/2000296/inno-setup-how-to-automatically-uninstall-previous-installed-version#

2021年2月22日

.NET5 创建Windows Service

Creating a Windows Service with C#/.NET5 | #ifdef Windows
https://devblogs.microsoft.com/ifdef-windows/creating-a-windows-service-with-c-net5/

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
Android 输入法开发是指为Android系统开发一款输入法应用程序的过程。输入法是一种用于输入文字的工具,通过输入法,用户可以在Android设备上输入各种语言的文字。 Android 输入法开发需要掌握一定的编程知识和技巧。首先,开发者需要了解Android系统的架构和输入法的工作原理。其次,需要使用Java等编程语言来编写输入法的代码。开发者还需要使用Android Studio开发工具来调试和测试输入法的功能。 在Android 输入法开发过程中,需要考虑以下几个关键点。首先,输入法需要正确地处理用户输入的字符,并将其显示在屏幕上。其次,输入法需要提供联想和自动完成的功能,以帮助用户更快地输入文字。此外,输入法还需要支持多种输入方式,例如手写输入、语音输入等。 同时,开发者还应该关注输入法的用户体验。输入法应该具有良好的界面设计和交互方式,方便用户使用。此外,输入法还应该具备一定的智能化功能,例如根据用户的输入习惯进行个性化设置,提供更准确的输入建议等。 最后,输入法开发完成后,开发者还需要将其发布到应用商店供用户下载和使用。在发布前,需要对输入法进行充分的测试和迭代,确保其稳定性和功能完善性。 总的来说,Android 输入法开发是一个复杂而有挑战的过程,但随着技术的不断发展和改进,越来越多的开发者参与到输入法开发中,推动了输入法的创新和提升。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值