VisualC++ のマクロ変数を追加する方法

VisualStudio2003/2005 の VC++ のプロパティ定義では,マクロ変数を使用することができます。マクロ変数にはプロジェクトのディレクトリなどがあらかじめ定義されていて,一覧表から選択する形式になっています。

p1.gif

この便利なマクロ変数に独自の定義を追加したいところなのですが,ダイアログには「追加」などのボタンは見当たりません。

で,登録できないのかと思うと,実はちゃんとできたりします。ただ,その手順がとても不自然(!!)な上,ヘルプの説明も見つけにくいので,備忘録として書き留めておきます。

1. 「プロパティマネージャ」ウィンドウを表示。

VC2005は,メニュー [表示]->[その他のウィンドウ]->[プロパティマネージャ]と指定します。VC2008は[表示]メニューで直接選択できるようです。

2. プロパティマネージャのツリーで,追加するプロジェクトを選択。

3. ウィンドウ上部のツールボタン「新しいプロジェクトプロパティシートの追加」を選択。

4. 「新しい項目の追加」ダイアログでファイル名を指定して,プロパティシートを新規作成。

p2.gif

5. プロパティマネージャで追加したプロパティシートをダブルクリックして開く。

6. 「プロパティページ」ダイアログで「共通プロパティ/ユーザーマクロ」を選択する。

7. マクロペインが開くので,マクロの追加を行う。

p3.gif

以上の操作で,設定されたマクロがプロジェクトから参照可能になっています。

P4.gif

もう少し直接的な入力UIはできないものでしょうか(^^;


auto_ptr の微妙な仕様(「バグ」とも言う(^^;)

C++ の std::auto_ptr に関する,ちょっとした覚書です。
# 以下の内容は VS2005 の VC++(8.0)で確認したものです。
# 他のバージョンや他処理系では動作に違いがあるかも知れません。

auto_ptr は C++ インスタンスの寿命(生成/開放)を管理するテンプレートで,
  auto_ptr<SomeClass> aPtr(new SomeClass());

のように宣言しておくと,メソッド終了時などにインスタンスを開放してくれる訳ですが,この宣言を
  auto_ptr<SomeClass> aPtr = new SomeClass();
とやってしまうと,実行時に Access Vioration で落ちてしまいます。

コンパイル時にはエラーや警告など,一切出ません。
で,理由が分からずに深く悩んだりします(^^;

仕方ないので出力されたコードを調べてみると,ポインタの代入時に auto_ptr_ref というクラスへの変換が挿入されてしまっているのが分かります。

  auto_ptr aPtr = auot_ptr_ref(new SomeClass();)

だいたいこんなイメージです(厳密にはちょっと違いますが)。

auto_ptr には T* を引数とする代入演算子がなく,auto_ptr_ref を引数として取る代入演算子が定義されているため,このような暗黙の変換が挿入されるのだと思われます。

これでうまく動くのならばOKなのですが,実際のコードはポインタの参照値がポインタとして扱われてしまっていて(SomeClass** として参照されていて),アクセスした時点でダウン,という構造になっています。

で,この auto_ptr_ref というクラス,ドキュメントにも記載されていない内部定義のようですが,どうにも実装が怪しい。

// TEMPLATE CLASS auto_ptr
template<class _Ty>
class auto_ptr;

template<class _Ty>
struct auto_ptr_ref
{ // proxy reference for auto_ptr copying
auto_ptr_ref(void *_Right)
: _Ref(_Right)
{ // construct from generic pointer to auto_ptr ptr
}

void *_Ref; // generic pointer to auto_ptr ptr
};

auto_ptr の宣言がこれに続くのですが,これを見ただけで
  • 前方参照もしていない auto_ptr を宣言しているのは何故?
  • テンプレート引数 _Ty は使用しない?(テンプレートじゃない?)
など,疑問点がふつふつ湧いてきます。

このクラス(テンプレート?)自体は,auto_ptr 同士を代入演算子で転送するときのためのものと思うのですが,実装のレベルや今回のポインタ代入の問題を見ると,どうにも失敗作としか思えません。

もう少し完成度の高い Smart Pointer が一般的に使えるようになるまでは,
  1. 初期化はコンストラクタ引数で行う
  2. auto_ptr 同士の代入などは行わない
という教訓の元で使いたい,と思うのでした(^^;

追記:
やっぱり同じ内容で悩んでいる人はいるものですね。ご苦労さまです:
http://www.velocityreviews.com/forums/t458584-autoptr-assignment-crash-with-vc-language-extensions.html

タグ:C++ STL

Template クラスを friend にする

タイトルどおり(^^)
まさにAdversaria(備忘録)です。


class aClass {
.................
// テンプレートfriend
template<class T>
friend class aTemplate;
.................


Googleで調べてもなかなか出てこなくて,ちょっと悩みましたが,
普通に書いて大丈夫でした。

当然ですが,Template の実装は別に記述します。


template<class T>
class aTempalte {
..................
T value;
..................
}

ATLをサポートするコマンドアプリの作り方

VisualStudio 2005 以降のプロジェクト・ウィザードでは, ATLプロジェクトで生成するアプリケーションの形式として, DLL, サービスの他に「実行可能なアプリケーション」が選択できるようになりました。

この時生成されるプロジェクトはATLで記述したCOMをサービスするものですが, これを改造して通常のコンソールアプリとするための修正内容をメモします。

ウィザードで生成されるメインコードは, 次のようなCAtlExeModuleTテンプレート派生クラスと メイン関数のみでできています。

class CMyAppModule : public CAtlExeModuleT< CMyAppModule >
{
public :
	DECLARE_LIBID(LIBID_MyAppLib)
	DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYAPP, "{E115D6B3-7016-4740-AF95-D2B76569B56F}")
}

CMyAppModule _AtlModule;

extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, 
                                LPTSTR /*lpCmdLine*/, int nShowCmd)
{
    return _AtlModule.WinMain(nShowCmd);
}

この _tWinMain で行っている処理を別スレッドにして,メインスレッドで 必要な処理をするように変更できれば,目的が達成できそうです。

方法はいろいろありますが,次のような形式で実装してみました。

1. _AtlModule::WinMain から CMyAppModule::Run メソッドがコールされるので これをオーバーロードします。
HRESULT
CMyAppModule::Run(int nShowCmd) throw() {
    this->showcmd_ = nShowCmd;
    unsigned int threadID;
    HANDLE hThread;
    startThread(hThread, threadID);
    doMyOperation();
    stopThread(hThread, threadID);
    return S_OK;
}

startThread / stopThread はスレッドの起動/停止メソッドで, ここで起動するスレッドで元々の Run メソッドを実行してやります。
void
CMyAppModule::startThread(HANDLE& hThread, unsigned int& threadID) {
    hThread = (HANDLE)::_beginthreadex(NULL, 0, threadFunc, (LPVOID)this, 0, &threadID);
}

void
CMyAppModule::stopThread(HANDLE hThread, DWORD threadID) {
    do {
        ::PostThreadMessage(threadID, WM_QUIT, NULL, NULL);
    } while(::WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT);
}

stopThread では, スレッドに WM_QUIT を投げた後,停止の待ち合わせをしています。

スレッド関数 threadFunc はこんな感じ:
static unsigned int __stdcall threadFunc(LPVOID param) {
    ::CoInitialize(NULL);
    CMyAppModule* this_ = (CMyAppModule*)param;
    return this_->CAtlExeModuleT::Run(this_->showcmd_);
}

インスタンス変数 showcmd_ は Run メソッドに渡された引数をスレッド関数に 引き渡すためのものです。

doMyOperation( ) は実際に行いたい処理を実装する場所です。

コード全体としては,おおよそ以下のような形式です。 エラー処理などは必要に応じて実装してください(^^;)。
class CMyAppModule : public CAtlExeModuleT< CMyAppModule >
{
public :
    DECLARE_LIBID(LIBID_MyAppLib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYAPP, "{E115D6B3-7016-4740-AF95-D2B76569B56F}")

    HRESULT
    CMyAppModule::Run(int nShowCmd) throw() {
        showcmd_ = nShowCmd;
        unsigned int threadID;
        HANDLE hThread;
        startThread(hThread, threadID);
	doMyOperation( );
        stopThread(hThread, threadID);
        return S_OK;
    }

private:
    int showcmd_;

    void startThread(HANDLE& hThread, unsigned int& threadID) {
        hThread = (HANDLE)::_beginthreadex(NULL, 0, threadFunc, (LPVOID)this, 0, &threadID);
    }

    void stopThread(HANDLE hThread, DWORD threadID) {
        do {
            ::PostThreadMessage(threadID, WM_QUIT, NULL, NULL);
        } while(::WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT);
    }

    static unsigned int __stdcall threadFunc(LPVOID param) {
        ::CoInitialize(NULL);
        CMyAppModule* this_ = (CMyAppModule*)param;
        return this_->CAtlExeModuleT::Run(this_->showcmd_);
    }

    void doMyOperation( ) {
        // ここに必要な処理を実装します。
	// このメソッドが終了すると,アプリケーションも停止します。
    }
};

CMyAppModule _AtlModule;


extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, 
                                LPTSTR /*lpCmdLine*/, int nShowCmd)
{
    return _AtlModule.WinMain(nShowCmd);
}
タグ:ATL C++

Shell Extension を使って Explorer にコンテキストメニューを追加する

ShellExtension の定義方法の覚書き,その2(^^;
今度はExplorerのコンテキストメニューを追加します。

ここで扱う「コンテキストメニュー」は,ExplorerでファイルをDrag&Dropしたときに表示される,「ここにコピー」などが入っているものです。

次のようなCOM実装を行うと,Drag&Dropされたファイル名を取得して処理を行うことができるようになります。

1. Wizard を使って,IDL に実装クラスを定義する。

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

[
    object,
    uuid(70C82BD2-E659-41A4-AAE8-27DC030F82BA),
    helpstring("IMyCtxExt インターフェイス"),
    pointer_default(unique)
]
interface IMyCtxExt : IUnknown{
};
...................................................
[
    uuid(7A7CEC4E-FD39-4983-9A3F-C791214721BC),
    version(1.0),
    helpstring("CtxExtTest 1.0 タイプ ライブラリ")
]
library HardLinkColumnLib
{
    importlib("stdole2.tlb");
    [
        uuid(D2E45164-9547-4356-8513-937EAF39421D),
        helpstring("MyCtxExt Class")
    ]
    coclass MyCtxExt
    {
        [default] interface IMyCtxExt;
    };
    ....................................................
};


2. IShellExtInit, IContextMenu を継承(実装)する COM クラスを定義する。

class ATL_NO_VTABLE CMyCtxExt :
    public CComObjectRootEx,
    public CComCoClass,
    public IShellExtInit,
    public IContextMenu
{
    std::vector selectedFiles;    // 選択されたファイルを保存
    ................................................

    // IShellExtInit
    STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY);

    // IContextMenu
    STDMETHODIMP GetCommandString(UINT, UINT, UINT*, LPSTR, UINT) {
        return E_NOTIMPL; }    // 今回は使用していない
    STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO);
    STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT);
};


3. Initialize メソッドでは,処理対象として選択されたファイル一覧を取得。

//    Initialize
//        選択されたファイルのリストを取得する

HRESULT
CMyCtxExt::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID)
{
    // パラメータとして与えられたDataObjectから,Dropファイル情報を取得する
    FORMATETC fmt = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    STGMEDIUM stg = { TYMED_HGLOBAL };
    if(FAILED(pDataObj->GetData(&fmt, &stg)))
        return E_INVALIDARG;
    HDROP hdrop = (HDROP)::GlobalLock(stg.hGlobal);
    if(hdrop == NULL)
        return E_INVALIDARG;

    // 選択されたファイル一覧を取得し,インスタンスに保存する
    selectedFiles.clear();
    UINT uNumFiles = ::DragQueryFile(hdrop, 0xFFFFFFFF, NULL, 0);
    for(UINT uFile = 0; uFile < uNumFiles; ++uFile) {
        TCHAR szFile[MAX_PATH];
        if(!DragQueryFile(hdrop, uFile, szFile, MAX_PATH))
            continue;
        selectedFiles.push_back(tstring(szFile));
    }レジストリへの登録は,次のスクリプトで。

    // パラメータ情報を開放し,処理結果を返す
    // 対象ファイルが1つ以上あれば OK
    ::GlobalUnlock(stg.hGlobal);
    return selectedFiles.size() > 0 ? S_OK : E_FAIL;
}


4. QueryContextMenu では,表示するメニューの追加を行う

//    QueryContextMenu
//        コンテキストメニューを設定する

HRESULT
CLinkCtxExt::QueryContextMenu(HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, 
                              UINT uidLastCmd, UINT uFlags)
{
    // デフォルトメニュー表示が指示されている場合は何もしない
    if(uFlags & CMF_DEFAULTONLY)
        return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);

    // コンテキストメニューに自身のメニューを追加する
    ::InsertMenu(hmenu, uMenuIndex, MF_STRING|MF_BYPOSITION, uidFirstCmd, "My Context Menu");
    return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
}


5. InvokeCommand に実施に行う処理を記述する。対象となるファイルリストは,Initialize で取得しておいたもの。

6. レジストリへの登録は,次のスクリプトで。

HKCR
{
    NoRemove CLSID
    {
        ForceRemove {D2E45164-9547-4356-8513-937EAF39421D} = s 'MyCtx Extension Class'
        {
            InprocServer32 = s '%MODULE%'
            {
                val ThreadingModel = s 'Apartment'
            }
        }
    }
    NoRemove *
    {
        NoRemove shellex
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove MyCtxExt = s '{D2E45164-9547-4356-8513-937EAF39421D}'
            }
        }
    }
    NoRemove Directory
    {
        NoRemove shellex
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove MyCtxExt = s '{D2E45164-9547-4356-8513-937EAF39421D}'
            }

        }
    }
    NoRemove Folder
    {
        NoRemove shellex
        {
            NoRemove ContextMenuHandlers
            {
                ForceRemove MyCtxExt = s '{D2E45164-9547-4356-8513-937EAF39421D}'
            }
        }
    }
}

タグ:Windows

Shell Extension を使って Explorer に項目を追加する

ShellExtension の定義方法の覚書き。
Explorer の「詳細」メニューで表示するカラムを追加することができます。

1. Wizard を使って,IDL に実装クラスを定義する。
// MyColumn.idl : MyColumn の IDL ソース
//

// このファイルは、タイプ ライブラリ (MyColumn.tlb) およびマーシャリング コードを
// 作成するために MIDL ツールによって処理されます。

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

[
	object,
	uuid(EEC69ED1-4D69-485B-8AF9-4A5EEEE0EFC8),
	helpstring("IMyColExt インターフェイス"),
	pointer_default(unique)
]
interface IMyColExt : IUnknown{
};
...................................................
[
	uuid(7A7CEC4E-FD39-4983-9A3F-C791214721BC),
	version(1.0),
	helpstring("ColExtTest 1.0 タイプ ライブラリ")
]
library HardLinkColumnLib
{
	importlib("stdole2.tlb");
	[
		uuid(5451F3B5-9CFB-4F66-9CEC-B4A596066670),
		helpstring("MyColExt Class")
	]
	coclass MyColExt
	{
		[default] interface IMyColExt;
	};
	....................................................
};

2. IComPrivider を継承(実装)する COM クラスを定義する。
class ATL_NO_VTABLE CMyColExt :
	public CComObjectRootEx,
	public CComCoClass,
	public IColumnProvider
{
	...................................
public:
	STDMETHODIMP Initialize(LPCSHCOLUMNINIT psci);
	STDMETHODIMP GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO* psci);
	STDMETHODIMP GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT* pvarData);
	...................................
};

3. IColumnProvider のメソッドを実装する。

* 特に処理がなければ,Initialize は何もしなくてよい
STDMETHODIMP
CMyColExt::Initialize(LPCSHCOLUMNINIT psci) {
    return S_OK;
}

* GetColumnInfo では,カラム定義を設定して返す。
STDMETHODIMP
CMyColExt::GetColumnInfo(DWORD dwIndex, SHCOLUMNINFO* psci) {
    if(dwIndex != 0) {
        return S_FALSE;
    }
    psci->scid.fmtid = CLSID_LinkColExt;
    psci->scid.pid   = 0;
    psci->vt         = VT_I4;				
    psci->fmt	     = LVCFMT_RIGHT;
    psci->csFlags    = SHCOLSTATE_TYPE_INT | SHCOLSTATE_SLOW;
    psci->cChars     = 10;

    ::wcsncpy_s(psci->wszTitle, OLESTR("MyColumn"));
    ::wcsncpy_s(psci->wszDescription, OLESTR("Length of File Name");

    return S_OK;
}

* GetItemData で個別の値を取得して返す。
STDMETHODIMP
CMyColExt::GetItemData(LPCSHCOLUMNID pscid, LPCSHCOLUMNDATA pscd, VARIANT* pvarData) {
    if(pscid->fmtid != CLSID_LinkColExt || pscid->pid != 0) {
        return S_FALSE;
    }
    return CComVariant(::wcslen(pscd->wszFile).Detach(pvarData);
}

4. レジストリへの登録は,次のスクリプトで。
HKCR
{
    NoRemove CLSID
    {
        ForceRemove {5451F3B5-9CFB-4F66-9CEC-B4A596066670} = s 'MyCol Extension Class'
        {
            InprocServer32 = s '%MODULE%'
            {
                val ThreadingModel = s 'Apartment'
            }
        }
    }
    NoRemove Folder
    {
        NoRemove Shellex
        {
            NoRemove ColumnHandlers
            {
                ForceRemove {5451F3B5-9CFB-4F66-9CEC-B4A596066670} = s 'MyCol column ext'
            }
        }
    }
}

JScriptオブジェクトをC++コードで生成する

JScript の任意のオブジェクトを C++(ATL)で生成する手順です。
ActiveScript のセットアップ手順の説明は省略していますので,こちらの資料などを参照してください。
  1. オブジェクト生成には,ScriptHost COM インスタンスが必要です。スクリプトのセットアップ時に以下のような処理を実行しているので,ここで生成した IActiveScript へのポインタ(下記リストでは pAS)を参照できるようにしておきます。
       LPOLESTR language = OLESTR("JScript");
       CLSID CLSID_Script;
       ::CLSIDFromProgID(language, &CLSID_Script);
    
       CComPtr pAS;
       pAS.CoCreateInstance(CLSID_Script, 0, CLSCTX_INPROC_SERVER);
       ..........................

  2. IActiveScript::GetScriptDispatch メソッドを使用してDispatchEx を取得します。ここで参照するオブジェクトが,JScript のルートオブジェクトに相当します。
       CComPtr idisp;
       pAS->GetScriptDispatch(NULL, &idisp);
       CComQIPtr jsroot(idisp);
  3. ルートオブジェクトを使用して,生成するオブジェクトの DispID を取得します。下記リストでは,JScript の Array オブジェクトのIDを取得しています。
       CComBSTR objectName("Array");
       DISPID objectID;
       jsroot->GetDispID(objectName, 0, &objectID));

  4. オブジェクトのコンストラクタを呼び出して,インスタンスを生成します。生成したオブジェクトへの参照は,Variant 変数に IDispatch 型で格納されているので,これを IDispatchEx に QueryInterface して JScript に返してやれば,作業完了!!
       DISPPARAMS noargs = { NULL, NULL, 0, 0 };
       CComVariant var;
       jsroot->InvokeEx(objectID, LOCALE_USER_DEFAULT, DISPATCH_CONSTRUCT, &noargs, &var, NULL, NULL));
       return CComQIPtr(var.pdispVal);


この方法で,JScript組み込みオブジェクト,JScriptで記述したユーザ定義オブジェクトのいずれのインスタンスも生成することができます。

Explorerの「詳細」表示に項目を追加する方法

以下のURLにサンプルが紹介されています:

http://www.codeproject.com/KB/shell/shellextguide8.aspx

詳しく調べたら,追記する予定。

Windows時間データの相互変換

Windowsでは,API/ライブラリによって時間を表現するデータ構造がまちまちで,とても不便です。そこで,おもに使用されている次の3種類のデータを相互変換する関数を作成しました。

time_t
FILETIME
SYSTEMTIME

各データ形式によって精度・表現できる範囲が違っています。プログラムではオーバーフローなどのチェックは行っていませんので,その点は注意してください。

//    time_t -> FILETIME 変換

FILETIME
UnixTimeToFileTime(time_t t){
FILETIME ft;
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
ft.dwLowDateTime = (DWORD)ll;
ft.dwHighDateTime = (DWORD)(ll >> 32);
return ft;
}

// time_t -> SYSTEMTIME 変換

SYSTEMTIME
UnixTimeToSystemTime(time_t t){
SYSTEMTIME st;
FILETIME ft = UnixTimeToFileTime(t);
::FileTimeToSystemTime(&ft, &st);
return st;
}

// FILETIME -> time_t 変換

time_t
FileTimeToUnixTime(const FILETIME& ft){
LONGLONG ll;
ll = ((LONGLONG)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
return (time_t)((ll - 116444736000000000) / 10000000);
}

// SYSTEMTIME -> time_t 変換

time_t
SystemTimeToUnixTime(const SYSTEMTIME& st){
FILETIME ft;
::SystemTimeToFileTime(&st, &ft);
return FileTimeToUnixTime(ft);
}
タグ:C++

ジュリアン通日(Julian date)を求めるC関数

必要に迫られて作った“ジュリアン通日(Julian date)”変換関数(C言語)です。

long toJulianDate(int year, int month, int mday) {
int a = (14 - month) / 12;
int y = year + 4800 - a;
int m = month + 12 * a - 3;
return mday + y * 365 - 32045
+ int((153 * m + 2) / 5)
+ int(y / 4) - int (y / 100) + int (y / 400);
}

void toYMD(long jdate, int& year, int &month, int &mday) {
int j = jdate + 32044;
int g = j / 146097;
int dg = j % 146097;
int c = (dg / 36524 + 1) * 3 / 4;
int dc = dg - c * 36524;
int b = dc / 1461;
int db = dc % 1461;
int a = (db / 365 + 1) * 3 / 4;
int da = db - a * 365;
int y = g * 400 + c * 100 + b * 4 + a;
int m = (da * 5 + 308) / 153 - 2;
int d = da - (m + 4) * 153 / 5 + 122;

year = y - 4800 + (m + 2) / 12;
month = (m + 2) % 12 + 1;
mday = d + 1;
}

JulianDateの7の剰余から曜日も求められます。7で割った余りが0→月曜日,1→火曜日,…,6→日曜日,になります。
タグ:C++

JScript配列をCOM(C++)で参照する

ちょっとした経緯で,JScriptから呼び出されるCOMをC++で実装することになりました。

そこでJScriptからCOMに対して,パラメータとして配列を渡す必要があったのですが,JScriptで扱う配列は,COMで通常扱うSafeArray配列ではないということをことを知りました。Webでいろいろ調べてみると,"length"と配列インデックス("0","1","2",...)をメソッドとして持つIDispatchインターフェースとして渡されているようです。

このインターフェース(JScript配列)を,COM内部で取り扱う形式に変換する処理を作成してみましたので,紹介します。なお,今回扱っているのは旧版のJScriptで,.Net 言語になった新しいもの(JScript.Net)には当てはまりませんので,その点了承下さい。

最初にIDispatchインターフェースを介して"name"で指定したプロパティ値を取得するメソッドを用意します。

CComVariant JsArray::GetIDispatchProperty(IDispatch* iDisp,
const char* name) {
HRESULT hr;

DISPID dispid = 0;
OLECHAR FAR* szMember = CA2W(name);
hr = iDisp->GetIDsOfNames(IID_NULL, &szMember, 1,
LOCALE_SYSTEM_DEFAULT, &dispid);
if(FAILED(hr)) {
throw exception("IDispatch::GetIDsOfNames");
}

DISPPARAMS params = { NULL, NULL, 0, 0 };
VARIANT var;
EXCEPINFO excepinfo;
UINT nArgErr;
hr = iDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYGET, ¶ms, &var,
&excepinfo, &nArgErr);
if(FAILED(hr)) {
throw exception("IDispatch::Invoke");
}

return CComVariant(var);
}
このメソッドを使って,JScript配列の要素数を取得します。
要素数は,プロパティ"length"から取り出せる整数値(VT_I4)です。

int JsArray::GetJScriptArrayLength(IDispatch* iDispatch) {
CComVariant length = GetIDispatchProperty(iDispatch, "length");
HRESULT hr = length.ChangeType(VT_I4);
if(FAILED(hr)) {
throw exception("CComVariant::ChangeType(VT_I4)");
}
return length.intVal;
}
要素数を参照して,配列の内容を取得するメソッドを作成します。

配列値はVariantで格納されているので,CComVariant配列(Vector)として取得しています。

vector JsArray::GetJScriptArrayValues(IDispatch* iDispatch) {
int length = GetJScriptArrayLength(iDispatch);
std::vector result(length);
for(int n = 0; n < length; ++n) {
char str[10];
::sprintf(str, "%d", n);
result[n] = GetIDispatchProperty(iDispatch, str);
}
return result;
}
後は取得したVariant配列の各要素を,所定の型の値として参照すればOKです。
タグ:C++ com

C++でVARIANT配列を扱う

COMで使用するVARIANT配列を生成するコンパクトな実装コードです。
ATL使用が前提で,1次元のみ対応しています。
template 
static HRESULT createVariantArray(T* value, int count, VARIANT*& retval)
{
SAFEARRAYBOUND sab = { count, 0 }; // 0 〜 count-1
CComSafeArray sarray(sab);
for(int n = 0; n < count; ++n) {
sarray.Add(*value++);
}
return CComVariant(sarray).Detach(retval);
}
タグ:C++ com
rank← ランキングはこちらをクリック!

×

この広告は180日以上新しい記事の投稿がないブログに表示されております。