I tried this:
public struct SHFILEINFOW
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260 * 2)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80 * 2)]
public string szTypeName;
}
[DllImport("shell32.dll")]
public static extern IntPtr SHGetFileInfoW(IntPtr pIDL, uint dwFileAttributes, out SHFILEINFOW psfi, uint cbFileInfo, SHGFI uFlags);
String DisplayName = shInfoW.szDisplayName;
But DisplayName contain only the first char
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260 * 2)]
ByValTStr means "same as the class or structure". But you didn't specify the CharSet attribute for the structure. It defaults to CharSet.Ansi so the string is getting marshaled as though it was a 8-bit character string. Since the real string is Unicode, you'll indeed get very high odds for only getting the first character. Fix:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHFILEINFOW {
// etc...
}
Related
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.
I'm working with SafeCom (print server) and figured out that they have a Administrator DLL Programmer’s Manual. As a proof of concept I wanted to make a small C# application that sends and retrieves data using this library. I've managed to insert a user object by calling LoginByLogon() followed by AddUser().
However, I'm not able to retrieve/return a user struct by calling LoginByLogon() followed by GetUser. I can see that ptrTemp is set (not null), but all properties in newStructure are still null. I don't fully understand PInvoke yet, so I hope someone here can enlighten me a little. :)
I'm for now importing the following:
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int sc_LoginByLogon(string szIpAddr, string szUserName, string szPassword);
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int sc_Logoff();
[DllImport("scAPI.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern int sc_GetUserInfoByUserLogon(string szUserLogon, out IntPtr ppUserInfo);
[DllImport("scAPI.dll", CallingConvention = CallingConvention.Cdecl)]
static extern short sc_AddUser(ref sUserInfoApiV4 ppUserInfo, double fAccount, double FLowLimit, ref int pNewId);
Define my structs:
struct SYSTEMTIME
{
short wYear;
short wMonth;
// short wDayOfWeek;
short wDay;
short wHour;
short wMinute;
short wSecond;
short wMilliseconds;
public static implicit operator SYSTEMTIME(DateTime time)
{
return new SYSTEMTIME
{
wYear = (short)time.Year,
wMonth = (short)time.Month,
wDay = (short)time.Day,
// wDayOfWeek = (short)time.DayOfWeek,
wHour = (short)time.Hour,
wMinute = (short)time.Minute,
wSecond = (short)time.Second,
wMilliseconds = (short)time.Millisecond
};
}
public static implicit operator DateTime(SYSTEMTIME time)
{
return new DateTime(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds);
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Card
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string m_szCardNo; //Card number
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string m_szPINCode; //PIN code - zero terminated
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string m_szPUKCode; //PUK code - zero terminated
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public struct sUserInfoApiV4
{
[MarshalAs(UnmanagedType.I4)]
public Int32 m_nStructLength; //Length of the struct including this field
[MarshalAs(UnmanagedType.I2)]
public short m_nVersion; //Version Number (set to 4)
[MarshalAs(UnmanagedType.I2)]
public short m_nSubVersion; //Version of Reserved (set to 0)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] m_achReserved; // Get room for m_nAccessRights
[MarshalAs(UnmanagedType.I2)]
public short m_nUserType; //USER_STRUCT (uses as UserType - BitFields)
[MarshalAs(UnmanagedType.I4)]
public int m_nUserId; //Unique id identifying the user
public int m_nSubRights; // Sub version 2
[MarshalAs(UnmanagedType.I4)]
public int m_nNotUsed1; // Sub version 5
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzFullName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 101)]
public string m_wzEMail;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 21)]
public string m_wzUserLogon; //Asc-ii string identifying the user
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public string m_wzPassword; //Asc-ii string password
public Card card;
[MarshalAs(UnmanagedType.I2)]
public short m_nLogonFails; //Number of failed logins
[MarshalAs(UnmanagedType.I2)]
public short m_bUserLocked; //Unlocked(0), Locked(1)
[MarshalAs(UnmanagedType.I2)]
public short m_bUserDisabled; //Not Disable(0), Open(1)
[MarshalAs(UnmanagedType.I2)]
public short m_bAvoidPin; //Pin Enabled(0), Pin disabled (1)
[MarshalAs(UnmanagedType.I2)]
public short m_bPrintAll; //Print All when card is slid - Yes(1) No(0)
[MarshalAs(UnmanagedType.I2)]
public short m_bCardOpen; //PIN code assigned(0), Awaiting pin assignment/Outstanding PUK(1)
[MarshalAs(UnmanagedType.I2)]
public short m_nBillingModel; //None(0), BillingDialog(1)
[MarshalAs(UnmanagedType.I2)]
public short m_nAccountingModel; //None(0), Tracking(1), Pay(2)
[MarshalAs(UnmanagedType.I4)]
public int m_nUserRights; // None(0)
[MarshalAs(UnmanagedType.I2)]
public short m_bAllowEncryption; // The user uses encryption YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bAllowCheckPrinting; // The user is allowed to receives checks YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bAllowPmail; // The user is allowed distribution YES(1) NO(0)
[MarshalAs(UnmanagedType.U1)]
bool m_bDenyRetain; // User allowed to retain his documents. New in SubVersion 3
public int m_lCreationDate;
public int m_lLastLogin;
[MarshalAs(UnmanagedType.I4)]
public int m_nServerId; // HomeServer of this user.
[MarshalAs(UnmanagedType.I4)]
public int m_nDomainId; // New in Subversion 1 - Reference to the windows domain the user are located in
[MarshalAs(UnmanagedType.I4)]
public int m_nTreeNodeId;
[MarshalAs(UnmanagedType.I4)]
public int m_nNid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
public string m_wzCostCode; // Cost center, new in SubVersion 3
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SCardInfoApiV4
{
[MarshalAs(UnmanagedType.I4)]
public Int32 m_nStructLength; // Length of entire struct
[MarshalAs(UnmanagedType.I2)]
public short m_nVersion; // Version (short)
[MarshalAs(UnmanagedType.I2)]
public short m_nSubVersion; // Subversion of reserved, set to 1 (short)
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] m_achReserved; // Reserved for protocol expansion (fixed char)
[MarshalAs(UnmanagedType.I4)]
public int m_nCardId;
[MarshalAs(UnmanagedType.I4)]
public int m_nUserId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string m_achCardNo; // originalt fixed char [40]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
public string m_achReservedPinCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string m_achReservedPukCode;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)]
public string m_achDescription;
[MarshalAs(UnmanagedType.I4)]
int m_nCreatorId;
SYSTEMTIME m_sCreated;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public string m_nUseLifeTime; //originalt char
SYSTEMTIME m_sLifeStart;
SYSTEMTIME m_sLifeEnd;
[MarshalAs(UnmanagedType.I4)]
int m_nSourceId; // Sub version 1
}
And finally my methods:
public int LoginByLogon(string ipAddr, string userLogon, string password)
{
return sc_LoginByLogon(ipAddr, userLogon, password);
}
public int Logoff()
{
return sc_Logoff();
}
public sUserInfoApiV4 GetUser(string userLogon)
{
sUserInfoApiV4 tmpUserInfo = new sUserInfoApiV4();
IntPtr ptrTemp = Marshal.AllocHGlobal(Marshal.SizeOf(tmpUserInfo));
sc_GetUserInfoByUserLogon(userLogon, out ptrTemp);
Marshal.StructureToPtr(tmpUserInfo, ptrTemp, false);
sUserInfoApiV4 newStructure;
newStructure = (sUserInfoApiV4)Marshal.PtrToStructure(ptrTemp, typeof(sUserInfoApiV4));
return newStructure;
}
public unsafe int AddUser(string usercode, string email, string pinCode, string fullName)
{
sUserInfoApiV4 sUserstruct = new sUserInfoApiV4();
int sizeOfstruct = Marshal.SizeOf(typeof(sUserInfoApiV4));
IntPtr pSUserstruct = Marshal.AllocHGlobal(sizeOfstruct); //Make note of memory used
Marshal.StructureToPtr(sUserstruct, pSUserstruct, false);
sUserstruct.m_wzUserLogon = usercode;
sUserstruct.card.m_szCardNo = usercode; //Card number same as logon
sUserstruct.card.m_szPINCode = pinCode;
sUserstruct.m_wzFullName = fullName;
sUserstruct.m_wzEMail = email;
sUserstruct.m_nUserRights = 3; //Standard user rights
sUserstruct.m_nAccountingModel = 2; //Pay user
sUserstruct.m_nVersion = 4; //Safecom version
sUserstruct.m_nStructLength = sizeOfstruct;
sUserstruct.m_bAvoidPin = 1; // Disable PIN
int userId = 0; //Value of variable not used, just to initialize the variable. Used as pointer for return value. Index number for user in db
int* userIndex = &userId;
int nRes = sc_AddUser(ref sUserstruct, 0, 0, ref *userIndex);
Marshal.FreeHGlobal(pSUserstruct); // Release memory
pSUserstruct = IntPtr.Zero; // used by pointer.
return nRes;
}
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.
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;
};
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