question

needmoreraminbrain-3554 avatar image
0 Votes"
needmoreraminbrain-3554 asked ·

C# AccessViolationException with interop callbacks in .NET 4.5 or above

I'm investigating screen reader technology in C# and experiencing an issue regarding the capturing of events using an interop library written in C++ (Java Access Bridge - https://github.com/openjdk/jdk/tree/05a764f4ffb8030d6b768f2d362c388e5aabd92d/src/jdk.accessibility/windows/native/libwindowsaccessbridge) when compiled using .NET 4.5+ (64-bit). It crashes in both Debug and Release build configurations. The functionality works fine in .NET 4.0 however.

Whenever an event occurs that should trigger the registered callback, an AccessViolationException occurs, crashing the application. I suspect it has something to do with the Garbage Collector changes introduced in .NET 4.5 but would appreciate assistance in being able to track down the exact issue.

There is a complete minimal reproducible code project located here that subscribes to a mouseclick event -> https://github.com/needmoreraminbrainnow/screenreadertest.
When this is compiled with .NET 4.5 it will crash when a mouseclick event occurs within a Java-based application

The delegate is created as a class variable to maintain a reference. The two important methods on the C# side are...

 [DllImport(WINDOWS_ACCESS_BRIDGE, SetLastError = true, ThrowOnUnmappableChar = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
 private static extern void setMouseClickedFP(MouseClickedDelegate fp);
    
 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
 public delegate void MouseClickedDelegate(System.Int64 vmID, IntPtr jevent, IntPtr ac);

I believe the relevant C++ methods are...

 void fireMouseClicked(long vmID, JOBJECT64 event, JOBJECT64 source);

and...

 void AccessBridgeEventHandler::method(long vmID, JOBJECT64 event, JOBJECT64 source) { \
     DEBUG_CODE(char debugBuf[255]); \
     DEBUG_CODE(sprintf(debugBuf, fireEventDebugString, #method, event, source, vmID)); \
     DEBUG_CODE(AppendToCallInfo(debugBuf)); \
     if (eventFP != (FPprototype) 0) { \
         eventFP(vmID, event, source); \
     } else { \
         DEBUG_CODE(AppendToCallInfo("[ERROR]: eventFP == 0")); \
     } \
 }

All I can find that corresponds to setMouseClicked is in C code here...

 void SetMouseClicked(AccessBridge_MouseClickedFP fp) {
     if (theAccessBridgeInitializedFlag == TRUE) {
         theAccessBridge.SetMouseClicked(fp);
     }
 }

and also...

 #define CALL_SET_EVENT_FP(function, callbackFP)         \
     void WinAccessBridge::function(callbackFP fp) { \
             eventHandler->function(fp, this);                       \
             /* eventHandler calls back to winAccessBridgeDLL to set eventMask */    \
     }
    
 CALL_SET_EVENT_FP(setMouseClickedFP, AccessBridge_MouseClickedFP)

I've tried pinning the delegate using GCHandle.Alloc(_mouseClickDelegate, GCHandleType.Pinned); but when run this results in a Object contains non-primitive or non-blittable data error. And based on research of similar questions and Microsoft articles it appears this should not be necessary.

I've tried using GC.KeepAlive for the delegate

I have also tried changes related to Marshalling of the delegate when setting the callback and using a different calling convention but to no avail.

I have also tried using the following struct as the callback argument type instead of IntPtr...

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
     public class JOBJECT64
     {
         public static JOBJECT64 Zero = default(JOBJECT64);
    
         private readonly long _value;
    
         public JOBJECT64(long value)
         {
             _value = value;
         }
    
         public long Value
         {
             get { return _value; }
         }
    
         public static bool operator ==(JOBJECT64 x, JOBJECT64 y)
         {
             return x._value == y._value;
         }
    
         public static bool operator !=(JOBJECT64 x, JOBJECT64 y)
         {
             return x._value == y._value;
         }
    
         public override bool Equals(object obj)
         {
             if (obj is JOBJECT64)
             {
                 return this == (JOBJECT64) obj;
             }
    
             return false;
         }
    
         public override int GetHashCode()
         {
             return _value.GetHashCode();
         }
     }

How can I confirm that it is a problem with the delegate being moved or inaccessible?









dotnet-csharpc++
· 7
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.


Maybe the first parameter of callback must be Int32 instead of Int64?

Also note that samples that use [UnmanagedFunctionPointer] attribute still use Marshal.GetFunctionPointerForDelegate. (For example: https://docs.microsoft.com/en-us/dotnet/standard/memory-and-spans/memory-t-usage-guidelines#usage-guidelines, sample for Rule #10). The parameter of setMouseClickedFP maybe must be IntPtr?

1 Vote 1 ·

Why would it be different though between .NET 4.0 and .NET 4.5 and above? It works as it is in .NET 4.0

0 Votes 0 ·
Viorel-1 avatar image Viorel-1 needmoreraminbrain-3554 ·

Sometimes program defects are not manifested instantly.

0 Votes 0 ·
Show more comments

@needmoreraminbrain-3554
I downloaded the code project you provided and found a GUI application written in Java, but I cannot trigger the mouse click event, and the code cannot enter the HandleMouseClick method.
Could you please tell me what should I do to reproduce your problem?

0 Votes 0 ·

Hi

Sure, I have added a readme to the project here (ScreenReaderTest.Output). I will list them here also...

Prerequisites are…

*The 64-bit JRE needs to be installed -> (https://java.com/en/download/)
*Requires the jabswitch to be enabled via the command line -> C:\ProgramFiles\Java\<jre-version>\bin\jabswitch.exe -enable (ref: https://docs.oracle.com/en/java/javase/13/docs/specs/man/jabswitch.html)
*Can be tested using a Java-based application for example, yEd Graph Editor -> (https://www.yworks.com/products/yed)



Thanks

0 Votes 0 ·
TimonYang-MSFT avatar image TimonYang-MSFT needmoreraminbrain-3554 ·

@needmoreraminbrain-3554
I followed the above steps, but did not fully reproduce the problem.
When using .Net Framework 4, it works fine, but when using .Net Framework 4.5 and above, the program briefly gets stuck and then exits, and the code does not execute to the HandleMouseClick method.
I tried searching for similar problems to find a solution, but unfortunately I didn't find it. Some people say that running netsh winsock reset can solve similar problems, but I have tried and found that it is not suitable for the current problem.
Maybe you can ask an issue on GitHub to see if anyone knows more about this issue.

0 Votes 0 ·

0 Answers