# winex.ctrl.treeview 外部树形控件

通过窗口消息与远程内存访问其他进程的标准 `SysTreeView32` 控件。
使用方式与 `win.ui.ctrl.treeview` 相近，支持导航、遍历、查找、展开折叠、Unicode 文本读写及节点增删。

## 快速开始：访问已经打开的 oleview

```aardio
import winex.ctrl.treeview;
import console;

var tv,err = winex.ctrl.treeview(0x511500);
if(!tv){
    console.error(err);
    return;
}

console.log("节点总数：",tv.count);
for hItem in tv.each(){
    console.log(hItem,tv.getItemText(hItem));
}

console.log("当前选中节点：",tv.getItemText());
console.log("当前节点路径：",tv.getItemPath());
tv.free();
```

窗口句柄会在窗口重新创建后变化，`0x511500` 仅为本次会话中的示例。
构造失败返回 `null,err`。如果目标以管理员身份运行，调用方也需要相应权限。

## 遍历与查找

- `each(parent)`：只遍历直接子节点，省略 `parent` 表示根层。
- `enum(callback,parent)`：深度遍历已经存在的子节点，回调参数为 `(hItem,parent)`，返回 `false` 停止。
- `getChildren(parent)`：获取直接子节点句柄数组。
- `find("Object Classes", "子项文本")`：按精确文本逐级查找，中间节点会被展开；也可先传父节点句柄。
- `findPath("Object Classes\子项文本")`：反斜杠分隔的路径查找。
- `getItemPath(hItem)`：返回节点的完整文本路径，省略节点使用选中项。
- `getFirst/getSelection/getChildItem/getParentItem/getNextSibling/getPrevSibling`：返回节点句柄。

节点句柄类型为 `pointer`，窗口句柄 `hwnd` 类型为数值。不得解引用目标进程中的节点指针。
遍历不自动展开尚未加载的节点。`expandAll(parent)` 先展开父节点，再递归展开新加载的子节点；不要轻易对大型外部树调用无参数 `expandAll()`。

## 读取节点字段

```aardio
import winex.ctrl.treeview;
var tv,err=winex.ctrl.treeview(0x511500);
if(!tv) return null,err;

var hItem=tv.getFirst();
var item=tv.getItem(hItem=hItem;mask=0x7F;stateMask=0xFFFF);
// item.text / state / iImage / iSelectedImage / cChildren / lParam

tv.free();
return item;
```

`getItem(hItem)` 默认读取文本。也可以传入 `win.ui.ctrl.treeview.TVITEM()` 或普通属性表。
只支持基本 `TVITEMW` 字段，不支持 `TVITEMEX`。

`getItemText(hItem)` 默认从 260 个 UTF-16 单元开始，遇到截断自动扩容，最大 32768 单元。
显式传入第二个长度参数时采用 Win32 截断语义，长度包含尾部终结符空间。空节点文本返回 `""`，失败返回 `null,err`。

## 修改与定位

- `setSelected(hItem)`：选中节点，省略参数清除选中。
- `expand/collapse/toggle(hItem)`：展开、折叠、切换；省略节点使用选中项。
- `ensureVisible(hItem)`：展开祖先并滚动到目标节点。
- `setItemText(hItem,text)`：修改文本，空字符串可清空；仅传文本时修改选中节点。
- `setItem({hItem=...;text=...;lParam=...})`：自动推断基本字段的 mask，不修改输入表。
- `getItemState/setItemState`：读取或修改状态位，默认状态掩码为 `0xF000`。
- `getChecked/setChecked/checkAll`：复选框状态。
- `getItemRect(hItem,true)`：获取节点文本矩形，坐标为控件客户区设备像素。
- `hitTest(x,y,screen)`：返回节点句柄与 `TVHT` 标志；允许零坐标，`screen=true` 表示屏幕设备像素。
- `insertItem/delItem/clear`：增删节点，支持 `children` 子树；会实际改变目标程序的数据，谨慎使用。

结构体与文本缓冲区都在目标进程内存中传递，调用结束会释放；不会返回已释放的远程文本地址。

## 关联数据与资源释放

`setItemData/getItemData` 保留本地树控件的语义：数据只保存在调用方，支持 aardio 对象，**不是目标节点的原生 lParam**。
访问原生 lParam 请使用 `getItem({hItem=...;mask=4})` 或 `setItem({hItem=...;lParam=...})`，不要将目标地址当作本地地址使用。

`free()` 仅释放该绑定持有的进程控制句柄与临时资源，不关闭目标程序。
不要用继承的 `close()` 来释放绑定，它会关闭目标控件。释放后不要继续使用该绑定。

不支持外部窗口的 `onnotify/onSelChanged` 等事件；通知结构解析、本地 `imageList` 和本地窗口子类化不适用于此绑定。

## 目标进程位数

本版支持 **32 位目标进程**，包括 64 位 Windows 中运行的 32 位应用。64 位目标明确返回 `null,err`。

ListView 通常使用固定 32 位的行列索引；适配远程结构体中的指针宽度后即可读写文本。
TreeView 的 `HTREEITEM` 则是指针宽度，`TVM_GETNEXTITEM` 返回节点句柄，后续消息又需要传回该句柄。
32 位调用方直接使用 `SendMessage` 不能完整传递任意 64 位节点句柄，单独定义 `TVITEM64` 并不足以解决问题。
后续如果需要支持 64 位树，可增加 64 位辅助消息调用通道；本版不引入这层依赖。
