Trouble calling SystemParametersInfo - c#

Recently I've been trying to call the SystemParametersInfo method from managed code, without any success.
The problem is that, after calling the method, the method returns false (indicating failure), however GetLastError (retrieved by Marshal.GetLastWin32Error()) is 0.
I tried to invoke the method from C++ as a test (with the exact same parameters), and it works completely fine from there.
The P/Invoke declaration of the method is this:
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool SystemParametersInfo(SPI uiAction, int uiParam, ref STICKYKEYS pvParam, SPIF fWinIni);
internal struct STICKYKEYS
{
public int cbSize;
public int dwFlags;
}
And the invocation is as follows:
NativeMethods.STICKYKEYS stickyKeys = default(NativeMethods.STICKYKEYS);
bool result = NativeMethods.SystemParametersInfo(NativeMethods.SPI.SPI_GETSTICKYKEYS, StickyKeysSize, ref stickyKeys, 0);
int error = Marshal.GetLastWin32Error();
The SPI.SPI_GETSTICKYKEYS is0x003A (as seen on MSDN).
Here the result is false, and the error returned is 0.
Also this is complied as a 64-bit executable if that matters.
I'm completely at my wit's end, do you have any idea what I might be doing wrong?

As GSerg pointed it out to me, my issue was that I need to pass in the size of the struct both directly as a parameter, and as the cbSize member of the struct that I passed in by reference.
The correct code is:
int stickyKeysSize = Marshal.SizeOf(typeof (NativeMethods.STICKYKEYS));
NativeMethods.STICKYKEYS stickyKeys = new NativeMethods.STICKYKEYS {cbSize = stickyKeysSize, dwFlags = 0};
bool result = NativeMethods.SystemParametersInfo(NativeMethods.SPI.SPI_GETSTICKYKEYS, stickyKeysSize, ref stickyKeys, 0);
if (!result) throw new System.ComponentModel.Win32Exception();
return stickyKeys;

Related

Calling C++ dll Method from C#

I am trying to call method available in C++ dll
HRESULT WINAPI TestMethod(
_Out_ BOOL *isSuccess,
_In_opt_ DWORD UsernmaeLength,
_Out_opt_ LPWSTR userName );
Wrapper method Which I have written in C# looks like this
[DllImport("Test.dll", CharSet = CharSet.Unicode, SetLastError = true ,CallingConvention = CallingConvention.StdCall)]
public static extern int TestMethod (
IntPtr isSuccess,
[In, Optional] int UsernmaeLength,
out string userName
);
I am calling this method in program
Wrapper. TestMethod (isSuccess, 200, out userName);
I am getting System.AccessViolationException
tried changing the C# wrapper method with
[DllImport("Test.dll", CharSet = CharSet.Unicode, SetLastError = true ,CallingConvention = CallingConvention.StdCall)]
public static extern int TestMethod (
bool isSuccess,
[In, Optional] int UsernmaeLength,
out string userName
);
//Caller
bool isSuccess = false;
Wrapper. TestMethod (isSuccess, 200, out userName);
Could you please help me to understand what I am doing wrong here?
_In_opt_ DWORD UsernmaeLength
The SAL annotation is not very useful. What it probably is trying to tell you is that you can pass NULL for the string buffer argument. In which case what you pass for the buffer length doesn't matter. It is not actually [Optional], you'd consider simply passing 0 if you really don't want a string back.
The 3rd argument cannot be String or out since that is an immutable type and the function wants to write into the buffer you pass. It must be StringBuilder. The 2nd argument must be its Capacity. Be sure to make the StringBuilder big enough to fit a user name. If it is not then it isn't very obvious what will happen, hopefully the function then just returns an error code instead of silently truncating string. Test that.
The 1st argument is bool passed by reference, [Out] out bool. Not very likely that it SetLastError, that is only done by winapi functions. It already returns an error code embedded in the HResult. A value less than 0 is an error. Stdcall is the default. Summarizing:
[DllImport("Test.dll", CharSet = CharSet.Unicode)]
public static extern int TestMethod (
[Out] out bool isSuccess,
int userNameLength,
StringBuilder userName
);
Called as:
bool success;
var name = new StringBuilder(666);
int hr = TestMethod(out success, name.Capacity, name);
if (hr < 0) Marshal.ThrowExceptionForHR(hr);
If you still have trouble then you need the help of the author of this code if you cannot debug it yourself. Have a small repro available so he can easily repro the issue.

c# Calling method using P/Invoke causing AccessViolation Exception

I interfacing with a device using P/Invoke. But I've come stuck at calling the following method:
BOOL __stdcall voGetFirmwareVersion(IN OUT LPTSTR lpVersion, IN OUT DWORD *dwSize);
In my wrapper I have called the method like so:
[DllImport(DLL_LOCATION, CharSet = CharSet.Ansi)]
private static extern Boolean voGetFirmwareVersion(string s, uint d);
I have attempted to change the inputs to Out/Ref and tried adding the [In,Out] attributes but I am constantly getting a AccessViolation Exception. Can anyone point me in the right direction? I've been at this for a couple of hours now and google hasn't been able to put me straight
dwSize is passed by address, so it should be a ref parameter.
BOOL return type must be marshaled if any nonzero value may come as a true value
I am not sure if you really want to use Ansi charset. Try Auto or Unicode.
LPStr and StdCall are not a must, they are the default settings.
So try this:
[DllImport(DLL_LOCATION, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool voGetFirmwareVersion([MarshalAs(UnmanagedType.LPTStr)] string s, ref uint d);

Memory access violation while passing void* param to DLL function

I'm adding new disk device to system from my C# code, so I want to call
[System.Runtime.InteropServices.DllImport("Shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public extern static void SHChangeNotify(long wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
like below
MyWin32Functions.SHChangeNotify(0x00000100/*ADDRIVE*/, 0x0005/*PATHW*/, driveLetter, IntPtr.Zero);
dwItem1 is void* and we should pass a wchar_t* (pointing to null terminated string) containing drive root in this case; so driveLetter above is
string letter = "Z:\\";
byte[] data = Encoding.Default.GetBytes(letter);
byte[] zdata = new byte[data.Length + 1];
data.CopyTo(zdata, 0);
IntPtr p = System.Runtime.InteropServices.Marshal.AllocHGlobal(zdata.Length);
System.Runtime.InteropServices.Marshal.Copy(zdata, 0, p, zdata.Length);
(my code almost same as code in similiar case: How to call SHChangeNotify from C# without error 14007
but I get System.AccessViolationException)
Any suggestions what am I doing wrong?
The first parameter in your interop signature should be an int, not a long. Though the Win32 function is declared as LONG, a LONG in Win32 is 32-bit.
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
public extern static void SHChangeNotify(int wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
This MSDN article shows the common mapping between Win32 types an .NET types for Platform Invoke.

DLLImport -> how to handle a HANDLE in C#

in my C# code I want to import a C++ DLL. I use the dllimport and it works fine with a some of the functions. But in one function I get a HANDLE which I need later to call another function.
[DllImport("SiUSBXp.dll")]
public static extern int SI_Open(UInt32 deviceNum,ref IntPtr devHandle ); // this function gets the HANDLE
[DllImport("SiUSBXp.dll")]
public static extern int SI_Write([In]IntPtr devHandle, [In, Out] byte[] inputByte, UInt32 size,ref UInt32 bytesWritten); // this function needs the HANDLE
In my code these functions are called like this:
IntPtr devHandle = new IntPtr();
UInt32 bytesWritten = new UInt32();
byte[] byteArr = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
SI_Open(0, ref devHandle);
SI_Write(devHandle, byteArr, 10, ref bytesWritten);
If I do it like this I get an "System.AccessViolationException". I searched here and in the internet but didnt find a specific answer. How do I use the IntPtr correctly, so it works?
Best Regards
Toby
Your SI_Write function looks quite like Windows Kernel32's WriteFile.
So, I would do this:
[DllImport("SiUSBXp.dll", SetLastError = true)]
static extern int SI_Open(uint dwDevice, ref IntPtr cyHandle);
[DllImport("SiUSBXp.dll", SetLastError = true)]
static extern int SI_Write(IntPtr cyHandle, byte[] lpBuffer,
uint dwBytesToWrite, out uint lpdwBytesWritten);
EDIT: I found this documentation USBXPRESS® PROGRAMMER’S GUIDE on the web, and it states that the SI_Write prototype looks actually much closer to WriteFile than I thought. The doc states this:
SI_STATUS SI_Write (HANDLE Handle, LPVOID Buffer, DWORD NumBytesToWrite,
DWORD *NumBytesWritten, OVERLAPPED* o = NULL)
It means the .NET prototype should be this instead:
[DllImport("SiUSBXp.dll")]
static extern int SI_Write(IntPtr Handle, byte[] Buffer,
uint NumBytesToWrite, out uint NumBytesWritten, IntPtr o);
o is optional so you can pass IntPtr.Zero.
You are making a classic C programmer mistake, you don't check the return value of the functions. Which tells you whether or not the function failed. A likely scenario is that SI_Open() returned a failure code. You ignore it and use the uninitialized handle value anyway. A kaboom is not unusual.
The next possible mistake is that you don't use the CallingConvention property in the [DllImport] statement. It is fairly likely to be needed, Cdecl is the default unless the native function is declared with __stdcall. Also an excellent way to invoke a kaboom. If you still have trouble then you are going to have to debug the native code.
Btw, you get rid of the awkward syntax by using out instead of ref. In both functions.
[DllImport("SiUSBXp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SI_Open(UInt32 deviceNum, out IntPtr devHandle );
try this:
[DllImport("SiUSBXp.dll")]
public static extern int SI_Open(UInt32 deviceNum, ref IntPtr devHandle); // this function gets the HANDLE
[DllImport("SiUSBXp.dll")]
public static extern int SI_Write(IntPtr devHandle, ref byte[] inputByte, UInt32 size, ref UInt32 bytesWritten); // this function needs the HANDLE
EDIT:
#Hans Passant is right. This is the correct way to pass a byte[] into a LPVOID parameter. ref used to coerce an object into LPVOID, but isn't needed for an array. What happens when you try this?
[DllImport("SiUSBXp.dll")]
public static extern int SI_Write(IntPtr devHandle, byte[] inputByte, UInt32 size, ref UInt32 bytesWritten); // this function needs the HANDLE
Did you try the answer #Simon Mourier gave? He was first to provide this declaration and his answer deserves to be accepted.
bad: static extern void DoStuff(**byte[] inputByte**);
good: static extern void DoStuff(**[In, MarshalAs(UnmanagedType.LPArray)] byte[] inputByte**);

P/Invoking CreateToolhelp32Snapshot failing in Compact Framework

Hey, im doing a little app for my smart phone, using Windows Mobile 6. I'm trying to get all currently running processec, but method CreateToolhelp32Snapshot always returns -1. So now im stuck. I tried to get error with invoking GetLastError() method, but that method returns 0 value.
Here is a snippet of my code.
private const int TH32CS_SNAPPROCESS = 0x00000002;
[DllImport("toolhelp.dll")]
public static extern IntPtr CreateToolhelp32Snapshot(uint flags,
uint processid);
public static Process[] GetProcesses()
{
ArrayList procList = new ArrayList();
IntPtr handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if ((int)handle > 0)
{
try
{
PROCESSENTRY32 peCurr;
PROCESSENTRY32 pe32 = new PROCESSENTRY32();
// get byte array to pass to API call
byte[] peBytes = pe32.ToByteArray();
// get the first process
int retval = Process32First(handle, peBytes);
First, your handle check is wrong. It's common for the high bit to be on in a handle, causing it to look like a negative number when cast to a signed int. You should be checking that is isn't NULL (0) or INVALID_HANDLE_VALUE (-1 / 0xffffffff).
You shouldn't be "invoking GetLastError" but calling Marshal.GetLastWin32Error()
You've not set the SetLastError attribute in the P/Invoke declaration. In C# it defaults to false, in VB it defaults to true.
Where's your PROCESS32 implementation? The docs clearly state that the dwLength member must be set before the call and it's not clear here if that's happening.
As a side note, the Smart Device Framework's OpenNETCF.ToolHelp namespace has all of this implemented and working (in case you'd rather not reinvent the wheel).
Instead of
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
use
private const int TH32CS_SNAPNOHEAPS = 0x40000000;
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPNOHEAPS, 0);
By default CreateToolhelp32Snapshot will try to snapshot the heaps and that can cause an out of memory error.
Found this at https://social.msdn.microsoft.com/Forums/en-US/e91d845d-d51e-45ad-8acf-737e832c20d0/createtoolhelp32snapshot-windows-mobile-5?forum=vssmartdevicesnative and it solved my problem.
If you're not seeing valid "last error" information, perhaps you might need to add the "SetLastError" attribute on the API's DllImport attribute (MSDN reference with code examples). According to the documentation of this attribute, you should set SetLastError to...
...true to indicate that the callee will
call SetLastError; otherwise, false.
The default is false.
The runtime marshaler calls
GetLastError and caches the value
returned to prevent it from being
overwritten by other API calls. You
can retrieve the error code by calling
GetLastWin32Error
As for the API failure you're seeing, I don't spot anything obvious offhand; the code you have seems very similar to the sample code here.
This is the proper implementation based on the MSDN documentation
private const int INVALID_HANDLE_VALUE = -1;
[Flags]
private enum SnapshotFlags : uint
{
HeapList = 0x00000001,
Process = 0x00000002,
Thread = 0x00000004,
Module = 0x00000008,
Module32 = 0x00000010,
Inherit = 0x80000000,
All = 0x0000001F,
NoHeaps = 0x40000000
}
[DllImport("toolhelp.dll"]
private static extern IntPtr CreateToolhelp32Snapshot(SnapshotFlags dwFlags, int th32ProcessID);
[StructLayout(LayoutKind.Sequential)]
public struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szExeFile;
};
IntPtr hSnap = CreateToolhelp32Snapshot(SnapshotFlags.Process, 0);
if (hSnap.ToInt64() != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 procEntry = new PROCESSENTRY32();
procEntry.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));
if (Process32First(hSnap, ref procEntry))
{
do
{
//do whatever you want here
} while (Process32Next(hSnap, ref procEntry));
}
}
CloseHandle(hSnap);
Most importantly is this line, because you must set the size of the procEntry:
procEntry.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));

Categories