Stephen M
Stephen M

Reputation: 71

System.AccessViolationException when calling C++ dll

I am trying to get the Java Access Bridge (2.02) to work with C# (.NET 3.5). I do have it working for some functions, but when I call a function that references a struct(getAccessibleContextInfo), I get this error message: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Here is my code:

[DllImport("Windowsaccessbridge.dll", CallingConvention = CallingConvention.Cdecl)]
internal extern static void Windows_run();

[DllImport("Windowsaccessbridge.dll", CallingConvention = CallingConvention.Cdecl)]
private extern static void releaseJavaObject(long vmID, IntPtr javaObject);

[DllImport("Windowsaccessbridge.dll", CallingConvention = CallingConvention.Cdecl)]
private extern static bool isJavaWindow(IntPtr window);

[DllImport("Windowsaccessbridge.dll", CallingConvention = CallingConvention.Cdecl)]
private extern static bool getAccessibleContextInfo(long vmID, IntPtr ac, out AccessibleContextInfo textInfo);

[DllImport("Windowsaccessbridge.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool getAccessibleContextFromHWND(IntPtr hwnd, out long vmID, out IntPtr ac);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct AccessibleContextInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string description;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string role;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string role_en_US;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string states;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string states_en_US;

[MarshalAs(UnmanagedType.I4)]
public int indexInParent;
[MarshalAs(UnmanagedType.I4)]
public int childrenCount;
[MarshalAs(UnmanagedType.I4)]
public int x;
[MarshalAs(UnmanagedType.I4)]
public int y;
[MarshalAs(UnmanagedType.I4)]
public int width;
[MarshalAs(UnmanagedType.I4)]
public int height;

[MarshalAs(UnmanagedType.Bool)]
public bool accessibleComponent;
[MarshalAs(UnmanagedType.Bool)]
public bool accessibleAction;
[MarshalAs(UnmanagedType.Bool)]
public bool accessibleSelection;
[MarshalAs(UnmanagedType.Bool)]
public bool accessibleText;
[MarshalAs(UnmanagedType.Bool)]
public bool accessibleInterfaces;
};

private void Form1_Load(object sender, EventArgs e)
{
Windows_run();
}

private void button1_Click(object sender, EventArgs e)
{
long vmID;
IntPtr ac;
if (getAccessibleContextFromHWND(mainWindowHwnd, out vmID, out ac))
{
MessageBox.Show("Got Context: " + vmID.ToString() + ", " + ac.ToString());
AccessibleContextInfo info;

if (getAccessibleContextInfo(vmID, ac, out info)) //this is where the error is thrown
{
MessageBox.Show("Got Context Info: " + info.name);
}
else
{
MessageBox.Show("Getting info failed");
}
}
else
{
MessageBox.Show("Accessing failed");
}
}

I don't think it's a problem with the java dll, as it works fine with the example C++ program that comes with the API.

I'm guessing from searching google that it's a problem with the way I am marshalling the struct AccessibleContextInfo, but I don't have a clue as to how to do it correctly.

Here is the way the struct is declared in the sample program's "AccessBridgePackages.h"

#define MAX_STRING_SIZE   1024
#define SHORT_STRING_SIZE   256

typedef struct AccessibleContextInfoTag {
    wchar_t name[MAX_STRING_SIZE];      // the AccessibleName of the object
    wchar_t description[MAX_STRING_SIZE];   // the AccessibleDescription of the object

    wchar_t role[SHORT_STRING_SIZE];    // localized AccesibleRole string
    wchar_t role_en_US[SHORT_STRING_SIZE];  // AccesibleRole string in the en_US locale
    wchar_t states[SHORT_STRING_SIZE];  // localized AccesibleStateSet string (comma separated)
    wchar_t states_en_US[SHORT_STRING_SIZE]; // AccesibleStateSet string in the en_US locale (comma separated)

    jint indexInParent;         // index of object in parent
    jint childrenCount;         // # of children, if any

    jint x;                 // screen coords in pixels
    jint y;                 // "
    jint width;             // pixel width of object
    jint height;                // pixel height of object

    BOOL accessibleComponent;           // flags for various additional
    BOOL accessibleAction;          //  Java Accessibility interfaces
    BOOL accessibleSelection;       //  FALSE if this object doesn't
    BOOL accessibleText;            //  implement the additional interface
                                                //  in question

    // BOOL accessibleValue;                // old BOOL indicating whether AccessibleValue is supported
    BOOL accessibleInterfaces;      // new bitfield containing additional interface flags

    } AccessibleContextInfo;

Any help is much appreciated!

Upvotes: 7

Views: 12001

Answers (1)

Orion Edwards
Orion Edwards

Reputation: 123622

Normally AccessViolations indicate you've messed up the PInvoke signature, but it looks like it is generally OK.

I can think of two things to try that may help

1: Potentially suspicious use of Charset.Unicode at the struct level.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct AccessibleContextInfo
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
    public string name;
    ...

One might expect that the CharSet=Unicode on the struct should "propagate" to it's members, but I've seen situations where it doesn't appear to do this. You could try specifying CharSet=Unicode on each string's MarshalAs attribute.

I'm not sure if this would make a difference though, given that the default marshalling of strings from .NET is already Unicode, but it may be worth a shot.

2: Perhaps try passing the AccessibleContextInfo struct as a ref parameter rather than an out parameter - eg

[DllImport("Windowsaccessbridge.dll", CallingConvention = CallingConvention.Cdecl)]
private extern static bool getAccessibleContextInfo(long vmID, IntPtr ac, ref AccessibleContextInfo textInfo);

Update: Also, as Hans Passant notes in a comment, remember long in C# is a 64-bit int, whereas in C/C++ it's 32-bit. Depending on the C++ function declarations, that may be wrong

Upvotes: 1

Related Questions