这Microsoft CFileDialog 示例是否会导致潜在的内存冲突

Does this Microsoft CFileDialog example lead to a potential memory violation

本文关键字:冲突 内存 CFileDialog Microsoft 是否      更新时间:2023-10-16

我在使用 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:  %srnrn"), start );
p++;
int fileCount = 1;
while( ( p < pBufEnd ) && ( *p ) )
{
start = p;
while( ( p < pBufEnd ) && ( *p ) )
p++;
if( p > start )
_tprintf(_T("%2d. %srn"), fileCount, start );
p++;
fileCount++;
}
}

根据我的阅读,语句fileName.ReleaseBuffer();使缓冲区变量中指向的内存p无效,因此其余代码很容易遇到内存冲突。 同时,我也假设Microsoft在发布这些示例之前会检查它们。 我在这里错过了一些明显的东西吗? 是否有任何理由在这里使用CString而不是简单的new,然后在不再需要缓冲区后使用delete

示例代码不是正式文档。此示例是错误的。文档是正确的:

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();的新变量。

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


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


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

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

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