是否可以将来自 Win32 EDIT 控件的文本输入存储在C++ std::string 中?

Is it possible to store text input from a Win32 EDIT control in a C++ std::string?

本文关键字:C++ 存储 std 输入 string 文本 将来 Win32 控件 EDIT 是否      更新时间:2023-10-16

我最近下载了 DevC++,我现在正在用 C++ 编写一个 Windows API 程序,其中我放置了一个TextBox输入控件和一个 OK 按钮以在MessageBox()中显示文本输入。

这里有一个小片段:

HWND TextBox;
char txtStore[200];
/*WM_CREATE*/
TextBox = CreateWindow("edit", "",
WS_VISIBLE | WS_CHILD | WS_BORDER | ES_AUTOHSCROLL,
0 ,30 ,500 ,20 ,
hwnd, (HMENU) 0, NULL, NULL
); /* Text Input field */
CreateWindow("button", "Go",
WS_VISIBLE | WS_CHILD,
500 ,30 ,100 ,20 ,
hwnd, (HMENU) 2, NULL, NULL
); /* Button 8/
/*WM_COMMAND*/
if(LOWORD(wParam)==2){
GetWindowText(TextBox,&txtStore[0],200);
MessageBox(0,txtStore,"Title",0);
}

如何将输入保存到std::string txtStore;而不是char txtStore[200];

对于保证连续存储字符(C++11 及以上)的实现,您可以将文本直接放入std::wstring中。

// UNICODE build - prefered
int potential_length = GetWindowTextLengthW( hwnd )
std::wstring text;
// Theoretically, some implementations can keep internal buffer not NUL terminated
// Allocate one character more just to be safe
text.resize( potential_length + 1 );
int final_length = GetWindowTextW( hwnd, &text[0], potential_length + 1 );
text.resize( final_length );

如果你正在学习Windows API或编写新软件,你绝对应该更喜欢UNICODE构建而不是ANSI构建。这迫使您使用wstring而不是stringwchar_t而不是char,但它使您免于许多不可翻译字符的问题(并非所有UNICODE字符都可以由当前代码页表示),内部API转换(API必须重复将字符串从窄字符转换为宽字符以及其他方式)。

如果您定义项目范围的宏UNICODE_UNICODE,则可以将自己从某些陷阱中拯救出来,如果MBCS在某处定义,则可以将其删除。您可以在选项对话框中执行此操作,其中包含名为"其他定义"或类似内容的字段。如果您不知道如何配置其他宏,则可以在包含标头之前定义它们<windows.h>这是可以接受的。

#define UNICODE
#define _UNICODE
#include <windows.h>

如果你坚持使用stringchar,就在这里。

// ANSI build - obsolete, use only if you have to
// You will lose characters untranslatable into current code page
int potential_length = GetWindowTextLengthA( hwnd );
std::string text;
// Theoretically, some implementations can keep internal buffer not NUL terminated
// Allocate one character more just to be safe
text.resize( potential_length + 1 );
int final_length = GetWindowTextA( hwnd, &text[0], potential_length + 1 );
text.resize( final_length );

较旧的C++标准不允许修改内部字符串数据。您可以使用堆栈上定义的一些中间缓冲区,通过new wchar_t[]获得或我更喜欢使用std::vector.

// UNICODE build - preferred
int potential_length = GetWindowTextLengthW( hwnd );
std::vector<wchar_t> buff;
buff.resize( potential_length + 1 );
int final_length = GetWindowTextW( hwnd, buff.data(), potential_length + 1 );
std::wstring text( buff.data(), final_length );
// ANSI build - obsolete, use only if you have to
// You will lose characters untranslatable into current code page
int potential_length = GetWindowTextLengthA( hwnd );
std::vector<char> buff;
buff.resize( potential_length + 1 );
int final_length = GetWindowTextA( hwnd, buff.data(), potential_length + 1 );
std::string text( buff.data(), final_length );

如果编译器不支持buff.data(),请使用&buff[0]


错误处理

出错时GetWindowTextLengthW返回 0,因此要检查错误,您需要在调用它之前SetLastError(0)并在调用后检查GetLastError()结果。 错误返回 0 时GetWindowTextW,必须使用相同的步骤进行检查。这是因为 0 是有效的文本长度。

SetLastError( 0 );
int potential_length = GetWindowTextLengthW( hwnd );
if ( potential_length == 0 )
{
DWORD error = GetLastError();
if ( error != 0 )
{
// Handle error here. Throw exception, log message, display dialog etc.
// Most probably hwnd handle is invalid.
return;
}
}
std::wstring text;
// Theoretically, some implementations can keep internal buffer not NUL terminated
// Allocate one character more just to be safe
text.resize( potential_length + 1 );
SetLastError( 0 );
int final_length = GetWindowTextW( hwnd, &text[0], potential_length + 1 );
if ( final_length == 0 )
{
DWORD error = GetLastError();
if ( error != 0 )
{
// Handle error here. Throw exception, log message, display dialog etc.
// Most probably hwnd handle is invalid or belongs to another process.
return;
}
}
text.resize( final_length );

+ 1 和长度操作说明

其他+ 1涵盖了 Windows API 的怪癖,其中某些 API 调用会考虑尾随的 NUL 字符,而有些则不会。GetWindowTextLengthW就是这种情况,它返回没有 NUL 字符的文本长度,但GetWindowTextW中的最后一个参数需要声明缓冲区大小,包括 NUL 字符的位置。如果我们省略+ 1缓冲区将少包含一个字符,因为 API 必须输入 NUL 字符。

文档还指出,GetWindowTextLength可以返回几个字符大的值(我认为它主要适用于GetWindowTextLengthA变体)。这解释了长度校正,因为只有在GetWindowTextW之后我们才知道真正的长度。


过度分配字符串空间

大多数连续保留string字符的字符串实现也会在末尾添加 NUL 字符以简化c_str()data()处理,但从字面上看C++11 标准,它们不必这样做。由于GetWindowText始终将 NUL 字符放在末尾,因此对于不为 NUL 字符永久保留额外空间的实现,它可能会覆盖某些内部数据。

虽然

text.resize( potential_length );

可以工作,再调整一个字符串的大小

text.resize( potential_length + 1 );

将涵盖这些实现。如果我们交叉字符串内联存储大小,这会产生始终将后者调整为较小长度和不必要的分配text代价。