我正在使用需要LPWSTR
的WinAPI GetLogicalDriveStrings()
函数,并且想知道是否有更安全的方法来确保没有内存泄漏。
目前,我使用:
构建一个指向缓冲区buf
的初始指针
auto buf = GetLogicalDriveStrings(0, nullptr);
然后我使用以下命令创建LPWSTR来代替实际调用中的空指针:
auto driveStrings = static_cast<LPWSTR>(malloc((buf + 1) * sizeof(WCHAR)));
接下来,我创建一个指向driveStrings
的指针,以便稍后释放它。在检查driveStrings
是否为空指针或缓冲区(buf
)是否为NULL
之后(如果无法分配内存),我使用{{GetLogicalDriveStrings()
调用driveStrings
1}}。
获得结果后,我使用分配后的指针手动free()
LPWSTR。
如何为LPWSTR使用智能指针,这样我就不必使用malloc()
和free()
,但是它仍然可以使用GetLogicalDriveStrings()
函数?< / p>
最低工作示例:
auto buf = GetLogicalDriveStrings(0, nullptr);
auto driveStrings = static_cast<LPWSTR>(malloc((buf + 1) * sizeof(WCHAR)));
auto pDriveStrings = driveStrings;
if (driveStrings == nullptr || buf == NULL)
{
std::stringstream msg;
msg << "Can't allocate memory for drive list: ";
msg << GetLastError();
throw std::runtime_error(msg.str());
}
// get drive strings
if (GetLogicalDriveStrings(buf, driveStrings) == NULL)
{
std::stringstream msg;
msg << "GetLogicalDriveStrings error: ";
msg << GetLastError();
throw std::runtime_error(msg.str());
}
// iterate over results
while (*driveStrings)
{
// GetDriveType() requires a LPCWSTR
if (GetDriveType(driveStrings) == DRIVE_FIXED || GetDriveType(driveStrings) == DRIVE_REMOVABLE)
{
std::wcout << driveStrings << std::endl;
}
driveStrings += lstrlen(driveStrings) + 1;
}
free(pDriveStrings);
如果我使用std::wstring
,我无法弄清楚如何迭代driveStrings
缓冲区中的每个字符串。如果我使用std::vector<WCHAR>
,我无法弄清楚如何将每个元素投射到GetDriveType()
的LPCWSTR。
这样可以正常工作,但有更好/更安全的方法吗?我愿意接受任何改进。
答案 0 :(得分:2)
我想我会做这样的事情:
std::wstring s(buf+1, '\0');
auto len = GetLogicalDriveStrings(buf, &s[0]);
s.resize(len);
这会创建一个包含NUL的wstring
,然后GetLogicalDriveStrings
会用它产生的内容覆盖内容。最后,我们将字符串的大小调整为GetLogicalDriveStrings
实际写入的字符数。
从那里开始,我们有一个完全正常的字符串,当它超出范围时会释放它的内存,就像任何其他字符串一样。
答案 1 :(得分:2)
我如何使用智能指针代替LPWSTR,所以我不必这样做 使用malloc()和free(),但它仍然可以使用 GetLogicalDriveStrings()函数?
您可以使用std::unique_ptr
。它可以用来分配像这样的字符数组:
<div id="audio-player">
<button id="play"></button>
<button id="pause"></button>
<ul id="playlist">
<li song="song1.wav" songtitle="Song1" artist="Artist1"></li>
<li song="song2.wav" songtitle="Song2" artist="Artist2"></li>
<li song="song3.wav" songtitle="Song3" artist="Artist3"></li>
<li song="song4.wav" songtitle="Song4" artist="Artist4"></li>
</ul>
<div id="audio-info">
<span class="artist"></span> <span class="title"></span>
</div>
</div>
示例说明如何将其与std::unique_ptr<wchar_t[]> buffer( new wchar_t[ size ] );
一起使用。该示例还说明了如何正确调用GetLogicalDriveStrings()
。必须在设置最后一个错误值的函数后立即调用它。中间的任何其他系统调用(可能隐藏在C或C ++标准代码中)可能使最后一个错误值无效。为了便于使用,我已将其包含在GetLastError()
函数中,但规则仍然适用。
ThrowLastError()
就个人而言,我会选择#include <Windows.h>
#include <iostream>
#include <string>
#include <set>
#include <memory>
void ThrowLastError( const char* msg ) {
DWORD err = ::GetLastError();
throw std::system_error( static_cast<int>( err ), std::system_category(), msg );
}
std::set< std::wstring > GetLogicalDriveSet() {
// Call GetLogicalDriveStrings() to get required buffer size.
DWORD bufSize = ::GetLogicalDriveStrings( 0, nullptr );
if( bufSize == 0 )
ThrowLastError( "Could not get logical drives" );
// Allocate an array of wchar_t and manage it using unique_ptr.
// Make sure to allocate space for last '\0'.
std::unique_ptr<wchar_t[]> buffer( new wchar_t[ bufSize + 1 ] );
// Call GetLogicalDriveStrings() 2nd time to actually receive the strings.
DWORD len = ::GetLogicalDriveStrings( bufSize, buffer.get() );
if( len == 0 )
ThrowLastError( "Could not get logical drives" );
// In a rare case the number of drives may have changed after
// the first call to GetLogicalDriveStrings().
if( len > bufSize )
throw std::runtime_error( "Could not get logical drives - buffer size mismatch" );
std::set< std::wstring > result;
// Split the string returned by GetLogicalDriveStrings() at '\0'.
auto p = buffer.get();
while( *p ) {
std::wstring path( p );
result.insert( path );
p += path.size() + 1;
}
return result;
}
int main(int argc, char* argv[]) {
std::set< std::wstring > drives;
try {
drives = GetLogicalDriveSet();
}
catch( std::exception& e ) {
std::cout << "Error: " << e.what() << std::endl;
return 1;
}
std::cout << "Fixed and removable drives:\n";
for( const auto& drv : drives ) {
DWORD driveType = ::GetDriveType( drv.c_str() );
if( driveType == DRIVE_FIXED || driveType == DRIVE_REMOVABLE ){
std::wcout << drv << std::endl;
}
}
return 0;
}
,这样可以完全避免缓冲管理的麻烦。此外,简化了错误处理,因为您只需调用此函数一次。为了完整起见,我提供了一个如何使用下面GetLogicalDrives()
的示例。
GetLogicalDrives()