非マネージドコードから Marshal.StringToXXX は使えない

.NET 1.1 までは、.NET のマネージドコードから COM コンポーネントの呼び出しには、RCW*1 を使用する方法しかありませんでした*2が、.NET 2.0 で C++/CLI を利用した CRCW*3 と呼ばれる方法が増えました。CRCW では非 blitable 型のマーシャリングのみを記述するだけで、あとは参照設定をして使うだけなので結構らくちんです。
さて、CRCW のマーシャリングを楽するため、C++ の一時オブジェクトを利用するために、

class S
{
  public:
    S(String ^s)
    {
        ptr = Marshal::StringToHGlobalUni(s);
    }

    ~S()
    {
        Marshal::FreeHGlobal(ptr);
    }

    operator LPCWSTR()
    {
        return static_cast<LPCWSTR>(ptr.ToPointer());
    }

  private:
    IntPtr ptr;
};

のようなクラスを作成し、

void CRCW::WrapperClass::Method(String ^s1, String ^s2, int i1)
{
   (*InterfacePtr)->Method( S(s1), S(s2), i1 );
}

みたいなかんじでお手軽メモリ管理をしようとしたのですが、標題の通りで失敗しました。まあ、たんにマネージドコードにすればよいだけなので、

ref class S
{
  public:
    S(String ^s)
    {
        ptr = Marshal::StringToHGlobalUni(s);
    }

    ~S()
    {
        this->!S();
    }

    !S()
    {
        Marshal::FreeHGlobal(ptr);
    }

    operator LPCWSTR()
    {
        return static_cast<LPCWSTR>(ptr.ToPointer());
    }

  private:
    System::IntPtr ptr;
};

と、ref を付けてファイナライザとデストラクタの関係を作ってしまえば、あとは C++/CLI が期待通りの処理をしてくれます。

*1:Runtime Callable Wrapper

*2:C-style COM CALL を P/Invoke するという方法や、vtbl を自分で処理するという方法はあるけど

*3:Custom Runtime Callable Wrapper