如何将std::wstring转换为LSA_UNICODE_STRING

How do I convert a std::wstring to an LSA_UNICODE_STRING

本文关键字:LSA UNICODE STRING wstring std 转换      更新时间:2023-10-16

今天我能够编写一个简单的C++程序,该程序授予用户"作为服务登录"权限。其中一部分涉及在LPCWSTRLSA_UNICODE_STRING之间进行转换。代码在这里:

LSA_UNICODE_STRING StringToLsaUnicodeString(LPCWSTR string) {
    LSA_UNICODE_STRING lsaString;
    DWORD dwLen = 0;
    dwLen = wcslen(string);
    lsaString.Buffer = (LPWSTR) string;
    lsaString.Length = (USHORT)((dwLen) * sizeof(WCHAR));
    lsaString.MaximumLength = (USHORT)((dwLen + 1) * sizeof(WCHAR));
    return lsaString;
}

当我在这个函数中出现一些小错误时,我对LsaLookupNames2()的调用失败,代码为87(十六进制0x57)"参数不正确"。我试图在使用std::wstring的C++应用程序中进行这个调用,但它失败了。我目前的功能如下:

#if defined(_UNICODE)
    LSA_UNICODE_STRING toLsaUnicodeString (std::wstring str) {
        LSA_UNICODE_STRING lsaWStr;
        DWORD len = 0;
        LPWSTR cstr = (LPWSTR)str.c_str();
        len = wcslen(cstr);
        lsaWStr.Buffer = cstr;
        lsaWStr.Length = (USHORT)((len) * sizeof(WCHAR));
        lsaWStr.MaximumLength = (USHORT)((len + 1) * sizeof(WCHAR));
        return lsaWStr;
    } 
#endif

我做错了什么?

您可能会遇到从str.c_str()返回的wchar_t*的生存期问题。str.c_str()将返回一个指向其生存期由str控制的底层字符串的指针。由于str是按值传递的,因此它将在toLsaUnicodeString函数结束时被销毁,导致返回的LSA_UNICODE_STRING指向已解除分配的内存。为了避免这种情况,您需要在toLsaUnicodeString函数中创建一个底层字符串的副本,并将该副本与返回的LSA_UNICODE_STRING相关联,类似于:

LSA_UNICODE_STRING toLsaUnicodeString (const std::wstring& str) {
    LSA_UNICODE_STRING lsaWStr;
    DWORD len = 0;
    len = str.length(); 
    LPWSTR cstr = new WCHAR[len + 1];
    memcpy(cstr, str.c_str(), (len + 1) * sizeof(WCHAR));
    lsaWStr.Buffer = cstr;
    lsaWStr.Length = (USHORT)((len) * sizeof(WCHAR));
    lsaWStr.MaximumLength = (USHORT)((len + 1) * sizeof(WCHAR));
    return lsaWStr;
}

由于内存现在是在堆上分配的,您有责任确保它被释放。您可以使用以下函数来处理此问题。

void freeLsaUnicodeString(LSA_UNICODE_STRING& str) {
    delete [] str.Buffer;
    str.Buffer = 0;
    str.Length = 0;
    str.MaximumLength = 0;
}

更好的方法是使用RAII来管理内存,并保证在变量不再使用时释放内存。有关此方法的详细信息,请参阅_C64先生的回答。

我认为在C++中实现这一点的正确方法是围绕原始C结构LSA_UNICODE_STRING编写一个RAII包装器类。

该类的构造函数重载正确地初始化它,析构函数释放分配的资源(帮助编写异常安全代码),并且您可以提供一些operator=重载来进行正确的深度复制。

动态分配的WCHAR缓冲区不是使用显式的new[]和delete[],而是由std::vector的实例管理,这简化了代码(例如std::vector的析构函数将自动释放分配的内存)。

类似这样的东西:

#include <windows.h>     // Win32 SDK header
#include <LsaLookup.h>   // LSA_UNICODE_STRING
#include <vector>        // std::vector
#include <string>        // std::wstring

//
// C++ RAII wrapper to LSA_UNICODE_STRING
//
class LsaUnicodeString
{
public:
    LsaUnicodeString()
    {
        SetEmpty();
    }

    LsaUnicodeString(const LsaUnicodeString & source)
    {
        CopyFrom(source);
    }

    explicit LsaUnicodeString(const std::wstring & source)
    {
        CopyFrom(source);
    }

    ~LsaUnicodeString()
    {
        // Nothing to do:
        // the string buffer is managed by std::vector data member
    }

    LsaUnicodeString & operator=(const LsaUnicodeString & source)
    {
        if (&source != this)
        {
            CopyFrom(source);
        }
        return *this;
    }

    LsaUnicodeString & operator=(const std::wstring & source)
    {
        CopyFrom(source);
        return *this;
    }

    const LSA_UNICODE_STRING & Get() const
    {
        return m_us;
    }

    //
    // Implementation
    //
private:
    LSA_UNICODE_STRING m_us;        // raw C structure
    std::vector<WCHAR> m_buffer;    // string content

    void SetEmpty()
    {
        m_buffer.resize(1);
        m_buffer[0] = L''; // end-of-string
        m_us.Length = 0;
        m_us.MaximumLength = sizeof(WCHAR);
        m_us.Buffer = &m_buffer[0];
    }

    void CopyFrom(const std::wstring & source)
    {
        if ( source.empty() )
        {
            SetEmpty();
            return;
        }
        const int len = source.length();
        m_buffer.resize(len + 1);
        ::CopyMemory(&m_buffer[0], source.c_str(), (len+1)*sizeof(WCHAR));
        m_us.Length = len * sizeof(WCHAR);
        m_us.MaximumLength = m_us.Length + sizeof(WCHAR);
        m_us.Buffer = &m_buffer[0];
    }

    void CopyFrom(const LsaUnicodeString & source)
    {
        if (source.m_us.Length == 0)
        {
            SetEmpty();
            return;
        }
        m_buffer = source.m_buffer;
        m_us.Length = source.m_us.Length;
        m_us.MaximumLength = source.m_us.MaximumLength;
        m_us.Buffer = &m_buffer[0];
    }
};

您可以使用RtlInitUnicodeString函数来初始化unicode字符串。在使用UNICODE_STRING调用RtlFreeUnicodeString之后。

UNICODE_STRING和LSA_UNCODE_STRING是相同的。