我正在使用 Rich Edit 控件,并且想要迭代其中的
WCHAR
值。
我写了这个例程:
int GetCharacterAtIndex(int pos) const
{
TEXTRANGE tr{};
tr.chrg.cpMin = pos;
tr.chrg.cpMax = pos + 1;
WCHAR buffer[2]{};
tr.lpstrText = buffer;
DWORD charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr);
assert(charsRetrieved < DIM(buffer));
if (charsRetrieved <= 0) return -1;
return buffer[0];
}
只要
pos
处的字符不是 UTF-16 代理项对的一部分,此方法就非常有效。但是,如果它是代理对的一部分,则返回的值始终是低代理项。这非常烦人,因为 charsRetrieved
中返回的值仍然只是 1。我修改了代码如下,以使用EM_GETSELTEXT
获取高代理:
int GetCharacterAtIndex(int pos) const
{
TEXTRANGE tr{};
tr.chrg.cpMin = pos;
tr.chrg.cpMax = pos + 1;
WCHAR buffer[10]{}; // add some extra space
tr.lpstrText = buffer;
DWORD charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr);
assert(charsRetrieved < DIM(buffer));
if (charsRetrieved <= 0) return -1;
int index = 0;
if (buffer[0] >= 0xD800 && buffer[0] < 0xDC00)
{
SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_SETSEL, pos, pos + 1);
DWORD surrogateStart{}, surrogateEnd{};
SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETSEL, (WPARAM)&surrogateStart, (LPARAM)&surrogateEnd);
if (surrogateStart == surrogateEnd)
{
// this may be a WinAPI bug. When pos actually points to the low surrogate, you sometimes have to do this twice.
SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_SETSEL, pos, pos + 1);
SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETSEL, (WPARAM)&surrogateStart, (LPARAM)&surrogateEnd);
}
LRESULT gotChars = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETSELTEXT, 0, (LPARAM)buffer);
assert(gotChars < DIM(buffer));
if (gotChars <= 0) return -1;
index = pos - surrogateStart;
assert(index < gotChars);
}
return buffer[index];
}
这可行,但它依赖于
EM_GETSELTEXT
。
EM_GETSELTEXT
的问题在于它需要对选择进行破坏。即使您禁止重绘,这也会导致视觉伪像。它可能会导致更混乱的代码来抑制选择更改通知(或不抑制),并且需要调用者在需要时保存和恢复选择。最重要的是它非常慢并且会产生丑陋的视觉伪像。
我是否错过了一种更优雅的方式来完成此任务?
EM_GETTEXTRANGE
快速、安静且独立。我真的不想使用
EM_GETSELTEXT
。std::wstring
形式返回完整的 Unicode 字符。然后,如果需要,调用者可以在返回值上使用
size
方法来递增迭代器。此外,我还纠正了问题中的术语。高代理优先。 (在我的问题中,我把术语搞反了。)
std::wstring GetCharacterAtIndex(int pos) const
{
TEXTRANGE tr{};
tr.chrg.cpMin = pos;
tr.chrg.cpMax = pos + 1;
WCHAR buffer[10]{}; // extra space
tr.lpstrText = buffer;
DWORD charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr);
assert(charsRetrieved < DIM(buffer));
if (charsRetrieved <= 0) return L"";
if (buffer[0] >= 0xD800 && buffer[0] < 0xDC00) // if it is the high surrogate of UTF-16 surrogate pair
{
// EM_GETTEXTRANGE always returns the high surrogate, even if pos points to a low surrogate.
buffer[0] = 0;
tr.chrg.cpMin = pos;
tr.chrg.cpMax = pos + 2;
charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr);
assert(charsRetrieved < DIM(buffer));
if (charsRetrieved <= 0) return L"";
}
return buffer;
}