Visual Studio 2017 Visual C++ : COM データ型 VARIANT と CComVariant

#はじめに
VARIANT 型は Visual Basic.NET では使われませんが、以前の Visual Basic 6.0 ではよく使われていました。また、VBA や VBS (内部的に) では使用されています。

COM はこれらの言語に対応しているので、VARIANT 型データを扱うようになっています。そして、VARIANT 型のラッパーとして ATL CComVariant クラスがあります。このクラスを使うと、VARIANT 型を直接扱うより処理が簡単になります。

[MSDN: CComVariant クラス](https://msdn.microsoft.com/ja-jp/library/ac97df2h.aspx)

必要なヘッダー： atlcomcli.h

#CComVariant の機能
##コンストラクタ
変数から直接、CComVariant オブジェクトを作成できます。例えば、LPCSTR 型変数から文字列の VARIANT を作れます。何も指定しなければ、空の VARIANT を作れます。その他にも多くのバージョンがあります。(下記)

- CComVariant() throw();
- CComVariant(const CComVariant& varSrc);
- CComVariant(const VARIANT& varSrc);
- CComVariant(LPCOLESTR lpszSrc);
- CComVariant(LPCSTR lpszSrc);
- CComVariant(bool bSrc);
- CComVariant(BYTE nSrc) throw();
- CComVariant(int nSrc, VARTYPE vtSrc = VT_I4) throw();
- CComVariant(unsigned int nSrc, VARTYPE vtSrc = VT_UI4) throw();
- CComVariant(shor nSrc) throw();
- CComVariant(unsigned short nSrc) throw();
- CComVariant(long nSrc, VARTYPE vtSrc = VT_I4) throw();
- CComVariant(unsigned long nSrc) throw();
- CComVariant(LONGLONG nSrc) throw();
- CComVariant(ULONGLONG nSrc) throw();
- CComVariant(float fltSrc) throw();
- CComVariant(double dblSrc, VARTYPE vtSrc = VT_R8) throw();
- CComVariant(CY cySrc) throw();
- CComVariant(IDispatch* pSrc) throw();
- CComVariant(IUnknown* pSrc) throw();
- CComVariant(const SAFEARRAY* pSrc);
- CComVariant(char cSrc) throw();
- CComVariant(const CComBSTR& bstrSrc);

##メソッド
- **HRESULT CComVariant::Attach(VARIANT* pSrc)**　VARIANT 型変数をアタッチする。
- **HRESULT CComVariant::ChangeType(VARTYPE vtNew, const VARIANT* pSrc = NULL)**　VARIANT の型を変更する。デフォルトでは値は NULL になる。
- **HRESULT CComVariant::Clear()**　中身をクリアする。
- **HRESULT CComVariant::Copy(const VARIANT* pSrc)**　VARIANT 型変数の中身をこのオブジェクトにコピーする。
- **HRESULT CComVariant::CopyTo(BSTR* pstrDest)**　このオブジェクトの中身を対象(BSTR型)にコピーする。このメソッドは VARTYPE が VT_BSTR のみ使用可能。
- **HRESULT CComVariant::Detach(VARIANT* pDest)**　VARIANT 型変数にデタッチする。
- **ULONG CComVariant::GetSize() const**　VARTYPE を含むサイズを返す。
- **HRESULT CComVariant::ReadFromStream(IStream* pStream)**　IStream* 型のストリームから VARIANT を読み込んで自分の値とする。
- **void CComVariant::SetByRef(T* pT)**　自分の VARIANT をパラメータで初期化し VRATYPE に VT_BYREF 型を追加する。
- **HRESULT CComVariant::WriteToStream(IStream* pStream)**　IStream* 型のストリームに自 VARIANT を書き込む。

##演算子
以下のような代入演算子と比較演算子が用意されている。

- CComVariant::operator =　このオブジェクトを対象のオブジェクトに代入する。下記のバージョンがある。
 - CComVariant& operator=(const CComVariant& varSrc);
 - CComVariant& operator=(const VARIANT& varSrc);
 - CComVariant& operator=(const CComBSTR& bstrSrc);
 - CComVariant& operator=(LPCOLESTR lpszSrc);
 - CComVariant& operator=(LPCSTR lpszSrc);
 - CComVariant& operator=(bool bSrc);
 - CComVariant& operator=(BYTE nSrc) throw();
 - CComVariant& operator=(int nSrc) throw();
 - CComVariant& operator=(unsigned int nSrc) throw();
 - CComVariant& operator=(short nSrc) throw();
 - CComVariant& operator=(unsigned short nSrc) throw();
 - CComVariant& operator=(long nSrc) throw();
 - CComVariant& operator=(unsigned long nSrc) throw();
 - CComVariant& operator=(LONGLONG nSrc) throw();
 - CComVariant& operator=(ULONGLONG nSrc) throw();
 - CComVariant& operator=(float fltSrc) throw();
 - CComVariant& operator=(double dblSrc) throw();
 - CComVariant& operator=(CY cySrc) throw();
 - CComVariant& operator=(IDispatch* pSrc) throw();
 - CComVariant& operator=(IUnknown* pSrc) throw();
 - CComVariant& operator=(const SAFEARRAY* pSrc);
 - CComVariant& operator=(char cSrc) throw();
- CComVariant::operator ==　このオブジェクトと VARIANT が等しいかを示す。
- CComVariant::operator !=　このオブジェクトと VARIANT が等しくないかを示す。
- CComVariant::operator <　このオブジェクトより小さいかを示す。
- CComVariant::operator >　このオブジェクトより大きいかを示す。

#VARTYPE について
VARIANT 型は様々な型の変数を収容できる構造体ですが、元々は BASIC 言語 (Visual Basic の先祖) で使われていたものです。そして、VARIANT は Visual C++ では次のように定義されています。

```
typedef struct tagVARIANT {
  union {
    struct __tagVARIANT {
      VARTYPE vt;
      WORD    wReserved1;
      WORD    wReserved2;
      WORD    wReserved3;
      union {
        LONGLONG            llVal;
        LONG                lVal;
        BYTE                bVal;
        SHORT               iVal;
        FLOAT               fltVal;
        DOUBLE              dblVal;
        VARIANT_BOOL        boolVal;
        _VARIANT_BOOL       bool;
        SCODE               scode;
        CY                  cyVal;
        DATE                date;
        BSTR                bstrVal;
        IUnknown            *punkVal;
        IDispatch           *pdispVal;
        SAFEARRAY           *parray;
        BYTE                *pbVal;
        SHORT               *piVal;
        LONG                *plVal;
        LONGLONG            *pllVal;
        FLOAT               *pfltVal;
        DOUBLE              *pdblVal;
        VARIANT_BOOL        *pboolVal;
        _VARIANT_BOOL       *pbool;
        SCODE               *pscode;
        CY                  *pcyVal;
        DATE                *pdate;
        BSTR                *pbstrVal;
        IUnknown            **ppunkVal;
        IDispatch           **ppdispVal;
        SAFEARRAY           **pparray;
        VARIANT             *pvarVal;
        PVOID               byref;
        CHAR                cVal;
        USHORT              uiVal;
        ULONG               ulVal;
        ULONGLONG           ullVal;
        INT                 intVal;
        UINT                uintVal;
        DECIMAL             *pdecVal;
        CHAR                *pcVal;
        USHORT              *puiVal;
        ULONG               *pulVal;
        ULONGLONG           *pullVal;
        INT                 *pintVal;
        UINT                *puintVal;
        struct __tagBRECORD {
          PVOID       pvRecord;
          IRecordInfo *pRecInfo;
        } __VARIANT_NAME_4;
      } __VARIANT_NAME_3;
    } __VARIANT_NAME_2;
    DECIMAL             decVal;
  } __VARIANT_NAME_1;
} VARIANT, *LPVARIANT, VARIANTARG, *LPVARIANTARG;
```

中身の変数の型が何かは VARIANT 構造体の vt 変数で知ることができます。vt の型は VARTYPE ですが、次のように定義されています。


> typedef unsigned short VARTYPE;

VARTYPE の値は次のようなシンボルで定義されています。

- VT_ARRAY　SAFEARRAY のポインタ
- VT_BLOB　Indicates length prefixed bytes.
- VT_BLOB_OBJECT　Indicates that a blob contains an object.
- VT_BOOL　ブール型
- VT_BSTR　BSTR 型文字列
- VT_BYREF　参照型であることを示す。
- VT_CARRAY　C スタイル配列
- VT_CF　Clipboard フォーマット
- VT_CLSID　クラスID
- VT_CY　CURRENCY 型
- VT_DATE　DATE 型
- VT_DECIMAL　10進数
- VT_DISPATCH　IDispatch ポインタ
- VT_EMPTY　値が未定義であることを示す。
- VT_ERROR　SCODE 型
- VT_FILETIME　FILETIME 型
- VT_HRESULT　HRESULT 型
- VT_I1　char 型
- VT_I2　short 型
- VT_I4　long 型 (32bit). COM では 32bit 整数は LONG と定義される。
- VT_I8　64-bit 整数型 (LONG LONG)
- VT_INT　整数
- VT_LPSTR　NULL 終端された文字列
- VT_LPWSTR　NULL 終端されたワイド文字列
- VT_NULL　NULL 値. SQL の NULL 相当
- VT_PTR　ポインタ型
- VT_R4　float 型
- VT_R8　double 型
- VT_RECORD　ユーザ定義型
- VT_SAFEARRAY　SAFEARRAY 型. VARIANT の中では無効 (エラー) になる。
- VT_STORAGE　Indicates that the name of a storage follows.
- VT_STORED_OBJECT　Indicates that a storage contains an object.
- VT_STREAM　Indicates that the name of a stream follows.
- VT_STREAMED_OBJECT　Indicates that a stream contains an object.
- VT_UI1　BYTE 型
- VT_UI2　unsigned short 型
- VT_UI4　unsigned long 型 (32-bit)
- VT_UI8　64-bit unsigned integer.
- VT_UINT　符号なしの整数
- VT_UNKNOWN　IUnknown ポインタ
- VT_USERDEFINED　ユーザ定義型
- VT_VARIANT　VARIANT のポインタ
- VT_VECTOR　Indicates a simple, counted array.
- VT_VOID　C の void 型

#サンプル
##ヘッダーファイル
```
// stdafx.h : 標準のシステム インクルード ファイルのインクルード ファイル、または
// 参照回数が多く、かつあまり変更されない、プロジェクト専用のインクルード ファイル
// を記述します。
//

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

// TODO: プログラムに必要な追加ヘッダーをここで参照してください
#include <atlcomcli.h>
#include <atlstr.h>
#include <atlsafe.h>
```

##CPP ファイル

```C++
// TestCComVariant.cpp : アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"

//
//  CComVariant のテスト
//  ====================
int main()
{
	// LONG 型の VARIANT
	CComVariant v3(1000L);
	printf_s("%s\n", v3.vt == VT_I4 ? "True" : "False");  // True になる。

	// VARIANT に代入
	VARIANT x = v3;
	printf_s("%d\n", x.lVal);

	// BSTR の CComVariant を作成
	CComBSTR bv0(L"bv0");
	CComVariant cv0(bv0);
	wprintf_s(L"%s\n", cv0.bstrVal);

	// VARIANT を作成
	VARIANT cv1;
	cv1.bstrVal = CComBSTR(L"VARIANT cv1");
	cv1.vt = VT_BSTR;

	// BSTR 型の VARIANT
	CComVariant v1(cv1);
	printf_s("%s\n", v1.vt == VT_BSTR ? "True" : "False");  // True になる。
	wprintf_s(L"%s\n", v1.bstrVal);

	// BSTR 型の VARIANT をアタッチ
	CComVariant v2;
	v2.Attach(&cv1);
	printf_s("%s\n", v2.vt == VT_BSTR ? "True" : "False");  // True になる。
	wprintf_s(L"%s\n", v2.bstrVal);

	// 他の VARIANT の中身を自分にコピーする。
	CComVariant x2;
	x2.Copy(&v3);
	printf_s("%d\n", x2.lVal);

	// 自 BSTR を 他の BSTR へコピーする。
	BSTR bstr;
	v1.CopyTo(&bstr);
	wprintf_s(L"%s\n", bstr);

	getchar();
	return 0;
}
```

![SampleCComVariant.png](https://qiita-image-store.s3.amazonaws.com/0/62802/bacc15e6-37c2-6537-c867-815ccbac578e.png)

Fig.1 サンプルの実行例