【问题标题】:How do I convert a std::wstring to an LSA_UNICODE_STRING如何将 std::wstring 转换为 LSA_UNICODE_STRING
【发布时间】:2015-03-19 05:10:42
【问题描述】:

今天我可以写a simple C++ program that granted a user the "Log on as a service" privilege。其中一部分涉及在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(hex 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

我做错了什么?

【问题讨论】:

  • 您不需要#if defined(_UNICODE)。无论如何,代码都是正确的。
  • 要添加到 MSSalters 的评论中,最好通过 const 引用(即:const std::wstring & str)而不是值来传递 std::wstring 的实例,以避免无用的 deep-副本。

标签: c++ winapi unicode wstring local-security-authority


【解决方案1】:

您可能会遇到从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 来管理内存并保证在变量不再使用时释放它。有关此方法的详细信息,请参阅 Mr_C64 的回答。

【讨论】:

  • 或者,您可以只将 const std::wstring& 传递给函数而不是 std::wstring。
  • @StilesCrisis:虽然这可以解决立即崩溃的问题,但它会引入一个非常危险的 API。例如。如果调用者通过L"Foo",您将得到一个无声转换为临时std::wstring。当toLsaUnicodeString 返回时,该临时值超出范围,但返回的LSA_UNICODE_STRING 仍将指向它。
  • 我认为传递 "const std::wstring &" 作为函数参数是一个很好的建议:不需要按值传递 std::wstring 导致的无用深度复制。此外,在 freeLsaUnicodeString 函数中,“str”参数应作为引用传递(而不是按值传递),因此函数可以修改其内容,并且调用者可以看到修改。此外,在 delete[] 处理缓冲区指针后,应将指针设置为 NULL 以避免重复删除(并且应更新 LSA_UNICODE_STRING 的其他成员以反映空字符串的新状态)。
  • 此外,由于我们处于 C++ 世界(而不是 C 世界),最好使用 RAII 和 C++ 析构函数,而不是使用“C++ 作为更好的 C”进行编程,定义 create /free 函数不是异常安全的,如果调用不当会产生内存泄漏(相反,编译器会自动调用 C++ 析构函数,因此正确使用 RAII 和析构函数可以编写结构上无法泄漏的代码,以及更容易编写和维护)。
  • @Mr_C64,感谢您的建议,我已更新代码以使用引用并清除 LSA_UNICODE_STRING。我同意 RAII 在这里会是一个有用的习语,并添加了对您答案的引用。
【解决方案2】:

我认为在 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'\0'; // 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];
    }
};

【讨论】:

    【解决方案3】:

    您可以使用RtlInitUnicodeString 函数来初始化一个Unicode 字符串。使用 UNICODE_STRING 后调用RtlFreeUnicodeString

    UNICODE_STRING 和 LSA_UNICODE_STRING 相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-05
      • 2011-01-03
      • 2012-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-01
      • 2014-04-30
      相关资源
      最近更新 更多