aardio 文档

winex.ctrl.listview 外部列表视图

通过窗口消息与远程内存访问外部进程(兼容 64/32 位)的标准 SysListView32 列表视图控件。对象以 win.ui.ctrl.listview 为基础,重新实现了需要跨进程传递结构体、文本缓冲区的常用操作。

winex.desktop 就使用了这个库:winex.desktop.listview 是绑定到 Windows 桌面图标列表的 winex.ctrl.listview 对象。

快速开始:绑定外部列表

构造参数是列表控件自身的窗口句柄,不是主窗口句柄、进程 ID 或 TreeView 的节点句柄。

import winex;
import winex.ctrl.listview;
import console;

// 按实际情况替换窗口标题。
// findExists 返回值 1 是父窗口句柄,返回值 2 才是匹配的控件句柄。
var hwndParent,hwndList = winex.findExists("目标窗口标题",,,"SysListView32");
if(!hwndList){
    console.log("未找到目标列表控件");
    return;
}

var lv = winex.ctrl.listview(hwndList);
console.log("项目数:",lv.count);

for row,text in lv.each(){
    console.log(row,text);
}

lv.process.free(); // 只释放本次绑定持有的进程资源,不关闭外部窗口。

如果已经取得控件句柄,可以直接调用 winex.ctrl.listview(hwndList)。 窗口重新创建后句柄可能改变,需要重新查找、绑定。 调用方需要有权打开目标进程并读写其内存;目标以管理员身份运行时,通常也需要相应权限。

行列索引与文本读取

常用读写接口的行号、列号从 1 开始。

接口 说明
count 项目总数
columnCount 报表视图的列数,通过列表头获取;图标视图不一定有可用的列表头
getItemText(row,col=1,len=100) 读取单个单元格的 Unicode 文本
each(i=0,flags,col) 从指定项目之后开始遍历,依次返回行号、指定列文本;默认遍历全部项目、读取第一列
getItemRect(row,col,rc,code=0) 获取项目或子项矩形;省略 col 取整项范围,省略 rc 自动创建矩形

getItemText 的 len 是包含末尾终结符空间的 UTF-16 单元数,不是 UTF-8 字节数。 默认值为 100,长文本应显式增大;当前实现不会自动扩容。 空文本返回 "",不能仅据此判断项目是否存在。

外部绑定的 getItemText 每次只读取一列,不支持用 col=-1 读取整行。 读取报表视图的全部列时,请逐列调用:

// lv 为已经绑定的 winex.ctrl.listview 对象。
var readRow = function(lv,row){
    var cells = [];
    var columns = lv.columnCount;
    for(col=1;columns){
        table.push(cells,lv.getItemText(row,col,260));
    }
    return cells;
};

矩形坐标相对于列表控件客户区,单位是运行时的设备像素。 可使用 win.toScreen(lv.hwnd,x,y) 转为屏幕坐标,不要直接当作屏幕坐标点击。

遍历、查找与选中

查找起点的例外:当前 findItem 将 start 原样传给 Win32。 start=-1 表示从首项开始;指定其他值时,它是 0 起始、排除自身的原生项目索引,不要与返回的 1 起始行号混用。

// 只定义操作函数;调用时传入自己允许修改选择状态的列表对象。
var selectByText = function(lv,text){
    var row = lv.findItem(text,-1,false,false); // 精确匹配,从首项开始,不回绕。
    if(!row) return 0;

    lv.setSelected(row);   // 设置选中状态,不保证清除其他行的选中状态。
    lv.ensureVisible(row); // 滚动到该项。
    return row;
};

setSelected(row,false) 取消指定行的选中状态;selIndex=row 设置内部焦点及选中状态。 这些操作会影响目标程序的界面,并可能触发目标程序自己的事件处理。

修改列表

仅在明确允许修改的目标上使用这些接口,建议先在自己创建的测试列表中验证。

接口 说明
setItemText(text,row,col=1) 修改指定单元格;显式传入有效行号
setItemText(["第一列","第二列"],row) 从第一列开始批量写入该行,数组写法不使用第三个列号参数
addItem(text,ind,...) 插入项目,可传多列文本数组;省略位置追加,返回新增行号
delItem(row) / clear() 删除指定项 / 全部项目
setItemState(row,state=0,mask) 按掩码设置状态;省略掩码使用 state,清除状态位时应显式指定掩码
getItemState(row,mask=0) 按掩码读取状态,读取特定位时应显式传入掩码
getChecked(row) / setChecked(row,value=true) 读取 / 修改复选框状态,仅适用于本来就支持复选框的列表
setItemPos(row,x,y) 设置图标项在客户区的位置;自动排列或对齐到网格可能影响结果

插入、删除、重命名以及修改样式可能破坏目标程序自身的数据模型。 尤其不要把桌面当作普通测试列表执行 clear()、delItem() 或批量改名。

winex.desktop:直接访问桌面图标

winex.desktop 会查找桌面的 SHELLDLL_DefView,再取得其中的 SysListView32 控件,使用 winex.ctrl.listview 进行绑定。 因此下面的 lv 不是另一套桌面专用列表 API,而是可直接使用上述接口的列表对象。

import winex.desktop;
import console;

var lv = winex.desktop.listview;
console.log("桌面图标数:",lv.count);

// 只读枚举,不移动图标,也不修改选择或排列方式。
for(row=1;lv.count){
    var text = lv.getItemText(row,1,260);
    var rc = lv.getItemRect(row);
    console.log(row,text,rc.left,rc.top,rc.right,rc.bottom);
}

桌面图标通常按图标视图显示,读取显示名称用第一列即可,不要依赖 columnCount 枚举桌面图标。 如果确实需要调整图标位置,可使用 lv.setItemPos(row,x,y);需先考虑桌面的自动排列、对齐到网格设置,并自行保存原位置以便恢复。

winex.desktop.listview 是库级共享对象,不要随手调用其 process.free(),以免影响后续桌面操作。 Explorer 重启后,旧的控件句柄可能失效,需要重新获取桌面控件。 创建、查找或管理桌面快捷方式文件请使用 fsys.lnk,而不是这个窗口控件绑定。

跨进程边界与资源释放

Markdown 格式