LoginSignup
11
8

More than 5 years have passed since last update.

Visual Studio 2017 Visual C++ による ATL COM DLL の作成

Last updated at Posted at 2018-02-10

はじめに

前に 「Visual Studio 2017 Visual C++ による ATL の基本」 という投稿を行いましたが、かなり前に作った原稿なので Visual Studio 2017 Update 3 (v15.3) より前のバージョンで作成しています。

Update 3 において、画面等の操作性がかなり変更になっており、もしかしたら現在の Update 5 と異なっているかもしれません。

そこで新たに ATL を使って COM DLL のサンプル BitField2.dll を作成してみました。

サンプル AxBitField2.dll の仕様

概要

AxBitField2.dll は 32bit ワードをビット単位で扱う COM DLL です。ワードの一部をビット、バイト、16ビットワード単位で書き換えできます。指定されたビットフィールド。バイト、16ビットワードの読み出しもできます。

名称

  • インターフェース名: IBitField
  • インターフェース ID (IID): 459d383d-5bd4-4beb-83ee-48ca15bfe317
  • ライブラリ名: AxBitField2Lib
  • ライブラリ ID: e188ca82-11fe-41ef-bae5-4c51d103fa30
  • クラス名: BitField
  • クラス ID (CLSID): c1a7d0f-29b0-415e-9c3d-cd2486369f39

プロパティ

  • long LongWord (get / set) 32 bit のワードを取得/設定する。(* 64 bit は long long 型)
  • short SubWord (get / set) GetBitsEx メソッドで使用する16 bit のワードを取得/設定する。

メソッド

  • void Endian(): バイト並びを逆にする。
  • long GetBits(short p, short n): ビット指定位置 p から n ビット分のビットフィールドを切り出す。
  • long GetBitsEx(short p, short n): GetBits と同じだが、ビットフィールドの範囲を超えたら SubWord を使って補完する。
  • short GetByte(short p): バイト指定位置 p のバイトを取得する。(p = 0, 1, 2, 3)
  • short GetWord(short p): 16ビットワード指定位置 p のバイトを取得する。(p = 0, 1)
  • void SetBits(short p, short n, long data): ビット指定位置 p から n ビット分のビットフィールドを書き換える。
  • void SetByte(short p, short data): 指定位置のバイトを書き換える。(p = 0, 1, 2, 3)
  • void SetWord(short p, short data): 指定位置の16ビットワードを書き換える。(p = 0, 1)

プロジェクトの作成

「新しいプロジェクト」ダイアログを開いて、「ATL プロジェクト」を選んで OK ボタンをクリックします。

VCProj_ATL.png
Fig.1 「新しいプロジェクト」ダイアログ

Fig.2 のようなダイアログが開くのでプロジェクトのオプションを設定します。アプリケーションの種類は、DLL のままとし、その他もそのままとします。

ATLProject5.png
Fig.2 ATL プロジェクトのオプション

ソリューションエクスプローラでこのプロジェクトを選択して、「追加」メニューで「新しい項目の追加」を実行します。Fig.3 のようなダイアログが開いたら、 「ATL シンプルオブジェクト」を選択し、名前を適切なものに変更して「追加」ボタンをクリックします。この名前は クラスファイルの名称で、デフォルトのままだと DLL に含まれるクラス名 (ただし、"C" が先頭に付加される) になります。

ATLNewProject5.png

Fig.3 「新しい項目の追加」ダイアログ

次に Fig.4 のようなダイアログが開きます。必要なら名前を変更できますが、たいていはこのままでいいので「次へ」ボタンをクリックします。

SimpleObject5.png
Fig.4 「名前の入力」ダイアログ

次に、Fig.5 のようなダイアログが開きますが、これはこのままでいいので「次へ」ボタンをクリックします。

SimpleObject5-1.png
Fig.5 「ファイルの種類のオプション」ダイアログ

次に、Fig.6 のようなダイアログが開きますが、これもこのままでいいので「完了」ボタンをクリックします。

SimpleObject5-2.png
Fig.6 最後のダイアログ

最後に、プロジェクトに関連クラス等が追加されます。

AxBitField2_ProjTree.png
Fig.7 プロジェクトに含まれるファイル

プロパティの追加

  • ソリューションエクスプローラで当該プロジェクトを選択し、クラスビューを表示する。
  • ビューツリーでインターフェースを選択する。この例では IBitField を選ぶ。(複数あるが最後のもの)
  • コンテキストメニューを開き、「追加」/「プロパティの追加」を実行する。
  • Fig.8 のようなダイアログが開くのでプロパティの種類と名称などを入力して、次へをクリックする。
  • Fig.9 のようなダイアログが開くので、必要なら項目を入力して「完了」ボタンをクリックする。
  • プロパティがコードに追加される。(.idl, .h, .cpp ファイル)
  • (注意) 間違ってしまった場合は手動でコードを修正する。

IBitField_AddProp.png
Fig.8 プロパティの追加ウィザード

IBitField_AddProp2.png
Fig.9 プロパティの追加ウィザード (IDL 属性)

メソッドの追加

  • ソリューションエクスプローラで当該プロジェクトを選択し、クラスビューを表示する。
  • ビューツリーでインターフェースを選択する。この例では IBitField を選ぶ。(複数あるが最後のもの)
  • コンテキストメニューを開き、「追加」/「メソッドの追加」を実行する。
  • Fig.10 のようなダイアログが開くのでプロパティの種類と名称などを入力して、次へをクリックする。
  • IDL 属性入力ダイアログが開くので、必要なら項目を入力して「完了」ボタンをクリックする。
  • メソッドがコードに追加される。(.idl, .h, .cpp ファイル)
  • (注意) 間違ってしまった場合は手動でコードを修正する。

IBitField_AddMethod.png
Fig.10 メソッドの追加ウィザード

プロパティとメソッドの追加が終わったら、それらの具体的なコードを記述してビルドを行う。

ビルトと登録

ビルドでエラーがなくなったら、DLL と TLB (Type Library) ができているはずなので、適切なフォルダへコピーして、改めて regsvr32 によりレジストリに DLL (COM オブジェクト) を登録する。

ここでは c:\lib に AxBitField2.dll と AxBitField2.tlb をコピーして登録する例を示す。

C:\lib> regsvr32 AxBitField2.dll

テスト (COM DLL の使用例)

下にテストプログラムの例を示します。

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

#include "stdafx.h"
#import "C:\lib\AxBitField2.tlb" named_guids no_namespace

int main()
{
    // COM の初期化のため必要
    CoInitialize(NULL);

    // COM オブジェクトを定義
    CComPtr<IBitField> pObj;

    // COM オブジェクトを作成
    HRESULT hr = pObj.CoCreateInstance(__uuidof(BitField));

    // メソッドとプロパティの使用

    // LongWord
    pObj->LongWord = 0x01020304;
    printf_s("%08x\n", pObj->LongWord);
    pObj->Endian();
    printf_s("Endian: %08x\n", pObj->LongWord);

    // SetBits
    pObj->SetBits(7, 4, 0xb);
    printf_s("SetBits: %08x\n", pObj->LongWord);

    // SetByte
    pObj->SetByte(1, 0xbc);
    printf_s("SetByte: %08x\n", pObj->LongWord);

    // GetByte
    printf_s("%02x\n", pObj->GetByte(0));
    printf_s("%02x\n", pObj->GetByte(1));
    printf_s("%02x\n", pObj->GetByte(2));
    printf_s("%02x\n", pObj->GetByte(3));

    // SetWord
    pObj->SetWord(0, 0xc0d0);
    printf_s("SetWord: %08x\n", pObj->LongWord);

    // GetWord
    printf_s("%04x\n", pObj->GetWord(1));
    printf_s("%04x\n", pObj->GetWord(2));

    // GetBits
    printf_s("%01x\n", pObj->GetBits(7, 4));
    printf_s("%01x\n", pObj->GetBits(6, 4));
    printf_s("%01x\n", pObj->GetBits(5, 4));

    // SubWord
    pObj->SubWord = 0xffff;
    printf_s("SubWord: %08x\n", pObj->SubWord);

    // GetBitsEx
    printf_s("%08x\n", pObj->GetBitsEx(10, 6));

    getchar();
    return 0;
}

AxBitField2 のコード

以下に AxBitField2 プログラムのコードを示します。

AxBitField2.idl

// AxBitField2.idl : AxBitField2 の IDL ソース
//

// このファイルは、タイプ ライブラリ ([!output SAFE_IDL_NAME].tlb) およびマーシャリング コードを
// タイプ ライブラリ (AxBitField2.tlb) とマーシャリング コードを生成します。

import "oaidl.idl";
import "ocidl.idl";

[
    object,
    uuid(459d383d-5bd4-4beb-83ee-48ca15bfe317),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface IBitField : IDispatch
{
    [propget, id(1)] HRESULT LongWord([out, retval] ULONG* pVal);
    [propput, id(1)] HRESULT LongWord([in] ULONG newVal);
    [propget, id(2)] HRESULT SubWord([out, retval] USHORT* pVal);
    [propput, id(2)] HRESULT SubWord([in] USHORT newVal);
    [id(3)] HRESULT Endian();
    [id(4)] HRESULT GetBits([in] SHORT b, [in] SHORT n, [out, retval] ULONG* data);
    [id(5)] HRESULT GetByte([in] SHORT b, [out, retval] USHORT* data);
    [id(6)] HRESULT GetWord([in] SHORT w, [out, retval] USHORT* data);
    [id(7)] HRESULT SetBits([in] SHORT b, [in] SHORT n, [in] ULONG data);
    [id(8)] HRESULT SetByte([in] SHORT b, [in] USHORT data);
    [id(9)] HRESULT SetWord([in] SHORT w, [in] USHORT data);
    [id(10)] HRESULT GetBitsEx([in] SHORT b, [in] SHORT n, [out, retval] ULONG* data);
};
[
    uuid(e188ca82-11fe-41ef-bae5-4c51d103fa30),
    version(1.0),
]
library AxBitField2Lib
{
    importlib("stdole2.tlb");
    [
        uuidd(c1a7d0f-29b0-415e-9c3d-cd2486369f39)
    ]
    coclass BitField
    {
        [default] interface IBitField;
    };
};

import "shobjidl.idl";

BitField.h

// BitField.h : CBitField の宣言

#pragma once
#include "resource.h"       // メイン シンボル

#include "AxBitField2_i.h"

#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "DCOM の完全サポートを含んでいない Windows Mobile プラットフォームのような Windows CE プラットフォームでは、単一スレッド COM オブジェクトは正しくサポートされていません。ATL が単一スレッド COM オブジェクトの作成をサポートすること、およびその単一スレッド COM オブジェクトの実装の使用を許可することを強制するには、_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA を定義してください。ご使用の rgs ファイルのスレッド モデルは 'Free' に設定されており、DCOM Windows CE 以外のプラットフォームでサポートされる唯一のスレッド モデルと設定されていました。"
#endif

using namespace ATL;

// CBitField

class ATL_NO_VTABLE CBitField :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClasslt;CBitField, &CLSID_BitField>,
    public IDispatchImpllt;IBitField, &IID_IBitField, &LIBID_AxBitField2Lib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
    ULONG m_longWord;
    WORD  m_subWord;
    BYTE  m_bytes[4];
    BYTE  m_subbytes[2];
    BYTE  m_bits[32];
    BYTE  m_subbits[16];

    CBitField()
    {
        short i;

        m_longWord = 0;
        m_subWord = 0;

        for (i = 0; i lt; 32; i++)
            m_bits[i] = 0;

        for (i = 0; i lt; 16; i++)
            m_subbits[i] = 0;

        for (i = 0; i lt; 4; i++)
            m_bytes[i] = 0;

        for (i = 0; i lt; 2; i++)
            m_subbytes[i] = 0;
    }

DECLARE_REGISTRY_RESOURCEID(106)

BEGIN_COM_MAP(CBitField)
    COM_INTERFACE_ENTRY(IBitField)
    COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

private:
    void updateData();
public:
    STDMETHOD(get_LongWord)(ULONG* pVal);
    STDMETHOD(put_LongWord)(ULONG newVal);
    STDMETHOD(get_SubWord)(USHORT* pVal);
    STDMETHOD(put_SubWord)(USHORT newVal);
    STDMETHOD(Endian)();
    STDMETHOD(GetBits)(SHORT b, SHORT n, ULONG* data);
    STDMETHOD(GetByte)(SHORT b, USHORT* data);
    STDMETHOD(GetWord)(SHORT w, USHORT* data);
    STDMETHOD(SetBits)(SHORT b, SHORT n, ULONG data);
    STDMETHOD(SetByte)(SHORT b, USHORT data);
    STDMETHOD(SetWord)(SHORT w, USHORT data);
    STDMETHOD(GetBitsEx)(SHORT b, SHORT n, ULONG* data);
};

OBJECT_ENTRY_AUTO(__uuidof(BitField), CBitField)

BitField.cpp

// BitField.cpp : CBitField の実装

#include "stdafx.h"
#include "BitField.h"


template<class T>
void swap(T& x, T& y)
{
    T w;
    w = x;
    x = y;
    y = w;
}

// CBitField

//
//  内部変数を更新する
//  ==================
void CBitField::updateData()
{
    short i;
    m_bytes[0] = static_cast<BYTE>(m_longWord & 0xff);
    m_bytes[1] = static_cast<BYTE>((m_longWord >> 8) & 0xff);
    m_bytes[2] = static_cast<BYTE>((m_longWord >> 16) & 0xff);
    m_bytes[3] = static_cast<BYTE>((m_longWord >> 24) & 0xff);
    m_subbytes[0] = static_cast<BYTE>(m_subWord & 0xff);
    m_subbytes[1] = static_cast<BYTE>((m_subWord >> 8) & 0xff);

    for (i = 0; i < 32; i++)
    {
        m_bits[i] = (static_cast<ULONG>(m_longWord) & (1 << i)) ? 1 : 0;
    }
    for (i = 0; i < 16; i++)
    {
        m_subbits[i] = (static_cast<ULONG>(m_subWord) & (1 << i)) ? 1 : 0;
    }
}

//
//   現在の32bitデータを得る
//   =======================
STDMETHODIMP CBitField::get_LongWord(ULONG* pVal)
{
    *pVal = static_cast<long>(m_longWord);
    return S_OK;
    return S_OK;
}

//
//   現在の32bitデータを設定する
//   ===========================
STDMETHODIMP CBitField::put_LongWord(ULONG newVal)
{
    m_longWord = static_cast<ULONG>(newVal);
    updateData();
    return S_OK;
    return S_OK;
}

//
//  サブワード(32ビット境界を超えてビットフィールドを取得するとき使う)
//  ===================================================================
STDMETHODIMP CBitField::get_SubWord(USHORT* pVal)
{
    *pVal = m_subWord;
    return S_OK;
}


STDMETHODIMP CBitField::put_SubWord(USHORT newVal)
{
    m_subWord = newVal;
    updateData();
    return S_OK;
}

//
//   バイト並びを変更する
//   ====================
STDMETHODIMP CBitField::Endian()
{
    swap(m_bytes[3], m_bytes[0]);
    swap(m_bytes[2], m_bytes[1]);
    m_longWord = m_bytes[3] << 24 | m_bytes[2] << 16 | m_bytes[1] << 8 | m_bytes[0];
    for (short i = 0; i < 32; i++)
    {
        m_bits[i] = (static_cast<ULONG>(m_longWord) & (1 << i)) ? 1 : 0;
    }
    return S_OK;
}


//
//   指定位置bと長さnのビットフィールドを得る
//   ===========================================
STDMETHODIMP CBitField::GetBits(SHORT b, SHORT n, ULONG* data)
{
    short i, p;
    ULONG uData = 0;

    for (i = 0; i < n; i++)
    {
        p = b - n + i + 1;
        uData |= (m_bits[p] << i);
    }

    *data = static_cast<long>(uData);
    return S_OK;
}

//
//   指定位置のバイトを得る
//   ======================
STDMETHODIMP CBitField::GetByte(SHORT b, USHORT* data)
{
    *data = static_cast<short>(m_bytes[b]);
    return S_OK;
}

//
//   指定位置のワードを得る
//   ======================
STDMETHODIMP CBitField::GetWord(SHORT w, USHORT* data)
{
    switch (w)
    {
    case 0:
        *data = static_cast<short>(m_bytes[0]);
        break;

    case 1:
        *data = (static_cast<short>(m_bytes[1]) << 8) | static_cast<short>(m_bytes[0]);
        break;

    case 2:
        *data = (static_cast<short>(m_bytes[2]) << 8) | static_cast<short>(m_bytes[1]);
        break;

    case 3:
        *data = (static_cast<short>(m_bytes[3]) << 8) | static_cast<short>(m_bytes[2]);
        break;
    }
    return S_OK;
}


//
//   指定位置のビットフィールドを変更する
//   =====================================
STDMETHODIMP CBitField::SetBits(SHORT b, SHORT n, ULONG data)
{
    BYTE Bits[32];
    short i;

    // 与えられたデータをビットに分解
    for (i = 0; i < n; i++)
    {
        Bits[i] = (data & (1 << i)) ? 1 : 0;
    }

    // 元データの当該ビットを置き換え
    for (i = 0; i < n; i++)
    {
        m_bits[b - i] = Bits[n - i - 1];
    }

    // m_lonWord, m_bytesに反映
    m_longWord = 0;
    for (i = 0; i < 32; i++)
    {
        m_longWord |= ((m_bits[i] ? 1 : 0) << i);
    }

    m_bytes[0] = static_cast<BYTE>(m_longWord & 0xff);
    m_bytes[1] = static_cast<BYTE>((m_longWord >> 8) & 0xff);
    m_bytes[2] = static_cast<BYTE>((m_longWord >> 16) & 0xff);
    m_bytes[3] = static_cast<BYTE>((m_longWord >> 24) & 0xff);

    return S_OK;
}

//
//   指定位置のバイトを変更する
//   ==========================
STDMETHODIMP CBitField::SetByte(SHORT b, USHORT data)
{
    union {
        BYTE b[4];
        DWORD dw;
    } work;

    data &= 0xff;
    work.dw = m_longWord;

    switch (b)
    {
    case 0:
        work.b[0] = (BYTE)data;
        m_longWord = work.dw;
        break;

    case 1:
        work.b[1] = (BYTE)data;
        m_longWord = work.dw;
        break;

    case 2:
        work.b[2] = (BYTE)data;
        m_longWord = work.dw;
        break;

    case 3:
        work.b[3] = (BYTE)data;
        m_longWord = work.dw;
        break;
    }

    updateData();
    return S_OK;
}


//
//   指定位置のワードを変更する
//   ==========================
STDMETHODIMP CBitField::SetWord(SHORT w, USHORT data)
{
    union {
        WORD w[4];
        DWORD dw;
    } work;

    work.dw = m_longWord;

    if (w == 0)
    {
        // 下位ワード
        work.w[0] = data;
        m_longWord = work.dw;
    }
    else
    {
        // 上位ワード
        work.w[1] = data;
        m_longWord = work.dw;
    }

    updateData();

    return S_OK;
}

//
//  サブワードを使ったビット列抽出
//  ===============================
STDMETHODIMP CBitField::GetBitsEx(SHORT b, SHORT n, ULONG* data)
{
    short i, p;
    ULONG uData = 0;

    for (i = 0; i < n; i++)
    {
        p = b - n + i + 1;
        if (p >= 0)
        {
            uData |= (m_bits[p] << i);
        }
        else
        {
            uData |= (m_subbits[p + 16] << i);
        }
    }

    *data = static_cast<long>(uData);

    return S_OK;
}

-

11
8
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
11
8