/* * (c) 1995 Microsoft Corporation. All rights reserved. * Developed by ActiveWare Internet Corp., now known as * ActiveState Tool Corp., http://www.ActiveState.com * * Other modifications Copyright (c) 1997, 1998 by Gurusamy Sarathy * and Jan Dubois * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file * of the Perl distribution. * */ /* modified for win32ole (ruby) by Masaki.Suketa */ #include "win32ole.h" /* * unfortunately IID_IMultiLanguage2 is not included in any libXXX.a * in Cygwin(mingw32). */ #if defined(__CYGWIN__) || defined(__MINGW32__) #undef IID_IMultiLanguage2 const IID IID_IMultiLanguage2 = {0xDCCFC164, 0x2B38, 0x11d2, {0xB7, 0xEC, 0x00, 0xC0, 0x4F, 0x8F, 0x5D, 0x9A}}; #endif #define WIN32OLE_VERSION "1.9.0" typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX) (REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*); typedef HWND (WINAPI FNHTMLHELP)(HWND hwndCaller, LPCSTR pszFile, UINT uCommand, DWORD dwData); typedef BOOL (FNENUMSYSEMCODEPAGES) (CODEPAGE_ENUMPROC, DWORD); VALUE cWIN32OLE; #if defined(RB_THREAD_SPECIFIC) && (defined(__CYGWIN__)) static RB_THREAD_SPECIFIC BOOL g_ole_initialized; # define g_ole_initialized_init() ((void)0) # define g_ole_initialized_set(val) (g_ole_initialized = (val)) #else static volatile DWORD g_ole_initialized_key = TLS_OUT_OF_INDEXES; # define g_ole_initialized (TlsGetValue(g_ole_initialized_key)!=0) # define g_ole_initialized_init() (g_ole_initialized_key = TlsAlloc()) # define g_ole_initialized_set(val) TlsSetValue(g_ole_initialized_key, (void*)(val)) #endif static BOOL g_uninitialize_hooked = FALSE; static BOOL g_cp_installed = FALSE; static BOOL g_lcid_installed = FALSE; static BOOL g_running_nano = FALSE; static HINSTANCE ghhctrl = NULL; static HINSTANCE gole32 = NULL; static FNCOCREATEINSTANCEEX *gCoCreateInstanceEx = NULL; static VALUE com_hash; static VALUE enc2cp_hash; static IDispatchVtbl com_vtbl; static UINT cWIN32OLE_cp = CP_ACP; static rb_encoding *cWIN32OLE_enc; static UINT g_cp_to_check = CP_ACP; static char g_lcid_to_check[8 + 1]; static VARTYPE g_nil_to = VT_ERROR; static IMessageFilterVtbl message_filter; static IMessageFilter imessage_filter = { &message_filter }; static IMessageFilter* previous_filter; #if defined(HAVE_TYPE_IMULTILANGUAGE2) static IMultiLanguage2 *pIMultiLanguage = NULL; #elif defined(HAVE_TYPE_IMULTILANGUAGE) static IMultiLanguage *pIMultiLanguage = NULL; #else #define pIMultiLanguage NULL /* dummy */ #endif struct oleparam { DISPPARAMS dp; OLECHAR** pNamedArgs; }; static HRESULT ( STDMETHODCALLTYPE QueryInterface )(IDispatch __RPC_FAR *, REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject); static ULONG ( STDMETHODCALLTYPE AddRef )(IDispatch __RPC_FAR * This); static ULONG ( STDMETHODCALLTYPE Release )(IDispatch __RPC_FAR * This); static HRESULT ( STDMETHODCALLTYPE GetTypeInfoCount )(IDispatch __RPC_FAR * This, UINT __RPC_FAR *pctinfo); static HRESULT ( STDMETHODCALLTYPE GetTypeInfo )(IDispatch __RPC_FAR * This, UINT iTInfo, LCID lcid, ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); static HRESULT ( STDMETHODCALLTYPE GetIDsOfNames )(IDispatch __RPC_FAR * This, REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId); static HRESULT ( STDMETHODCALLTYPE Invoke )( IDispatch __RPC_FAR * This, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr); static IDispatch* val2dispatch(VALUE val); static double rbtime2vtdate(VALUE tmobj); static VALUE vtdate2rbtime(double date); static rb_encoding *ole_cp2encoding(UINT cp); static UINT ole_encoding2cp(rb_encoding *enc); NORETURN(static void failed_load_conv51932(void)); #ifndef pIMultiLanguage static void load_conv_function51932(void); #endif static UINT ole_init_cp(void); static void ole_freeexceptinfo(EXCEPINFO *pExInfo); static VALUE ole_excepinfo2msg(EXCEPINFO *pExInfo); static void ole_free(void *ptr); static size_t ole_size(const void *ptr); static LPWSTR ole_mb2wc(char *pm, int len, UINT cp); static VALUE ole_ary_m_entry(VALUE val, LONG *pid); static VALUE is_all_index_under(LONG *pid, long *pub, long dim); static void * get_ptr_of_variant(VARIANT *pvar); static void ole_set_safe_array(long n, SAFEARRAY *psa, LONG *pid, long *pub, VALUE val, long dim, VARTYPE vt); static long dimension(VALUE val); static long ary_len_of_dim(VALUE ary, long dim); static VALUE ole_set_member(VALUE self, IDispatch *dispatch); static VALUE fole_s_allocate(VALUE klass); static VALUE create_win32ole_object(VALUE klass, IDispatch *pDispatch, int argc, VALUE *argv); static VALUE ary_new_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim); static void ary_store_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim, VALUE val); static void ole_const_load(ITypeLib *pTypeLib, VALUE klass, VALUE self); static HRESULT clsid_from_remote(VALUE host, VALUE com, CLSID *pclsid); static VALUE ole_create_dcom(VALUE self, VALUE ole, VALUE host, VALUE others); static VALUE ole_bind_obj(VALUE moniker, int argc, VALUE *argv, VALUE self); static VALUE fole_s_connect(int argc, VALUE *argv, VALUE self); static VALUE fole_s_const_load(int argc, VALUE *argv, VALUE self); static ULONG reference_count(struct oledata * pole); static VALUE fole_s_reference_count(VALUE self, VALUE obj); static VALUE fole_s_free(VALUE self, VALUE obj); static HWND ole_show_help(VALUE helpfile, VALUE helpcontext); static VALUE fole_s_show_help(int argc, VALUE *argv, VALUE self); static VALUE fole_s_get_code_page(VALUE self); static BOOL CALLBACK installed_code_page_proc(LPTSTR str); static BOOL code_page_installed(UINT cp); static VALUE fole_s_set_code_page(VALUE self, VALUE vcp); static VALUE fole_s_get_locale(VALUE self); static BOOL CALLBACK installed_lcid_proc(LPTSTR str); static BOOL lcid_installed(LCID lcid); static VALUE fole_s_set_locale(VALUE self, VALUE vlcid); static VALUE fole_s_create_guid(VALUE self); static VALUE fole_s_ole_initialize(VALUE self); static VALUE fole_s_ole_uninitialize(VALUE self); static VALUE fole_initialize(int argc, VALUE *argv, VALUE self); static int hash2named_arg(VALUE key, VALUE val, VALUE pop); static VALUE set_argv(VARIANTARG* realargs, unsigned int beg, unsigned int end); static VALUE ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket); static VALUE fole_invoke(int argc, VALUE *argv, VALUE self); static VALUE ole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types, USHORT dispkind); static VALUE fole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types); static VALUE fole_getproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types); static VALUE fole_setproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types); static VALUE fole_setproperty_with_bracket(int argc, VALUE *argv, VALUE self); static VALUE fole_setproperty(int argc, VALUE *argv, VALUE self); static VALUE fole_getproperty_with_bracket(int argc, VALUE *argv, VALUE self); static VALUE ole_propertyput(VALUE self, VALUE property, VALUE value); static VALUE fole_free(VALUE self); static VALUE ole_each_sub(VALUE pEnumV); static VALUE ole_ienum_free(VALUE pEnumV); static VALUE fole_each(VALUE self); static VALUE fole_missing(int argc, VALUE *argv, VALUE self); static HRESULT typeinfo_from_ole(struct oledata *pole, ITypeInfo **ppti); static VALUE ole_methods(VALUE self, int mask); static VALUE fole_methods(VALUE self); static VALUE fole_get_methods(VALUE self); static VALUE fole_put_methods(VALUE self); static VALUE fole_func_methods(VALUE self); static VALUE fole_type(VALUE self); static VALUE fole_typelib(VALUE self); static VALUE fole_query_interface(VALUE self, VALUE str_iid); static VALUE fole_respond_to(VALUE self, VALUE method); static VALUE ole_usertype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails); static VALUE ole_ptrtype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails); static VALUE fole_method_help(VALUE self, VALUE cmdname); static VALUE fole_activex_initialize(VALUE self); static void com_hash_free(void *ptr); static void com_hash_mark(void *ptr); static size_t com_hash_size(const void *ptr); static void check_nano_server(void); static const rb_data_type_t ole_datatype = { "win32ole", {NULL, ole_free, ole_size,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; static const rb_data_type_t win32ole_hash_datatype = { "win32ole_hash", {com_hash_mark, com_hash_free, com_hash_size,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; static HRESULT (STDMETHODCALLTYPE mf_QueryInterface)( IMessageFilter __RPC_FAR * This, /* [in] */ REFIID riid, /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) { if (MEMCMP(riid, &IID_IUnknown, GUID, 1) == 0 || MEMCMP(riid, &IID_IMessageFilter, GUID, 1) == 0) { *ppvObject = &message_filter; return S_OK; } return E_NOINTERFACE; } static ULONG (STDMETHODCALLTYPE mf_AddRef)( IMessageFilter __RPC_FAR * This) { return 1; } static ULONG (STDMETHODCALLTYPE mf_Release)( IMessageFilter __RPC_FAR * This) { return 1; } static DWORD (STDMETHODCALLTYPE mf_HandleInComingCall)( IMessageFilter __RPC_FAR * pThis, DWORD dwCallType, //Type of incoming call HTASK threadIDCaller, //Task handle calling this task DWORD dwTickCount, //Elapsed tick count LPINTERFACEINFO lpInterfaceInfo //Pointer to INTERFACEINFO structure ) { #ifdef DEBUG_MESSAGEFILTER printf("incoming %08X, %08X, %d\n", dwCallType, threadIDCaller, dwTickCount); fflush(stdout); #endif switch (dwCallType) { case CALLTYPE_ASYNC: case CALLTYPE_TOPLEVEL_CALLPENDING: case CALLTYPE_ASYNC_CALLPENDING: if (rb_during_gc()) { return SERVERCALL_RETRYLATER; } break; default: break; } if (previous_filter) { return previous_filter->lpVtbl->HandleInComingCall(previous_filter, dwCallType, threadIDCaller, dwTickCount, lpInterfaceInfo); } return SERVERCALL_ISHANDLED; } static DWORD (STDMETHODCALLTYPE mf_RetryRejectedCall)( IMessageFilter* pThis, HTASK threadIDCallee, //Server task handle DWORD dwTickCount, //Elapsed tick count DWORD dwRejectType //Returned rejection message ) { if (previous_filter) { return previous_filter->lpVtbl->RetryRejectedCall(previous_filter, threadIDCallee, dwTickCount, dwRejectType); } return 1000; } static DWORD (STDMETHODCALLTYPE mf_MessagePending)( IMessageFilter* pThis, HTASK threadIDCallee, //Called applications task handle DWORD dwTickCount, //Elapsed tick count DWORD dwPendingType //Call type ) { if (rb_during_gc()) { return PENDINGMSG_WAITNOPROCESS; } if (previous_filter) { return previous_filter->lpVtbl->MessagePending(previous_filter, threadIDCallee, dwTickCount, dwPendingType); } return PENDINGMSG_WAITNOPROCESS; } typedef struct _Win32OLEIDispatch { IDispatch dispatch; ULONG refcount; VALUE obj; } Win32OLEIDispatch; static HRESULT ( STDMETHODCALLTYPE QueryInterface )( IDispatch __RPC_FAR * This, /* [in] */ REFIID riid, /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) { if (MEMCMP(riid, &IID_IUnknown, GUID, 1) == 0 || MEMCMP(riid, &IID_IDispatch, GUID, 1) == 0) { Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; p->refcount++; *ppvObject = This; return S_OK; } return E_NOINTERFACE; } static ULONG ( STDMETHODCALLTYPE AddRef )( IDispatch __RPC_FAR * This) { Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; return ++(p->refcount); } static ULONG ( STDMETHODCALLTYPE Release )( IDispatch __RPC_FAR * This) { Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; ULONG u = --(p->refcount); if (u == 0) { st_data_t key = p->obj; st_delete(DATA_PTR(com_hash), &key, 0); free(p); } return u; } static HRESULT ( STDMETHODCALLTYPE GetTypeInfoCount )( IDispatch __RPC_FAR * This, /* [out] */ UINT __RPC_FAR *pctinfo) { return E_NOTIMPL; } static HRESULT ( STDMETHODCALLTYPE GetTypeInfo )( IDispatch __RPC_FAR * This, /* [in] */ UINT iTInfo, /* [in] */ LCID lcid, /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo) { return E_NOTIMPL; } static HRESULT ( STDMETHODCALLTYPE GetIDsOfNames )( IDispatch __RPC_FAR * This, /* [in] */ REFIID riid, /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, /* [in] */ UINT cNames, /* [in] */ LCID lcid, /* [size_is][out] */ DISPID __RPC_FAR *rgDispId) { /* Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; */ char* psz = ole_wc2mb(*rgszNames); // support only one method ID nameid = rb_check_id_cstr(psz, (long)strlen(psz), cWIN32OLE_enc); free(psz); if ((ID)(DISPID)nameid != nameid) return E_NOINTERFACE; *rgDispId = (DISPID)nameid; return S_OK; } static /* [local] */ HRESULT ( STDMETHODCALLTYPE Invoke )( IDispatch __RPC_FAR * This, /* [in] */ DISPID dispIdMember, /* [in] */ REFIID riid, /* [in] */ LCID lcid, /* [in] */ WORD wFlags, /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, /* [out] */ VARIANT __RPC_FAR *pVarResult, /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, /* [out] */ UINT __RPC_FAR *puArgErr) { VALUE v; int i; int args = pDispParams->cArgs; Win32OLEIDispatch* p = (Win32OLEIDispatch*)This; VALUE* parg = ALLOCA_N(VALUE, args); ID mid = (ID)dispIdMember; for (i = 0; i < args; i++) { *(parg + i) = ole_variant2val(&pDispParams->rgvarg[args - i - 1]); } if (dispIdMember == DISPID_VALUE) { if (wFlags == DISPATCH_METHOD) { mid = rb_intern("call"); } else if (wFlags & DISPATCH_PROPERTYGET) { mid = rb_intern("value"); } } v = rb_funcallv(p->obj, mid, args, parg); ole_val2variant(v, pVarResult); return S_OK; } BOOL ole_initialized(void) { return g_ole_initialized; } static IDispatch* val2dispatch(VALUE val) { struct st_table *tbl = DATA_PTR(com_hash); Win32OLEIDispatch* pdisp; st_data_t data; if (st_lookup(tbl, val, &data)) { pdisp = (Win32OLEIDispatch *)(data & ~FIXNUM_FLAG); pdisp->refcount++; } else { pdisp = ALLOC(Win32OLEIDispatch); pdisp->dispatch.lpVtbl = &com_vtbl; pdisp->refcount = 1; pdisp->obj = val; st_insert(tbl, val, (VALUE)pdisp | FIXNUM_FLAG); } return &pdisp->dispatch; } static double rbtime2vtdate(VALUE tmobj) { SYSTEMTIME st; double t; double nsec; st.wYear = RB_FIX2INT(rb_funcall(tmobj, rb_intern("year"), 0)); st.wMonth = RB_FIX2INT(rb_funcall(tmobj, rb_intern("month"), 0)); st.wDay = RB_FIX2INT(rb_funcall(tmobj, rb_intern("mday"), 0)); st.wHour = RB_FIX2INT(rb_funcall(tmobj, rb_intern("hour"), 0)); st.wMinute = RB_FIX2INT(rb_funcall(tmobj, rb_intern("min"), 0)); st.wSecond = RB_FIX2INT(rb_funcall(tmobj, rb_intern("sec"), 0)); st.wMilliseconds = 0; SystemTimeToVariantTime(&st, &t); /* * Unfortunately SystemTimeToVariantTime function always ignores the * wMilliseconds of SYSTEMTIME struct. * So, we need to calculate milliseconds by ourselves. */ nsec = RB_FIX2INT(rb_funcall(tmobj, rb_intern("nsec"), 0)); nsec /= 1000000.0; nsec /= (24.0 * 3600.0); nsec /= 1000; return t + nsec; } static VALUE vtdate2rbtime(double date) { SYSTEMTIME st; VALUE v; double msec; double sec; VariantTimeToSystemTime(date, &st); v = rb_funcall(rb_cTime, rb_intern("new"), 6, RB_INT2FIX(st.wYear), RB_INT2FIX(st.wMonth), RB_INT2FIX(st.wDay), RB_INT2FIX(st.wHour), RB_INT2FIX(st.wMinute), RB_INT2FIX(st.wSecond)); st.wYear = RB_FIX2INT(rb_funcall(v, rb_intern("year"), 0)); st.wMonth = RB_FIX2INT(rb_funcall(v, rb_intern("month"), 0)); st.wDay = RB_FIX2INT(rb_funcall(v, rb_intern("mday"), 0)); st.wHour = RB_FIX2INT(rb_funcall(v, rb_intern("hour"), 0)); st.wMinute = RB_FIX2INT(rb_funcall(v, rb_intern("min"), 0)); st.wSecond = RB_FIX2INT(rb_funcall(v, rb_intern("sec"), 0)); st.wMilliseconds = 0; SystemTimeToVariantTime(&st, &sec); /* * Unfortunately VariantTimeToSystemTime always ignores the * wMilliseconds of SYSTEMTIME struct(The wMilliseconds is 0). * So, we need to calculate milliseconds by ourselves. */ msec = date - sec; msec *= 24 * 60; msec -= floor(msec); msec *= 60; if (msec >= 59) { msec -= 60; } if (msec != 0) { return rb_funcall(v, rb_intern("+"), 1, rb_float_new(msec)); } return v; } #define ENC_MACHING_CP(enc,encname,cp) if(strcasecmp(rb_enc_name((enc)),(encname)) == 0) return cp static UINT ole_encoding2cp(rb_encoding *enc) { /* * Is there any better solution to convert * Ruby encoding to Windows codepage??? */ ENC_MACHING_CP(enc, "Big5", 950); ENC_MACHING_CP(enc, "CP51932", 51932); ENC_MACHING_CP(enc, "CP850", 850); ENC_MACHING_CP(enc, "CP852", 852); ENC_MACHING_CP(enc, "CP855", 855); ENC_MACHING_CP(enc, "CP949", 949); ENC_MACHING_CP(enc, "EUC-JP", 20932); ENC_MACHING_CP(enc, "EUC-KR", 51949); ENC_MACHING_CP(enc, "EUC-TW", 51950); ENC_MACHING_CP(enc, "GB18030", 54936); ENC_MACHING_CP(enc, "GB2312", 20936); ENC_MACHING_CP(enc, "GBK", 936); ENC_MACHING_CP(enc, "IBM437", 437); ENC_MACHING_CP(enc, "IBM720", 720); ENC_MACHING_CP(enc, "IBM737", 737); ENC_MACHING_CP(enc, "IBM775", 775); ENC_MACHING_CP(enc, "IBM852", 852); ENC_MACHING_CP(enc, "IBM855", 855); ENC_MACHING_CP(enc, "IBM857", 857); ENC_MACHING_CP(enc, "IBM860", 860); ENC_MACHING_CP(enc, "IBM861", 861); ENC_MACHING_CP(enc, "IBM862", 862); ENC_MACHING_CP(enc, "IBM863", 863); ENC_MACHING_CP(enc, "IBM864", 864); ENC_MACHING_CP(enc, "IBM865", 865); ENC_MACHING_CP(enc, "IBM866", 866); ENC_MACHING_CP(enc, "IBM869", 869); ENC_MACHING_CP(enc, "ISO-2022-JP", 50220); ENC_MACHING_CP(enc, "ISO-8859-1", 28591); ENC_MACHING_CP(enc, "ISO-8859-15", 28605); ENC_MACHING_CP(enc, "ISO-8859-2", 28592); ENC_MACHING_CP(enc, "ISO-8859-3", 28593); ENC_MACHING_CP(enc, "ISO-8859-4", 28594); ENC_MACHING_CP(enc, "ISO-8859-5", 28595); ENC_MACHING_CP(enc, "ISO-8859-6", 28596); ENC_MACHING_CP(enc, "ISO-8859-7", 28597); ENC_MACHING_CP(enc, "ISO-8859-8", 28598); ENC_MACHING_CP(enc, "ISO-8859-9", 28599); ENC_MACHING_CP(enc, "KOI8-R", 20866); ENC_MACHING_CP(enc, "KOI8-U", 21866); ENC_MACHING_CP(enc, "Shift_JIS", 932); ENC_MACHING_CP(enc, "UTF-16BE", 1201); ENC_MACHING_CP(enc, "UTF-16LE", 1200); ENC_MACHING_CP(enc, "UTF-7", 65000); ENC_MACHING_CP(enc, "UTF-8", 65001); ENC_MACHING_CP(enc, "Windows-1250", 1250); ENC_MACHING_CP(enc, "Windows-1251", 1251); ENC_MACHING_CP(enc, "Windows-1252", 1252); ENC_MACHING_CP(enc, "Windows-1253", 1253); ENC_MACHING_CP(enc, "Windows-1254", 1254); ENC_MACHING_CP(enc, "Windows-1255", 1255); ENC_MACHING_CP(enc, "Windows-1256", 1256); ENC_MACHING_CP(enc, "Windows-1257", 1257); ENC_MACHING_CP(enc, "Windows-1258", 1258); ENC_MACHING_CP(enc, "Windows-31J", 932); ENC_MACHING_CP(enc, "Windows-874", 874); ENC_MACHING_CP(enc, "eucJP-ms", 20932); return CP_ACP; } static void failed_load_conv51932(void) { rb_raise(eWIN32OLERuntimeError, "fail to load convert function for CP51932"); } #ifndef pIMultiLanguage static void load_conv_function51932(void) { HRESULT hr = E_NOINTERFACE; void *p; if (!pIMultiLanguage) { #if defined(HAVE_TYPE_IMULTILANGUAGE2) hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, &IID_IMultiLanguage2, &p); #elif defined(HAVE_TYPE_IMULTILANGUAGE) hr = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, &IID_IMultiLanguage, &p); #endif if (FAILED(hr)) { failed_load_conv51932(); } pIMultiLanguage = p; } } #define need_conv_function51932() (load_conv_function51932(), 1) #else #define load_conv_function51932() failed_load_conv51932() #define need_conv_function51932() (failed_load_conv51932(), 0) #endif #define conv_51932(cp) ((cp) == 51932 && need_conv_function51932()) static void set_ole_codepage(UINT cp) { if (code_page_installed(cp)) { cWIN32OLE_cp = cp; } else { switch(cp) { case CP_ACP: case CP_OEMCP: case CP_MACCP: case CP_THREAD_ACP: case CP_SYMBOL: case CP_UTF7: case CP_UTF8: cWIN32OLE_cp = cp; break; case 51932: cWIN32OLE_cp = cp; load_conv_function51932(); break; default: rb_raise(eWIN32OLERuntimeError, "codepage should be WIN32OLE::CP_ACP, WIN32OLE::CP_OEMCP, WIN32OLE::CP_MACCP, WIN32OLE::CP_THREAD_ACP, WIN32OLE::CP_SYMBOL, WIN32OLE::CP_UTF7, WIN32OLE::CP_UTF8, or installed codepage."); break; } } cWIN32OLE_enc = ole_cp2encoding(cWIN32OLE_cp); } static UINT ole_init_cp(void) { UINT cp; rb_encoding *encdef; encdef = rb_default_internal_encoding(); if (!encdef) { encdef = rb_default_external_encoding(); } cp = ole_encoding2cp(encdef); set_ole_codepage(cp); return cp; } struct myCPINFOEX { UINT MaxCharSize; BYTE DefaultChar[2]; BYTE LeadByte[12]; WCHAR UnicodeDefaultChar; UINT CodePage; char CodePageName[MAX_PATH]; }; static rb_encoding * ole_cp2encoding(UINT cp) { static BOOL (*pGetCPInfoEx)(UINT, DWORD, struct myCPINFOEX *) = NULL; struct myCPINFOEX* buf; VALUE enc_name; char *enc_cstr; int idx; if (!code_page_installed(cp)) { switch(cp) { case CP_ACP: cp = GetACP(); break; case CP_OEMCP: cp = GetOEMCP(); break; case CP_MACCP: case CP_THREAD_ACP: if (!pGetCPInfoEx) { pGetCPInfoEx = (BOOL (*)(UINT, DWORD, struct myCPINFOEX *)) GetProcAddress(GetModuleHandle("kernel32"), "GetCPInfoEx"); if (!pGetCPInfoEx) { pGetCPInfoEx = (void*)-1; } } buf = ALLOCA_N(struct myCPINFOEX, 1); ZeroMemory(buf, sizeof(struct myCPINFOEX)); if (pGetCPInfoEx == (void*)-1 || !pGetCPInfoEx(cp, 0, buf)) { rb_raise(eWIN32OLERuntimeError, "cannot map codepage to encoding."); break; /* never reach here */ } cp = buf->CodePage; break; case CP_SYMBOL: case CP_UTF7: case CP_UTF8: break; case 51932: load_conv_function51932(); break; default: rb_raise(eWIN32OLERuntimeError, "codepage should be WIN32OLE::CP_ACP, WIN32OLE::CP_OEMCP, WIN32OLE::CP_MACCP, WIN32OLE::CP_THREAD_ACP, WIN32OLE::CP_SYMBOL, WIN32OLE::CP_UTF7, WIN32OLE::CP_UTF8, or installed codepage."); break; } } enc_name = rb_sprintf("CP%d", cp); idx = rb_enc_find_index(enc_cstr = StringValueCStr(enc_name)); if (idx < 0) idx = rb_define_dummy_encoding(enc_cstr); return rb_enc_from_index(idx); } #ifndef pIMultiLanguage static HRESULT ole_ml_wc2mb_conv0(LPWSTR pw, LPSTR pm, UINT *size) { DWORD dw = 0; return pIMultiLanguage->lpVtbl->ConvertStringFromUnicode(pIMultiLanguage, &dw, cWIN32OLE_cp, pw, NULL, pm, size); } #define ole_ml_wc2mb_conv(pw, pm, size, onfailure) do { \ HRESULT hr = ole_ml_wc2mb_conv0(pw, pm, &size); \ if (FAILED(hr)) { \ onfailure; \ ole_raise(hr, eWIN32OLERuntimeError, "fail to convert Unicode to CP%d", cWIN32OLE_cp); \ } \ } while (0) #endif #define ole_wc2mb_conv(pw, pm, size) WideCharToMultiByte(cWIN32OLE_cp, 0, (pw), -1, (pm), (size), NULL, NULL) static char * ole_wc2mb_alloc(LPWSTR pw, char *(alloc)(UINT size, void *arg), void *arg) { LPSTR pm; UINT size = 0; if (conv_51932(cWIN32OLE_cp)) { #ifndef pIMultiLanguage ole_ml_wc2mb_conv(pw, NULL, size, {}); pm = alloc(size, arg); if (size) ole_ml_wc2mb_conv(pw, pm, size, xfree(pm)); pm[size] = '\0'; return pm; #endif } size = ole_wc2mb_conv(pw, NULL, 0); pm = alloc(size, arg); if (size) ole_wc2mb_conv(pw, pm, size); pm[size] = '\0'; return pm; } static char * ole_alloc_str(UINT size, void *arg) { return ALLOC_N(char, size + 1); } char * ole_wc2mb(LPWSTR pw) { return ole_wc2mb_alloc(pw, ole_alloc_str, NULL); } static void ole_freeexceptinfo(EXCEPINFO *pExInfo) { SysFreeString(pExInfo->bstrDescription); SysFreeString(pExInfo->bstrSource); SysFreeString(pExInfo->bstrHelpFile); } static VALUE ole_excepinfo2msg(EXCEPINFO *pExInfo) { char error_code[40]; char *pSource = NULL; char *pDescription = NULL; VALUE error_msg; if(pExInfo->pfnDeferredFillIn != NULL) { (*pExInfo->pfnDeferredFillIn)(pExInfo); } if (pExInfo->bstrSource != NULL) { pSource = ole_wc2mb(pExInfo->bstrSource); } if (pExInfo->bstrDescription != NULL) { pDescription = ole_wc2mb(pExInfo->bstrDescription); } if(pExInfo->wCode == 0) { sprintf(error_code, "\n OLE error code:%lX in ", (unsigned long)pExInfo->scode); } else{ sprintf(error_code, "\n OLE error code:%u in ", pExInfo->wCode); } error_msg = rb_str_new2(error_code); if(pSource != NULL) { rb_str_cat2(error_msg, pSource); } else { rb_str_cat(error_msg, "", 9); } rb_str_cat2(error_msg, "\n "); if(pDescription != NULL) { rb_str_cat2(error_msg, pDescription); } else { rb_str_cat2(error_msg, ""); } if(pSource) free(pSource); if(pDescription) free(pDescription); ole_freeexceptinfo(pExInfo); return error_msg; } void ole_uninitialize(void) { if (!g_ole_initialized) return; OleUninitialize(); g_ole_initialized_set(FALSE); } static void ole_uninitialize_hook(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass) { ole_uninitialize(); } void ole_initialize(void) { HRESULT hr; if(!g_uninitialize_hooked) { rb_add_event_hook(ole_uninitialize_hook, RUBY_EVENT_THREAD_END, Qnil); g_uninitialize_hooked = TRUE; } if(g_ole_initialized == FALSE) { if(g_running_nano) { hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); } else { hr = OleInitialize(NULL); } if(FAILED(hr)) { ole_raise(hr, rb_eRuntimeError, "fail: OLE initialize"); } g_ole_initialized_set(TRUE); if (g_running_nano == FALSE) { hr = CoRegisterMessageFilter(&imessage_filter, &previous_filter); if(FAILED(hr)) { previous_filter = NULL; ole_raise(hr, rb_eRuntimeError, "fail: install OLE MessageFilter"); } } } } static void ole_free(void *ptr) { struct oledata *pole = ptr; OLE_FREE(pole->pDispatch); free(pole); } static size_t ole_size(const void *ptr) { return ptr ? sizeof(struct oledata) : 0; } struct oledata * oledata_get_struct(VALUE ole) { struct oledata *pole; TypedData_Get_Struct(ole, struct oledata, &ole_datatype, pole); return pole; } LPWSTR ole_vstr2wc(VALUE vstr) { rb_encoding *enc; int cp; LPWSTR pw; st_data_t data; struct st_table *tbl = DATA_PTR(enc2cp_hash); /* do not type-conversion here to prevent from other arguments * changing (if exist) */ Check_Type(vstr, T_STRING); if (RSTRING_LEN(vstr) == 0) { return NULL; } enc = rb_enc_get(vstr); if (st_lookup(tbl, (VALUE)enc | FIXNUM_FLAG, &data)) { cp = RB_FIX2INT((VALUE)data); } else { cp = ole_encoding2cp(enc); if (code_page_installed(cp) || cp == CP_ACP || cp == CP_OEMCP || cp == CP_MACCP || cp == CP_THREAD_ACP || cp == CP_SYMBOL || cp == CP_UTF7 || cp == CP_UTF8 || cp == 51932) { st_insert(tbl, (VALUE)enc | FIXNUM_FLAG, RB_INT2FIX(cp)); } else { rb_raise(eWIN32OLERuntimeError, "not installed Windows codepage(%d) according to `%s'", cp, rb_enc_name(enc)); } } pw = ole_mb2wc(RSTRING_PTR(vstr), RSTRING_LENINT(vstr), cp); RB_GC_GUARD(vstr); return pw; } static LPWSTR ole_mb2wc(char *pm, int len, UINT cp) { UINT size = 0; LPWSTR pw; if (conv_51932(cp)) { #ifndef pIMultiLanguage DWORD dw = 0; UINT n = len; HRESULT hr = pIMultiLanguage->lpVtbl->ConvertStringToUnicode(pIMultiLanguage, &dw, cp, pm, &n, NULL, &size); if (FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "fail to convert CP%d to Unicode", cp); } pw = SysAllocStringLen(NULL, size); n = len; hr = pIMultiLanguage->lpVtbl->ConvertStringToUnicode(pIMultiLanguage, &dw, cp, pm, &n, pw, &size); if (FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "fail to convert CP%d to Unicode", cp); } return pw; #endif } size = MultiByteToWideChar(cp, 0, pm, len, NULL, 0); pw = SysAllocStringLen(NULL, size); pw[size-1] = 0; MultiByteToWideChar(cp, 0, pm, len, pw, size); return pw; } static char * ole_alloc_vstr(UINT size, void *arg) { VALUE str = rb_enc_str_new(NULL, size, cWIN32OLE_enc); *(VALUE *)arg = str; return RSTRING_PTR(str); } VALUE ole_wc2vstr(LPWSTR pw, BOOL isfree) { VALUE vstr; ole_wc2mb_alloc(pw, ole_alloc_vstr, &vstr); rb_str_set_len(vstr, (long)strlen(RSTRING_PTR(vstr))); if(isfree) SysFreeString(pw); return vstr; } static VALUE ole_ary_m_entry(VALUE val, LONG *pid) { VALUE obj = Qnil; int i = 0; obj = val; while(RB_TYPE_P(obj, T_ARRAY)) { obj = rb_ary_entry(obj, pid[i]); i++; } return obj; } static VALUE is_all_index_under(LONG *pid, long *pub, long dim) { long i = 0; for (i = 0; i < dim; i++) { if (pid[i] > pub[i]) { return Qfalse; } } return Qtrue; } void ole_val2variant_ex(VALUE val, VARIANT *var, VARTYPE vt) { if (val == Qnil) { if (vt == VT_VARIANT) { ole_val2variant2(val, var); } else { V_VT(var) = (vt & ~VT_BYREF); if (V_VT(var) == VT_DISPATCH) { V_DISPATCH(var) = NULL; } else if (V_VT(var) == VT_UNKNOWN) { V_UNKNOWN(var) = NULL; } } return; } #if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) switch(vt & ~VT_BYREF) { case VT_I8: V_VT(var) = VT_I8; V_I8(var) = NUM2I8 (val); break; case VT_UI8: V_VT(var) = VT_UI8; V_UI8(var) = NUM2UI8(val); break; default: ole_val2variant2(val, var); break; } #else /* (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) */ ole_val2variant2(val, var); #endif } VOID * val2variant_ptr(VALUE val, VARIANT *var, VARTYPE vt) { VOID *p = NULL; HRESULT hr = S_OK; ole_val2variant_ex(val, var, vt); if ((vt & ~VT_BYREF) == VT_VARIANT) { p = var; } else { if ( (vt & ~VT_BYREF) != V_VT(var)) { hr = VariantChangeTypeEx(var, var, cWIN32OLE_lcid, 0, (VARTYPE)(vt & ~VT_BYREF)); if (FAILED(hr)) { ole_raise(hr, rb_eRuntimeError, "failed to change type"); } } p = get_ptr_of_variant(var); } if (p == NULL) { rb_raise(rb_eRuntimeError, "failed to get pointer of variant"); } return p; } static void * get_ptr_of_variant(VARIANT *pvar) { switch(V_VT(pvar)) { case VT_UI1: return &V_UI1(pvar); break; case VT_I2: return &V_I2(pvar); break; case VT_UI2: return &V_UI2(pvar); break; case VT_I4: return &V_I4(pvar); break; case VT_UI4: return &V_UI4(pvar); break; case VT_R4: return &V_R4(pvar); break; case VT_R8: return &V_R8(pvar); break; #if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) case VT_I8: return &V_I8(pvar); break; case VT_UI8: return &V_UI8(pvar); break; #endif case VT_INT: return &V_INT(pvar); break; case VT_UINT: return &V_UINT(pvar); break; case VT_CY: return &V_CY(pvar); break; case VT_DATE: return &V_DATE(pvar); break; case VT_BSTR: return V_BSTR(pvar); break; case VT_DISPATCH: return V_DISPATCH(pvar); break; case VT_ERROR: return &V_ERROR(pvar); break; case VT_BOOL: return &V_BOOL(pvar); break; case VT_UNKNOWN: return V_UNKNOWN(pvar); break; case VT_ARRAY: return &V_ARRAY(pvar); break; default: return NULL; break; } } static void ole_set_safe_array(long n, SAFEARRAY *psa, LONG *pid, long *pub, VALUE val, long dim, VARTYPE vt) { VALUE val1; HRESULT hr = S_OK; VARIANT var; VOID *p = NULL; long i = n; while(i >= 0) { val1 = ole_ary_m_entry(val, pid); VariantInit(&var); p = val2variant_ptr(val1, &var, vt); if (is_all_index_under(pid, pub, dim) == Qtrue) { if ((V_VT(&var) == VT_DISPATCH && V_DISPATCH(&var) == NULL) || (V_VT(&var) == VT_UNKNOWN && V_UNKNOWN(&var) == NULL)) { rb_raise(eWIN32OLERuntimeError, "element of array does not have IDispatch or IUnknown Interface"); } hr = SafeArrayPutElement(psa, pid, p); } if (FAILED(hr)) { ole_raise(hr, rb_eRuntimeError, "failed to SafeArrayPutElement"); } pid[i] += 1; if (pid[i] > pub[i]) { pid[i] = 0; i -= 1; } else { i = dim - 1; } } } static long dimension(VALUE val) { long dim = 0; long dim1 = 0; long len = 0; long i = 0; if (RB_TYPE_P(val, T_ARRAY)) { len = RARRAY_LEN(val); for (i = 0; i < len; i++) { dim1 = dimension(rb_ary_entry(val, i)); if (dim < dim1) { dim = dim1; } } dim += 1; } return dim; } static long ary_len_of_dim(VALUE ary, long dim) { long ary_len = 0; long ary_len1 = 0; long len = 0; long i = 0; VALUE val; if (dim == 0) { if (RB_TYPE_P(ary, T_ARRAY)) { ary_len = RARRAY_LEN(ary); } } else { if (RB_TYPE_P(ary, T_ARRAY)) { len = RARRAY_LEN(ary); for (i = 0; i < len; i++) { val = rb_ary_entry(ary, i); ary_len1 = ary_len_of_dim(val, dim-1); if (ary_len < ary_len1) { ary_len = ary_len1; } } } } return ary_len; } HRESULT ole_val_ary2variant_ary(VALUE val, VARIANT *var, VARTYPE vt) { long dim = 0; int i = 0; HRESULT hr = S_OK; SAFEARRAYBOUND *psab = NULL; SAFEARRAY *psa = NULL; long *pub; LONG *pid; Check_Type(val, T_ARRAY); dim = dimension(val); psab = ALLOC_N(SAFEARRAYBOUND, dim); pub = ALLOC_N(long, dim); pid = ALLOC_N(LONG, dim); if(!psab || !pub || !pid) { if(pub) free(pub); if(psab) free(psab); if(pid) free(pid); rb_raise(rb_eRuntimeError, "memory allocation error"); } for (i = 0; i < dim; i++) { psab[i].cElements = ary_len_of_dim(val, i); psab[i].lLbound = 0; pub[i] = psab[i].cElements - 1; pid[i] = 0; } /* Create and fill VARIANT array */ if ((vt & ~VT_BYREF) == VT_ARRAY) { vt = (vt | VT_VARIANT); } psa = SafeArrayCreate((VARTYPE)(vt & VT_TYPEMASK), dim, psab); if (psa == NULL) hr = E_OUTOFMEMORY; else hr = SafeArrayLock(psa); if (SUCCEEDED(hr)) { ole_set_safe_array(dim-1, psa, pid, pub, val, dim, (VARTYPE)(vt & VT_TYPEMASK)); hr = SafeArrayUnlock(psa); } if(pub) free(pub); if(psab) free(psab); if(pid) free(pid); if (SUCCEEDED(hr)) { V_VT(var) = vt; V_ARRAY(var) = psa; } else { if (psa != NULL) SafeArrayDestroy(psa); } return hr; } void ole_val2variant(VALUE val, VARIANT *var) { struct oledata *pole = NULL; if(rb_obj_is_kind_of(val, cWIN32OLE)) { pole = oledata_get_struct(val); OLE_ADDREF(pole->pDispatch); V_VT(var) = VT_DISPATCH; V_DISPATCH(var) = pole->pDispatch; return; } if (rb_obj_is_kind_of(val, cWIN32OLE_VARIANT)) { ole_variant2variant(val, var); return; } if (rb_obj_is_kind_of(val, cWIN32OLE_RECORD)) { ole_rec2variant(val, var); return; } if (rb_obj_is_kind_of(val, rb_cTime)) { V_VT(var) = VT_DATE; V_DATE(var) = rbtime2vtdate(val); return; } switch (TYPE(val)) { case T_ARRAY: ole_val_ary2variant_ary(val, var, VT_VARIANT|VT_ARRAY); break; case T_STRING: V_VT(var) = VT_BSTR; V_BSTR(var) = ole_vstr2wc(val); break; case T_FIXNUM: V_VT(var) = VT_I4; { long v = RB_NUM2LONG(val); V_I4(var) = (LONG)v; #if SIZEOF_LONG > 4 if (V_I4(var) != v) { V_I8(var) = v; V_VT(var) = VT_I8; } #endif } break; case T_BIGNUM: V_VT(var) = VT_R8; V_R8(var) = rb_big2dbl(val); break; case T_FLOAT: V_VT(var) = VT_R8; V_R8(var) = NUM2DBL(val); break; case T_TRUE: V_VT(var) = VT_BOOL; V_BOOL(var) = VARIANT_TRUE; break; case T_FALSE: V_VT(var) = VT_BOOL; V_BOOL(var) = VARIANT_FALSE; break; case T_NIL: if (g_nil_to == VT_ERROR) { V_VT(var) = VT_ERROR; V_ERROR(var) = DISP_E_PARAMNOTFOUND; }else { V_VT(var) = VT_EMPTY; } break; default: V_VT(var) = VT_DISPATCH; V_DISPATCH(var) = val2dispatch(val); break; } } void ole_val2variant2(VALUE val, VARIANT *var) { g_nil_to = VT_EMPTY; ole_val2variant(val, var); g_nil_to = VT_ERROR; } VALUE make_inspect(const char *class_name, VALUE detail) { VALUE str; str = rb_str_new2("#<"); rb_str_cat2(str, class_name); rb_str_cat2(str, ":"); rb_str_concat(str, detail); rb_str_cat2(str, ">"); return str; } VALUE default_inspect(VALUE self, const char *class_name) { VALUE detail = rb_funcall(self, rb_intern("to_s"), 0); return make_inspect(class_name, detail); } static VALUE ole_set_member(VALUE self, IDispatch *dispatch) { struct oledata *pole = NULL; pole = oledata_get_struct(self); if (pole->pDispatch) { OLE_RELEASE(pole->pDispatch); pole->pDispatch = NULL; } pole->pDispatch = dispatch; return self; } static VALUE fole_s_allocate(VALUE klass) { struct oledata *pole; VALUE obj; ole_initialize(); obj = TypedData_Make_Struct(klass, struct oledata, &ole_datatype, pole); pole->pDispatch = NULL; return obj; } static VALUE create_win32ole_object(VALUE klass, IDispatch *pDispatch, int argc, VALUE *argv) { VALUE obj = fole_s_allocate(klass); ole_set_member(obj, pDispatch); return obj; } static VALUE ary_new_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim) { long i; VALUE obj = Qnil; VALUE pobj = Qnil; long *ids = ALLOC_N(long, dim); if (!ids) { rb_raise(rb_eRuntimeError, "memory allocation error"); } for(i = 0; i < dim; i++) { ids[i] = pid[i] - plb[i]; } obj = myary; pobj = myary; for(i = 0; i < dim-1; i++) { obj = rb_ary_entry(pobj, ids[i]); if (obj == Qnil) { rb_ary_store(pobj, ids[i], rb_ary_new()); } obj = rb_ary_entry(pobj, ids[i]); pobj = obj; } if (ids) free(ids); return obj; } static void ary_store_dim(VALUE myary, LONG *pid, LONG *plb, LONG dim, VALUE val) { long id = pid[dim - 1] - plb[dim - 1]; VALUE obj = ary_new_dim(myary, pid, plb, dim); rb_ary_store(obj, id, val); } VALUE ole_variant2val(VARIANT *pvar) { VALUE obj = Qnil; VARTYPE vt = V_VT(pvar); HRESULT hr; while ( vt == (VT_BYREF | VT_VARIANT) ) { pvar = V_VARIANTREF(pvar); vt = V_VT(pvar); } #define ARG_AS(type, pvar) (V_ISBYREF(pvar) ? *V_##type##REF(pvar) : V_##type(pvar)) if(V_ISARRAY(pvar)) { VARTYPE vt_base = vt & VT_TYPEMASK; SAFEARRAY *psa = ARG_AS(ARRAY, pvar); UINT i = 0; LONG *pid, *plb, *pub; VARIANT variant; VALUE val; UINT dim = 0; if (!psa) { return obj; } dim = SafeArrayGetDim(psa); pid = ALLOC_N(LONG, dim); plb = ALLOC_N(LONG, dim); pub = ALLOC_N(LONG, dim); if(!pid || !plb || !pub) { if(pid) free(pid); if(plb) free(plb); if(pub) free(pub); rb_raise(rb_eRuntimeError, "memory allocation error"); } for(i = 0; i < dim; ++i) { SafeArrayGetLBound(psa, i+1, &plb[i]); SafeArrayGetLBound(psa, i+1, &pid[i]); SafeArrayGetUBound(psa, i+1, &pub[i]); } hr = SafeArrayLock(psa); if (SUCCEEDED(hr)) { obj = rb_ary_new(); i = 0; VariantInit(&variant); V_VT(&variant) = vt_base | VT_BYREF; if (vt_base == VT_RECORD) { hr = SafeArrayGetRecordInfo(psa, &V_RECORDINFO(&variant)); if (SUCCEEDED(hr)) { V_VT(&variant) = VT_RECORD; } } while (i < dim) { ary_new_dim(obj, pid, plb, dim); if (vt_base == VT_RECORD) hr = SafeArrayPtrOfIndex(psa, pid, &V_RECORD(&variant)); else hr = SafeArrayPtrOfIndex(psa, pid, &V_BYREF(&variant)); if (SUCCEEDED(hr)) { val = ole_variant2val(&variant); ary_store_dim(obj, pid, plb, dim, val); } for (i = 0; i < dim; ++i) { if (++pid[i] <= pub[i]) break; pid[i] = plb[i]; } } SafeArrayUnlock(psa); } if(pid) free(pid); if(plb) free(plb); if(pub) free(pub); return obj; } switch(V_VT(pvar) & ~VT_BYREF){ case VT_EMPTY: break; case VT_NULL: break; case VT_I1: obj = RB_INT2NUM((long)ARG_AS(I1, pvar)); break; case VT_UI1: obj = RB_INT2NUM((long)ARG_AS(UI1, pvar)); break; case VT_I2: obj = RB_INT2NUM((long)ARG_AS(I2, pvar)); break; case VT_UI2: obj = RB_INT2NUM((long)ARG_AS(UI2, pvar)); break; case VT_I4: obj = RB_INT2NUM((long)ARG_AS(I4, pvar)); break; case VT_UI4: obj = RB_INT2NUM((long)ARG_AS(UI4, pvar)); break; case VT_INT: obj = RB_INT2NUM((long)ARG_AS(INT, pvar)); break; case VT_UINT: obj = RB_INT2NUM((long)ARG_AS(UINT, pvar)); break; #if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) case VT_I8: obj = I8_2_NUM(ARG_AS(I8, pvar)); break; case VT_UI8: obj = UI8_2_NUM(ARG_AS(UI8, pvar)); break; #endif /* (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) */ case VT_R4: obj = rb_float_new(ARG_AS(R4, pvar)); break; case VT_R8: obj = rb_float_new(ARG_AS(R8, pvar)); break; case VT_BSTR: { BSTR bstr; bstr = ARG_AS(BSTR, pvar); obj = (SysStringLen(bstr) == 0) ? rb_str_new2("") : ole_wc2vstr(bstr, FALSE); break; } case VT_ERROR: obj = RB_INT2NUM(ARG_AS(ERROR, pvar)); break; case VT_BOOL: obj = (ARG_AS(BOOL, pvar) ? Qtrue : Qfalse); break; case VT_DISPATCH: { IDispatch *pDispatch; pDispatch = ARG_AS(DISPATCH, pvar); if (pDispatch != NULL ) { OLE_ADDREF(pDispatch); obj = create_win32ole_object(cWIN32OLE, pDispatch, 0, 0); } break; } case VT_UNKNOWN: { /* get IDispatch interface from IUnknown interface */ IUnknown *punk; IDispatch *pDispatch; void *p; HRESULT hr; punk = ARG_AS(UNKNOWN, pvar); if(punk != NULL) { hr = punk->lpVtbl->QueryInterface(punk, &IID_IDispatch, &p); if(SUCCEEDED(hr)) { pDispatch = p; obj = create_win32ole_object(cWIN32OLE, pDispatch, 0, 0); } } break; } case VT_DATE: { DATE date; date = ARG_AS(DATE, pvar); obj = vtdate2rbtime(date); break; } case VT_RECORD: { IRecordInfo *pri = V_RECORDINFO(pvar); void *prec = V_RECORD(pvar); obj = create_win32ole_record(pri, prec); break; } case VT_CY: default: { HRESULT hr; VARIANT variant; VariantInit(&variant); hr = VariantChangeTypeEx(&variant, pvar, cWIN32OLE_lcid, 0, VT_BSTR); if (SUCCEEDED(hr) && V_VT(&variant) == VT_BSTR) { obj = ole_wc2vstr(V_BSTR(&variant), FALSE); } VariantClear(&variant); break; } } return obj; #undef ARG_AS } LONG reg_open_key(HKEY hkey, const char *name, HKEY *phkey) { return RegOpenKeyEx(hkey, name, 0, KEY_READ, phkey); } LONG reg_open_vkey(HKEY hkey, VALUE key, HKEY *phkey) { return reg_open_key(hkey, StringValuePtr(key), phkey); } VALUE reg_enum_key(HKEY hkey, DWORD i) { char buf[BUFSIZ + 1]; DWORD size_buf = sizeof(buf); FILETIME ft; LONG err = RegEnumKeyEx(hkey, i, buf, &size_buf, NULL, NULL, NULL, &ft); if(err == ERROR_SUCCESS) { buf[BUFSIZ] = '\0'; return rb_str_new2(buf); } return Qnil; } VALUE reg_get_val(HKEY hkey, const char *subkey) { char *pbuf; DWORD dwtype = 0; DWORD size = 0; VALUE val = Qnil; LONG err = RegQueryValueEx(hkey, subkey, NULL, &dwtype, NULL, &size); if (err == ERROR_SUCCESS) { pbuf = ALLOC_N(char, size + 1); err = RegQueryValueEx(hkey, subkey, NULL, &dwtype, (BYTE *)pbuf, &size); if (err == ERROR_SUCCESS) { pbuf[size] = '\0'; if (dwtype == REG_EXPAND_SZ) { char* pbuf2 = (char *)pbuf; DWORD len = ExpandEnvironmentStrings(pbuf2, NULL, 0); pbuf = ALLOC_N(char, len + 1); ExpandEnvironmentStrings(pbuf2, pbuf, len + 1); free(pbuf2); } val = rb_str_new2((char *)pbuf); } free(pbuf); } return val; } VALUE reg_get_val2(HKEY hkey, const char *subkey) { HKEY hsubkey; LONG err; VALUE val = Qnil; err = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &hsubkey); if (err == ERROR_SUCCESS) { val = reg_get_val(hsubkey, NULL); RegCloseKey(hsubkey); } if (val == Qnil) { val = reg_get_val(hkey, subkey); } return val; } static void ole_const_load(ITypeLib *pTypeLib, VALUE klass, VALUE self) { unsigned int count; unsigned int index; int iVar; ITypeInfo *pTypeInfo; TYPEATTR *pTypeAttr; VARDESC *pVarDesc; HRESULT hr; unsigned int len; BSTR bstr; char *pName = NULL; VALUE val; VALUE constant; ID id; constant = rb_hash_new(); count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); for (index = 0; index < count; index++) { hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, index, &pTypeInfo); if (FAILED(hr)) continue; hr = OLE_GET_TYPEATTR(pTypeInfo, &pTypeAttr); if(FAILED(hr)) { OLE_RELEASE(pTypeInfo); continue; } for(iVar = 0; iVar < pTypeAttr->cVars; iVar++) { hr = pTypeInfo->lpVtbl->GetVarDesc(pTypeInfo, iVar, &pVarDesc); if(FAILED(hr)) continue; if(pVarDesc->varkind == VAR_CONST && !(pVarDesc->wVarFlags & (VARFLAG_FHIDDEN | VARFLAG_FRESTRICTED | VARFLAG_FNONBROWSABLE))) { hr = pTypeInfo->lpVtbl->GetNames(pTypeInfo, pVarDesc->memid, &bstr, 1, &len); if(FAILED(hr) || len == 0 || !bstr) continue; pName = ole_wc2mb(bstr); val = ole_variant2val(V_UNION1(pVarDesc, lpvarValue)); *pName = toupper((int)*pName); id = rb_intern(pName); if (rb_is_const_id(id)) { if(!rb_const_defined_at(klass, id)) { rb_define_const(klass, pName, val); } } else { rb_hash_aset(constant, rb_str_new2(pName), val); } SysFreeString(bstr); if(pName) { free(pName); pName = NULL; } } pTypeInfo->lpVtbl->ReleaseVarDesc(pTypeInfo, pVarDesc); } pTypeInfo->lpVtbl->ReleaseTypeAttr(pTypeInfo, pTypeAttr); OLE_RELEASE(pTypeInfo); } rb_define_const(klass, "CONSTANTS", constant); } static HRESULT clsid_from_remote(VALUE host, VALUE com, CLSID *pclsid) { HKEY hlm; HKEY hpid; VALUE subkey; LONG err; char clsid[100]; OLECHAR *pbuf; DWORD len; DWORD dwtype; HRESULT hr = S_OK; err = RegConnectRegistry(StringValuePtr(host), HKEY_LOCAL_MACHINE, &hlm); if (err != ERROR_SUCCESS) return HRESULT_FROM_WIN32(err); subkey = rb_str_new2("SOFTWARE\\Classes\\"); rb_str_concat(subkey, com); rb_str_cat2(subkey, "\\CLSID"); err = RegOpenKeyEx(hlm, StringValuePtr(subkey), 0, KEY_READ, &hpid); if (err != ERROR_SUCCESS) hr = HRESULT_FROM_WIN32(err); else { len = sizeof(clsid); err = RegQueryValueEx(hpid, "", NULL, &dwtype, (BYTE *)clsid, &len); if (err == ERROR_SUCCESS && dwtype == REG_SZ) { pbuf = ole_mb2wc(clsid, -1, cWIN32OLE_cp); hr = CLSIDFromString(pbuf, pclsid); SysFreeString(pbuf); } else { hr = HRESULT_FROM_WIN32(err); } RegCloseKey(hpid); } RegCloseKey(hlm); return hr; } static VALUE ole_create_dcom(VALUE self, VALUE ole, VALUE host, VALUE others) { HRESULT hr; CLSID clsid; OLECHAR *pbuf; COSERVERINFO serverinfo; MULTI_QI multi_qi; DWORD clsctx = CLSCTX_REMOTE_SERVER; if (!gole32) gole32 = LoadLibrary("OLE32"); if (!gole32) rb_raise(rb_eRuntimeError, "failed to load OLE32"); if (!gCoCreateInstanceEx) gCoCreateInstanceEx = (FNCOCREATEINSTANCEEX*) GetProcAddress(gole32, "CoCreateInstanceEx"); if (!gCoCreateInstanceEx) rb_raise(rb_eRuntimeError, "CoCreateInstanceEx is not supported in this environment"); pbuf = ole_vstr2wc(ole); hr = CLSIDFromProgID(pbuf, &clsid); if (FAILED(hr)) hr = clsid_from_remote(host, ole, &clsid); if (FAILED(hr)) hr = CLSIDFromString(pbuf, &clsid); SysFreeString(pbuf); if (FAILED(hr)) ole_raise(hr, eWIN32OLERuntimeError, "unknown OLE server: `%s'", StringValuePtr(ole)); memset(&serverinfo, 0, sizeof(COSERVERINFO)); serverinfo.pwszName = ole_vstr2wc(host); memset(&multi_qi, 0, sizeof(MULTI_QI)); multi_qi.pIID = &IID_IDispatch; hr = gCoCreateInstanceEx(&clsid, NULL, clsctx, &serverinfo, 1, &multi_qi); SysFreeString(serverinfo.pwszName); if (FAILED(hr)) ole_raise(hr, eWIN32OLERuntimeError, "failed to create DCOM server `%s' in `%s'", StringValuePtr(ole), StringValuePtr(host)); ole_set_member(self, (IDispatch*)multi_qi.pItf); return self; } static VALUE ole_bind_obj(VALUE moniker, int argc, VALUE *argv, VALUE self) { IBindCtx *pBindCtx; IMoniker *pMoniker; IDispatch *pDispatch; void *p; HRESULT hr; OLECHAR *pbuf; ULONG eaten = 0; ole_initialize(); hr = CreateBindCtx(0, &pBindCtx); if(FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "failed to create bind context"); } pbuf = ole_vstr2wc(moniker); hr = MkParseDisplayName(pBindCtx, pbuf, &eaten, &pMoniker); SysFreeString(pbuf); if(FAILED(hr)) { OLE_RELEASE(pBindCtx); ole_raise(hr, eWIN32OLERuntimeError, "failed to parse display name of moniker `%s'", StringValuePtr(moniker)); } hr = pMoniker->lpVtbl->BindToObject(pMoniker, pBindCtx, NULL, &IID_IDispatch, &p); pDispatch = p; OLE_RELEASE(pMoniker); OLE_RELEASE(pBindCtx); if(FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "failed to bind moniker `%s'", StringValuePtr(moniker)); } return create_win32ole_object(self, pDispatch, argc, argv); } /* * call-seq: * connect(ole) --> aWIN32OLE * * Returns running OLE Automation object or WIN32OLE object from moniker. * 1st argument should be OLE program id or class id or moniker. * * WIN32OLE.connect('Excel.Application') # => WIN32OLE object which represents running Excel. */ static VALUE fole_s_connect(int argc, VALUE *argv, VALUE self) { VALUE svr_name; VALUE others; HRESULT hr; CLSID clsid; OLECHAR *pBuf; IDispatch *pDispatch; void *p; IUnknown *pUnknown; /* initialize to use OLE */ ole_initialize(); rb_scan_args(argc, argv, "1*", &svr_name, &others); StringValue(svr_name); /* get CLSID from OLE server name */ pBuf = ole_vstr2wc(svr_name); hr = CLSIDFromProgID(pBuf, &clsid); if(FAILED(hr)) { hr = CLSIDFromString(pBuf, &clsid); } SysFreeString(pBuf); if(FAILED(hr)) { return ole_bind_obj(svr_name, argc, argv, self); } hr = GetActiveObject(&clsid, 0, &pUnknown); if (FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "OLE server `%s' not running", StringValuePtr(svr_name)); } hr = pUnknown->lpVtbl->QueryInterface(pUnknown, &IID_IDispatch, &p); pDispatch = p; if(FAILED(hr)) { OLE_RELEASE(pUnknown); ole_raise(hr, eWIN32OLERuntimeError, "failed to create WIN32OLE server `%s'", StringValuePtr(svr_name)); } OLE_RELEASE(pUnknown); return create_win32ole_object(self, pDispatch, argc, argv); } /* * call-seq: * const_load(ole, mod = WIN32OLE) * * Defines the constants of OLE Automation server as mod's constants. * The first argument is WIN32OLE object or type library name. * If 2nd argument is omitted, the default is WIN32OLE. * The first letter of Ruby's constant variable name is upper case, * so constant variable name of WIN32OLE object is capitalized. * For example, the 'xlTop' constant of Excel is changed to 'XlTop' * in WIN32OLE. * If the first letter of constant variable is not [A-Z], then * the constant is defined as CONSTANTS hash element. * * module EXCEL_CONST * end * excel = WIN32OLE.new('Excel.Application') * WIN32OLE.const_load(excel, EXCEL_CONST) * puts EXCEL_CONST::XlTop # => -4160 * puts EXCEL_CONST::CONSTANTS['_xlDialogChartSourceData'] # => 541 * * WIN32OLE.const_load(excel) * puts WIN32OLE::XlTop # => -4160 * * module MSO * end * WIN32OLE.const_load('Microsoft Office 9.0 Object Library', MSO) * puts MSO::MsoLineSingle # => 1 */ static VALUE fole_s_const_load(int argc, VALUE *argv, VALUE self) { VALUE ole; VALUE klass; struct oledata *pole = NULL; ITypeInfo *pTypeInfo; ITypeLib *pTypeLib; unsigned int index; HRESULT hr; OLECHAR *pBuf; VALUE file; LCID lcid = cWIN32OLE_lcid; rb_scan_args(argc, argv, "11", &ole, &klass); if (!RB_TYPE_P(klass, T_CLASS) && !RB_TYPE_P(klass, T_MODULE) && !RB_TYPE_P(klass, T_NIL)) { rb_raise(rb_eTypeError, "2nd parameter must be Class or Module"); } if (rb_obj_is_kind_of(ole, cWIN32OLE)) { pole = oledata_get_struct(ole); hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, 0, lcid, &pTypeInfo); if(FAILED(hr)) { ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); } hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &index); if(FAILED(hr)) { OLE_RELEASE(pTypeInfo); ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetContainingTypeLib"); } OLE_RELEASE(pTypeInfo); if(!RB_TYPE_P(klass, T_NIL)) { ole_const_load(pTypeLib, klass, self); } else { ole_const_load(pTypeLib, cWIN32OLE, self); } OLE_RELEASE(pTypeLib); } else if(RB_TYPE_P(ole, T_STRING)) { file = typelib_file(ole); if (file == Qnil) { file = ole; } pBuf = ole_vstr2wc(file); hr = LoadTypeLibEx(pBuf, REGKIND_NONE, &pTypeLib); SysFreeString(pBuf); if (FAILED(hr)) ole_raise(hr, eWIN32OLERuntimeError, "failed to LoadTypeLibEx"); if(!RB_TYPE_P(klass, T_NIL)) { ole_const_load(pTypeLib, klass, self); } else { ole_const_load(pTypeLib, cWIN32OLE, self); } OLE_RELEASE(pTypeLib); } else { rb_raise(rb_eTypeError, "1st parameter must be WIN32OLE instance"); } return Qnil; } static ULONG reference_count(struct oledata * pole) { ULONG n = 0; if(pole->pDispatch) { OLE_ADDREF(pole->pDispatch); n = OLE_RELEASE(pole->pDispatch); } return n; } /* * call-seq: * ole_reference_count(aWIN32OLE) --> number * * Returns reference counter of Dispatch interface of WIN32OLE object. * You should not use this method because this method * exists only for debugging WIN32OLE. */ static VALUE fole_s_reference_count(VALUE self, VALUE obj) { struct oledata * pole = NULL; pole = oledata_get_struct(obj); return RB_INT2NUM(reference_count(pole)); } /* * call-seq: * ole_free(aWIN32OLE) --> number * * Invokes Release method of Dispatch interface of WIN32OLE object. * You should not use this method because this method * exists only for debugging WIN32OLE. * The return value is reference counter of OLE object. */ static VALUE fole_s_free(VALUE self, VALUE obj) { ULONG n = 0; struct oledata * pole = NULL; pole = oledata_get_struct(obj); if(pole->pDispatch) { if (reference_count(pole) > 0) { n = OLE_RELEASE(pole->pDispatch); } } return RB_INT2NUM(n); } static HWND ole_show_help(VALUE helpfile, VALUE helpcontext) { FNHTMLHELP *pfnHtmlHelp; HWND hwnd = 0; if(!ghhctrl) ghhctrl = LoadLibrary("HHCTRL.OCX"); if (!ghhctrl) return hwnd; pfnHtmlHelp = (FNHTMLHELP*)GetProcAddress(ghhctrl, "HtmlHelpA"); if (!pfnHtmlHelp) return hwnd; hwnd = pfnHtmlHelp(GetDesktopWindow(), StringValuePtr(helpfile), 0x0f, RB_NUM2INT(helpcontext)); if (hwnd == 0) hwnd = pfnHtmlHelp(GetDesktopWindow(), StringValuePtr(helpfile), 0, RB_NUM2INT(helpcontext)); return hwnd; } /* * call-seq: * ole_show_help(obj [,helpcontext]) * * Displays helpfile. The 1st argument specifies WIN32OLE::Type * object or WIN32OLE::Method object or helpfile. * * excel = WIN32OLE.new('Excel.Application') * typeobj = excel.ole_type * WIN32OLE.ole_show_help(typeobj) */ static VALUE fole_s_show_help(int argc, VALUE *argv, VALUE self) { VALUE target; VALUE helpcontext; VALUE helpfile; VALUE name; HWND hwnd; rb_scan_args(argc, argv, "11", &target, &helpcontext); if (rb_obj_is_kind_of(target, cWIN32OLE_TYPE) || rb_obj_is_kind_of(target, cWIN32OLE_METHOD)) { helpfile = rb_funcall(target, rb_intern("helpfile"), 0); if(strlen(StringValuePtr(helpfile)) == 0) { name = rb_ivar_get(target, rb_intern("name")); rb_raise(rb_eRuntimeError, "no helpfile of `%s'", StringValuePtr(name)); } helpcontext = rb_funcall(target, rb_intern("helpcontext"), 0); } else { helpfile = target; } if (!RB_TYPE_P(helpfile, T_STRING)) { rb_raise(rb_eTypeError, "1st parameter must be (String|WIN32OLE::Type|WIN32OLE::Method)"); } hwnd = ole_show_help(helpfile, helpcontext); if(hwnd == 0) { rb_raise(rb_eRuntimeError, "failed to open help file `%s'", StringValuePtr(helpfile)); } return Qnil; } /* * call-seq: * codepage * * Returns current codepage. * WIN32OLE.codepage # => WIN32OLE::CP_ACP */ static VALUE fole_s_get_code_page(VALUE self) { return RB_INT2FIX(cWIN32OLE_cp); } static BOOL CALLBACK installed_code_page_proc(LPTSTR str) { if (strtoul(str, NULL, 10) == g_cp_to_check) { g_cp_installed = TRUE; return FALSE; } return TRUE; } static BOOL code_page_installed(UINT cp) { g_cp_installed = FALSE; g_cp_to_check = cp; EnumSystemCodePages(installed_code_page_proc, CP_INSTALLED); return g_cp_installed; } /* * call-seq: * codepage = CP * * Sets current codepage. * The WIN32OLE.codepage is initialized according to * Encoding.default_internal. * If Encoding.default_internal is nil then WIN32OLE.codepage * is initialized according to Encoding.default_external. * * WIN32OLE.codepage = WIN32OLE::CP_UTF8 * WIN32OLE.codepage = 65001 */ static VALUE fole_s_set_code_page(VALUE self, VALUE vcp) { UINT cp = RB_FIX2INT(vcp); set_ole_codepage(cp); /* * Should this method return old codepage? */ return Qnil; } /* * call-seq: * locale -> locale id. * * Returns current locale id (lcid). The default locale is * WIN32OLE::LOCALE_SYSTEM_DEFAULT. * * lcid = WIN32OLE.locale */ static VALUE fole_s_get_locale(VALUE self) { return RB_INT2FIX(cWIN32OLE_lcid); } static BOOL CALLBACK installed_lcid_proc(LPTSTR str) { if (strcmp(str, g_lcid_to_check) == 0) { g_lcid_installed = TRUE; return FALSE; } return TRUE; } static BOOL lcid_installed(LCID lcid) { g_lcid_installed = FALSE; snprintf(g_lcid_to_check, sizeof(g_lcid_to_check), "%08lx", (unsigned long)lcid); EnumSystemLocales(installed_lcid_proc, LCID_INSTALLED); return g_lcid_installed; } /* * call-seq: * locale = lcid * * Sets current locale id (lcid). * * WIN32OLE.locale = 1033 # set locale English(U.S) * obj = WIN32OLE::Variant.new("$100,000", WIN32OLE::VARIANT::VT_CY) * */ static VALUE fole_s_set_locale(VALUE self, VALUE vlcid) { LCID lcid = RB_FIX2INT(vlcid); if (lcid_installed(lcid)) { cWIN32OLE_lcid = lcid; } else { switch (lcid) { case LOCALE_SYSTEM_DEFAULT: case LOCALE_USER_DEFAULT: cWIN32OLE_lcid = lcid; break; default: rb_raise(eWIN32OLERuntimeError, "not installed locale: %u", (unsigned int)lcid); } } return Qnil; } /* * call-seq: * create_guid * * Creates GUID. * WIN32OLE.create_guid # => {1CB530F1-F6B1-404D-BCE6-1959BF91F4A8} */ static VALUE fole_s_create_guid(VALUE self) { GUID guid; HRESULT hr; OLECHAR bstr[80]; int len = 0; hr = CoCreateGuid(&guid); if (FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "failed to create GUID"); } len = StringFromGUID2(&guid, bstr, sizeof(bstr)/sizeof(OLECHAR)); if (len == 0) { rb_raise(rb_eRuntimeError, "failed to create GUID(buffer over)"); } return ole_wc2vstr(bstr, FALSE); } /* * WIN32OLE.ole_initialize and WIN32OLE.ole_uninitialize * are used in win32ole.rb to fix the issue bug #2618 (ruby-core:27634). * You must not use these method. */ /* :nodoc: */ static VALUE fole_s_ole_initialize(VALUE self) { ole_initialize(); return Qnil; } /* :nodoc: */ static VALUE fole_s_ole_uninitialize(VALUE self) { ole_uninitialize(); return Qnil; } /* * Document-class: WIN32OLE * * +WIN32OLE+ objects represent OLE Automation object in Ruby. * * By using +WIN32OLE+, you can access OLE server like VBScript. * * Here is sample script. * * require 'win32ole' * * excel = WIN32OLE.new('Excel.Application') * excel.visible = true * workbook = excel.Workbooks.Add(); * worksheet = workbook.Worksheets(1); * worksheet.Range("A1:D1").value = ["North","South","East","West"]; * worksheet.Range("A2:B2").value = [5.2, 10]; * worksheet.Range("C2").value = 8; * worksheet.Range("D2").value = 20; * * range = worksheet.Range("A1:D2"); * range.select * chart = workbook.Charts.Add; * * workbook.saved = true; * * excel.ActiveWorkbook.Close(0); * excel.Quit(); * * Unfortunately, +WIN32OLE+ doesn't support the argument passed by * reference directly. * Instead, +WIN32OLE+ provides WIN32OLE::ARGV or WIN32OLE::Variant object. * If you want to get the result value of argument passed by reference, * you can use WIN32OLE::ARGV or WIN32OLE::Variant. * * oleobj.method(arg1, arg2, refargv3) * puts WIN32OLE::ARGV[2] # the value of refargv3 after called oleobj.method * * or * * refargv3 = WIN32OLE::Variant.new(XXX, * WIN32OLE::VARIANT::VT_BYREF|WIN32OLE::VARIANT::VT_XXX) * oleobj.method(arg1, arg2, refargv3) * p refargv3.value # the value of refargv3 after called oleobj.method. * */ /* * call-seq: * new(server, [host]) -> WIN32OLE object * WIN32OLE.new(server, license: 'key') -> WIN32OLE object * * Returns a new WIN32OLE object(OLE Automation object). * The first argument server specifies OLE Automation server. * The first argument should be CLSID or PROGID. * If second argument host specified, then returns OLE Automation * object on host. * If :license keyword argument is provided, * IClassFactory2::CreateInstanceLic is used to create instance of * licensed server. * * WIN32OLE.new('Excel.Application') # => Excel OLE Automation WIN32OLE object. * WIN32OLE.new('{00024500-0000-0000-C000-000000000046}') # => Excel OLE Automation WIN32OLE object. */ static VALUE fole_initialize(int argc, VALUE *argv, VALUE self) { VALUE svr_name; VALUE host; VALUE others; VALUE opts; HRESULT hr; CLSID clsid; OLECHAR *pBuf; OLECHAR *key_buf; IDispatch *pDispatch; IClassFactory2 * pIClassFactory2; void *p; static ID keyword_ids[1]; VALUE kwargs[1]; rb_call_super(0, 0); rb_scan_args(argc, argv, "11*:", &svr_name, &host, &others, &opts); StringValue(svr_name); if (!NIL_P(host)) { StringValue(host); return ole_create_dcom(self, svr_name, host, others); } /* get CLSID from OLE server name */ pBuf = ole_vstr2wc(svr_name); hr = CLSIDFromProgID(pBuf, &clsid); if(FAILED(hr)) { hr = CLSIDFromString(pBuf, &clsid); } SysFreeString(pBuf); if(FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "unknown OLE server: `%s'", StringValuePtr(svr_name)); } if (!keyword_ids[0]) { keyword_ids[0] = rb_intern_const("license"); } rb_get_kwargs(opts, keyword_ids, 0, 1, kwargs); if (kwargs[0] == Qundef) { /* get IDispatch interface */ hr = CoCreateInstance( &clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, &IID_IDispatch, &p ); } else { hr = CoGetClassObject( &clsid, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, NULL, &IID_IClassFactory2, (LPVOID)&pIClassFactory2 ); if (hr == S_OK) { key_buf = ole_vstr2wc(kwargs[0]); hr = pIClassFactory2->lpVtbl->CreateInstanceLic(pIClassFactory2, NULL, NULL, &IID_IDispatch, key_buf, &p); SysFreeString(key_buf); OLE_RELEASE(pIClassFactory2); } } if(FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "failed to create WIN32OLE object from `%s'", StringValuePtr(svr_name)); } pDispatch = p; ole_set_member(self, pDispatch); return self; } static int hash2named_arg(VALUE key, VALUE val, VALUE pop) { struct oleparam* pOp = (struct oleparam *)pop; unsigned int index, i; index = pOp->dp.cNamedArgs; /*--------------------------------------------- the data-type of key must be String or Symbol -----------------------------------------------*/ if(!RB_TYPE_P(key, T_STRING) && !RB_TYPE_P(key, T_SYMBOL)) { /* clear name of dispatch parameters */ for(i = 1; i < index + 1; i++) { SysFreeString(pOp->pNamedArgs[i]); } /* clear dispatch parameters */ for(i = 0; i < index; i++ ) { VariantClear(&(pOp->dp.rgvarg[i])); } /* raise an exception */ rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); } if (RB_TYPE_P(key, T_SYMBOL)) { key = rb_sym2str(key); } /* pNamedArgs[0] is , so "index + 1" */ pOp->pNamedArgs[index + 1] = ole_vstr2wc(key); VariantInit(&(pOp->dp.rgvarg[index])); ole_val2variant(val, &(pOp->dp.rgvarg[index])); pOp->dp.cNamedArgs += 1; return ST_CONTINUE; } static VALUE set_argv(VARIANTARG* realargs, unsigned int beg, unsigned int end) { VALUE argv = rb_const_get(cWIN32OLE, rb_intern("ARGV")); Check_Type(argv, T_ARRAY); rb_ary_clear(argv); while (end-- > beg) { rb_ary_push(argv, ole_variant2val(&realargs[end])); if (V_VT(&realargs[end]) != VT_RECORD) { VariantClear(&realargs[end]); } } return argv; } static VALUE ole_invoke(int argc, VALUE *argv, VALUE self, USHORT wFlags, BOOL is_bracket) { LCID lcid = cWIN32OLE_lcid; struct oledata *pole = NULL; HRESULT hr; VALUE cmd; VALUE paramS; VALUE param; VALUE obj; VALUE v; BSTR wcmdname; DISPID DispID; DISPID* pDispID; EXCEPINFO excepinfo; VARIANT result; VARIANTARG* realargs = NULL; unsigned int argErr = 0; unsigned int i; unsigned int cNamedArgs; int n; struct oleparam op; memset(&excepinfo, 0, sizeof(EXCEPINFO)); VariantInit(&result); op.dp.rgvarg = NULL; op.dp.rgdispidNamedArgs = NULL; op.dp.cNamedArgs = 0; op.dp.cArgs = 0; rb_scan_args(argc, argv, "1*", &cmd, ¶mS); if(!RB_TYPE_P(cmd, T_STRING) && !RB_TYPE_P(cmd, T_SYMBOL) && !is_bracket) { rb_raise(rb_eTypeError, "method is wrong type (expected String or Symbol)"); } if (RB_TYPE_P(cmd, T_SYMBOL)) { cmd = rb_sym2str(cmd); } pole = oledata_get_struct(self); if(!pole->pDispatch) { rb_raise(rb_eRuntimeError, "failed to get dispatch interface"); } if (is_bracket) { DispID = DISPID_VALUE; argc += 1; rb_ary_unshift(paramS, cmd); } else { wcmdname = ole_vstr2wc(cmd); hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL, &wcmdname, 1, lcid, &DispID); SysFreeString(wcmdname); if(FAILED(hr)) { return rb_eNoMethodError; } } /* pick up last argument of method */ param = rb_ary_entry(paramS, argc-2); op.dp.cNamedArgs = 0; /* if last arg is hash object */ if(RB_TYPE_P(param, T_HASH)) { /*------------------------------------------ hash object ==> named dispatch parameters --------------------------------------------*/ cNamedArgs = rb_long2int((long)RHASH_SIZE(param)); op.dp.cArgs = cNamedArgs + argc - 2; op.pNamedArgs = ALLOCA_N(OLECHAR*, cNamedArgs + 1); op.dp.rgvarg = ALLOCA_N(VARIANTARG, op.dp.cArgs); rb_hash_foreach(param, hash2named_arg, (VALUE)&op); pDispID = ALLOCA_N(DISPID, cNamedArgs + 1); op.pNamedArgs[0] = ole_vstr2wc(cmd); hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch, &IID_NULL, op.pNamedArgs, op.dp.cNamedArgs + 1, lcid, pDispID); for(i = 0; i < op.dp.cNamedArgs + 1; i++) { SysFreeString(op.pNamedArgs[i]); op.pNamedArgs[i] = NULL; } if(FAILED(hr)) { /* clear dispatch parameters */ for(i = 0; i < op.dp.cArgs; i++ ) { VariantClear(&op.dp.rgvarg[i]); } ole_raise(hr, eWIN32OLERuntimeError, "failed to get named argument info: `%s'", StringValuePtr(cmd)); } op.dp.rgdispidNamedArgs = &(pDispID[1]); } else { 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); } } /*-------------------------------------- non hash args ==> dispatch parameters ----------------------------------------*/ if(op.dp.cArgs > cNamedArgs) { realargs = ALLOCA_N(VARIANTARG, op.dp.cArgs-cNamedArgs+1); for(i = cNamedArgs; i < op.dp.cArgs; i++) { n = op.dp.cArgs - i + cNamedArgs - 1; VariantInit(&realargs[n]); VariantInit(&op.dp.rgvarg[n]); param = rb_ary_entry(paramS, i-cNamedArgs); if (rb_obj_is_kind_of(param, cWIN32OLE_VARIANT)) { ole_variant2variant(param, &op.dp.rgvarg[n]); } else if (rb_obj_is_kind_of(param, cWIN32OLE_RECORD)) { ole_val2variant(param, &realargs[n]); op.dp.rgvarg[n] = realargs[n]; V_VT(&op.dp.rgvarg[n]) = VT_RECORD | VT_BYREF; } else { ole_val2variant(param, &realargs[n]); V_VT(&op.dp.rgvarg[n]) = VT_VARIANT | VT_BYREF; V_VARIANTREF(&op.dp.rgvarg[n]) = &realargs[n]; } } } /* apparent you need to call propput, you need this */ if (wFlags & DISPATCH_PROPERTYPUT) { if (op.dp.cArgs == 0) ole_raise(ResultFromScode(E_INVALIDARG), eWIN32OLERuntimeError, "argument error"); op.dp.cNamedArgs = 1; op.dp.rgdispidNamedArgs = ALLOCA_N( DISPID, 1 ); op.dp.rgdispidNamedArgs[0] = DISPID_PROPERTYPUT; } hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, &IID_NULL, lcid, wFlags, &op.dp, &result, &excepinfo, &argErr); if (FAILED(hr)) { /* retry to call args by value */ if(op.dp.cArgs >= cNamedArgs) { for(i = cNamedArgs; i < op.dp.cArgs; i++) { n = op.dp.cArgs - i + cNamedArgs - 1; param = rb_ary_entry(paramS, i-cNamedArgs); ole_val2variant(param, &op.dp.rgvarg[n]); } if (hr == DISP_E_EXCEPTION) { ole_freeexceptinfo(&excepinfo); } memset(&excepinfo, 0, sizeof(EXCEPINFO)); VariantInit(&result); hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, &IID_NULL, lcid, wFlags, &op.dp, &result, &excepinfo, &argErr); /* mega kludge. if a method in WORD is called and we ask * for a result when one is not returned then * hResult == DISP_E_EXCEPTION. this only happens on * functions whose DISPID > 0x8000 */ if ((hr == DISP_E_EXCEPTION || hr == DISP_E_MEMBERNOTFOUND) && DispID > 0x8000) { if (hr == DISP_E_EXCEPTION) { ole_freeexceptinfo(&excepinfo); } memset(&excepinfo, 0, sizeof(EXCEPINFO)); hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, &IID_NULL, lcid, wFlags, &op.dp, NULL, &excepinfo, &argErr); } for(i = cNamedArgs; i < op.dp.cArgs; i++) { n = op.dp.cArgs - i + cNamedArgs - 1; if (V_VT(&op.dp.rgvarg[n]) != VT_RECORD) { VariantClear(&op.dp.rgvarg[n]); } } } if (FAILED(hr)) { /* retry after converting nil to VT_EMPTY */ if (op.dp.cArgs > cNamedArgs) { for(i = cNamedArgs; i < op.dp.cArgs; i++) { n = op.dp.cArgs - i + cNamedArgs - 1; param = rb_ary_entry(paramS, i-cNamedArgs); ole_val2variant2(param, &op.dp.rgvarg[n]); } if (hr == DISP_E_EXCEPTION) { ole_freeexceptinfo(&excepinfo); } memset(&excepinfo, 0, sizeof(EXCEPINFO)); VariantInit(&result); hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DispID, &IID_NULL, lcid, wFlags, &op.dp, &result, &excepinfo, &argErr); for(i = cNamedArgs; i < op.dp.cArgs; i++) { n = op.dp.cArgs - i + cNamedArgs - 1; if (V_VT(&op.dp.rgvarg[n]) != VT_RECORD) { VariantClear(&op.dp.rgvarg[n]); } } } } } /* clear dispatch parameter */ if(op.dp.cArgs > cNamedArgs) { for(i = cNamedArgs; i < op.dp.cArgs; i++) { n = op.dp.cArgs - i + cNamedArgs - 1; param = rb_ary_entry(paramS, i-cNamedArgs); if (rb_obj_is_kind_of(param, cWIN32OLE_VARIANT)) { ole_val2variant(param, &realargs[n]); } else if ( rb_obj_is_kind_of(param, cWIN32OLE_RECORD) && V_VT(&realargs[n]) == VT_RECORD ) { olerecord_set_ivar(param, V_RECORDINFO(&realargs[n]), V_RECORD(&realargs[n])); } } set_argv(realargs, cNamedArgs, op.dp.cArgs); } else { for(i = 0; i < op.dp.cArgs; i++) { VariantClear(&op.dp.rgvarg[i]); } } if (FAILED(hr)) { v = ole_excepinfo2msg(&excepinfo); ole_raise(hr, eWIN32OLERuntimeError, "(in OLE method `%s': )%s", StringValuePtr(cmd), StringValuePtr(v)); } obj = ole_variant2val(&result); VariantClear(&result); return obj; } /* * call-seq: * invoke(method, [arg1,...]) => return value of method. * * Runs OLE method. * The first argument specifies the method name of OLE Automation object. * The others specify argument of the method. * If you can not execute method directly, then use this method instead. * * excel = WIN32OLE.new('Excel.Application') * excel.invoke('Quit') # => same as excel.Quit * */ static VALUE fole_invoke(int argc, VALUE *argv, VALUE self) { VALUE v = ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE); if (v == rb_eNoMethodError) { return rb_call_super(argc, argv); } return v; } static VALUE ole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types, USHORT dispkind) { HRESULT hr; struct oledata *pole = NULL; unsigned int argErr = 0; EXCEPINFO excepinfo; VARIANT result; DISPPARAMS dispParams; VARIANTARG* realargs = NULL; int i, j; VALUE obj = Qnil; VALUE tp, param; VALUE v; VARTYPE vt; Check_Type(args, T_ARRAY); Check_Type(types, T_ARRAY); memset(&excepinfo, 0, sizeof(EXCEPINFO)); memset(&dispParams, 0, sizeof(DISPPARAMS)); VariantInit(&result); pole = oledata_get_struct(self); dispParams.cArgs = RARRAY_LEN(args); dispParams.rgvarg = ALLOCA_N(VARIANTARG, dispParams.cArgs); realargs = ALLOCA_N(VARIANTARG, dispParams.cArgs); for (i = 0, j = dispParams.cArgs - 1; i < (int)dispParams.cArgs; i++, j--) { VariantInit(&realargs[i]); VariantInit(&dispParams.rgvarg[i]); tp = rb_ary_entry(types, j); vt = (VARTYPE)RB_FIX2INT(tp); V_VT(&dispParams.rgvarg[i]) = vt; param = rb_ary_entry(args, j); if (param == Qnil) { V_VT(&dispParams.rgvarg[i]) = V_VT(&realargs[i]) = VT_ERROR; V_ERROR(&dispParams.rgvarg[i]) = V_ERROR(&realargs[i]) = DISP_E_PARAMNOTFOUND; } else { if (vt & VT_ARRAY) { int ent; LPBYTE pb; short* ps; LPLONG pl; VARIANT* pv; CY *py; VARTYPE v; SAFEARRAYBOUND rgsabound[1]; Check_Type(param, T_ARRAY); rgsabound[0].lLbound = 0; rgsabound[0].cElements = RARRAY_LEN(param); v = vt & ~(VT_ARRAY | VT_BYREF); V_ARRAY(&realargs[i]) = SafeArrayCreate(v, 1, rgsabound); V_VT(&realargs[i]) = VT_ARRAY | v; SafeArrayLock(V_ARRAY(&realargs[i])); pb = V_ARRAY(&realargs[i])->pvData; ps = V_ARRAY(&realargs[i])->pvData; pl = V_ARRAY(&realargs[i])->pvData; py = V_ARRAY(&realargs[i])->pvData; pv = V_ARRAY(&realargs[i])->pvData; for (ent = 0; ent < (int)rgsabound[0].cElements; ent++) { VARIANT velem; VALUE elem = rb_ary_entry(param, ent); ole_val2variant(elem, &velem); if (v != VT_VARIANT) { VariantChangeTypeEx(&velem, &velem, cWIN32OLE_lcid, 0, v); } switch (v) { /* 128 bits */ case VT_VARIANT: *pv++ = velem; break; /* 64 bits */ case VT_R8: case VT_CY: case VT_DATE: *py++ = V_CY(&velem); break; /* 16 bits */ case VT_BOOL: case VT_I2: case VT_UI2: *ps++ = V_I2(&velem); break; /* 8 bites */ case VT_UI1: case VT_I1: *pb++ = V_UI1(&velem); break; /* 32 bits */ default: *pl++ = V_I4(&velem); break; } } SafeArrayUnlock(V_ARRAY(&realargs[i])); } else { ole_val2variant(param, &realargs[i]); if ((vt & (~VT_BYREF)) != VT_VARIANT) { hr = VariantChangeTypeEx(&realargs[i], &realargs[i], cWIN32OLE_lcid, 0, (VARTYPE)(vt & (~VT_BYREF))); if (hr != S_OK) { rb_raise(rb_eTypeError, "not valid value"); } } } if ((vt & VT_BYREF) || vt == VT_VARIANT) { if (vt == VT_VARIANT) V_VT(&dispParams.rgvarg[i]) = VT_VARIANT | VT_BYREF; switch (vt & (~VT_BYREF)) { /* 128 bits */ case VT_VARIANT: V_VARIANTREF(&dispParams.rgvarg[i]) = &realargs[i]; break; /* 64 bits */ case VT_R8: case VT_CY: case VT_DATE: V_CYREF(&dispParams.rgvarg[i]) = &V_CY(&realargs[i]); break; /* 16 bits */ case VT_BOOL: case VT_I2: case VT_UI2: V_I2REF(&dispParams.rgvarg[i]) = &V_I2(&realargs[i]); break; /* 8 bites */ case VT_UI1: case VT_I1: V_UI1REF(&dispParams.rgvarg[i]) = &V_UI1(&realargs[i]); break; /* 32 bits */ default: V_I4REF(&dispParams.rgvarg[i]) = &V_I4(&realargs[i]); break; } } else { /* copy 64 bits of data */ V_CY(&dispParams.rgvarg[i]) = V_CY(&realargs[i]); } } } if (dispkind & DISPATCH_PROPERTYPUT) { dispParams.cNamedArgs = 1; dispParams.rgdispidNamedArgs = ALLOCA_N( DISPID, 1 ); dispParams.rgdispidNamedArgs[0] = DISPID_PROPERTYPUT; } hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, RB_NUM2INT(dispid), &IID_NULL, cWIN32OLE_lcid, dispkind, &dispParams, &result, &excepinfo, &argErr); if (FAILED(hr)) { v = ole_excepinfo2msg(&excepinfo); ole_raise(hr, eWIN32OLERuntimeError, "(in OLE method `': )%s", RB_NUM2INT(dispid), StringValuePtr(v)); } /* clear dispatch parameter */ if(dispParams.cArgs > 0) { set_argv(realargs, 0, dispParams.cArgs); } obj = ole_variant2val(&result); VariantClear(&result); return obj; } /* * call-seq: * _invoke(dispid, args, types) * * Runs the early binding method. * The 1st argument specifies dispatch ID, * the 2nd argument specifies the array of arguments, * the 3rd argument specifies the array of the type of arguments. * * excel = WIN32OLE.new('Excel.Application') * excel._invoke(302, [], []) # same effect as excel.Quit */ static VALUE fole_invoke2(VALUE self, VALUE dispid, VALUE args, VALUE types) { return ole_invoke2(self, dispid, args, types, DISPATCH_METHOD); } /* * call-seq: * _getproperty(dispid, args, types) * * Runs the early binding method to get property. * The 1st argument specifies dispatch ID, * the 2nd argument specifies the array of arguments, * the 3rd argument specifies the array of the type of arguments. * * excel = WIN32OLE.new('Excel.Application') * puts excel._getproperty(558, [], []) # same effect as puts excel.visible */ static VALUE fole_getproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types) { return ole_invoke2(self, dispid, args, types, DISPATCH_PROPERTYGET); } /* * call-seq: * _setproperty(dispid, args, types) * * Runs the early binding method to set property. * The 1st argument specifies dispatch ID, * the 2nd argument specifies the array of arguments, * the 3rd argument specifies the array of the type of arguments. * * excel = WIN32OLE.new('Excel.Application') * excel._setproperty(558, [true], [WIN32OLE::VARIANT::VT_BOOL]) # same effect as excel.visible = true */ static VALUE fole_setproperty2(VALUE self, VALUE dispid, VALUE args, VALUE types) { return ole_invoke2(self, dispid, args, types, DISPATCH_PROPERTYPUT); } /* * call-seq: * WIN32OLE[a1, a2, ...]=val * * Sets the value to WIN32OLE object specified by a1, a2, ... * * dict = WIN32OLE.new('Scripting.Dictionary') * dict.add('ruby', 'RUBY') * dict['ruby'] = 'Ruby' * puts dict['ruby'] # => 'Ruby' * * Remark: You can not use this method to set the property value. * * excel = WIN32OLE.new('Excel.Application') * # excel['Visible'] = true # This is error !!! * excel.Visible = true # You should to use this style to set the property. * */ static VALUE fole_setproperty_with_bracket(int argc, VALUE *argv, VALUE self) { VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, TRUE); if (v == rb_eNoMethodError) { return rb_call_super(argc, argv); } return v; } /* * call-seq: * setproperty('property', [arg1, arg2,...] val) * * Sets property of OLE object. * When you want to set property with argument, you can use this method. * * excel = WIN32OLE.new('Excel.Application') * excel.Visible = true * book = excel.workbooks.add * sheet = book.worksheets(1) * sheet.setproperty('Cells', 1, 2, 10) # => The B1 cell value is 10. */ static VALUE fole_setproperty(int argc, VALUE *argv, VALUE self) { VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYPUT, FALSE); if (v == rb_eNoMethodError) { return rb_call_super(argc, argv); } return v; } /* * call-seq: * WIN32OLE[a1,a2,...] * * Returns the value of Collection specified by a1, a2,.... * * dict = WIN32OLE.new('Scripting.Dictionary') * dict.add('ruby', 'Ruby') * puts dict['ruby'] # => 'Ruby' (same as `puts dict.item('ruby')') * * Remark: You can not use this method to get the property. * excel = WIN32OLE.new('Excel.Application') * # puts excel['Visible'] This is error !!! * puts excel.Visible # You should to use this style to get the property. * */ static VALUE fole_getproperty_with_bracket(int argc, VALUE *argv, VALUE self) { VALUE v = ole_invoke(argc, argv, self, DISPATCH_PROPERTYGET, TRUE); if (v == rb_eNoMethodError) { return rb_call_super(argc, argv); } return v; } static VALUE ole_propertyput(VALUE self, VALUE property, VALUE value) { struct oledata *pole = NULL; unsigned argErr; unsigned int index; HRESULT hr; EXCEPINFO excepinfo; DISPID dispID = DISPID_VALUE; DISPID dispIDParam = DISPID_PROPERTYPUT; USHORT wFlags = DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF; DISPPARAMS dispParams; VARIANTARG propertyValue[2]; OLECHAR* pBuf[1]; VALUE v; LCID lcid = cWIN32OLE_lcid; dispParams.rgdispidNamedArgs = &dispIDParam; dispParams.rgvarg = propertyValue; dispParams.cNamedArgs = 1; dispParams.cArgs = 1; VariantInit(&propertyValue[0]); VariantInit(&propertyValue[1]); memset(&excepinfo, 0, sizeof(excepinfo)); pole = oledata_get_struct(self); /* get ID from property name */ pBuf[0] = ole_vstr2wc(property); hr = pole->pDispatch->lpVtbl->GetIDsOfNames(pole->pDispatch, &IID_NULL, pBuf, 1, lcid, &dispID); SysFreeString(pBuf[0]); pBuf[0] = NULL; if(FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "unknown property or method: `%s'", StringValuePtr(property)); } /* set property value */ ole_val2variant(value, &propertyValue[0]); hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, dispID, &IID_NULL, lcid, wFlags, &dispParams, NULL, &excepinfo, &argErr); for(index = 0; index < dispParams.cArgs; ++index) { VariantClear(&propertyValue[index]); } if (FAILED(hr)) { v = ole_excepinfo2msg(&excepinfo); ole_raise(hr, eWIN32OLERuntimeError, "(in setting property `%s': )%s", StringValuePtr(property), StringValuePtr(v)); } return Qnil; } /* * call-seq: * ole_free * * invokes Release method of Dispatch interface of WIN32OLE object. * Usually, you do not need to call this method because Release method * called automatically when WIN32OLE object garbaged. * */ static VALUE fole_free(VALUE self) { struct oledata *pole = NULL; pole = oledata_get_struct(self); OLE_FREE(pole->pDispatch); pole->pDispatch = NULL; return Qnil; } static VALUE ole_each_sub(VALUE pEnumV) { VARIANT variant; VALUE obj = Qnil; IEnumVARIANT *pEnum = (IEnumVARIANT *)pEnumV; VariantInit(&variant); while(pEnum->lpVtbl->Next(pEnum, 1, &variant, NULL) == S_OK) { obj = ole_variant2val(&variant); VariantClear(&variant); VariantInit(&variant); rb_yield(obj); } return Qnil; } static VALUE ole_ienum_free(VALUE pEnumV) { IEnumVARIANT *pEnum = (IEnumVARIANT *)pEnumV; OLE_RELEASE(pEnum); return Qnil; } /* * call-seq: * each {|i|...} * * Iterates over each item of OLE collection which has IEnumVARIANT interface. * * excel = WIN32OLE.new('Excel.Application') * book = excel.workbooks.add * sheets = book.worksheets(1) * cells = sheets.cells("A1:A5") * cells.each do |cell| * cell.value = 10 * end */ static VALUE fole_each(VALUE self) { LCID lcid = cWIN32OLE_lcid; struct oledata *pole = NULL; unsigned int argErr; EXCEPINFO excepinfo; DISPPARAMS dispParams; VARIANT result; HRESULT hr; IEnumVARIANT *pEnum = NULL; void *p; RETURN_ENUMERATOR(self, 0, 0); VariantInit(&result); dispParams.rgvarg = NULL; dispParams.rgdispidNamedArgs = NULL; dispParams.cNamedArgs = 0; dispParams.cArgs = 0; memset(&excepinfo, 0, sizeof(excepinfo)); pole = oledata_get_struct(self); hr = pole->pDispatch->lpVtbl->Invoke(pole->pDispatch, DISPID_NEWENUM, &IID_NULL, lcid, DISPATCH_METHOD | DISPATCH_PROPERTYGET, &dispParams, &result, &excepinfo, &argErr); if (FAILED(hr)) { VariantClear(&result); ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get IEnum Interface"); } if (V_VT(&result) == VT_UNKNOWN) { hr = V_UNKNOWN(&result)->lpVtbl->QueryInterface(V_UNKNOWN(&result), &IID_IEnumVARIANT, &p); pEnum = p; } else if (V_VT(&result) == VT_DISPATCH) { hr = V_DISPATCH(&result)->lpVtbl->QueryInterface(V_DISPATCH(&result), &IID_IEnumVARIANT, &p); pEnum = p; } if (FAILED(hr) || !pEnum) { VariantClear(&result); ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get IEnum Interface"); } VariantClear(&result); rb_ensure(ole_each_sub, (VALUE)pEnum, ole_ienum_free, (VALUE)pEnum); return Qnil; } /* * call-seq: * method_missing(id [,arg1, arg2, ...]) * * Calls WIN32OLE#invoke method. */ static VALUE fole_missing(int argc, VALUE *argv, VALUE self) { VALUE mid, org_mid, sym, v; const char* mname; long n; rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS); mid = org_mid = argv[0]; sym = rb_check_symbol(&mid); if (!NIL_P(sym)) mid = rb_sym2str(sym); mname = StringValueCStr(mid); if(!mname) { rb_raise(rb_eRuntimeError, "fail: unknown method or property"); } n = RSTRING_LEN(mid); if(mname[n-1] == '=') { rb_check_arity(argc, 2, 2); argv[0] = rb_enc_associate(rb_str_subseq(mid, 0, n-1), cWIN32OLE_enc); return ole_propertyput(self, argv[0], argv[1]); } else { argv[0] = rb_enc_associate(rb_str_dup(mid), cWIN32OLE_enc); v = ole_invoke(argc, argv, self, DISPATCH_METHOD|DISPATCH_PROPERTYGET, FALSE); if (v == rb_eNoMethodError) { argv[0] = org_mid; return rb_call_super(argc, argv); } return v; } } static HRESULT typeinfo_from_ole(struct oledata *pole, ITypeInfo **ppti) { ITypeInfo *pTypeInfo; ITypeLib *pTypeLib; BSTR bstr; VALUE type; UINT i; UINT count; LCID lcid = cWIN32OLE_lcid; HRESULT hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, 0, lcid, &pTypeInfo); if(FAILED(hr)) { ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); } hr = pTypeInfo->lpVtbl->GetDocumentation(pTypeInfo, -1, &bstr, NULL, NULL, NULL); type = WC2VSTR(bstr); hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &i); OLE_RELEASE(pTypeInfo); if (FAILED(hr)) { ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetContainingTypeLib"); } count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib); for (i = 0; i < count; i++) { hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i, &bstr, NULL, NULL, NULL); if (SUCCEEDED(hr) && rb_str_cmp(WC2VSTR(bstr), type) == 0) { hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo); if (SUCCEEDED(hr)) { *ppti = pTypeInfo; break; } } } OLE_RELEASE(pTypeLib); return hr; } static VALUE ole_methods(VALUE self, int mask) { ITypeInfo *pTypeInfo; HRESULT hr; VALUE methods; struct oledata *pole = NULL; pole = oledata_get_struct(self); methods = rb_ary_new(); hr = typeinfo_from_ole(pole, &pTypeInfo); if(FAILED(hr)) return methods; rb_ary_concat(methods, ole_methods_from_typeinfo(pTypeInfo, mask)); OLE_RELEASE(pTypeInfo); return methods; } /* * call-seq: * ole_methods * * Returns the array of WIN32OLE::Method object. * The element is OLE method of WIN32OLE object. * * excel = WIN32OLE.new('Excel.Application') * methods = excel.ole_methods * */ static VALUE fole_methods(VALUE self) { return ole_methods( self, INVOKE_FUNC | INVOKE_PROPERTYGET | INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF); } /* * call-seq: * ole_get_methods * * Returns the array of WIN32OLE::Method object . * The element of the array is property (gettable) of WIN32OLE object. * * excel = WIN32OLE.new('Excel.Application') * properties = excel.ole_get_methods */ static VALUE fole_get_methods(VALUE self) { return ole_methods( self, INVOKE_PROPERTYGET); } /* * call-seq: * ole_put_methods * * Returns the array of WIN32OLE::Method object . * The element of the array is property (settable) of WIN32OLE object. * * excel = WIN32OLE.new('Excel.Application') * properties = excel.ole_put_methods */ static VALUE fole_put_methods(VALUE self) { return ole_methods( self, INVOKE_PROPERTYPUT|INVOKE_PROPERTYPUTREF); } /* * call-seq: * ole_func_methods * * Returns the array of WIN32OLE::Method object . * The element of the array is property (settable) of WIN32OLE object. * * excel = WIN32OLE.new('Excel.Application') * properties = excel.ole_func_methods * */ static VALUE fole_func_methods(VALUE self) { return ole_methods( self, INVOKE_FUNC); } /* * call-seq: * ole_type * * Returns WIN32OLE::Type object. * * excel = WIN32OLE.new('Excel.Application') * tobj = excel.ole_type */ static VALUE fole_type(VALUE self) { ITypeInfo *pTypeInfo; HRESULT hr; struct oledata *pole = NULL; LCID lcid = cWIN32OLE_lcid; VALUE type = Qnil; pole = oledata_get_struct(self); hr = pole->pDispatch->lpVtbl->GetTypeInfo( pole->pDispatch, 0, lcid, &pTypeInfo ); if(FAILED(hr)) { ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); } type = ole_type_from_itypeinfo(pTypeInfo); OLE_RELEASE(pTypeInfo); if (type == Qnil) { rb_raise(rb_eRuntimeError, "failed to create WIN32OLE::Type obj from ITypeInfo"); } return type; } /* * call-seq: * ole_typelib -> The WIN32OLE_TYPELIB object * * Returns the WIN32OLE::TypeLib object. The object represents the * type library which contains the WIN32OLE object. * * excel = WIN32OLE.new('Excel.Application') * tlib = excel.ole_typelib * puts tlib.name # -> 'Microsoft Excel 9.0 Object Library' */ static VALUE fole_typelib(VALUE self) { struct oledata *pole = NULL; HRESULT hr; ITypeInfo *pTypeInfo; LCID lcid = cWIN32OLE_lcid; VALUE vtlib = Qnil; pole = oledata_get_struct(self); hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, 0, lcid, &pTypeInfo); if(FAILED(hr)) { ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to GetTypeInfo"); } vtlib = ole_typelib_from_itypeinfo(pTypeInfo); OLE_RELEASE(pTypeInfo); if (vtlib == Qnil) { rb_raise(rb_eRuntimeError, "failed to get type library info."); } return vtlib; } /* * call-seq: * ole_query_interface(iid) -> WIN32OLE object * * Returns WIN32OLE object for a specific dispatch or dual * interface specified by iid. * * ie = WIN32OLE.new('InternetExplorer.Application') * ie_web_app = ie.ole_query_interface('{0002DF05-0000-0000-C000-000000000046}') # => WIN32OLE object for dispinterface IWebBrowserApp */ static VALUE fole_query_interface(VALUE self, VALUE str_iid) { HRESULT hr; OLECHAR *pBuf; IID iid; struct oledata *pole = NULL; IDispatch *pDispatch; void *p; pBuf = ole_vstr2wc(str_iid); hr = CLSIDFromString(pBuf, &iid); SysFreeString(pBuf); if(FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "invalid iid: `%s'", StringValuePtr(str_iid)); } pole = oledata_get_struct(self); if(!pole->pDispatch) { rb_raise(rb_eRuntimeError, "failed to get dispatch interface"); } hr = pole->pDispatch->lpVtbl->QueryInterface(pole->pDispatch, &iid, &p); if(FAILED(hr)) { ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get interface `%s'", StringValuePtr(str_iid)); } pDispatch = p; return create_win32ole_object(cWIN32OLE, pDispatch, 0, 0); } /* * call-seq: * ole_respond_to?(method) -> true or false * * Returns true when OLE object has OLE method, otherwise returns false. * * ie = WIN32OLE.new('InternetExplorer.Application') * ie.ole_respond_to?("gohome") => true */ static VALUE fole_respond_to(VALUE self, VALUE method) { struct oledata *pole = NULL; BSTR wcmdname; DISPID DispID; HRESULT hr; if(!RB_TYPE_P(method, T_STRING) && !RB_TYPE_P(method, T_SYMBOL)) { rb_raise(rb_eTypeError, "wrong argument type (expected String or Symbol)"); } if (RB_TYPE_P(method, T_SYMBOL)) { method = rb_sym2str(method); } pole = oledata_get_struct(self); wcmdname = ole_vstr2wc(method); hr = pole->pDispatch->lpVtbl->GetIDsOfNames( pole->pDispatch, &IID_NULL, &wcmdname, 1, cWIN32OLE_lcid, &DispID); SysFreeString(wcmdname); return SUCCEEDED(hr) ? Qtrue : Qfalse; } HRESULT ole_docinfo_from_type(ITypeInfo *pTypeInfo, BSTR *name, BSTR *helpstr, DWORD *helpcontext, BSTR *helpfile) { HRESULT hr; ITypeLib *pTypeLib; UINT i; hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &i); if (FAILED(hr)) { return hr; } hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i, name, helpstr, helpcontext, helpfile); if (FAILED(hr)) { OLE_RELEASE(pTypeLib); return hr; } OLE_RELEASE(pTypeLib); return hr; } static VALUE ole_usertype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails) { HRESULT hr; BSTR bstr; ITypeInfo *pRefTypeInfo; VALUE type = Qnil; hr = pTypeInfo->lpVtbl->GetRefTypeInfo(pTypeInfo, V_UNION1(pTypeDesc, hreftype), &pRefTypeInfo); if(FAILED(hr)) return Qnil; hr = ole_docinfo_from_type(pRefTypeInfo, &bstr, NULL, NULL, NULL); if(FAILED(hr)) { OLE_RELEASE(pRefTypeInfo); return Qnil; } OLE_RELEASE(pRefTypeInfo); type = WC2VSTR(bstr); if(typedetails != Qnil) rb_ary_push(typedetails, type); return type; } static VALUE ole_ptrtype2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails) { TYPEDESC *p = pTypeDesc; VALUE type = rb_str_new2(""); if (p->vt == VT_PTR || p->vt == VT_SAFEARRAY) { p = V_UNION1(p, lptdesc); type = ole_typedesc2val(pTypeInfo, p, typedetails); } return type; } VALUE ole_typedesc2val(ITypeInfo *pTypeInfo, TYPEDESC *pTypeDesc, VALUE typedetails) { VALUE str; VALUE typestr = Qnil; switch(pTypeDesc->vt) { case VT_I2: typestr = rb_str_new2("I2"); break; case VT_I4: typestr = rb_str_new2("I4"); break; case VT_R4: typestr = rb_str_new2("R4"); break; case VT_R8: typestr = rb_str_new2("R8"); break; case VT_CY: typestr = rb_str_new2("CY"); break; case VT_DATE: typestr = rb_str_new2("DATE"); break; case VT_BSTR: typestr = rb_str_new2("BSTR"); break; case VT_BOOL: typestr = rb_str_new2("BOOL"); break; case VT_VARIANT: typestr = rb_str_new2("VARIANT"); break; case VT_DECIMAL: typestr = rb_str_new2("DECIMAL"); break; case VT_I1: typestr = rb_str_new2("I1"); break; case VT_UI1: typestr = rb_str_new2("UI1"); break; case VT_UI2: typestr = rb_str_new2("UI2"); break; case VT_UI4: typestr = rb_str_new2("UI4"); break; #if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__CYGWIN__) || defined(__MINGW32__) case VT_I8: typestr = rb_str_new2("I8"); break; case VT_UI8: typestr = rb_str_new2("UI8"); break; #endif case VT_INT: typestr = rb_str_new2("INT"); break; case VT_UINT: typestr = rb_str_new2("UINT"); break; case VT_VOID: typestr = rb_str_new2("VOID"); break; case VT_HRESULT: typestr = rb_str_new2("HRESULT"); break; case VT_PTR: typestr = rb_str_new2("PTR"); if(typedetails != Qnil) rb_ary_push(typedetails, typestr); return ole_ptrtype2val(pTypeInfo, pTypeDesc, typedetails); case VT_SAFEARRAY: typestr = rb_str_new2("SAFEARRAY"); if(typedetails != Qnil) rb_ary_push(typedetails, typestr); return ole_ptrtype2val(pTypeInfo, pTypeDesc, typedetails); case VT_CARRAY: typestr = rb_str_new2("CARRAY"); break; case VT_USERDEFINED: typestr = rb_str_new2("USERDEFINED"); if (typedetails != Qnil) rb_ary_push(typedetails, typestr); str = ole_usertype2val(pTypeInfo, pTypeDesc, typedetails); if (str != Qnil) { return str; } return typestr; case VT_UNKNOWN: typestr = rb_str_new2("UNKNOWN"); break; case VT_DISPATCH: typestr = rb_str_new2("DISPATCH"); break; case VT_ERROR: typestr = rb_str_new2("ERROR"); break; case VT_LPWSTR: typestr = rb_str_new2("LPWSTR"); break; case VT_LPSTR: typestr = rb_str_new2("LPSTR"); break; case VT_RECORD: typestr = rb_str_new2("RECORD"); break; default: typestr = rb_str_new2("Unknown Type "); rb_str_concat(typestr, rb_fix2str(RB_INT2FIX(pTypeDesc->vt), 10)); break; } if (typedetails != Qnil) rb_ary_push(typedetails, typestr); return typestr; } /* * call-seq: * ole_method_help(method) * * Returns WIN32OLE::Method object corresponding with method * specified by 1st argument. * * excel = WIN32OLE.new('Excel.Application') * method = excel.ole_method_help('Quit') * */ static VALUE fole_method_help(VALUE self, VALUE cmdname) { ITypeInfo *pTypeInfo; HRESULT hr; struct oledata *pole = NULL; VALUE obj; SafeStringValue(cmdname); pole = oledata_get_struct(self); hr = typeinfo_from_ole(pole, &pTypeInfo); if(FAILED(hr)) ole_raise(hr, eWIN32OLEQueryInterfaceError, "failed to get ITypeInfo"); obj = create_win32ole_method(pTypeInfo, cmdname); OLE_RELEASE(pTypeInfo); if (obj == Qnil) rb_raise(eWIN32OLERuntimeError, "not found %s", StringValuePtr(cmdname)); return obj; } /* * call-seq: * ole_activex_initialize() -> Qnil * * Initialize WIN32OLE object(ActiveX Control) by calling * IPersistMemory::InitNew. * * Before calling OLE method, some kind of the ActiveX controls * created with MFC should be initialized by calling * IPersistXXX::InitNew. * * If and only if you received the exception "HRESULT error code: * 0x8000ffff catastrophic failure", try this method before * invoking any ole_method. * * obj = WIN32OLE.new("ProgID_or_GUID_of_ActiveX_Control") * obj.ole_activex_initialize * obj.method(...) * */ static VALUE fole_activex_initialize(VALUE self) { struct oledata *pole = NULL; IPersistMemory *pPersistMemory; void *p; HRESULT hr = S_OK; pole = oledata_get_struct(self); hr = pole->pDispatch->lpVtbl->QueryInterface(pole->pDispatch, &IID_IPersistMemory, &p); pPersistMemory = p; if (SUCCEEDED(hr)) { hr = pPersistMemory->lpVtbl->InitNew(pPersistMemory); OLE_RELEASE(pPersistMemory); if (SUCCEEDED(hr)) { return Qnil; } } if (FAILED(hr)) { ole_raise(hr, eWIN32OLERuntimeError, "fail to initialize ActiveX control"); } return Qnil; } HRESULT typelib_from_val(VALUE obj, ITypeLib **pTypeLib) { LCID lcid = cWIN32OLE_lcid; HRESULT hr; struct oledata *pole = NULL; unsigned int index; ITypeInfo *pTypeInfo; pole = oledata_get_struct(obj); hr = pole->pDispatch->lpVtbl->GetTypeInfo(pole->pDispatch, 0, lcid, &pTypeInfo); if (FAILED(hr)) { return hr; } hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, pTypeLib, &index); OLE_RELEASE(pTypeInfo); return hr; } static void com_hash_free(void *ptr) { st_table *tbl = ptr; st_free_table(tbl); } static void com_hash_mark(void *ptr) { st_table *tbl = ptr; rb_mark_hash(tbl); } static size_t com_hash_size(const void *ptr) { const st_table *tbl = ptr; return st_memsize(tbl); } static void check_nano_server(void) { HKEY hsubkey; LONG err; const char * subkey = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Server\\ServerLevels"; const char * regval = "NanoServer"; err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ, &hsubkey); if (err == ERROR_SUCCESS) { err = RegQueryValueEx(hsubkey, regval, NULL, NULL, NULL, NULL); if (err == ERROR_SUCCESS) { g_running_nano = TRUE; } RegCloseKey(hsubkey); } } LCID cWIN32OLE_lcid; void Init_win32ole(void) { cWIN32OLE_lcid = LOCALE_SYSTEM_DEFAULT; g_ole_initialized_init(); check_nano_server(); com_vtbl.QueryInterface = QueryInterface; com_vtbl.AddRef = AddRef; com_vtbl.Release = Release; com_vtbl.GetTypeInfoCount = GetTypeInfoCount; com_vtbl.GetTypeInfo = GetTypeInfo; com_vtbl.GetIDsOfNames = GetIDsOfNames; com_vtbl.Invoke = Invoke; message_filter.QueryInterface = mf_QueryInterface; message_filter.AddRef = mf_AddRef; message_filter.Release = mf_Release; message_filter.HandleInComingCall = mf_HandleInComingCall; message_filter.RetryRejectedCall = mf_RetryRejectedCall; message_filter.MessagePending = mf_MessagePending; enc2cp_hash = TypedData_Wrap_Struct(0, &win32ole_hash_datatype, 0); RTYPEDDATA_DATA(enc2cp_hash) = st_init_numtable(); rb_gc_register_mark_object(enc2cp_hash); com_hash = TypedData_Wrap_Struct(0, &win32ole_hash_datatype, 0); RTYPEDDATA_DATA(com_hash) = st_init_numtable(); rb_gc_register_mark_object(com_hash); cWIN32OLE = rb_define_class("WIN32OLE", rb_cObject); rb_define_alloc_func(cWIN32OLE, fole_s_allocate); rb_define_method(cWIN32OLE, "initialize", fole_initialize, -1); rb_define_singleton_method(cWIN32OLE, "connect", fole_s_connect, -1); rb_define_singleton_method(cWIN32OLE, "const_load", fole_s_const_load, -1); rb_define_singleton_method(cWIN32OLE, "ole_free", fole_s_free, 1); rb_define_singleton_method(cWIN32OLE, "ole_reference_count", fole_s_reference_count, 1); rb_define_singleton_method(cWIN32OLE, "ole_show_help", fole_s_show_help, -1); rb_define_singleton_method(cWIN32OLE, "codepage", fole_s_get_code_page, 0); rb_define_singleton_method(cWIN32OLE, "codepage=", fole_s_set_code_page, 1); rb_define_singleton_method(cWIN32OLE, "locale", fole_s_get_locale, 0); rb_define_singleton_method(cWIN32OLE, "locale=", fole_s_set_locale, 1); rb_define_singleton_method(cWIN32OLE, "create_guid", fole_s_create_guid, 0); rb_define_singleton_method(cWIN32OLE, "ole_initialize", fole_s_ole_initialize, 0); rb_define_singleton_method(cWIN32OLE, "ole_uninitialize", fole_s_ole_uninitialize, 0); rb_define_method(cWIN32OLE, "invoke", fole_invoke, -1); rb_define_method(cWIN32OLE, "[]", fole_getproperty_with_bracket, -1); rb_define_method(cWIN32OLE, "_invoke", fole_invoke2, 3); rb_define_method(cWIN32OLE, "_getproperty", fole_getproperty2, 3); rb_define_method(cWIN32OLE, "_setproperty", fole_setproperty2, 3); /* support propput method that takes an argument */ rb_define_method(cWIN32OLE, "[]=", fole_setproperty_with_bracket, -1); rb_define_method(cWIN32OLE, "ole_free", fole_free, 0); rb_define_method(cWIN32OLE, "each", fole_each, 0); rb_define_method(cWIN32OLE, "method_missing", fole_missing, -1); /* support setproperty method much like Perl ;-) */ rb_define_method(cWIN32OLE, "setproperty", fole_setproperty, -1); rb_define_method(cWIN32OLE, "ole_methods", fole_methods, 0); rb_define_method(cWIN32OLE, "ole_get_methods", fole_get_methods, 0); rb_define_method(cWIN32OLE, "ole_put_methods", fole_put_methods, 0); rb_define_method(cWIN32OLE, "ole_func_methods", fole_func_methods, 0); rb_define_method(cWIN32OLE, "ole_method", fole_method_help, 1); rb_define_alias(cWIN32OLE, "ole_method_help", "ole_method"); rb_define_method(cWIN32OLE, "ole_activex_initialize", fole_activex_initialize, 0); rb_define_method(cWIN32OLE, "ole_type", fole_type, 0); rb_define_alias(cWIN32OLE, "ole_obj_help", "ole_type"); rb_define_method(cWIN32OLE, "ole_typelib", fole_typelib, 0); rb_define_method(cWIN32OLE, "ole_query_interface", fole_query_interface, 1); rb_define_method(cWIN32OLE, "ole_respond_to?", fole_respond_to, 1); /* Constants definition */ /* * Version string of WIN32OLE. */ rb_define_const(cWIN32OLE, "VERSION", rb_str_new2(WIN32OLE_VERSION)); /* * After invoking OLE methods with reference arguments, you can access * the value of arguments by using ARGV. * * If the method of OLE(COM) server written by C#.NET is following: * * void calcsum(int a, int b, out int c) { * c = a + b; * } * * then, the Ruby OLE(COM) client script to retrieve the value of * argument c after invoking calcsum method is following: * * a = 10 * b = 20 * c = 0 * comserver.calcsum(a, b, c) * p c # => 0 * p WIN32OLE::ARGV # => [10, 20, 30] * * You can use WIN32OLE::Variant object to retrieve the value of reference * arguments instead of referring WIN32OLE::ARGV. * */ rb_define_const(cWIN32OLE, "ARGV", rb_ary_new()); /* * 0: ANSI code page. See WIN32OLE.codepage and WIN32OLE.codepage=. */ rb_define_const(cWIN32OLE, "CP_ACP", RB_INT2FIX(CP_ACP)); /* * 1: OEM code page. See WIN32OLE.codepage and WIN32OLE.codepage=. */ rb_define_const(cWIN32OLE, "CP_OEMCP", RB_INT2FIX(CP_OEMCP)); /* * 2 */ rb_define_const(cWIN32OLE, "CP_MACCP", RB_INT2FIX(CP_MACCP)); /* * 3: current thread ANSI code page. See WIN32OLE.codepage and * WIN32OLE.codepage=. */ rb_define_const(cWIN32OLE, "CP_THREAD_ACP", RB_INT2FIX(CP_THREAD_ACP)); /* * 42: symbol code page. See WIN32OLE.codepage and WIN32OLE.codepage=. */ rb_define_const(cWIN32OLE, "CP_SYMBOL", RB_INT2FIX(CP_SYMBOL)); /* * 65000: UTF-7 code page. See WIN32OLE.codepage and WIN32OLE.codepage=. */ rb_define_const(cWIN32OLE, "CP_UTF7", RB_INT2FIX(CP_UTF7)); /* * 65001: UTF-8 code page. See WIN32OLE.codepage and WIN32OLE.codepage=. */ rb_define_const(cWIN32OLE, "CP_UTF8", RB_INT2FIX(CP_UTF8)); /* * 0x0800: default locale for the operating system. See WIN32OLE.locale * and WIN32OLE.locale=. */ rb_define_const(cWIN32OLE, "LOCALE_SYSTEM_DEFAULT", RB_INT2FIX(LOCALE_SYSTEM_DEFAULT)); /* * 0x0400: default locale for the user or process. See WIN32OLE.locale * and WIN32OLE.locale=. */ rb_define_const(cWIN32OLE, "LOCALE_USER_DEFAULT", RB_INT2FIX(LOCALE_USER_DEFAULT)); Init_win32ole_variant_m(); Init_win32ole_typelib(); Init_win32ole_type(); Init_win32ole_variable(); Init_win32ole_method(); Init_win32ole_param(); Init_win32ole_event(); Init_win32ole_variant(); Init_win32ole_record(); Init_win32ole_error(); ole_init_cp(); }