2

今日、私はユーザーに「サービスとしてログオン」特権を付与する単純なC++プログラムを作成することができました。LPCWSTRこれの一部には、aと。の間の変換が含まれていました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

私は何が間違っているのですか?

4

3 に答える 3

4

wchar_t*からの返品で生涯の問題が発生している可能性がありますstr.c_str()str.c_str()有効期間がによって制御される基になる文字列へのポインタを返しますstrstr値で渡されるため、関数の最後で破棄され、割り当てが解除されたメモリを指す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の回答を参照してください。

于 2012-01-16T03:50:29.353 に答える
3

C ++でこれを行う正しい方法は、生のC構造体LSA_UNICODE_STRINGの周りにRAIIラッパークラスを作成することだと思います。

このクラスのコンストラクターオーバーロードはそれを適切に初期化し、デストラクタは割り当てられたリソースを解放し(例外安全コードの記述を支援します)、適切なディープコピーを実行するためにいくつかの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];
    }
};
于 2012-01-16T09:57:41.587 に答える
1

RtlInitUnicodeString関数を使用して、Unicode文字列を初期化できます。UNICODE_STRINGを使用した後、 RtlFreeUnicodeStringを呼び出します。

UNICODE_STRINGとLSA_UNICODE_STRINGは同じです。

于 2012-01-16T14:53:45.907 に答える