Check if SafeFileHandle has Data to read - c#

I'm working on a plug and play version POS for .NET Service Object. I have a FTDI device that has 2 devices on it. One is a MSR and the other is a RFID. For a quick catch up POS for .NET supports Plug in play if you give it a Hardware ID. The FTDI device's hardware id is QUADPORT\QUAD_SERIAL_INTERFACE. So on my Service Object (SO) I start it with this
[HardwareId("QUADPORT\\QUAD_SERIAL_INTERFACE")]
[ServiceObject(DeviceType.Msr, "FTDI Device", "MSR FTDI Device", 1, 8)]
public class FTDIDevice : Msr
{
...
}
In the open method I can query the DevicePath property and it is the correct DevicePath. As a quick test to make sure it was indeed talking to the serial port through the device path I opened a FileStream to this Device Path, then tried to open both of the Virtual Com Ports and sure enough one of them had "Access Denied". Awesome! My next test I did was to write to the RFID device since I know most all of the commands and how to format the byte array to communicate with the RFID device. In fact here is the code I use.
public override void Open()
{
string str = this.DevicePath;
handle_read = Kernel32.CreateExistingRWFile(str);
FS_read = new FileStream(handle_read, FileAccess.ReadWrite, 9);
Console.WriteLine("File Stream Created, Writing Open Sequence");
byte[] open = { 0xAA, 0xBB, 0x04, 0xFB, 0x00, 0x00, 0x03, 0xF8 };
FS_read.Write(open, 0, open.Length);
//byte[] receive = new byte[11];
//var something = FS_read.Read(receive, 0, receive.Length);
//Console.WriteLine(BitConverter.ToString(receive, 0, something));
//Close the FileStream and the SafeFileHandle
close();
}
private SafeFileHandle handle_read;
private FileStream FS_read;
The first time I ran this code I selected the MSR (there are 2 devices with the exact name both in device manager and in the microsoft test app, so I was guessing anyway) and sending that command gave me no response from the MSR (for obvious reasons, its not the RFID Device). According to the MSDN site the Read command blocks until at least one byte is read from my stream. That is a problem since the MSR does not respond to RFID commands. So essentially my program is deadlocked. I force closed the program then fired it up again and selected the RFID and the Read method did indeed return with a successful response from the RFID.
So my question is is how can I query the Stream to see if there was any response from my device? I've tried using the CanRead property (always returns true). I've tried using ReadByte to see if it would return -1 like the document said it is supposed to, but it too hangs. I tried using the Length property but it throws an exception saying that the Stream does not support seeking. It feels like I'm out of options, hence why I'm asking here. Any suggestions?
EDIT
Hans wants to see my how I created my handle
internal class Kernel32
{
internal static SafeFileHandle CreateExistingRWFile(string devicePath)
{
return CreateFile(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
}
internal const short FILE_ATTRIBUTE_NORMAL = 0x80;
internal const short INVALID_HANDLE_VALUE = -1;
internal const uint GENERIC_READ = 0x80000000;
internal const uint GENERIC_WRITE = 0x40000000;
internal const uint FILE_SHARE_READ = 0x00000001;
internal const uint FILE_SHARE_WRITE = 0x00000002;
internal const uint CREATE_NEW = 1;
internal const uint CREATE_ALWAYS = 2;
internal const uint OPEN_EXISTING = 3;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadFile(SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteFile(SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, ref uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
}
I was working with a BeginRead when I saw Hans suggest something, so far it EndRead always returns 0 :/
here is the code for that.
byte[] receive = new byte[11];
IAsyncResult iAR = FS_read.BeginRead(receive, 0, receive.Length, ASyncFileCallBack, receive);
byte[] wildGuess = (byte[])iAR.AsyncState;
Console.WriteLine("Guessing:{0}", BitConverter.ToString(wildGuess));
}
private void ASyncFileCallBack(IAsyncResult result)
{
byte[] buffer = (byte[])result.AsyncState;
int length = FS_read.EndRead(result);
if (length > 0)
Console.WriteLine("Response:{0}", BitConverter.ToString(buffer, 0, length));
else
Console.WriteLine("No Resposne");
}
The guessing always just spits out 11 0's almost immediatly. Shortly after "No Response" is put up on the Console.

Related

OpenFileById getting System.AccessViolationException when running as a user (Running as admin works)

I have some code which retrieves the 128bit NTFS Ids from files at specific paths. Then I attempted to retrieve the file path using this ID. The code works as long as when retrieving the paths I run as admin. This is not going to be possible in production. Unfortunately I am unable to call Marshal.GetLastWin32Error() because the System.AccessViolationException causes the application to completely crash. Below is the code to retrieve the paths.
public const int NO_PERMISSION = 0;
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
[InAttribute()] System.IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
[InAttribute()] System.IntPtr hTemplateFile
);
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle OpenFileById(
IntPtr hVolumeHint,
FILE_ID_DESCRIPTOR lpFileId,
uint dwDesiredAccess,
uint dwShareMode,
[InAttribute()] System.IntPtr lpSecurityAttributes,
uint dwFlagsAndAttributes
);
public enum _FILE_ID_TYPE
{
FileIdType = 0,
ObjectIdType,
ExtendedFileIdType,
MaximumFileIdType
}
[StructLayout(LayoutKind.Explicit)]
public struct FILE_ID_128
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
[FieldOffset(0)]
public byte[] Identifier;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct FILE_ID_DESCRIPTOR
{
public uint dwSize;
public _FILE_ID_TYPE Type;
public FILE_ID_128 ExtendedFileId;
}
public static string GetObjectPathFromId(string pathToSection, string hexId)
{
// We need a file handle to the drive we are looking in
using (SafeFileHandle handle = Methods.CreateFile(
pathToSection,
Constants.NO_PERMISSION,
Constants.NO_PERMISSION,
IntPtr.Zero,
Constants.OPEN_EXISTING,
0x02000000 | 0x00000080,
IntPtr.Zero))
{
// Build descriptor
FILE_ID_DESCRIPTOR descriptor = new FILE_ID_DESCRIPTOR();
descriptor.dwSize = (uint)Marshal.SizeOf(descriptor);
descriptor.Type = _FILE_ID_TYPE.ExtendedFileIdType;
descriptor.ExtendedFileId.Identifier = StringToByteArrayFastest(hexId);
using (SafeFileHandle actualFile = OpenFileById(handle.DangerousGetHandle(), descriptor,
Constants.NO_PERMISSION, Constants.NO_PERMISSION,
IntPtr.Zero, 0))
{
if (actualFile.IsInvalid)
return "";
// Buffer for the path, this should be way big enough
int sizeOfBuffer = 1024;
// Allocate a buffer
IntPtr pointer = Marshal.AllocHGlobal(sizeOfBuffer);
uint size = (uint)sizeOfBuffer;
uint returnValue = GetFinalPathNameByHandleW(actualFile.DangerousGetHandle(), pointer, size, 0);
// Copy it into a managed array
byte[] outPut = new byte[sizeOfBuffer];
Marshal.Copy(pointer, outPut, 0, (int)returnValue);
// Decode it
var str = Encoding.Unicode.GetString(outPut);
// Will be an empty string if the call fails
return str;
}
}
}
Again I want to specify - this code works perfectly when running as admin. The files are owned by the user, the user is able to delete, rename and move the files without any additional permissions.
Any help would be greatly appreciated thanks!
Edit1:
I implemented the answer found here How to handle AccessViolationException to successfully catch the exception. However even after doing this Marshal.GetLastWin32Error() returns 0. If anyone has any idea of how I can debug this type of issue please let me know.
Also it's still functioning when I run as admin, just not as a user.
Edit2:
Not sure if it's relevant - library with this code is building for .NET Standard 2.0 - Application using this library code is building for .NET Framework 4.6.2

C# Pinvoke invalid file handle

I'm having a problem with kernal32 Pinvoke functions as they keeps throwing an INVALID_FILE_HANDLE. The program reads the first sector of the current hard disk. I can't see what is wrong with the following code.
class Program
{
const uint GENERIC_READ = 0x80000000;
const uint FILE_SHARE_READ = 0x00000001;
const uint OPEN_EXISTING = 0x00000003;
const uint FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateFile(string Disk, uint Access, uint ShareMode, IntPtr SecurityAttributes, uint CreationDisposition, uint Flags, IntPtr TemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern uint SetFilePointer([In] SafeFileHandle Handle, [In] int DistanceToMove, [Out] out int DistanceToMoveHigh, [In] int MoveMethod);
[DllImport("kernel32.dll", SetLastError = true)]
unsafe public static extern int ReadFile(SafeFileHandle Handle, [Out] byte[] Buffer, int NumberOfBytesToRead, out int NumberOfBytesRead, IntPtr Overlapped);
unsafe public static void Main(string[] args)
{
string Drive = #"\\.\C";
int SectorSize = 512;
int Sector = 0;
int BytesRead, DistanceToMoveHigh;
byte[] Buffer = new byte[SectorSize];
SafeFileHandle Handle = CreateFile(Drive, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, IntPtr.Zero);
SetFilePointer(Handle, Sector * SectorSize, out DistanceToMoveHigh, 0);
ReadFile(Handle, Buffer, SectorSize, out BytesRead, IntPtr.Zero);
Console.WriteLine(Marshal.GetLastWin32Error()); // It gives 6 which translates to an INVALID_FILE_HANDLE error
Console.ReadKey();
}
}
Your call to CreateFile fails. Of course you cannot know that because you omitted any error checking. Read the documentation. Errors for all three functions that you call are signaled by the return value. Which you ignore.
Your call to CreateFile returns INVALID_HANDLE_VALUE. You need to test for that. When you encounter that, and only then, call GetLastWin32Error. Likely ERROR_ACCESS_DENIED will be returned then.
Passing FILE_FLAG_DELETE_ON_CLOSE is a mistake. Remove that flag.
I believe that the share flags must be FILE_SHARE_READ | FILE_SHARE_WRITE.
The file name must be #"\\.\C:" with a trailing colon.
And you will need the process to be executed elevated.
You use GetLastWin32Error in a wrong way.
The method that fails here is CreateFile and it returns an INVALID_HANDLE_VALUE (indicating that it failed). To determine what went wrong you have to call GetLastWin32Error directly after CreateFile.
When you call it after trying to read, the error is of course ERROR_INVALID_HANDLE (6) as you passed an invalid handle to ReadFile.
If you call GetLastWin32Error directly after the failing CreateFile you get error 2:
The system cannot find the file specified.
That is because the the drive name misses a :
string Drive = #"\\.\C:"; // <- add colon :
I tried with that drive name, but then got the error 32:
The process cannot access the file because it is being used by another process.
I keep trying to figure out how that can be handled...

Using SetCommTimeouts on parallel port fails

SetCommTimeouts and GetCommTimeouts are the function in kernel32 to set and get timeout when communicate with devices.
Now GetCommTimeouts works for me, but SetCommTimeouts returns error code 87 which indicates parameter error.
Now my question is whether this SetCommTimeouts works when it talks to a parallel port?
If so what can do I to fix it?
[DllImport("kernel32.dll")]
private static extern bool SetCommTimeouts(IntPtr hFile, ref LPCOMMTIMEOUTS lpCommTimeouts);
[DllImport("kernel32.dll ")]
private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);
[StructLayout(LayoutKind.Sequential)]
private struct LPCOMMTIMEOUTS
{
public UInt32 ReadIntervalTimeout;
public UInt32 ReadTotalTimeoutMultiplier;
public UInt32 ReadTotalTimeoutConstant;
public UInt32 WriteTotalTimeoutMultiplier;
public UInt32 WriteTotalTimeoutConstant;
}
private const uint GENERIC_WRITE = 0x40000000;
private const int OPEN_EXISTING = 3;
PHandler = CreateFile("LPT1", GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
IntPtr hnd = new System.IntPtr(PHandler);
LPCOMMTIMEOUTS lpcto = new LPCOMMTIMEOUTS();
Boolean bb = SetCommTimeouts(hnd, ref lpcto);
Console.WriteLine(bb); // get false here
Your declaration for CreateFile() is quite wrong and can never work in 64-bit mode. Since you don't do any of the required error checking and just keep plowing on, the next call that will fail is your SetCommTimeouts() call. It will complain about getting a bad handle value. Make it look like this instead:
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr CreateFile(
string FileName,
FileAccess DesiredAccess,
FileShare ShareMode,
IntPtr SecurityAttributes,
FileMode CreationDisposition,
FileAttributes FlagsAndAttributes,
IntPtr TemplateFile);
Proper error handling looks like this:
IntPtr hnd = CreateFile("LPT1", FileAccess.Write, FileShare.None, IntPtr.Zero,
FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
if (hnd == (IntPtr)-1) throw new System.ComponentModel.Win32Exception();
Additional failure modes are your machine not having a LPT1 port, parallel ports went the way of the dodo a long time ago. And the parallel port driver you have installed not supporting timeouts, it is normally only used for serial ports. Ask the vendor from which you obtained the parallel port hardware for support if necessary.

C# WriteFile(), unable to write to USB HID device

I am fairly new to C# as well as windows programming and I am attempting to establish communication between a USB HID device. I got the device path successfully using 'SetupDiGetDevicexxxxxx' and used 'CreateFile()' to get Handle. Below is my code.
public const uint FILE_SHARE_READ = 0x00000001;
public const uint FILE_SHARE_WRITE = 0x00000002;
public const int OPEN_EXISTING = 3;
public const uint GENERIC_READ = 0x80000000;
public const uint GENERIC_WRITE = 0x40000000;
CreateFile(string Devicepath)
{
HidHandle = CreateFile(Devicepath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
}
After obtaining Device handle info, I am calling write file functions as below.
Result = WriteFile(HidHandle, outputReportBuffer[], outputReportBuffer.Length, NumberOfBytesWritten, 0);
Outputbuffer is the byte array of length 8. For some reason, I was not able to write to USB HID device. "Result" is always zero. Any Help is appreciated. Also, can any one tell me how to verify that HidHandle is a valid or not. When I run the program I am getting it as "1124".
I did followed previous post on this type of question: Cannot communicate successfully with USB HID device using writefile(), but no help.
Below are the two menthods for create file and writefile.
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, uint lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, uint hTemplateFile );
[DllImport("kernel32.dll")]
static public extern bool WriteFile(int hFile, byte lpBuffer, int nNumberOfBytesToWrite, int lpNumberOfBytesWritten, int lpOverlapped)
You'll probably have to use a USB library, like this one
I am sure there is some reason why you are dipping into such low level Win32 functions, but I would advise if you just want to write a file somewhere just use the File class within .net.
If you are just wanting to write bytes then one of the simplest ways is to use File.WriteAllBytes() method.
http://msdn.microsoft.com/en-us/library/system.io.file.writeallbytes.aspx

AccessViolationException when using FileRead API

I am using the FileRead API.
I used Windows 7 x64 and my code worked good and correct.
Now I installed a new Windows 7 x86 and VS2008 teamsuit and .NET 2, 3+SP1+SP2, 3.5, 3.5.1.
I run my code as Administrator but still encounter the follwoing error:
AccessViolationException(Attempted to read or write protected memory. This is often an indication that other memory is corrupt.)
int nread = 0;
uint handle;
byte[] buff = new byte[1024];
string driveRoot = string.Concat("\\\\.\\", driveLetter);
uint hRoot = CreateFile(driveRoot,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero);
if (hRoot != -1)
handle = ReadFile(hRoot, buff, 1024, nread, new System.Threading.NativeOverlapped());
While I'm no C# guru, it appears to me that you're invoking ReadFile() with wrong parameters.
The 4th parameter must be a pointer to an integer that will receive the number of bytes read. You supply the integer itself (nread), not its address (&nread).
And unless you want asynchronous file I/O, the last parameter to ReadFile() must be a NULL pointer (or just 0).
See this example on MSDN.
I suspect that the main problem with your code is that you are requesting overlapped I/O but supplying a buffer that ceases to exist when ReadFile returns. It works on some systems an not others because the system decides whether or not to perform the operation asynchronously, and it may choose not to do async on one system and choose differently on another.
I'm sure you don't want overlapped I/O so you should simply pass NULL to the final parameter of ReadFile.
On the other hand, perhaps your code isn't working at all on the x64 system and never gets as far as an AV. Your handle types are mis-declared as 32 bit integers.
There are many other minor problems with your code. Here's an edited version of the code that corrects these errors. The P/invoke signatures were taken from pinvoke.net.
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadFile(
IntPtr hFile,
[Out] byte[] lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped
);
static void Main(string[] args)
{
string physicalDrive = #"\\.\PhysicalDrive0";
IntPtr hFile = CreateFile(
physicalDrive,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero
);
if (hFile.ToInt64() != INVALID_HANDLE_VALUE)
{
byte[] buff = new byte[1024];
uint nread;
if (ReadFile(hFile, buff, (uint)buff.Length, out nread, IntPtr.Zero))
Console.WriteLine("Read successful");
}
}
To summarise the errors in your code:
Incorrect use of 32 bit integers to store handles.
Your P/invoke declaration of ReadFile declares the lpNumberOfBytesRead incorrectly.
ReadFile does not return a handle, it returns a boolean indicating success of the function call.
Use of overlapped I/O which you do not want, and which cannot work with a marshalled byte[] buffer.
You must never call GetLastError from managed code (you did so in code shown in a comment). Instead call Marshal.GetLastWin32Error. The reasons are explained in the documentation for that method.

Categories

Resources