Как мне преобразовать std::wstring в LSA_UNICODE_STRING
Сегодня я смог написать простую программу на C++, которая предоставила пользователю привилегию "Вход в систему как сервис". Часть этого включала преобразование между LPCWSTR
и LSA_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) "Параметр неверен". Я пытаюсь сделать этот вызов в приложении C++, которое использует std::wstring
и это терпит неудачу. Моя текущая функция заключается в следующем:
#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
Что я делаю неправильно?
3 ответа
Вы, вероятно, столкнулись с проблемой на всю жизнь с wchar_t*
вернулся из str.c_str()
, 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 для деталей об этом подходе.
Я думаю, что правильный способ сделать это в C++ - написать класс- оболочку RAII вокруг необработанной структуры C LSA_UNICODE_STRING.
Перегрузки конструктора этого класса должным образом инициализируют его, деструктор высвобождает выделенные ресурсы (помогая писать безопасный для исключения код), и вы можете предоставить некоторые перегрузки operator= для правильного глубокого копирования.
Вместо использования явных new[] и delete[] динамически распределяемый буфер WCHAR управляется экземпляром 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];
}
};
Вы можете использовать функцию RtlInitUnicodeString для инициализации строки Unicode. После использования UNICODE_STRING вызовите RtlFreeUnicodeString.
UNICODE_STRING и LSA_UNICODE_STRING идентичны.