EM_SETSEL交换参数

EM_SETSEL swaps parameters

本文关键字:参数 交换 SETSEL EM      更新时间:2023-10-16

我使用EM_SETSEL消息来选择编辑控件中的文本。我需要选择一些从末尾到中间的文本,这样插入符号的位置就在文本的中间。MSDN文档说明如下:

起始值可以大于结束值。两个值中较低的一个指定所选内容中第一个字符的字符位置。较大的值指定第一个字符超出所选范围的位置。

起始值为选择的锚点,结束值为活动结束。如果用户使用SHIFT键调整所选区域的大小,则活动端可以移动,但锚点保持不变。

但似乎较小的值总是成为一个锚,例如,我不能实现期望的行为。

代码示例(where "parent"CWnd *):

TRACE("EM_SETSEL(%d, %d)n", pos1, pos2);
parent->SendMessage(EM_SETSEL, pos1, pos2);
parent->SendMessage(EM_GETSEL, (WPARAM)&pos1, (LPARAM)&pos2);
TRACE("EM_GETSEL(%d, %d)n", pos1, pos2);

产生输出:

EM_SETSEL(5, 1)
EM_GETSEL(1, 5)

是否有其他方法来获得所需的选择?

关于EM_GETSEL/EM_SETSEL:

  • EM_GETSEL检索左/右位置
  • EM_SETSEL设置锚点/活动位置

EM_SETSEL使用锚/活动位置,允许您轻松地将插入符号放置在选择的左侧/右侧,因此我不确定为什么在另一个答案中使用了kludge。

EM_GETSEL是一个笨拙的窗口消息,它需要一个拼凑。为了检索活动位置,这个kludge暂时将所选内容更改为0个字符,但是,当我使用它时,我没有看到任何明显的变化。

检索锚点/活动位置:

  • 使用EM_GETSEL检索左/右位置
  • 使用EM_SETSEL将选择临时设置为0个字符,将插入符号留在活动位置
  • 使用EM_GETSEL检索活动位置
  • 使用EM_SETSEL恢复原始选择

设置选择的AutoHotkey代码示例:

q:: ;Notepad - set active position (caret) at right
PostMessage, 0xB1, 5, 10, Edit1, A ;EM_SETSEL := 0xB1
return
w:: ;Notepad - set active position (caret) at left
PostMessage, 0xB1, 10, 5, Edit1, A ;EM_SETSEL := 0xB1
return

获取/设置选择的AutoHotkey函数示例:

JEE_EditGetRange(hCtl, ByRef vPos1, ByRef vPos2)
{
    VarSetCapacity(vPos1, 4), VarSetCapacity(vPos2, 4)
    SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0 ;(left, right)
    vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
}
;==================================================
JEE_EditSetRange(hCtl, vPos1, vPos2, vDoScroll:=0)
{
    SendMessage, 0xB1, % vPos1, % vPos2,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1 ;(anchor, active)
    if vDoScroll
        SendMessage, 0xB7, 0, 0,, % "ahk_id " hCtl ;EM_SCROLLCARET := 0xB7
}
;==================================================
;note: although this involves deselecting and selecting it seems to happen invisibly
JEE_EditGetRangeAnchorActive(hCtl, ByRef vPos1, ByRef vPos2)
{
    ;get selection
    VarSetCapacity(vPos1, 4), VarSetCapacity(vPos2, 4)
    SendMessage, 0xB0, % &vPos1, % &vPos2,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0
    vPos1 := NumGet(&vPos1, 0, "UInt"), vPos2 := NumGet(&vPos2, 0, "UInt")
    if (vPos1 = vPos2)
        return
    vPos1X := vPos1, vPos2X := vPos2
    ;set selection to 0 characters and get active position
    SendMessage, 0xB1, -1, 0,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1
    VarSetCapacity(vPos2, 4)
    SendMessage, 0xB0, % &vPos2, 0,, % "ahk_id " hCtl ;EM_GETSEL := 0xB0
    vPos2 := NumGet(&vPos2, 0, "UInt")
    ;restore selection
    vPos1 := (vPos2 = vPos2X) ? vPos1X : vPos2X
    SendMessage, 0xB1, % vPos1, % vPos2,, % "ahk_id " hCtl ;EM_SETSEL := 0xB1 ;(anchor, active)
}

链接:

我最初在AutoHotkey论坛上发布的上述功能:
GUI命令:完全重新思考- AutoHotkey社区
https://autohotkey.com/boards/viewtopic.php?f=5& t = 25893, p = 138292 # p138292

@vafylec的回答很好。我只是在这里重写了Delphi,它比AHK更容易解析和翻译成其他语言。

基本上,EM_SETSEL允许您在范围的两端设置锚,但EM_GETSEL只返回第一个和最后一个字符,丢失了该信息。使用一个小工具,您可以解决这个问题:

procedure GetEditSelection(Handle: THandle; var Anchor, Start, Finish: Integer);
begin
  SendMessage(Handle, EM_GETSEL, NativeUInt(@Start), NativeUInt(@Finish));
  SendMessage(Handle, EM_SETSEL, -1, 0);
  SendMessage(Handle, EM_GETSEL, NativeUInt(@Anchor), 0);
  if Anchor = Start then 
    SendMessage(Handle, EM_SETSEL, Finish, Start)
  else
    SendMessage(Handle, EM_SETSEL, Start, Finish);
end;