package com.sun.jna.platform.win32.COM.util;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.COM.COMException;
import com.sun.jna.platform.win32.COM.COMLateBindingObject;
import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.platform.win32.COM.Dispatch;
import com.sun.jna.platform.win32.COM.util.annotation.ComInterface;
import com.sun.jna.platform.win32.COM.util.annotation.ComMethod;
import com.sun.jna.platform.win32.COM.util.annotation.ComObject;
import com.sun.jna.platform.win32.Guid;
import com.sun.jna.platform.win32.Guid.CLSID;
import com.sun.jna.platform.win32.Guid.GUID;
import com.sun.jna.platform.win32.Guid.IID;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.OaIdl;
import com.sun.jna.platform.win32.OaIdl.DISPID;
import com.sun.jna.platform.win32.Ole32;
import com.sun.jna.platform.win32.OleAuto;
import com.sun.jna.platform.win32.Variant;
import com.sun.jna.platform.win32.Variant.VARIANT;
import com.sun.jna.platform.win32.WTypes;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.UINT;
import com.sun.jna.platform.win32.WinDef.WORD;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.hamcrest.CoreMatchers.is;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Before;
/**
* In the word COM bindings it was determined, that some methods can't be called
* with only wFlags OleAuto.DISPATCH_METHOD or OleAuto.DISPATCH_PROPERTYGET.
*
* For these methods both flags need to be set.
*
* https://www.delphitools.info/2013/04/30/gaining-visual-basic-ole-super-powers/
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms221486(v=vs.85).aspx
*
* A sample function is InchesToPoints from thw word typelibrary
*/
public class HybdridCOMInvocationTest {
static {
ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
}
private static final Logger LOG = Logger.getLogger(HybdridCOMInvocationTest.class.getName());
private static final String CLSID_WORD_STRING = "{000209FF-0000-0000-C000-000000000046}";
private static final String IID_APPLICATION_STRING = "{00020970-0000-0000-C000-000000000046}";
private static final GUID CLSID_WORD = new GUID(CLSID_WORD_STRING);
private static final IID IID_APPLICATION = new IID(new GUID(IID_APPLICATION_STRING));
@After
public void tearDown() throws Exception {
Ole32.INSTANCE.CoUninitialize();
}
@Before
public void setUp() throws Exception {
// Initialize COM for this thread...
Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
}
@Test
public void testOfficeInvocationProblemCOMUtil() {
ObjectFactory fact = new ObjectFactory();
Application app;
try {
app = fact.createObject(Application.class);
} catch (COMException ex) {
LOG.log(Level.INFO, "HybdridCOMInvocationTest test was not run, MS Word object could not be instantiated.", ex);
return;
}
// If this fails: remember: floats are not exact, if this happens replace
// with a range check
assertThat(app.InchesToPoints(1F), is(72.0f));
fact.disposeAll();
}
@Test
public void testOfficeInvocationProblemCOMBindingObject() {
WordApplication app;
try {
app = new WordApplication(false);
} catch (COMException ex) {
LOG.log(Level.INFO, "HybdridCOMInvocationTest test was not run, MS Word object could not be instantiated.", ex);
return;
}
assertThat(app.InchesToPoints(1F), is(72.0f));
}
public void testOfficeInvocationDemonstration() {
// THIS IS NOT A TEST
//
// This reproduces the problem by using the dispatch directly.
PointerByReference pDispatch = new PointerByReference();
HRESULT hr = Ole32.INSTANCE.CoCreateInstance(CLSID_WORD, null,
WTypes.CLSCTX_SERVER, IID_APPLICATION, pDispatch);
if(! COMUtils.SUCCEEDED(hr)) {
LOG.log(Level.INFO, "HybdridCOMInvocationTest test was not run, MS Word object could not be instantiated.");
return;
}
Dispatch dp = new Dispatch(pDispatch.getValue());
// DispID of InchesToPoints
DISPID dispId = new OaIdl.DISPID(0x00000172);
// Interface _Application of MS Word type library
WinDef.LCID LOCALE_SYSTEM_DEFAULT = Kernel32.INSTANCE.GetSystemDefaultLCID();
Variant.VARIANT.ByReference result = new Variant.VARIANT.ByReference();
OaIdl.EXCEPINFO.ByReference pExcepInfo = new OaIdl.EXCEPINFO.ByReference();
IntByReference puArgErr = new IntByReference();
WORD wFlagsMethod = new WinDef.WORD(OleAuto.DISPATCH_METHOD);
WORD wFlagsGet = new WinDef.WORD(OleAuto.DISPATCH_PROPERTYGET);
WORD wFlagsCombined = new WinDef.WORD(OleAuto.DISPATCH_METHOD | OleAuto.DISPATCH_PROPERTYGET);
OleAuto.DISPPARAMS.ByReference pDispParams = new OleAuto.DISPPARAMS.ByReference();
VARIANT[] params = new VARIANT[] {new VARIANT(1f)};
pDispParams.setArgs(params);
// Call InchesToPoints as a method
hr = dp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT, wFlagsMethod, pDispParams, result, pExcepInfo, puArgErr);
assertTrue(COMUtils.FAILED(hr));
// Call InchesToPoints as a property getter
hr = dp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT, wFlagsGet, pDispParams, result, pExcepInfo, puArgErr);
assertTrue(COMUtils.FAILED(hr));
// Call InchesToPoints as a hybrid
hr = dp.Invoke(dispId, new REFIID(Guid.IID_NULL), LOCALE_SYSTEM_DEFAULT, wFlagsCombined, pDispParams, result, pExcepInfo, puArgErr);
assertTrue(COMUtils.SUCCEEDED(hr));
assertEquals(72.0f, result.floatValue());
}
@ComObject(clsId = CLSID_WORD_STRING)
public static interface Application extends IDispatch, _Application {
}
@ComInterface(iid = IID_APPLICATION_STRING)
public static interface _Application {
@ComMethod
Float InchesToPoints(Float value);
}
public static class WordApplication extends COMLateBindingObject {
public WordApplication(boolean useActiveInstance) {
super(new CLSID(CLSID_WORD), useActiveInstance);
}
public Float InchesToPoints(Float value) {
VARIANT.ByReference pvResult = new VARIANT.ByReference();
this.oleMethod(OleAuto.DISPATCH_METHOD , pvResult, this.getIDispatch(), "InchesToPoints", new VARIANT[] {new VARIANT(value)});
return pvResult.floatValue();
}
}
}