System.AccessViolationException when calling C++ dll - c#
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!
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
Related
Pass C# string to C++ pointer Char in typedef
I need pass a string of IP "192.168.1.1" from C# code to the typedef char pointer in DLL which written by c++. and I declare the char *pcAddrs like char *pcAddrs; //c++ [MarshalAs(UnmanagedType.LPStr)] public string ip //C# and declare open function //c++ int Open( COMMIF_INFO *pInfo ) //c# [DllImport("Open-IF.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int Open(COMMIFINFO info); Now i get an error of wrong parameter when press button1 to trigger the Open function. It is the string and char pointer wrong? Thanks in advance. C++ DLL info typedef struct CommIfInfo { char *pcAddrs; long lPortNo; long lRetry; long lSendTimeOut; long lCommSide; long lMode; long lKind; } COMMIF_INFO; //Function need to call. int Open( COMMIF_INFO *pInfo ) Code in C# // DLL import [DllImport("Open-IF.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int Open(COMMIFINFO info); // Structure [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct COMMIFINFO { [MarshalAs(UnmanagedType.LPStr)] public string ip; public int PortNo; public int Retry; public int SendTimeOut; public int CommSide; public int Mode; public int Kind; } private void button1_Click(object sender, EventArgs e) { string _ip = "192.168.1.1"; COMMIFINFO info = new COMMIFINF(); info.ip = _ip; info.Kind = 1; int ErrCode = Open(info); }
Program will get wrong parameter error if passing the COMMIFINFO using method below [DllImport("Open-IF.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int Open(COMMIFINFO info); Found is because the parameter pass to open function need by reference. So program working if use "in" to pass parameter with reference. [DllImport("Open-IF.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int Open(in COMMIFINFO info);
SetupVerifyInfFile - The parameter is incorrect. How to use C# pinvoke
I have problem with calling/implementing SetupVerifyInfFile. When I call the VerifyInfFile function, I receive Exception "The parameter is incorrect". [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool SetupVerifyInfFile( string FileName, IntPtr AltPlatformInfo, ref IntPtr InfSignerInfo); [StructLayout(LayoutKind.Sequential)] public struct SP_INF_SIGNER_INFO { public int CbSize; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string CatalogFile; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string DigitalSigner; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string DigitalSignerVersion; } public static void VerifyInfFile(string infPath) { SP_INF_SIGNER_INFO infSignerInfo = default; infSignerInfo.CbSize = Marshal.SizeOf(infSignerInfo); var infSignerPtr = Marshal.AllocHGlobal(infSignerInfo.CbSize); try { Marshal.StructureToPtr(infSignerInfo, infSignerPtr, false); if (!SetupVerifyInfFile(infPath, IntPtr.Zero, ref infSignerPtr)) throw new Exception("Error calling SetupVerifyInfFile().", new Win32Exception(Marshal.GetLastWin32Error())); } finally { Marshal.FreeHGlobal(infSignerPtr); } }
There are four obvious mistakes that I can see: The third parameter you pass as ref IntPtr InfSignerInfo. It is a mistake to pass this as ref. As documented, this parameter is a pointer to the struct. So you need to remove the ref. You don't specify a character set for SP_INF_SIGNER_INFO and so will get the default character set which is ANSI. That doesn't match CharSet.Auto in your function declaration. You use the wrong value for CbSize. You provide the size of a pointer, but you need to provide the size of the structure, Marshal.SizeOf(typeof(SP_INF_SIGNER_INFO)). You have used an incorrect value for MAX_PATH. The correct value is 260, and not 256.
c#-Call SetupDiGetDriverInfoDetail API to get driver info but pop up error
I'm using setup.api in c#. There are all work successfully but SetupDiGetDriverInfoDetail. When the program execute this line, it will pop up the exception message SetupDiGetDriverInfoDetail' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target. I think the error is because of the declare signature: [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)] internal struct SP_DRVINFO_DETAIL_DATA { public Int32 cbSize; public System.Runtime.InteropServices.ComTypes.FILETIME InfDate; public Int32 CompatIDsOffset; public Int32 CompatIDsLength; public IntPtr Reserved; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public String SectionName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public String InfFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public String DrvDescription; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] public String HardwareID; }; [DllImport("setupapi.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)] static extern uint SetupDiGetDriverInfoDetail( IntPtr DeviceInfoSet, SP_DEVINFO_DATA DeviceInfoData, SP_DRVINFO_DATA DriverInfoData, ref SP_DRVINFO_DETAIL_DATA DriverInfoDetailData, Int32 DriverInfoDetailDataSize, ref Int32 RequiredSize); And I use these code from the following: private string GetDrivInfoDetailHWID() { SP_DRVINFO_DETAIL_DATA driInfoDetailData = new SP_DRVINFO_DETAIL_DATA(); Int32 requiredSize = 0; driInfoDetailData.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(SP_DRVINFO_DETAIL_DATA)); while(SetupDiGetDriverInfoDetail(_deviceHandle, _devInfoData, _driInfoData, ref driInfoDetailData, requiredSize, ref requiredSize)==0) { int e = Marshal.GetLastWin32Error(); } return driInfoDetailData.HardwareID; } Please help me to solve this problem, I'll really appreciate about that.
SetupDiEnumDriverInfo always return with Error 259 (No more Data available)
I am trying to access information about drivers associated with devices in C# utilizing the win32 APIs. I have managed to enable/disable devices (so the handles I am retrieving seem OK), however I have no luck when trying to call SetupDiEnumDriverInfo. This is the code I am using: private List<String> ListCompatibleDrivers(IntPtr hDevInfo, SP_DEVINFO_DATA devInfoData) { List<String> result = new List<String>(); try { SP_DRVINFO_DATA drvInfo = new SP_DRVINFO_DATA(); for (int i = 0; SetupDiEnumDriverInfo(hDevInfo, devInfoData, SPDIT_CLASSDRIVER, i, drvInfo); i++) { result.Add(drvInfo.Description); } if (result.Count < 1) { result.Add(Marshal.GetLastWin32Error().ToString()); } return result; } catch { throw; } } Where the parameters can be assumed to be okay (as I said, other methods from the setup API use them successfully). These are the struct and the DllImport which might be corrupt: [StructLayout(LayoutKind.Sequential)] public class SP_DRVINFO_DATA { public Int32 cbSize; public Int32 driverType; public UIntPtr reserved; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public String description; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public String mfgName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public String providerName; public FILETIME driverDate; public Int64 driverVersion; }; [DllImport("setupapi.dll", SetLastError = true)] public static extern bool SetupDiEnumDriverInfo(IntPtr lpInfoSet, SP_DEVINFO_DATA deviceInfoData, UInt32 driverType, Int32 memberIndex, SP_DRVINFO_DATA driverInfoData); The API call returns with false immediately, the Marshal.GetLastWin32Error().ToString() returns 259, which is ERROR_NO_MORE_ITEMS. I just don't get it, and my hopes are high I am just making some stupid mistake that I am not able to see because I have read hardly anything but msdn lately, and it gets incredibly tiring... Any help is greatly appreciated, thanks a lot!
Well off the top I can tell you you don't match the function signature which should be: [DllImport("setupapi.dll", SetLastError = true, charset=Charset.Unicode)] [return:MarshalAs(UnmanagedType.Bool)] private static extern bool SetupDiEnumDriverInfo( [In] IntPtr lpInfoSet, [In, Optional] SP_DEVINFO_DATA deviceInfoData, [In] UInt32 driverType, [In] Int32 memberIndex, [Out] out SP_DRVINFO_DATA driverInfoData); That out is important as it specifies that it needs to pull back out the data from the PInvoke.
There are several problems with the struct, the most annoying being that one has to specify pack=4 so the native code will find the correct entry points. This works: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)] public struct SP_DRVINFO_DATA { public int cbSize; public int DriverType; public UInt32 Reserved; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string Description; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string MfgName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string ProviderName; public System.Runtime.InteropServices.ComTypes.FILETIME DriverDate; public long DriverVersion; } Of course it is a god idea to actually prefix the P/Invokes with Charset=Charset.Unicode, too.
Here are the API and struct definitions that both worked on x64 and x86. I am adding also SetupDiGetDriverInfoDetail, good chance that you'll also need it. API: DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool SetupDiGetDriverInfoDetail( IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, ref SP_DRVINFO_DATA DriverInfoData, ref SP_DRVINFO_DETAIL_DATA DriverInfoDetailData, Int32 DriverInfoDetailDataSize, ref Int32 RequiredSize); [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool SetupDiEnumDriverInfo( IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, int DriverType, int MemberIndex, ref SP_DRVINFO_DATA DriverInfoData); structs: #if !WIN64 [StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Unicode)] #else [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] #endif public struct SP_DRVINFO_DATA { public int cbSize; public uint DriverType; public UIntPtr Reserved; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string Description; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string MfgName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string ProviderName; public System.Runtime.InteropServices.ComTypes.FILETIME DriverDate; public ulong DriverVersion; } #if !WIN64 [StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Unicode)] #else [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] #endif public struct SP_DRVINFO_DETAIL_DATA { public Int32 cbSize; public System.Runtime.InteropServices.ComTypes.FILETIME InfDate; public Int32 CompatIDsOffset; public Int32 CompatIDsLength; public IntPtr Reserved; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public String SectionName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public String InfFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public String DrvDescription; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)] public String HardwareID; };
Detecting Audio Input & output devices connected to system
I want to findout how many audio input and output devices connected to my system in C#.net Please suggest any. Thanks.
You need to access the necessary Windows API functions. This class should get you started - Win32.GetSoundDevices returns a list of device names. Look up WAVEOUTCAPS in the Windows SDK for details of the other information you can get. public class Win32 { [DllImport("winmm.dll", SetLastError=true)] static extern uint waveOutGetNumDevs(); [DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern uint waveOutGetDevCaps(uint hwo,ref WAVEOUTCAPS pwoc,uint cbwoc); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct WAVEOUTCAPS { public ushort wMid; public ushort wPid; public uint vDriverVersion; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string szPname; public uint dwFormats; public ushort wChannels; public ushort wReserved1; public uint dwSupport; } public static string [] GetSoundDevices() { uint devices = waveOutGetNumDevs(); string [] result = new string[devices]; WAVEOUTCAPS caps = new WAVEOUTCAPS(); for(uint i = 0; i < devices; i++) { waveOutGetDevCaps(i, ref caps, (uint)Marshal.SizeOf(caps)); result[i] = caps.szPname; } return result; } }