LoginSignup
2
4

More than 5 years have passed since last update.

Visual Studio 2017 Visual C++ : COM 配列 SAFEARRAY と CComSafeArray

Posted at

はじめに

Windows には SAFEARRAY というデータ構造がありますが、ATL の CComSafeArray クラスはこの SAFEARRAY のラッパーです。SAFEARRAY を扱うためには、CreateSafeArray 関数など Win32 API 関数がありますが、CComSafeArray クラスを使ったほうがより簡単になります。

SAFEARRAY を使う理由ですが、C++ で書かれたモジュールと他のモジュールとのやり取りが主な目的だそうです。SAFEARRAY はモジュール境界を超えられますが、std::vector はモジュール境界を超えられないので、外部とのやり取りには利用できないそうです。

CComSafeArray の詳しい説明は下のリンクを参照してください。

MSDN Magazine: C++ - CComSafeArray による C++ でのセーフ配列プログラミングの簡素化

MSDN: CComSafeArray クラス

必要なヘッダー: atlsafe.h

CComSafeArray の構築

CComSafeArray オブジェクトを構築する方法は、以下のように多数あります。空の SAFEARRAY を構築した場合は、Add メソッドや Atach メソッドで後から要素を追加したり設定できます。

配列の長さだけ指定して、中身は後から指定することもできます。配列の添字はデフォルトでは 0 ですが、それ以外の値に変更することもできます。

  • CComSafeArray();
  • CComSafeArray(const SAFEARRAYBOUND& bound);
  • CComSafeArray(ULONG ulCount, LONG lLBound = 0);
  • CComSafeArray(const SAFEARRAYBOUND* pBound, UINT uDims = 1);
  • CComSafeArray(const CComSafeArray& saSrc);
  • CComSafeArray(const SAFEARRAY& saSrc);
  • CComSafeArray(const SAFEARRAY* psaSrc);

CComSafeArray のメソッド

  • CComSafeArray::Add Add メソッドは要素を配列の最後に追加します。次のように3種類のバージョンがあります。
    • HRESULT Add(const SAFEARRAY* psaSrc);
    • HRESULT Add(ULONG ulCount, const T* pT, BOOL bCopy = TRUE);
    • HRESULT Add(const T& t, BOOL bCopy = TRUE);
  • HRESULT Attach(const SAFEARRAY* psaSrc); SAFEARRAY* 型の psaSrc をこのオブジェクトの配列にアタッチ (psaSrc に設定されます) します。
  • HRESULT CopyFrom(LPSAFEARRAY* ppArray); LPSAFEARRAY* ppArray の内容をこのオブジェクトの配列にコピーする (このオブジェクトの配列が ppArray の中身に置き換わる)。
  • HRESULT CopyTo(LPSAFEARRAY* ppArray); このオブジェクトの配列のコピーを LPSAFEARRAY* ppArray に作成する。
  • CComSafeArray::Create  CComSafeArray オブジェクトを構築する。次の2つのバージョンがある。
    • HRESULT Create(const SAFEARRAYBOUND* pBound, UINT uDims = 1);
    • HRESULT Create(ULONG ulCount = 0, LONG lLBound = 0);
  • HRESULT Destroy(); CComSafeArray オブジェクトを破棄する。
  • LPSAFEARRAY Detach(); このオブジェクトから SAFEARRAY 配列をデタッチする (配列の割り当て解除を行う)。戻り値はデタッチした配列のポインタになる。
  • T& GetAt(LONG lIndex) const; 配列要素の添字 lIndex で指定した要素を返す。
  • ULONG GetCount(UINT uDim = 0) const; 配列の要素の数を返す。
  • UINT GetDimensions() const; 配列の次元を返す。
  • LONG GetLowerBound(UINT uDim = 0) const; uDim で指定した次元の下の境界番号 (要素の添字の最小値) を返す。
  • LPSAFEARRAY* GetSafeArrayPtr() throw(); この CComSafeArray オブジェクトに含まれる SAFEARRAY のポインタを返す。
  • VARTYPE GetType() const; SAFEARRAY のデータ型を返す。
    • VT_I1 : char
    • VT_I2 : short
    • VT_I4 : int
    • VT_I4 : long
    • VT_I8 : longlong
    • VT_UI1 : byte
    • VT_UI2 : ushort
    • VT_UI4 : uint
    • VT_UI4 : ulong
    • VT_UI8 : ulonglong
    • VT_R4 : float
    • VT_R8 : double
    • VT_DECIMAL : decimal pointer
    • VT_VARIANT : variant pointer
    • VT_CY : Currency data type
  • LONG GetUpperBound(UINT uDim = 0) const; uDim で指定した次元の上の境界番号 (要素の添字の最大値) を返す。
  • bool IsSizable() const; このオブジェクトの配列はサイズを変更可能かを返す。
  • LPSAFEARRAY m_psa; この CComSafeArray オブジェクトの SAFEARRAY 構造体へのポインタを保持する公開メンバー変数。
  • HRESULT MultiDimGetAt(const LONG* alIndex, T& t); 多次元配列の要素を得る。結果は t に返される。
  • HRESULT MultiDimSetAt(const LONG* alIndex, const T& t); 多次元配列の要素をセットする。
  • HRESULT SetAt(LONG lIndex, const T& t, BOOL bCopy = TRUE); 配列の要素をセットする。
  • CComSafeArray::Resize 配列をリサイズする。次の2つのバージョンがある。
    • HRESULT Resize(const SAFEARRAYBOUND* pBound);
    • HRESULT Resize(ULONG ulCount, LONG lLBound = 0);

CComSafeArray の演算子

  • CComSafeArray::operator [] 配列の要素を得る。これは GetAt メソッドと同じである。次の2つのバージョンがある。
    • T& operator const;
    • T& operator const;
  • CComSafeArray::operator = 代入演算子。次の2つのバージョンがある。
    • ATL::CComSafeArray& operator=(const ATL::CComSafeArray& saSrc);
    • ATL::CComSafeArray& operator=(const SAFEARRAY* psaSrc);

サンプル

ヘッダーファイル

// stdafx.h : 標準のシステム インクルード ファイルのインクルード ファイル、または
// 参照回数が多く、かつあまり変更されない、プロジェクト専用のインクルード ファイル
// を記述します。
//
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>

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

CPP ファイル

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

#include "stdafx.h"

//
//  CComSafeArray のテスト
//  ======================
int main()
{
    // インデックスが 0 から始まる長さ 100 の配列。中身は 0 で初期化される。
    CComSafeArray ai(100);
    int i;

    // GetAt, SetAt メソッド
    printf_s("%d\n", ai.GetAt(0));
    ai.SetAt(0, 50);
    printf_s("%d\n", ai.GetAt(0));
    // 境界の外に書き込み
    //   DEBUG モードでは Assert が起こる。RELEASE モードでは何もしない。
    ai.SetAt(100, 100);

    // 配列の境界は指定可能
    CComSafeArray<short> as(10, 1);  // インデックスが 1 から始まる長さ 10 の配列。
    printf_s("LB = %d, UB = %d\n", as.GetLowerBound(), as.GetUpperBound());

    // Add メソッド
    as.Add(1);
    as.Add(2);
    printf_s("LB = %d, UB = %d\n", as.GetLowerBound(), as.GetUpperBound());
    printf_s("%d, %d\n", as.GetAt(11), as.GetAt(12));

    // 中身の SAFEARRAY を参照
    LPSAFEARRAY *sa = ai.GetSafeArrayPtr();

    // 終わり
    getchar();
    return 0;
}

SampleCComSafeArray.png
Fig.1 サンプルの実行例

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4