使用GetDlgItemText()转换字符串类型以在C ++中用作字符串缓冲区

时间:2009-10-24 09:18:36

标签: c++ winapi string

我对Win32(ANSI)函数中的以下行为感到困惑: (多字节字符集NOT UNICODE)

void sOut( HWND hwnd, string sText ) // Add new text to EDIT Control
{ 
 int len;
 string sBuf, sDisplay;

  len = GetWindowTextLength( GetDlgItem(hwnd, IDC_EDIT_RESULTS) ); 
  if(len > 0)
  {
   // HERE:
   sBuf.resize(len+1, 0); // Create a string big enough for the data
   GetDlgItemText( hwnd, IDC_EDIT_RESULTS, (LPSTR)sBuf.data(), len+1 );
  } // MessageBox(hwnd, (LPSTR)sBuf.c_str(), "Debug", MB_OK);

  sDisplay = sBuf + sText;
  sDisplay = sDisplay + "\n\0"; // terminate the string
  SetDlgItemText( hwnd, IDC_EDIT_RESULTS, (LPSTR)sDisplay.c_str() );
} 

这应该在每次调用时将文本附加到控件。

相反,在调用GetDlgItemText()之后,所有字符串连接都失败了,我假设是因为类型转换?

我使用了三个字符串变量来使它变得非常明显。如果sBuf受到影响,则不应影响sDisplay。

(另外,为什么len 1 char小于缓冲区中的长度?)

GetDlgItemText()会正确返回EDIT控件的内容,SetDlgItemText()将正确设置sDisplay中的任何文本,但两者之间的连接不会发生。

这是字符串类的“隐藏功能”吗?

加了:

是的,看起来问题是中间终止的NUL。现在我理解为什么len +1。该函数确保最后一个char是NUL。

使用sBuf.resize(len);会将其删除,一切都很好。

加了:

查尔斯,

不考虑这个特定函数的古怪返回长度,并讨论使用字符串作为缓冲区:

  

该标准将basic_string :: data()的返回值描述为指向数组的指针,该数组的成员等于字符串本身的元素。

这正是所需要的不是吗?

  

此外,它要求程序不得更改该数组的任何值。

据我所知,它会随着所有字节连续的保证而改变。我忘记了我在这里读了很长篇文章的地方,但MS已经实现了这个断言。

我不喜欢使用向量是在我返回之前将字节复制两次:一次进入向量并再次进入字符串。我还需要实例化一个矢量对象和一个字符串对象。这是很多开销。如果有一些字符串友好使用向量(或CStrings)而不依靠旧的C函数或逐个复杂的字符,我会使用它们。这个字符串非常符合语法。

3 个答案:

答案 0 :(得分:3)

data()上的std::string功能会返回const char*。您不能直接进入它返回的缓冲区,它可能是一个重复的缓冲区。

您可以做的是使用std::vector<char>作为临时缓冲区。

E.g. (untested)

std::vector<char> sBuf( len + 1 );
GetDlgItemText( /* ... */, &sBuf[0], len + 1 );

std::string newText( &sBuf[0] );
newText += sText;

此外,您传递给SetDlgItemText的字符串应该\0终止,因此您应该使用c_str()而不是data()

SetDlgItemText( /* ... */, newText.c_str() );

修改

好的,我刚检查了GetWindowTextLengthGetDlgItemText的合同。检查上面的编辑。两者都将包含空终止符的空间,因此您需要将其从字符串的末尾删除,否则两个字符串的连接将在字符串的中间包含空终止符,并且SetDlgItemText调用将仅使用字符串的第一部分。

还有一个复杂的问题是GetWindowTextLength不能保证准确,它只能保证返回一个足够大的数字,以便程序创建一个缓冲区来存储结果。这实际上不太可能影响调用代码所拥有的对话框项,但在其他情况下,实际文本可能短于返回的长度。因此,您应该在返回的文本中搜索第一个\0

我选择使用std::string构造函数来获取const char*,以便它正确找到第一个\0

该标准将basic_string::data()的返回值描述为指向数组的指针,该数组的成员等于string本身的元素。此外,它要求程序不得更改该数组的任何值。这意味着data()的返回值可能是也可能不是字符串内部表示的副本,即使它不是副本,您仍然不允许写入它。

答案 1 :(得分:0)

我远离win32 api和他们的字符串噩梦,但代码中有一些东西你可以检查。标准C ++字符串不需要以null结尾,并且null可以在字符串中的任何位置发生。我不会评论你用你的C风格演员抛弃恒定的事实,这本身就是一个问题,而是你的奇怪效果

当您最初创建字符串时,为null分配额外空间(并将所有元素初始化为'\ 0'),然后复制元素。此时,您的字符串大小为len+1,最后一个元素为null。之后你附加一些其他字符串,你得到的是一个字符串,它在位置len仍然有一个空字符。当您使用data()(不保证空终止!)或c_str()检索数据时,返回的缓冲区仍将在len位置具有空字符。如果将其传递给在null上停止的函数(采用C样式字符串),那么即使字符串完成,该函数也只会处理第一个len字符并忘记其余字符。

#include <string>
#include <cstdio>
#include <iostream>
int main()
{
   const char hi[] = "Hello, ";
   const char all[] = "world!";
   std::string result;
   result.resize( sizeof(hi), 0 );
   // simulate GetDlgItemText call
   std::copy( hi, hi+sizeof(hi), const_cast<char*>(result.data()) ); // this is what your C-style cast is probably doing
   // append
   result.append( all );

   std::cout << "size: " << result.size() // 14
      << ", contents" << result // "Hello, \0world!" - dump to a file and edit with a binary editor
      << std::endl;
   std::printf( "%s\n", result.c_str() ); // "Hello, "
}

正如您所看到的,printf需要一个C风格的字符串,并在找到第一个空字符时停止,这样看起来好像从未发生过追加操作。另一方面,c ++流与std::string一起正常工作,并将转储整个内容,检查字符串是否实际附加。

您的追加操作的补丁消失将从初始字符串中删除'\ 0'(在字符串中仅保留len空格)。但这不是一个很好的解决方案,你应该从不使用const_cast(很少有地方需要它,而这不是其中之一),你不要这样做看到它更糟糕:使用C风格的强制转换使你的代码看起来比它更好。

您已经评论了另一个答案,您不想添加std::vector(这将提供正确的解决方案,因为&v[0]是一个适当的可变指针进入缓冲区),当然,不添加'\ 0'的额外空间。请考虑这是实现文件的一部分,并且您使用或不使用std::vector的事实不会超出此单个编译单元。由于您已经在使用某些STL功能,因此不会向系统添加任何额外要求。那对我来说就是这样。如果删除额外的空字符,Charles Bailey提供的解决方案应该可以工作。

答案 2 :(得分:0)

答案。我在此处仅将其添加为答案,以便我可以在有关const_cast的长期讨论中使用格式。

这是一个使用const_cast可能会破坏正在运行的应用程序的示例:

#include <iostream>
#include <map>
typedef std::map<int,int> map_type;
void dump( map_type const & m ); // implemented somewhere else for concision
int main() {
   map_type m;
   m[1] = 10;
   m[2] = 20;
   m[3] = 30;
   map_type::iterator it = m.find(2);
   const_cast<int&>(it->first) = 10;
   // At this point the order invariant of the container is broken:
   dump(); // (1,10),(10,20),(3,30) !!! unordered by key!!!!
   // This happens with g++-4.0.1 in MacOSX 10.5
   if ( m.find(3) == m.end() ) std::cout << "key 3 not found!!!" << std::endl;
}

这是使用const_cast的危险。你可以在某些情况下逃脱,但在其他情况下它会咬回来,而且可能很难。尝试调试数千行,其中带有键3的元素已从容器中删除。祝你的搜索顺利,因为它从未删除过。