From 187cb8f9279584548eb331c5edc0813b1768a672 Mon Sep 17 00:00:00 2001
From: Wolf <wolf@wolfsden.cz>
Date: Wed, 10 Jan 2018 12:19:49 +0100
Subject: [PATCH] win32ole: Enable COM classes with multiple IDispatch

Enable usage of weird COM classes with multiple IDispatch
implementations.
---
 ext/win32ole/win32ole.c | 56 +++++++++++++++++++++++++++++++++++++------------
 1 file changed, 43 insertions(+), 13 deletions(-)

diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c
index 9d0b1b0431..f43d7b0f0f 100644
--- a/ext/win32ole/win32ole.c
+++ b/ext/win32ole/win32ole.c
@@ -2392,6 +2392,20 @@ fole_s_ole_uninitialize(VALUE self)
     return Qnil;
 }
 
+static HRESULT str_to_clsid(VALUE str, CLSID * clsid)
+{
+    OLECHAR *pBuf;
+    HRESULT hr;
+
+    pBuf  = ole_vstr2wc(str);
+    hr = CLSIDFromProgID(pBuf, clsid);
+    if(FAILED(hr)) {
+        hr = CLSIDFromString(pBuf, clsid);
+    }
+    SysFreeString(pBuf);
+    return hr;
+}
+
 /*
  * Document-class: WIN32OLE
  *
@@ -2443,6 +2457,7 @@ fole_s_ole_uninitialize(VALUE self)
  *  call-seq:
  *     WIN32OLE.new(server, [host]) -> WIN32OLE object
  *     WIN32OLE.new(server, license: 'key') -> WIN32OLE object
+ *     WIN32OLE.new(server, iface: '{00000000-0000-0000-0000-000000000000}') -> WIN32OLE object
  *
  *  Returns a new WIN32OLE object(OLE Automation object).
  *  The first argument server specifies OLE Automation server.
@@ -2452,6 +2467,9 @@ fole_s_ole_uninitialize(VALUE self)
  *  If :license keyword argument is provided,
  *  IClassFactory2::CreateInstanceLic is used to create instance of
  *  licensed server.
+ *  If :iface keyword argument (GUID) is provided,
+ *  that interface is retrieved instead of IDispatch. But it must
+ *  also inherit from IDispatch. In 99.999% cases you don't need this.
  *
  *      WIN32OLE.new('Excel.Application') # => Excel OLE Automation WIN32OLE object.
  *      WIN32OLE.new('{00024500-0000-0000-C000-000000000046}') # => Excel OLE Automation WIN32OLE object.
@@ -2464,14 +2482,14 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
     VALUE others;
     VALUE opts;
     HRESULT hr;
-    CLSID   clsid;
-    OLECHAR *pBuf;
+    CLSID clsid;
+    CLSID iface;
     OLECHAR *key_buf;
     IDispatch *pDispatch;
     IClassFactory2 * pIClassFactory2;
     void *p;
-    static ID keyword_ids[1];
-    VALUE kwargs[1];
+    static ID keyword_ids[2];
+    VALUE kwargs[2];
 
     rb_call_super(0, 0);
     rb_scan_args(argc, argv, "11*:", &svr_name, &host, &others, &opts);
@@ -2491,12 +2509,7 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
     }
 
     /* get CLSID from OLE server name */
-    pBuf  = ole_vstr2wc(svr_name);
-    hr = CLSIDFromProgID(pBuf, &clsid);
-    if(FAILED(hr)) {
-        hr = CLSIDFromString(pBuf, &clsid);
-    }
-    SysFreeString(pBuf);
+    hr = str_to_clsid(svr_name, &clsid);
     if(FAILED(hr)) {
         ole_raise(hr, eWIN32OLERuntimeError,
                   "unknown OLE server: `%s'",
@@ -2505,8 +2518,23 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
 
     if (!keyword_ids[0]) {
         keyword_ids[0] = rb_intern_const("license");
+        keyword_ids[1] = rb_intern_const("iface");
+    }
+    rb_get_kwargs(opts, keyword_ids, 0, 2, kwargs);
+
+    if (kwargs[1] == Qundef) {
+	iface = IID_IDispatch;
+    } else {
+	hr = str_to_clsid(kwargs[1], &iface);
+	if (FAILED(hr)) {
+	    ole_raise(
+		hr,
+		eWIN32OLERuntimeError,
+		"unknown interface: `%s'",
+		StringValuePtr(kwargs[1])
+	    );
+	}
     }
-    rb_get_kwargs(opts, keyword_ids, 0, 1, kwargs);
 
     if (kwargs[0] == Qundef) {
         /* get IDispatch interface */
@@ -2514,7 +2542,7 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
             &clsid,
             NULL,
             CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
-            &IID_IDispatch,
+            &iface,
             &p
         );
     } else {
@@ -2527,7 +2555,7 @@ fole_initialize(int argc, VALUE *argv, VALUE self)
         );
         if (hr == S_OK) {
             key_buf = ole_vstr2wc(kwargs[0]);
-            hr = pIClassFactory2->lpVtbl->CreateInstanceLic(pIClassFactory2, NULL, NULL, &IID_IDispatch, key_buf, &p);
+            hr = pIClassFactory2->lpVtbl->CreateInstanceLic(pIClassFactory2, NULL, NULL, &iface, key_buf, &p);
             SysFreeString(key_buf);
             OLE_RELEASE(pIClassFactory2);
         }
@@ -4149,3 +4177,5 @@ Init_win32ole(void)
 
     ole_init_cp();
 }
+
+/* vim: set ts=8 sw=4 noexpandtab: */
-- 
2.15.1

