2

我一直在使用 MFC CFileDialog 类遇到一些随机崩溃,所以我从这个页面查看了他们的示例代码,内容如下;

#define MAX_CFileDialog_FILE_COUNT 99
#define FILE_LIST_BUFFER_SIZE ((MAX_CFileDialog_FILE_COUNT * (MAX_PATH + 1)) + 1)

CString fileName;
wchar_t* p = fileName.GetBuffer( FILE_LIST_BUFFER_SIZE );
CFileDialog dlgFile(TRUE);
OPENFILENAME& ofn = dlgFile.GetOFN( );
ofn.Flags |= OFN_ALLOWMULTISELECT;
ofn.lpstrFile = p;
ofn.nMaxFile = FILE_LIST_BUFFER_SIZE;

dlgFile.DoModal();
fileName.ReleaseBuffer();  

wchar_t* pBufEnd = p + FILE_LIST_BUFFER_SIZE - 2;  
wchar_t* start = p;
while( ( p < pBufEnd ) && ( *p ) )
  p++;
if( p > start )
{
  _tprintf(_T("Path to folder where files were selected:  %s\r\n\r\n"), start );
  p++;

  int fileCount = 1;
  while( ( p < pBufEnd ) && ( *p ) )
  {
    start = p;
    while( ( p < pBufEnd ) && ( *p ) )
      p++;
    if( p > start )
      _tprintf(_T("%2d. %s\r\n"), fileCount, start );
    p++;
    fileCount++;
  }
}

通过我的阅读,该语句fileName.ReleaseBuffer();使缓冲区变量中指向的内存p无效,因此剩余的代码很容易发生内存冲突。同时,我还假设微软会在发布这些示例之前对其进行检查。我在这里遗漏了一些明显的东西吗?在不再需要缓冲区之后,是否有任何理由在CString这里使用 a 而不是简单的new后跟 a ?delete

4

2 回答 2

3

示例代码不是正式文档。这个样本是错误的。文档是正确的:

调用后返回的地址GetBuffer可能无效,ReleaseBuffer因为额外CSimpleStringT的操作可能会导致CSimpleStringT缓冲区被重新分配。

该示例使用CString(通过原始指针和手动内存管理)进行自动内存管理和异常安全。后者更难通过手动内存管理来正确处理(尽管这个示例也没有获得正确的异常安全性)。

如果您想修复示例代码以遵守合同,则需要进行以下更改:*

  1. 替换wchar_t* pBufEnd = p + FILE_LIST_BUFFER_SIZE - 2;const wchar_t* pBufEnd = fileName.GetString() + FILE_LIST_BUFFER_SIZE - 2;
  2. 替换wchar_t* start = p;const wchar_t* start = fileName.GetString();
  3. p将对话框调用后代码中所有剩余的 替换为一个新变量,初始化为const wchar_t* current = fileName.GetString();)。

这是一个常见的错误。每当开发人员认为他们需要 achar*时,他们忽略了他们需要 aconst char*而不是,几乎每个字符串类型都通过成员函数提供。


请注意,示例代码中还有其他错误,未在此答案中明确解决(如另一个答案中解释的字符类型不匹配)。


* 可以在此答案中找到检索所选文件列表的 C++ 实现。

于 2017-02-02T13:26:03.197 回答
2

您可能会注意到规范和实现之间的差异。上面的代码有效,因为CString实现允许它,即使CString规范禁止它。

并突出示例的质量:它混合了TCHARwchar_t. 在tprintf("%s", start)字符串start中必须是 aTCHAR*但示例使用 wchar_t* start

于 2017-02-02T13:26:43.460 回答