Skip to content

Commit 0864d49

Browse files
committed
Merge pull request #595 from matthiasblaesing/com_hybrid_calling_convention
Allow calling COM methods/getters requirering hybrid calling (METHOD+PROPERTYGET convention)
2 parents 8cb0dae + 5ab9cd0 commit 0864d49

File tree

6 files changed

+237
-5
lines changed

6 files changed

+237
-5
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Features
3535
* [#569](https://github.com/java-native-access/jna/pull/569): Added `com.sun.jna.platform.win32.Winspool.PRINTER_INFO_2` support. Added GetPrinter and ClosePrinter functions in `com.sun.jna.platform.win32.Winspool` - [@IvanRF](https://github.com/IvanRF).
3636
* [#583](https://github.com/java-native-access/jna/pull/583): Added printer attributes and status - [@IvanRF](https://github.com/IvanRF).
3737
* [#589](https://github.com/java-native-access/jna/pull/589): Use MethodResultContext in direct mapping (as done in interface mapping) - [@marco2357](https://github.com/marco2357).
38+
* [#595](https://github.com/java-native-access/jna/pull/595): Allow calling COM methods/getters requiring hybrid calling (METHOD+PROPERTYGET) - [@matthiasblaesing](https://github.com/matthiasblaesing).
3839

3940
Bug Fixes
4041
---------

contrib/platform/src/com/sun/jna/platform/win32/COM/COMBindingBaseObject.java

+32-1
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,40 @@ protected HRESULT oleMethod(int nType, VARIANT.ByReference pvResult,
262262
dp.write();
263263
}
264264

265+
// Apply "fix" according to
266+
// https://www.delphitools.info/2013/04/30/gaining-visual-basic-ole-super-powers/
267+
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms221486(v=vs.85).aspx
268+
//
269+
// Summary: there are methods in the word typelibrary that require both
270+
// PROPERTYGET _and_ METHOD to be set. With only one of these set the call
271+
// fails.
272+
//
273+
// The article from delphitools argues, that automation compatible libraries
274+
// need to be compatible with VisualBasic which does not distingish methods
275+
// and property getters and will set both flags always.
276+
//
277+
// The MSDN article advises this behaviour: "[...] Some languages cannot
278+
// distinguish between retrieving a property and calling a method. In this
279+
//case, you should set the flags DISPATCH_PROPERTYGET and DISPATCH_METHOD.
280+
// [...]"))
281+
//
282+
// This was found when trying to bind InchesToPoints from the _Application
283+
// dispatch interface of the MS Word 15 type library
284+
//
285+
// The signature according the ITypeLib Viewer (OLE/COM Object Viewer):
286+
// [id(0x00000172), helpcontext(0x09700172)]
287+
// single InchesToPoints([in] single Inches);
288+
289+
final int finalNType;
290+
if (nType == OleAuto.DISPATCH_METHOD || nType == OleAuto.DISPATCH_PROPERTYGET) {
291+
finalNType = OleAuto.DISPATCH_METHOD | OleAuto.DISPATCH_PROPERTYGET;
292+
} else {
293+
finalNType = nType;
294+
}
295+
265296
// Make the call!
266297
HRESULT hr = pDisp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT,
267-
new WinDef.WORD(nType), dp, pvResult, pExcepInfo, puArgErr);
298+
new WinDef.WORD(finalNType), dp, pvResult, pExcepInfo, puArgErr);
268299

269300
COMUtils.checkRC(hr, pExcepInfo, puArgErr);
270301
return hr;

contrib/platform/src/com/sun/jna/platform/win32/COM/util/ProxyObject.java

+31-1
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,36 @@ protected HRESULT oleMethod(final int nType, final VARIANT.ByReference pvResult,
611611
dp.cNamedArgs = new UINT(_argsLen);
612612
dp.rgdispidNamedArgs = new DISPIDByReference(OaIdl.DISPID_PROPERTYPUT);
613613
}
614+
615+
// Apply "fix" according to
616+
// https://www.delphitools.info/2013/04/30/gaining-visual-basic-ole-super-powers/
617+
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms221486(v=vs.85).aspx
618+
//
619+
// Summary: there are methods in the word typelibrary that require both
620+
// PROPERTYGET _and_ METHOD to be set. With only one of these set the call
621+
// fails.
622+
//
623+
// The article from delphitools argues, that automation compatible libraries
624+
// need to be compatible with VisualBasic which does not distingish methods
625+
// and property getters and will set both flags always.
626+
//
627+
// The MSDN article advises this behaviour: "[...] Some languages cannot
628+
// distinguish between retrieving a property and calling a method. In this
629+
//case, you should set the flags DISPATCH_PROPERTYGET and DISPATCH_METHOD.
630+
// [...]"))
631+
//
632+
// This was found when trying to bind InchesToPoints from the _Application
633+
// dispatch interface of the MS Word 15 type library
634+
//
635+
// The signature according the ITypeLib Viewer (OLE/COM Object Viewer):
636+
// [id(0x00000172), helpcontext(0x09700172)]
637+
// single InchesToPoints([in] single Inches);
638+
final int finalNType;
639+
if(nType == OleAuto.DISPATCH_METHOD || nType == OleAuto.DISPATCH_PROPERTYGET) {
640+
finalNType = OleAuto.DISPATCH_METHOD | OleAuto.DISPATCH_PROPERTYGET;
641+
} else {
642+
finalNType = nType;
643+
}
614644

615645
// Build DISPPARAMS
616646
if (_argsLen > 0) {
@@ -629,7 +659,7 @@ protected HRESULT oleMethod(final int nType, final VARIANT.ByReference pvResult,
629659
@Override
630660
public HRESULT call() throws Exception {
631661
return pDisp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT,
632-
new WinDef.WORD(nType), dp, pvResult, pExcepInfo, puArgErr);
662+
new WinDef.WORD(finalNType), dp, pvResult, pExcepInfo, puArgErr);
633663
}
634664
});
635665

contrib/platform/test/com/sun/jna/platform/win32/COM/IDispatchTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private Dispatch createIDispatch() {
3939
try {
4040
PointerByReference pDispatch = new PointerByReference();
4141

42-
// Get CLSID for Word.Application...
42+
// Get CLSID for Shell.Application...
4343
CLSID.ByReference clsid = new CLSID.ByReference();
4444
HRESULT hr = Ole32.INSTANCE.CLSIDFromProgID("Shell.Application",
4545
clsid);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package com.sun.jna.platform.win32.COM.util;
2+
3+
import com.sun.jna.platform.win32.COM.COMException;
4+
import com.sun.jna.platform.win32.COM.COMLateBindingObject;
5+
import com.sun.jna.platform.win32.COM.COMUtils;
6+
import com.sun.jna.platform.win32.COM.Dispatch;
7+
import com.sun.jna.platform.win32.COM.util.annotation.ComInterface;
8+
import com.sun.jna.platform.win32.COM.util.annotation.ComMethod;
9+
import com.sun.jna.platform.win32.COM.util.annotation.ComObject;
10+
import com.sun.jna.platform.win32.Guid;
11+
import com.sun.jna.platform.win32.Guid.CLSID;
12+
import com.sun.jna.platform.win32.Guid.GUID;
13+
import com.sun.jna.platform.win32.Guid.IID;
14+
import com.sun.jna.platform.win32.Guid.REFIID;
15+
import com.sun.jna.platform.win32.Kernel32;
16+
import com.sun.jna.platform.win32.OaIdl;
17+
import com.sun.jna.platform.win32.OaIdl.DISPID;
18+
import com.sun.jna.platform.win32.Ole32;
19+
import com.sun.jna.platform.win32.OleAuto;
20+
import com.sun.jna.platform.win32.Variant;
21+
import com.sun.jna.platform.win32.Variant.VARIANT;
22+
import com.sun.jna.platform.win32.WTypes;
23+
import com.sun.jna.platform.win32.WinDef;
24+
import com.sun.jna.platform.win32.WinDef.UINT;
25+
import com.sun.jna.platform.win32.WinDef.WORD;
26+
import com.sun.jna.platform.win32.WinNT.HRESULT;
27+
import com.sun.jna.ptr.IntByReference;
28+
import com.sun.jna.ptr.PointerByReference;
29+
import java.util.logging.Level;
30+
import java.util.logging.Logger;
31+
import junit.framework.TestCase;
32+
import org.junit.Test;
33+
import static junit.framework.TestCase.assertEquals;
34+
import static junit.framework.TestCase.assertTrue;
35+
36+
/**
37+
* In the word COM bindings it was determined, that some methods can't be called
38+
* with only wFlags OleAuto.DISPATCH_METHOD or OleAuto.DISPATCH_PROPERTYGET.
39+
*
40+
* For these methods both flags need to be set.
41+
*
42+
* https://www.delphitools.info/2013/04/30/gaining-visual-basic-ole-super-powers/
43+
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms221486(v=vs.85).aspx
44+
*
45+
* A sample function is InchesToPoints from thw word typelibrary
46+
*/
47+
public class HybdridCOMInvocationTest extends TestCase {
48+
49+
private static final Logger LOG = Logger.getLogger(HybdridCOMInvocationTest.class.getName());
50+
51+
private static final String CLSID_WORD_STRING = "{000209FF-0000-0000-C000-000000000046}";
52+
private static final String IID_APPLICATION_STRING = "{00020970-0000-0000-C000-000000000046}";
53+
private static final GUID CLSID_WORD = new GUID(CLSID_WORD_STRING);
54+
private static final IID IID_APPLICATION = new IID(new GUID(IID_APPLICATION_STRING));
55+
56+
@Override
57+
protected void tearDown() throws Exception {
58+
super.tearDown();
59+
Ole32.INSTANCE.CoUninitialize();
60+
}
61+
62+
@Override
63+
protected void setUp() throws Exception {
64+
// Initialize COM for this thread...
65+
HRESULT hr = Ole32.INSTANCE.CoInitialize(null);
66+
}
67+
68+
@Test
69+
public void testOfficeInvocationProblemCOMUtil() {
70+
Factory fact = new Factory();
71+
Application app;
72+
try {
73+
app = fact.createObject(Application.class);
74+
} catch (COMException ex) {
75+
LOG.log(Level.INFO, "HybdridCOMInvocationTest test was not run, MS Word object could not be instantiated.", ex);
76+
return;
77+
}
78+
// If this fails: remember: floats are not exact, if this happens replace
79+
// with a range check
80+
assertEquals(72.0f, app.InchesToPoints(1F));
81+
}
82+
83+
@Test
84+
public void testOfficeInvocationProblemCOMBindingObject() {
85+
WordApplication app;
86+
try {
87+
app = new WordApplication(false);
88+
} catch (COMException ex) {
89+
LOG.log(Level.INFO, "HybdridCOMInvocationTest test was not run, MS Word object could not be instantiated.", ex);
90+
return;
91+
}
92+
assertEquals(72.0f, app.InchesToPoints(1F));
93+
}
94+
95+
96+
public void testOfficeInvocationDemonstration() {
97+
// THIS IS NOT A TEST
98+
//
99+
// This reproduces the problem by using the dispatch directly.
100+
101+
PointerByReference pDispatch = new PointerByReference();
102+
103+
HRESULT hr = Ole32.INSTANCE.CoCreateInstance(CLSID_WORD, null,
104+
WTypes.CLSCTX_SERVER, IID_APPLICATION, pDispatch);
105+
106+
if(! COMUtils.SUCCEEDED(hr)) {
107+
LOG.log(Level.INFO, "HybdridCOMInvocationTest test was not run, MS Word object could not be instantiated.");
108+
return;
109+
}
110+
111+
Dispatch dp = new Dispatch(pDispatch.getValue());
112+
113+
// DispID of InchesToPoints
114+
DISPID dispId = new OaIdl.DISPID(0x00000172);
115+
// Interface _Application of MS Word type library
116+
WinDef.LCID LOCALE_SYSTEM_DEFAULT = Kernel32.INSTANCE.GetSystemDefaultLCID();
117+
Variant.VARIANT.ByReference result = new Variant.VARIANT.ByReference();
118+
OaIdl.EXCEPINFO.ByReference pExcepInfo = new OaIdl.EXCEPINFO.ByReference();
119+
IntByReference puArgErr = new IntByReference();
120+
121+
WORD wFlagsMethod = new WinDef.WORD(OleAuto.DISPATCH_METHOD);
122+
WORD wFlagsGet = new WinDef.WORD(OleAuto.DISPATCH_PROPERTYGET);
123+
WORD wFlagsCombined = new WinDef.WORD(OleAuto.DISPATCH_METHOD | OleAuto.DISPATCH_PROPERTYGET);
124+
125+
OleAuto.DISPPARAMS.ByReference pDispParams = new OleAuto.DISPPARAMS.ByReference();
126+
VARIANT[] params = new VARIANT[1];
127+
params[0] = new VARIANT(1f);
128+
pDispParams.cArgs = new UINT(1);
129+
pDispParams.cNamedArgs = new UINT(0);
130+
pDispParams.rgvarg = new Variant.VariantArg.ByReference(params);
131+
pDispParams.rgdispidNamedArgs = new OaIdl.DISPIDByReference();
132+
133+
// Call InchesToPoints as a method
134+
hr = dp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT, wFlagsMethod, pDispParams, result, pExcepInfo, puArgErr);
135+
assertTrue(COMUtils.FAILED(hr));
136+
137+
// Call InchesToPoints as a property getter
138+
hr = dp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT, wFlagsGet, pDispParams, result, pExcepInfo, puArgErr);
139+
assertTrue(COMUtils.FAILED(hr));
140+
141+
// Call InchesToPoints as a hybrid
142+
hr = dp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT, wFlagsCombined, pDispParams, result, pExcepInfo, puArgErr);
143+
assertTrue(COMUtils.SUCCEEDED(hr));
144+
145+
assertEquals(72.0f, result.floatValue());
146+
}
147+
148+
@ComObject(clsId = CLSID_WORD_STRING)
149+
public static interface Application extends IDispatch, _Application {
150+
}
151+
152+
@ComInterface(iid = IID_APPLICATION_STRING)
153+
public static interface _Application {
154+
@ComMethod
155+
Float InchesToPoints(Float value);
156+
}
157+
158+
public static class WordApplication extends COMLateBindingObject {
159+
160+
public WordApplication(boolean useActiveInstance) {
161+
super(new CLSID(CLSID_WORD), useActiveInstance);
162+
}
163+
164+
public Float InchesToPoints(Float value) {
165+
VARIANT.ByReference pvResult = new VARIANT.ByReference();
166+
this.oleMethod(OleAuto.DISPATCH_METHOD , pvResult, this.getIDispatch(), "InchesToPoints", new VARIANT[] {new VARIANT(value)});
167+
return pvResult.floatValue();
168+
}
169+
}
170+
}

contrib/platform/test/com/sun/jna/platform/win32/COM/util/RunningObjectTable_Test.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ public class RunningObjectTable_Test {
3131
@ComInterface(iid="{00020970-0000-0000-C000-000000000046}")
3232
interface Application extends IUnknown {
3333
@ComProperty
34-
boolean getVisible();
34+
Boolean getVisible();
3535

3636
@ComProperty
37-
void setVisible(boolean value);
37+
void setVisible(Boolean value);
3838

3939
@ComMethod
4040
void Quit(boolean SaveChanges, Object OriginalFormat, Boolean RouteDocument);

0 commit comments

Comments
 (0)