# 窗口相关

# 创建自定义窗口

如果你的插件需要用到窗口界面,易语言助手目前支持两种方式创建窗口界面
一、易语言核心库与扩展界面库
实例:

优点:
可以在开发插件的时候直接调用易语言核心库和扩展界面库的命令创建界面,不必再创建界面上消耗太多时间。
缺点:
由于是插件注入形式,部分控件事件与控件消息将被易语言IDE给接管,导致被接管的事件无法响应或发送对应消息无效果。
相关插件:
音效插件、易文件关联修复插件、组件箱扩展插件等

二、黑月界面编译器自带的窗口设计器与黑月界面类模块
实例:

优点:
可以不受易语言IDE事件与消息的接管,风格属性完全可控,配合黑月编译插件体积也小,执行效率也高。
缺点:
创建界面需要消耗大量时间,窗口属性事件调用也相对复杂,缺少扩展界面库部分组件的创建。
相关插件:
提示输出扩展插件、整体搜索替换插件、新建增强插件、工具箱插件等

结尾:
如果你对插件执行效率或实际用到的事件与消息不多的话,建议更容易上手的核心库与扩展库。如果你对插件执行效率,事件、消息、属性等可控要求高的话,推荐使用黑月界面类模块创建界面,黑月编译器自带的界面设计相对黑月界面类模块也比较麻烦。

# 获取窗口与控件的窗口改变大小

易语言助手插件是注入到易语言IDE的,为了实时获取易语言IDE窗口或控件的改变,我们需要子类化易语言IDE窗口或控件,获取窗口所有消息达到监控的目的。

一、需要的工具
1、Microsoft Spy++
2、精易编程助手
3、Google Chrome浏览器

4、https://docs.microsoft.com/zh-cn/windows/win32/winmsg/windowing

二、工具的使用
1、按住Shift启动易语言
2、打开 Microsoft Spy++
3、点击“查找窗口”


4、拖动探测图标到易语言标题栏位置放开,在点击确定

5、右键树型框中易语言项目

6、点击顶部菜单栏“记录选项”

7、点击“消息”子夹,全部清除,只勾选“常规”

8、拖动易语言窗口改变大小或位置,Messages窗口就会列出刚才易语言窗口消息,找到 S WM_SIZE 这条消息,右键属性

9、这样就能查看到发送的具体消息值与参数值

10、Messages窗口能预览到参数值具体代表的数值,nWidth:1424 nHeight:786
通过精易编程助手再修改窗口大小,会发现实际窗口消息得到的数值和实际设置的有偏差,这是由于需要减去标题栏与边框得到实际
需要修改的用户区域大小导致的。

11、其他的消息用处可以用谷歌浏览器搜索相关常量名,右键翻译得到实际每个参数值作用是什么

三、子类化操作

SetWindowLong:
此函数更改指定窗口的属性。
参数一:子类化的窗口句柄
参数二:指定要设置的值的从零开始的偏移量。 子类化为 #GWL_WNDPROC -4
参数三:回调函数的指针

回调函数:
与SendMessage的参数一致,判断uMsg的消息再获取wParam与lParam值来做更多操作。返回值为整数型

CallWindowProcA:
将消息信息传送给指定的窗口过程的函数。
参数一:通过 SetWindowLong 返回的值;
参数二:子类化的窗口句柄
参数三:窗口消息
参数四:其他特定于消息的信息。此参数的内容取决于Msg参数的值。
参数五:其他特定于消息的信息。此参数的内容取决于Msg参数的值。

四、更多说明
1、被子类化窗口如果是固定窗口(如:易语言IDE主窗口、状态夹、工作夹等),在停用插件的时候务必还原子类化,否则会再加载插件或操作易语言会导致崩溃;
2、被子类化窗口即时销毁的则不需要还原(如何:搜索替换,搜索,新建等对话框);
3、在使用 spy++ 的时候,不再探测窗口消息的情况下务必关闭这个工具,否则桌面会略卡;
4、探测易语言窗口消息时务必时按住Shift启动的易语言窗口,这样避免由于其他插件或支持库的干扰;
5、探测控件消息可以在 spy++ 消息选项里选中对应的控件来获取相应的消息;
6、通过 spy++ 探测到的消息常量名可以通过微软官方的MSDN查询对应的值或通过 易语言助手的词库 查询;
7、拦截到的都是文本指针,取出文本数据需调用 指针到文本 ;

# 获取易语言IDE对话框窗口ID与菜单ID

如果是我们想知道易语言IDE的当前创建的对话框窗口是哪个,或者想知道当前易语言IDE窗口点击可哪个菜单,这时候我们就需要使用第三方工具与监控易语言IDE主窗口的消息获得

一、需要的工具
1、PE-Explorer 或 ResHacker
2、易语言主程序 e.exe

二、工具的使用
1、打开 PE-Explorer 后将易语言主程序e.exe拖入;
2、点击 资源查看

3、点击 对话框

4、点击子项目即可预览对应的对话框(资源)窗口,这里以导入新长文本常量的对话框为例。
592 就是这个对话框(资源)窗口的ID

5、点击 菜单

6、点击子项即可预览易语言IDE顶部与右键菜单,这里以易语言顶部菜单“编辑 - 复制”为例,顶部菜单ID为127,复制菜单的ID是57634

三、监听代码
1、监听易语言IDE对话框(资源窗口)的创建动作 调用接口 接口_FindResourceEx管理

3、监听易语言IDE菜单被点击的动作,调用接口 接口_添加主窗口消息调用前处理

四、更多说明
1、千万千万千万不要用资源查看工具修改易语言主程序的资源,否则易语言主程序将无法打开;
2、阻止易语言IDE的对话框(资源)查看创建后就可以弹出我们自己的窗口;
3、如果菜单不是易语言IDE的也可以通过上面的代码直接输出 wParam 的值来获取,例如获取:黑月编译的菜单ID;
4、知道菜单ID后我们就可以直接条用 ET.点击菜单(菜单ID)来执行对应的菜单ID功能;
5、在给易语言IDE添加菜单时切记 菜单标识不要重复,不仅要和易语言IDE原有的不能重复,插件与插件之间的菜单ID也不能重复;
6、阻止对话框(资源)窗口后切记不要没有其他操作,否则会让用户造成易语言IDE出问题的错觉;

# 屏蔽信息框

有时候需要点击一下易语言IDE弹出的信息框才能操作下一步,如果不想点击,就需要调用易语言助手接口直接屏蔽(自动点击)弹出的信息框

接口:
接口_获取MessageBox管理,管理易语言IDE窗口弹出的信息框
回调函数:

  
.版本 2  
  
 .子程序 回调_MessageBox管理  
 .参数 hWnd, 整数型, , 此参数代表消息框拥有的窗口。  
 .参数 lpText, 整数型, , 消息框的内容。  
 .参数 lpCaption, 整数型, , 消息框的标题。  
 .参数 uType, 整数型, , 指定一个决定对话框的内容和行为的位标志集。  
  
  

实例:
以拦截提示是否创建子程序的信息框为例

通过获取到信息框的内容,判断内容是否符合是否创建子程序的信息框,是就直接点击 确定
其他信息框的拦截也是相同操作即可拦截,返回值参考常量:#ID 开头的 6个常量值

# 添加自定义菜单

除了在易语言IDE中添加子夹或者创建控件置父来显示我们需要操作的组件,还可以在易语言IDE菜单中插入或添加我们的菜单。

一、添加顶部菜单
顶部菜单用到窗口消息
#WM_INITMENU = 278
菜单即将变为活动状态时发送。当用户单击菜单栏上的项目或按下菜单键时,会发生这种情况。这允许应用程序在显示菜单之前修改它。


二、添加子菜单
顶部菜单的子菜单用到窗口消息
#WM_COMMAND = 273
当用户从菜单中选择命令项,控件向其父窗口发送通知消息或转换加速键击时发送。


三、添加右键菜单
右键菜单用到了窗口消息
#WM_COMMAND = 273
当用户从菜单中选择命令项,控件向其父窗口发送通知消息或转换加速键击时发送。

# 状态夹添加子夹与自定义窗口

如果需要扩展功能,易语言IDE状态夹是个不错的位置,添加一个子夹就可以显示自己的组件并操作了。

接口:
接口_状态夹添加子夹

实例:

回调函数:
5个事件的回调函数一致

  
.版本 2  
  
 .子程序 UI_显示  
 .参数 参选择夹句柄, 整数型  
 .参数 子夹标识, 整数型  
 .参数 窗口句柄, 整数型  
  
  

说明:
创建一个黑月窗口,或者载入一个易语言核心库的组件;
添加子夹的时候传入我们的窗口句柄,设置好名称和图标,再一顿操作就可以得到一个“工具箱插件”;
注意:停用插件的时候一直要移除子夹并销毁释放掉我们的窗口,否则在为释放窗口资源的情况下再加载插件易语言就会出异常;
特别说明:
工作夹添加子夹的步骤和状态夹添加子夹步骤一致,所以发挥你们想象自由扩展更多功能吧!

# 代码视图加入自定义窗口

代码视图插件提供了扩展接口,可以在代码视图的选择夹中加入自己的子夹来实现一些功能

接口:
接口_IDE代码窗口工作夹管理
由代码视图插件提供接口,调用此接口必须开启 代码视图插件 ,否则调用无效!
实例:

  
.版本 2  
  
 .程序集 入口程序集  
 .程序集变量 集_视图, 接口_IDE代码窗口工作夹管理  
 .程序集变量 计数, 整数型  
 .程序集变量 集_子夹标识, 整数型  
 .程序集变量 集_窗口句柄, 整数型  
  
 .子程序 _启动子程序, 整数型  
  
 ET.添加事件处理 (#事件标识_插件启用, &事件_插件启用)  
 ET.添加事件处理 (#事件标识_插件停用, &事件_插件停用)  
 ET.添加事件处理 (#事件标识_插件配置被更改, &事件_插件配置被更改)  
 返回 (0)  
  
 .子程序 插件信息, 整数型, 公开  
 .局部变量 依赖, 依赖插件, , "0"  
  
 加入成员 (依赖, 依赖插件 (“CodeView”, #依赖类型_强制))  
 返回 (生成插件信息 (“插件名称”, “插件备注”, “1.0”, “插件作者”, , 依赖))  
  
 .子程序 事件_插件停用  
  
 主窗口.销毁 ()  
 集_视图.删除子夹处理 (集_子夹标识)  
  
 .子程序 事件_插件启用  
 .局部变量 局_事件数组, 事件, , "5"  
  
 载入 (主窗口, , 假)  
 集_窗口句柄 = 主窗口.取窗口句柄 ()  
 窗口_置父 (集_窗口句柄, ET.获取主窗口句柄 ())  
  
 集_视图 = 接口_获取IDE代码窗口工作夹管理 ()  
 局_事件数组 [1] = 事件 (#IDE代码窗口工作夹事件_子夹被创建, &UI_创建)  
 局_事件数组 [2] = 事件 (#IDE代码窗口工作夹事件_子夹被显示, &UI_显示)  
 局_事件数组 [3] = 事件 (#IDE代码窗口工作夹事件_子夹被隐藏, &UI_隐藏)  
 局_事件数组 [4] = 事件 (#IDE代码窗口工作夹事件_子夹尺寸被改变, &UI_尺寸被改变)  
 局_事件数组 [5] = 事件 (#IDE代码窗口工作夹事件_子夹被销毁, &UI_销毁)  
 集_子夹标识 = 集_视图.添加子夹处理 (“我是子夹”, , , { #代码类型_窗口 }, 局_事件数组)  
  
 .子程序 UI_创建  
 .参数 参_子夹标识, 整数型  
 .参数 参_子夹类型, 整数型  
 .参数 参_子夹句柄, 整数型  
  
 窗口_置父 (集_窗口句柄, 参_子夹句柄)  
 窗口_显示隐藏 (集_窗口句柄, 真)  
 原子递增变量 (计数)  
  
 .子程序 UI_显示  
 .参数 参_子夹标识, 整数型  
 .参数 参_子夹类型, 整数型  
 .参数 参_子夹句柄, 整数型  
  
 窗口_置父 (集_窗口句柄, 参_子夹句柄)  
 窗口_显示隐藏 (集_窗口句柄, 真)  
  
 .子程序 UI_隐藏  
 .参数 参_子夹标识, 整数型  
 .参数 参_子夹类型, 整数型  
 .参数 参_子夹句柄, 整数型  
  
 窗口_显示隐藏 (集_窗口句柄, 假)  
  
 .子程序 UI_尺寸被改变  
 .参数 参_子夹标识, 整数型  
 .参数 参_子夹类型, 整数型  
 .参数 参_子夹句柄, 整数型  
 .局部变量 局_尺寸, 精易_位置和大小  
  
 局_尺寸 = 窗口_取位置和大小 (参_子夹句柄)  
 窗口_置位置和大小 (集_窗口句柄, 0, 0, 局_尺寸.宽度, 局_尺寸.高度)  
  
 .子程序 UI_销毁  
 .参数 参_子夹标识, 整数型  
 .参数 参_子夹类型, 整数型  
 .参数 参_子夹句柄, 整数型  
  
 原子递减变量 (计数)  
 .如果真 (计数 = 0)  
 窗口_显示隐藏 (集_窗口句柄, 假)  
 窗口_置父 (集_窗口句柄, ET.获取主窗口句柄 ())  
 .如果真结束  
  
  
  

回调函数:
其他事件回调函数参数一致

  
.版本 2  
  
 .子程序 UI_创建  
 .参数 参_子夹标识, 整数型  
 .参数 参_子夹类型, 整数型  
 .参数 参_子夹句柄, 整数型  
  
  

说明:
1、本接口为 代码视图插件 提供,如果没有加载此插件,则无法调用此接口;
2、添加的子夹需自行通过 接口_IDE代码窗口管理 来判断是否需要在说明类型的代码窗口显示自己的窗口;
3、停用插件时务必销毁释放窗口资源;
4、如果先停用 代码视图插件 则调用此接口的插件功能也会失效;
5、建议使用黑月界面类模块创建窗口并置父,这样可以无视易语言IDE接管消息的问题;

# 隐藏显示窗口

问:想后台模拟操作窗口又不想窗口显示怎么办?
答:调用易语言助手接口执行隐藏操作。

接口:
接口_ShowWindow管理

实例:
1、修改返回值,达到隐藏效果

2、调用异步命令达到隐藏效果

回调函数:

  
.版本 2  
  
 .子程序 回调_ShowWindow  
 .参数 hwnd, 整数型, , 指窗口句柄  
 .参数 nCmdShow, 整数型, , 指定窗口如何显示。  
  
 ' nCmdShow 常用常量值 SW_SHOWNORMAL = 1 与 SW_HIDE = 0  
  
  

说明:
1、隐藏窗口需要看是否是对话框窗口,如果是对话框窗口隐藏之后易语言主窗口是无法操作的,这里需要调用 窗口_锁住解锁 解锁主窗口;
2、隐藏窗口前可以判断当前窗口状态,实例中是不管什么状态都执行隐藏;
3、通过取窗口标题或者类名可以判断当前易语言IDE显示或隐藏的是什么窗口;

# 拦截键盘消息统计键入信息

有了监听易语言IDE键盘消息的接口了,我们是不是可以做一个类似于输入法的字数统计插件呢?

输入法是以实际输入的字符来判断的,这里由于易语言代码的特殊性,暂时没法获取键入的代码字符数是多少,但是我们可以通过监听键盘来实现一个统计敲了多少次键盘的插件。

一、接口:

  
ET.添加事件处理 (#事件标识_编译完成, &事件_编译完成)  
  
  
接口_添加键盘消息处理ex (-1, -1, -1, &事件_键盘消息)  
  

二、回调函数:

  
  
.版本 2  
  
 .子程序 事件_键盘消息  
 .参数 参_键代码, 整数型  
 .参数 参_键状态, 整数型, , 0=按下 1=弹起  
 .参数 参_功能键, 整数型  
 .版本 2  
 .子程序 事件_编译完成  
 .参数 当前源码类型, 整数型  
 .参数 当前编译的文件路径, 整数型, , 文件绝对路径  
  
  
  

三、统计代码

四、实际效果

发挥大家的想法,自(fu)由(zhi)扩(zhan)展(tie)吧~

# 添加或隐藏自定义组件

开发插件的时候,为了便于使用,会隐藏易语言IDE某个控件,再将我们自己插件组件放到易语言IDE某个控件的某个位置上去。以下步骤跟着操作一遍
一、准备工作:
1、必要接口:

  
.版本 2  
  
 .程序集变量 集_ShowWindow管理, 接口_ShowWindow管理  
  
 集_ShowWindow管理 = 接口_获取ShowWindow管理 ()  
 集_ShowWindow管理.添加调用前处理 (&回调_ShowWindow管理)  
  
 .子程序 回调_ShowWindow管理  
 .参数 hWnd, 整数型  
 .参数 nCmdShow, 整数型  
  
  

2、相关资料:
WM_NOTIFY
https://docs.microsoft.com/en-us/previous-versions/aa923584%28v%3dmsdn.10%29
NMHDR
https://docs.microsoft.com/en-us/previous-versions/aa921258%28v%3dmsdn.10%29

自定义数据结构

  
.版本 2  
  
 .数据类型 tagNMHDR  
 .成员 hwndFrom, 整数型, , , 0 来自窗口句柄  
 .成员 idFrom, 整数型, , , 1 控件ID  
 .成员 code, 整数型, , , 2 消息  
  
  

常量

  
.版本 2  
  
 .常量 TCN_SELCHANGE, "-551"  
 .常量 SW_SHOWNORMAL, "1"  
  
  

二、开始操作:














通过以上操作,我们已经实现了隐藏易语言IDE系统配置窗口显示子夹部分控件隐藏之后替换成自定义控件的功能。
其他控件的替换之后接管并扩展功能的实例:
搜索增强插件、提示输出扩展插件、书签插件
难点:
窗口消息的获取,需要耐心分析,当我们需要截获一个消息之前可以清空前面的消息或记下操作前消息记录的序号,那么下一次操作的消息就有可能是我们需要的消息了。