岡本といいます。

Cによる拡張ライブラリ作りの練習として、WIN32OLEに個人的に欲しいメソッドを
追加してみました。

・OLEオブジェクトのメソッドを列挙するもの
WIN32OLE#ole_methods( flag = WIN32OLE::APPEND_ALL )
WIN32OLE#ole_get_methods( flag = WIN32OLE::APPEND_NONE )
WIN32OLE#ole_put_methods( flag = WIN32OLE::APPEND_NONE )
WIN32OLE#ole_func_methods( flag = WIN32OLE::APPEND_NONE )

flagに指定する(勝手に追加した)定数は以下のとおり。
    WIN32OLE::APPEND_NONE
        何もしない
    WIN32OLE::APPEND_EQUAL
        put_method の名前の末尾に"="を付加して返す
    WIN32OLE::APPEND_BRACKET
        func_method の名前の末尾に"()"を付加して返す
    WIN32OLE::APPEND_ALL
        WIN32OLE::APPEND_EQUAL と WIN32OLE::APPEND_BRACKET の両方

GetTypeInfoCountやGetTypeInfoが呼べればrubyでも書けたんじゃないかという気もしますが。

・OLEオブジェクトやそのメソッドの情報を適当に取ってくるもの
WIN32OLE3#ole_obj_help              #OLEオブジェクトの情報を取ってくる
WIN32OLE3#ole_method_help( name )   #OLEオブジェクトのメソッドの情報を取ってくる
WIN32OLE3#ole_method( name )        #ole_method_helpの別名

TYPEATTR構造体やFUNCDESC構造体の各メンバの意味を良く知らないので、
取ってくる情報は本当に適当です。

・OLEのメソッドを呼び出すたびに、method_missingが呼び出されて、名前からIDを得て、
という手順を踏むのがもったいない、というこすい発想から生まれたもの
WIN32OLE3#ole_methods_bind
WIN32OLE3#ole_method_bind( name )
WIN32OLE3#ole_invoke_byid( dispid, wflags, *params )

ole_method_bindにOLEのメソッド名を渡して呼ぶと、同名のRubyのメソッドを定義します。
(ただし先頭小文字化したもの)
そのメソッドでは ole_method_bind であらかじめ求めておいたディスパッチIDで、
(ole_invoke_byidを使って)OLEのメソッドを呼びます。
ole_methods_bindは全てのOLEのメソッド名に対して、同様のことを行ないます。

定義はできても、呼び出してみると上手く機能しない場合が多いのが問題ですが。

また、OLEのメソッドの引数の情報の取得方法が分からないので、
ole_method_bind で定義されるメソッドは引数を全部配列で受け取ってOLEのメソッドに渡すようになってま
す。
これも何とかしたい。



あとついでに、ライブラリ内で include Enumerable しておいて欲しいと思っていたので、そのようにしてみま
した。


////追加する定数/////////////////////////////////////////

#define APPEND_NONE 0
#define APPEND_EQUAL 1
#define APPEND_BRACKET 2
#define APPEND_ALL ( APPEND_EQUAL | APPEND_BRACKET )

////追加する関数///////////////////////////////////////

static int
ole_methods_sub( pTypeInfo, array, mask, apdflag)
    ITypeInfo *pTypeInfo;
    VALUE array;
    int mask;
    int apdflag;
{
    int iVar;
    FUNCDESC *pFuncDesc;
    HRESULT hr;
    BSTR bstr;
    VALUE val;
    char *pName = NULL;

    for( iVar=0; ; iVar++ ){
        hr = pTypeInfo->lpVtbl->GetFuncDesc( pTypeInfo, iVar, &pFuncDesc );
        if(FAILED(hr))
            break;

        if( pFuncDesc->invkind & mask ){
            //INVOKE_FUNC : INVOKE_PROPERTYGET : INVOKE_PROPERTYPUT
            hr = pTypeInfo->lpVtbl->GetDocumentation( pTypeInfo, pFuncDesc->memid,
                                                      &bstr, NULL, NULL, NULL);
            if(FAILED(hr)) {
                ole_raise(hr, rb_eRuntimeError, "fail to GetDocumentation");
            }
            pName = ole_wc2mb(bstr);
            val = rb_str_new2(pName);
            if( (pFuncDesc->invkind & INVOKE_FUNC) && (apdflag&APPEND_BRACKET) ){
                val = rb_str_cat( val, "()", 2 );
            }
            if( (pFuncDesc->invkind & INVOKE_PROPERTYPUT) && (apdflag&APPEND_EQUAL) ){
                val = rb_str_cat( val, "=", 1 );
            }
            SysFreeString(bstr);
            SysFreeString((BSTR)pName);

            rb_ary_push( array, val );
        }
        pTypeInfo->lpVtbl->ReleaseFuncDesc( pTypeInfo, pFuncDesc );
    }

    return 0;
}

static VALUE
ole_methods(self,mask,apdflag)
    VALUE self;
    int mask;
    VALUE apdflag;
{
    unsigned int index;
    int iVar,iVar2;
    int count;
    ITypeInfo *pTypeInfo;
    ITypeInfo *pTypeInfo2;
    HRESULT hr;
    HRESULT hr2;
    VALUE method_names;
    struct oledata *pole;
    LCID    lcid = LOCALE_SYSTEM_DEFAULT;
    int iapdflag = NUM2INT(apdflag);
    HREFTYPE hreftype;

    Data_Get_Struct(self, struct oledata, pole);
    method_names = rb_ary_new();

    hr = pole->pDispatch->lpVtbl->GetTypeInfoCount( pole->pDispatch, &count );
    for( iVar=0; iVar<count; iVar++ ){
        hr = pole->pDispatch->lpVtbl->GetTypeInfo( pole->pDispatch, 0, lcid, &pTypeInfo );
        if(FAILED(hr)) {
            ole_raise(hr, rb_eRuntimeError, "fail to GetTypeInfo");
        }
        ole_methods_sub( pTypeInfo, method_names, mask, iapdflag );

        for( iVar2=0; ; iVar2++ ){
            hr = pTypeInfo->lpVtbl->GetRefTypeOfImplType( pTypeInfo, iVar2, &hreftype );
            hr2 = pTypeInfo->lpVtbl->GetRefTypeInfo( pTypeInfo, hreftype, &pTypeInfo2 );

            ole_methods_sub( pTypeInfo2, method_names, mask, iapdflag );

            pTypeInfo2->lpVtbl->Release(pTypeInfo2);
            if(FAILED(hr))
                break;
        }
        pTypeInfo->lpVtbl->Release(pTypeInfo);
    }

    rb_funcall( method_names, rb_intern("uniq!"), 0 );
    return method_names;
}

static VALUE
fole_methods( argc, argv, self )
    int argc;
    VALUE *argv;
    VALUE self;
{
    VALUE apdflag;
    rb_scan_args( argc, argv, "01", &apdflag );
    if( apdflag == Qnil ){
        //apdflag = rb_const_get( cWIN32OLE, rb_intern("APPEND_ALL") );
        apdflag = INT2FIX( APPEND_ALL );
    }
    return ole_methods( self, INVOKE_FUNC | INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT, apdflag );
}

static VALUE
fole_get_methods( argc, argv, self )
    int argc;
    VALUE *argv;
    VALUE self;
{
    VALUE apdflag;
    rb_scan_args( argc, argv, "01", &apdflag );
    if( apdflag == Qnil ){
        //apdflag = rb_const_get( cWIN32OLE, rb_intern("APPEND_NONE") );
        apdflag = INT2FIX( APPEND_NONE );
    }
    return ole_methods( self, INVOKE_PROPERTYGET, apdflag );
}

static VALUE
fole_put_methods( argc, argv, self )
    int argc;
    VALUE *argv;
    VALUE self;
{
    VALUE apdflag;
    rb_scan_args( argc, argv, "01", &apdflag );
    if( apdflag == Qnil ){
        //apdflag = rb_const_get( cWIN32OLE, rb_intern("APPEND_NONE") );
    apdflag = INT2FIX( APPEND_NONE );
    }
    return ole_methods( self, INVOKE_PROPERTYPUT, apdflag );
}

static VALUE
fole_func_methods( argc, argv, self )
    int argc;
    VALUE *argv;
    VALUE self;
{
    VALUE apdflag;
    rb_scan_args( argc, argv, "01", &apdflag );
    if( apdflag == Qnil ){
        //apdflag = rb_const_get( cWIN32OLE, rb_intern("APPEND_NONE") );
        apdflag = INT2FIX( APPEND_NONE );
    }
    return ole_methods( self, INVOKE_FUNC, apdflag );
}

static VALUE
ole_method_help( self, cmdname )
    VALUE self;
    VALUE cmdname;
{
    unsigned int index;
    int count;
    ITypeInfo *pTypeInfo;
    FUNCDESC *pFuncDesc;
    DISPID DispID;
    HRESULT hr;
    VALUE hash;
    ID id;
    struct oledata *pole;
    LCID    lcid = LOCALE_SYSTEM_DEFAULT;
    BSTR wcmdname;
    BSTR bstr,bhelpstring;
    VALUE vstr,vhelpstring;
    VALUE err_msg;
    char *pName = NULL;

    Data_Get_Struct(self, struct oledata, pole);

    wcmdname = ole_mb2wc(RSTRING(cmdname)->ptr, -1);
    hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL,
                                                 &wcmdname, 1, lcid, &DispID);
    if(FAILED(hr)) {
        err_msg = rb_str_new2("Unknown property or method : ");
        rb_str_concat(err_msg, cmdname);
        ole_raise(hr, rb_eRuntimeError, RSTRING(err_msg)->ptr);
    }
    hr = pole->pDispatch->lpVtbl->GetTypeInfo( pole->pDispatch, 0, lcid, &pTypeInfo );
    if(FAILED(hr)) {
        ole_raise(hr, rb_eRuntimeError, "fail to GetTypeInfo");
    }
    hr = pTypeInfo->lpVtbl->GetDocumentation( pTypeInfo, DispID,
                                              &bstr, &bhelpstring, (DWORD*)&index, NULL);
    hr = pTypeInfo->lpVtbl->GetFuncDesc( pTypeInfo, index, &pFuncDesc );

    hash = rb_hash_new();
//    rb_hash_aset( hash, rb_str_new("name",4), cmdname );

    pName = ole_wc2mb(bstr);
    vstr = rb_str_new2( pName );
    SysFreeString( bstr );
    SysFreeString( (BSTR)pName );
    rb_hash_aset( hash, rb_str_new("name",4), vstr );

    pName = ole_wc2mb(bhelpstring);
    vhelpstring = rb_str_new2( pName );
    SysFreeString( bhelpstring );
    SysFreeString( (BSTR)pName );
    rb_hash_aset( hash, rb_str_new("helpstring",10), vhelpstring );

    rb_hash_aset( hash, rb_str_new("cParams",7), INT2FIX(pFuncDesc->cParams) );
    //なんか必ず5だし
    rb_hash_aset( hash, rb_str_new("cParamsOpt",10), INT2FIX(pFuncDesc->cParamsOpt) );
    //引数や返り値の型の情報とか無いのかな
    rb_hash_aset( hash, rb_str_new("oVft",4), INT2FIX(pFuncDesc->oVft) );
    rb_hash_aset( hash, rb_str_new("cScodes",7), INT2FIX(pFuncDesc->cScodes) );

    return hash;
}

static VALUE
ole_obj_help( self )
    VALUE self;
{
    unsigned int index;
    int count;
    ITypeInfo *pTypeInfo;
    ITypeLib *pTypeLib;
    TYPEATTR  *pTypeAttr;
    HRESULT hr;
    VALUE hash;
    ID id;
    struct oledata *pole;
    LCID    lcid = LOCALE_SYSTEM_DEFAULT;
    BSTR wcmdname;
    BSTR bstr,bhelpstring;
    VALUE vstr,vhelpstring;
    VALUE err_msg;
    char *pName = NULL;

    Data_Get_Struct(self, struct oledata, pole);

    hr = pole->pDispatch->lpVtbl->GetTypeInfo( pole->pDispatch, 0, lcid, &pTypeInfo );
    if(FAILED(hr)) {
        ole_raise(hr, rb_eRuntimeError, "fail to GetTypeInfo");
    }
    hr = pTypeInfo->lpVtbl->GetContainingTypeLib( pTypeInfo, &pTypeLib, &index );
    hr = pTypeLib->lpVtbl->GetDocumentation( pTypeLib, index,
                                             &bstr, &bhelpstring, NULL, NULL);
    hr = pTypeInfo->lpVtbl->GetTypeAttr( pTypeInfo, &pTypeAttr );

    hash = rb_hash_new();

    pName = ole_wc2mb(bstr);
    vstr = rb_str_new2( pName );
    SysFreeString( bstr );
    SysFreeString( (BSTR)pName );
    rb_hash_aset( hash, rb_str_new("name",4), vstr );

    pName = ole_wc2mb(bhelpstring);
    vhelpstring = rb_str_new2( pName );
    SysFreeString( bhelpstring );
    SysFreeString( (BSTR)pName );
    rb_hash_aset( hash, rb_str_new("helpstring",10), vhelpstring );

    rb_hash_aset( hash, rb_str_new("typekind",8), INT2FIX(pTypeAttr->typekind) );
    rb_hash_aset( hash, rb_str_new("cFuncs",6), INT2FIX(pTypeAttr->cFuncs) );
    rb_hash_aset( hash, rb_str_new("cVars",5), INT2FIX(pTypeAttr->cVars) );
    rb_hash_aset( hash, rb_str_new("wMajorVerNum",12),
    INT2FIX(pTypeAttr->wMajorVerNum) );
    rb_hash_aset( hash, rb_str_new("wMinorVerNum",12),
    INT2FIX(pTypeAttr->wMinorVerNum) );

    return hash;
}


static VALUE
ole_method_bind( self, name )
    VALUE self;
    VALUE name;
{
    VALUE method_def_str;
    LCID    lcid = LOCALE_SYSTEM_DEFAULT;
    DISPID DispID;
    BSTR wcmdname;
    HRESULT hr;
    int achar;
    struct oledata *pole;
    VALUE item;
    USHORT wFlags;
    int n;
    VALUE name2;

    n = RSTRING(name)->len;
    if( RSTRING(name)->ptr[n-1] == '=' ) {
        name2 = rb_str_substr(name, 0, n-1);
        wFlags = DISPATCH_PROPERTYPUT;
    }else{
    name2 = name;
    wFlags = DISPATCH_METHOD | DISPATCH_PROPERTYGET;
    }

    Data_Get_Struct(self, struct oledata, pole);
    wcmdname = ole_mb2wc( RSTRING(name2)->ptr, -1 );
    hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL,
                                            &wcmdname, 1, lcid, &DispID);
    SysFreeString(wcmdname);

    achar = RSTRING(name)->ptr[0];
    if( (achar >= 'A') && (achar <= 'Z') ){
        RSTRING(name)->ptr[0] = achar + 0x20;
    }
    item = rb_str_dup(name);
    rb_str_cat(item,"(*params)",9); //引数の数(とりあえず配列で?)
    method_def_str = rb_str_new2( "def " );
    rb_str_concat( method_def_str, item );
    rb_str_cat( method_def_str, ";", 1 );
    rb_str_cat( method_def_str, "self.ole_invoke_byid(", 21 );
    rb_str_concat( method_def_str, rb_String( INT2NUM(DispID) ) );
    rb_str_cat( method_def_str, ", ", 2 );
    rb_str_concat( method_def_str, rb_String( INT2NUM(wFlags) ) );
    rb_str_cat( method_def_str, ", *params);", 10 );
    rb_str_cat( method_def_str, "end", 3 );

    rb_funcall(self, rb_intern("instance_eval"), 1, method_def_str );
    return self;
}

static VALUE
ole_methods_bind(self)
    VALUE self;
{
    int i;
    VALUE methods;
    struct oledata *pole;

    Data_Get_Struct(self, struct oledata, pole);
    methods = ole_methods( self, INVOKE_FUNC | INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT,
                           INT2FIX(APPEND_EQUAL) );

    for( i=0; i<(RARRAY(methods)->len); i++ ){
        ole_method_bind( self, RARRAY(methods)->ptr[i] );
    }

    return self;
}

static VALUE
ole_invoke_byid( argc, argv, self )
    int argc;
    VALUE *argv;
    VALUE self;
{
    LCID    lcid = LOCALE_SYSTEM_DEFAULT;
    struct oledata *pole;
    VALUE vDispID;
    DISPID lDispID;
    HRESULT hr;
    VALUE param;
    VALUE paramS;
    EXCEPINFO excepinfo;
    VARIANT result;
    unsigned int argErr;
    struct oleparam op;
    unsigned int cNamedArgs;
    USHORT wFlags;
    VALUE vwFlags;
    VALUE obj;
    int i;
    VALUE error_msg;

    Data_Get_Struct(self, struct oledata, pole);
    rb_scan_args(argc, argv, "2*", &vDispID, &vwFlags, &paramS);
    lDispID = NUM2INT(vDispID);
    wFlags = NUM2INT(vwFlags);
    param = rb_ary_entry(paramS, argc-3);
    op.dp.cNamedArgs = 0;
    op.dp.rgvarg = NULL;
    op.dp.rgdispidNamedArgs = NULL;
    op.dp.cNamedArgs = 0;
    op.dp.cArgs = 0;

    cNamedArgs = 0;
    op.dp.cArgs = argc - 1;
    op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1);
    if (op.dp.cArgs > 0) {
        op.dp.rgvarg  = ALLOCA_N(VARIANTARG, op.dp.cArgs);
    }

    if(op.dp.cArgs > cNamedArgs) {
        for(i = cNamedArgs; i < op.dp.cArgs; i++) {
            int n = op.dp.cArgs - i + cNamedArgs - 1;
            VariantInit(&op.dp.rgvarg[n]);
            param = rb_ary_entry(paramS, i-cNamedArgs);
            ole_val2variant(param, &op.dp.rgvarg[n]);
        }
    }

    hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, lDispID,
                                         &IID_NULL, lcid, wFlags, &op.dp,
                                         &result, &excepinfo, &argErr);
    if (FAILED(hr)) {
        if (hr == DISP_E_EXCEPTION && lDispID > 0x8000) {
            memset(&excepinfo, 0, sizeof(EXCEPINFO));
            hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, lDispID,
                                                 &IID_NULL, lcid, wFlags,
                                                 &op.dp, NULL,
                                                 &excepinfo, &argErr);
        }
    }

    /* clear dispatch parameter */
    for(i = 0; i < op.dp.cArgs; i++ ) {
        VariantClear(&op.dp.rgvarg[i]);
    }

    if (FAILED(hr)) {
        error_msg = ole_excepinfo2msg(&excepinfo);
        ole_raise(hr, rb_eRuntimeError, RSTRING(error_msg)->ptr);
    }

    obj = ole_variant2val(&result);
    VariantClear(&result);
    return obj;
}


////Init_win32ole()内に追加////////////////////////////

    rb_include_module(cWIN32OLE, rb_mEnumerable);

    rb_define_method(cWIN32OLE, "ole_methods", fole_methods, -1);
    rb_define_method(cWIN32OLE, "ole_get_methods", fole_get_methods, -1);
    rb_define_method(cWIN32OLE, "ole_put_methods", fole_put_methods, -1);
    rb_define_method(cWIN32OLE, "ole_func_methods", fole_func_methods, -1);

    rb_define_method(cWIN32OLE, "ole_methods_bind", ole_methods_bind, 0);
    rb_define_method(cWIN32OLE, "ole_method_bind", ole_method_bind, 1);
    rb_define_method(cWIN32OLE, "ole_invoke_byid", ole_invoke_byid, -1);

    rb_define_method(cWIN32OLE, "ole_method", ole_method_help, 1);
    rb_define_method(cWIN32OLE, "ole_method_help", ole_method_help, 1);
    rb_define_method(cWIN32OLE, "ole_obj_help", ole_obj_help, 0);

    rb_define_const(cWIN32OLE, "APPEND_NONE", INT2FIX(APPEND_NONE) );
    rb_define_const(cWIN32OLE, "APPEND_EQUAL", INT2FIX(APPEND_EQUAL) );
    rb_define_const(cWIN32OLE, "APPEND_BRACKET", INT2FIX(APPEND_BRACKET) );
    rb_define_const(cWIN32OLE, "APPEND_ALL", INT2FIX(APPEND_EQUAL | APPEND_BRACKET) );