构建LPWSTR的更安全的方法

时间:2017-03-03 18:19:53

标签: c++ winapi memory-leaks

我正在使用需要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。

这样可以正常工作,但有更好/更安全的方法吗?我愿意接受任何改进。

2 个答案:

答案 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>&nbsp;&nbsp;<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()