Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some of the failing COM unittests #578

Merged
merged 5 commits into from Feb 21, 2016

Conversation

matthiasblaesing
Copy link
Member

I had a look at the failing COM unittests and especially the failing CallbackHandlers.

For details please refer to the comments in the commits - I tried to explain the reasoning in the commit messages.

*/
public class REFIID extends IID {
public class REFIID extends ByReference {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only makes a difference when the Structure is used as a field within a Structure. It's better to leave the main definition untagged and use an explicitly tagged inner class which derives from the main one.

Is REFIID simply an alias for IID*?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my first thought and would align with the FAQ about this, but then this does not explain the behavior in callbacks - (please see the comment for the first commit, first paragraph)

The difference between my explicit use of ByReference and the suggested Structure/Structure.ByReference is the special casing the latter receives in com.sun.jna.CallbackReference (lines 511-516). My approach circumvents the autowrite.

The alternative would have been to mark REFIID not to autosync, but that looked worse.

Yes REFIID is typedeffed to IIID* (https://msdn.microsoft.com/en-us/library/cc237816.aspx)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I see your point, but I fail to see how your strategy should be any different; it would seem to be a bug exploiting different code paths somehow, which should be resolved more systemically.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning for the com.sun.jna.ptr.ByReference approach was that it is closest to the underlying definition. It is clear by that no magic handling is expected from JNA, just passing the pointer.

If this is strictly out of line - I only see the Option to make the members of the GUID structure final so that when passed in they are only read and in general GUID must be interpreted as a value type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that this has exposed a latent bug and we’d only be masking it.

Your definition is otherwise sane and certainly not misleading (except that it varies from the recommended method of establishing “struct” vs “struct *”).

Give me a few days to take a look at this (I’ve been totally swamped this week paying face time to visiting dignitaries).

T.

On Jan 13, 2016, at 11:03 AM, matthiasblaesing notifications@github.com wrote:

In contrib/platform/src/com/sun/jna/platform/win32/Guid.java:

  */
  • public class REFIID extends IID {
  • public class REFIID extends ByReference {

My reasoning for the com.sun.jna.ptr.ByReference approach was that it is closest to the underlying definition. It is clear by that no magic handling is expected from JNA, just passing the pointer.

If this is strictly out of line - I only see the Option to make the members of the GUID structure final so that when passed in they are only read and in general GUID must be interpreted as a value type.


Reply to this email directly or view it on GitHub.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I misread this. You should implement REFIID as a PointerType (I though you were using Structure.ByReference rather than com.sun.jna.ptr.ByReference). com.sun.jna.ptr.ByReference should generally only be used when your called function expects a pointer to something else in order to "return" a value via parameter.

If you change REFIID to PointerType your code should still work and will not be ambiguous or misleading.

@matthiasblaesing
Copy link
Member Author

Hello Timothy, did you find some time to look at this?

@twall
Copy link
Contributor

twall commented Jan 25, 2016

Not yet, had some issues spinning up my windows VMs.

This one’s on my plate as well as the stdcall issue.

On Jan 25, 2016, at 9:46 AM, matthiasblaesing notifications@github.com wrote:

Hello Timothy, did you find some time to look at this?


Reply to this email directly or view it on GitHub.

@twall
Copy link
Contributor

twall commented Feb 14, 2016

writeFieldsToMemory seems superfluous to Structure.write().

After the change introduced in d7f91f1
native callbacks for the COM handling error out (see below). Analysing the
stack trace this happens after the java function was called. There
structures are syncing back from java to native. I asume the calling
programm passes in a readonly version of the IID and so the write fails.

This code path was not hit before the above mentioned changeset, because
there is a typeguard, that prevents syncing for ByValue calls (which were
removed be the changeset).

In addition to this, a fix ComEventCallbacks_Test to not depend on an
installed office was commited. Now the Internet Explorer is used, as it
can be expected to be present.

Tests from ConnectionPointerContainer_Test duplicated a good part of
ComEventCallbacks_Test so the cases were integrated into ComEventCallbacks_Test.

The exception leading to this fix
(CallbackReference.java:513 synchronised structed passed by reference after the java invocation):

JNA: Callback com.sun.jna.platform.win32.COM.DispatchListener$1@233c0b17 threw the following exception:
java.lang.Error: Invalid memory access
	at com.sun.jna.Native.setInt(Native Method)
	at com.sun.jna.Pointer.setInt(Pointer.java:1124)
	at com.sun.jna.Pointer.setValue(Pointer.java:925)
	at com.sun.jna.Structure.writeField(Structure.java:842)
	at com.sun.jna.Structure.write(Structure.java:754)
	at com.sun.jna.Structure.autoWrite(Structure.java:2047)
	at com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback(CallbackReference.java:513)
	at com.sun.jna.CallbackReference$DefaultCallbackProxy.callback(CallbackReference.java:528)
	at com.sun.jna.Native.invokeInt(Native Method)
	at com.sun.jna.Function.invoke(Function.java:390)
	at com.sun.jna.Function.invoke(Function.java:323)
	at com.sun.jna.Function.invoke(Function.java:275)
	at com.sun.jna.Function.invoke(Function.java:266)
	at com.sun.jna.platform.win32.COM.COMInvoker._invokeNativeObject(COMInvoker.java:37)
	at com.sun.jna.platform.win32.COM.ConnectionPoint.Advise(ConnectionPoint.java:42)
	at com.sun.jna.platform.win32.COM.ComEventCallbacks_Test.cause_Quit_Event(ComEventCallbacks_Test.java:236)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at junit.framework.JUnit4TestAdapter.run(JUnit4TestAdapter.java:38)
	at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:532)
	at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:1179)
	at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:1030)
Depending on the context the unmarshalling of the supplied data in callbacks
is unwrapped further than before. The NavigateComplete2 event from the interface
DWebBrowserEvents2 demonstrates this. The URL is passed as a Variant pointing to
a Variant containing the string. Without this fix NULL is returned.

To test this ComEventCallbacks_Test.java was moved to use Internet Explorer
instead of MS Office.
…ng thread

Dispatching the invocation of the callback handler into an executor
makes it impossible to fill [out] parameters, as the return has already
happend and [in] parameters can not be savely used if they are not
marshalled to java code because the calling code will free the parameters
after the call.

To prevent deadlocks ComThread is modified to allow COM calls from the
callback by modifying the ComThread helper to only dispatch the COM call
into the ComThread only if the calling thread has not COM already enabled.

Reference counting was modified, so that now on construction of a
ProxyObject the reference count is AddRef'ed once and Released once on
finalization.
According to MSDN the caller is responsible to free BSTRs.
@matthiasblaesing
Copy link
Member Author

Thank you for the feedback - I updated the pull request:

  • rebased onto master
  • based REFIID on PointerType instead of c.s.j.ptr.ByReference
  • fix native memory leak - this was discovered when investigating the calling in ProxyObject

The memory leak is based on the description in MSDN:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms686638%28v=vs.85%29.aspx

The caller is responsible for freeing memory in all cases. The most obvious case are the strings that are converted from/to BSTRs. The BSTRs are allocated as native memory when wrapped into a VARIANT, that memory is currently nowhere freed.

The freeing is only done for values that pass through the automatic conversion - if users directly use VARIANTs they are responsible to free it themselves.

I could not place your comment with regard to "writeFieldsToMemory" though - I assume this is directed to another pull?

@twall
Copy link
Contributor

twall commented Feb 21, 2016

Take a look at the implementation of NativeString, which is used when a String is used as an argument and requires a native "copy" for the duration of a call.

@twall
Copy link
Contributor

twall commented Feb 21, 2016

I think the comment about writeFieldsToMemory was referring to this usage.

@ComProperty
String getText();
/**
* navOpenInNewWindow = 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to explicitly expose these values as constants?

twall added a commit that referenced this pull request Feb 21, 2016
LGTM, nice work.  The `writeFieldsToMemory`, `Convert` class, and any `NativeString` improvements can be addressed in a separate PR.
@twall twall merged commit bf32054 into java-native-access:master Feb 21, 2016
@twall
Copy link
Contributor

twall commented Feb 21, 2016

LGTM, nice work. The writeFieldsToMemory, Convert class, and any NativeString improvements can be addressed in a separate PR.

@dblock
Copy link
Member

dblock commented Feb 22, 2016

👍

@matthiasblaesing matthiasblaesing deleted the fix_com branch February 22, 2016 19:00
@matthiasblaesing
Copy link
Member Author

Thanks for merging this! I'll revisit the mentioned cases and added them to my internal todo list. I have a few things (SAFEARRAY, threading) I'd like to get to first and I can't make a hard promise with regard to time I can invest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants