Atomically check if a file exists and open it - c#

I am trying to open a FileStream only if the file exists, and do something else otherwise (not create it, so FileMode.OpenOrCreate is not applicable).
However, simply checking File.Exists before creating the FileStream will not prevent race conditions as the file could be deleted before the FileStream has a chance to be created, in which case a FileNotFoundException would be thrown.
Is there a way to achieve this "natively", without resorting to the following try catch wrapper:
/// <returns>false if the file does not exists, true otherwise.</returns>
public static bool TryOpenFileStreamIfExists(string filePath, FileAccess fileAccess, FileShare fileShare, out FileStream fs, FileOptions fileOptions = FileOptions.None) {
try {
if (!File.Exists(filePath)) {
fs = null;
return false;
}
fs = new FileStream(filePath, FileMode.Open, fileAccess, fileShare, short.MaxValue, fileOptions);
return true;
}
catch (FileNotFoundException) {
fs = null;
return false;
}
}

You could use P/Invoke to call the Windows API's CreateFile() function to open the file. This returns a null handle if the file can't be opened (although you'll have to call GetLastError() to determine exactly why the file could not be opened).
Make sure you use a P/Invoke declaration for CreateFile() which returns a SafeHandle, such as:
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile
(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr lpSecurityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
If you do that then you can pass the handle to the overload of the FileStream() constructor which accepts a SafeHandle.
That's about as "native" as you're going to get...
However, I recommend that you just catch the exception.

Related

CreateFile, FILE_FLAG_BACKUP_SEMANTICS, Directory Management and Handles

I am attempting to grab a directory handle for the purpose of retrieving a an identifier for the directory. The documentation (linked above) specifies that the FILE_FLAG_BACKUP_SEMANTICS flag needs to be passed to the CreateFile function in order to retrieve the handle, itself.
However, in consulting pinvoke's kernel32.dll signatures, most of the C# candidates look like the following:
[DllImport("kernell32.dll", SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
[MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
[MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
The above one-to-one parameter mapping to the C++ CreateFile implies that the dwFlagsAndAttributes parameter is the placeholder for the FILE_FLAG_BACKUP_SEMANTICS; however, the FileAttribute enumeration doesn't appear to have a match for that flag.
Right now (and it's broken) my call looks like:
var createdFolder =
FileSystemInteractor.CreateFile(
fullPathWithFolderName,
FileAccess.Read,
FileShare.Read,
IntPtr.Zero,
FileMode.Open,
Kernel32.FILE_FLAG_BACKUP_SEMANTICS,
IntPtr.Zero
);
Kernel32 obviously contains the right flag. The compiler error received back is:
cannot convert from 'uint' to 'System.IO.FileAttributes'
The error makes sense; I am just unsure of what massaging of the signature I am able to do because I am new to extern functions.
Is there a FileAttributes that corresponds to the needed flag? Do I need to change the extern signature?
Include this enum definition in your code:
[Flags]
private enum File_Attributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
The file attributes defined in System.IO does not include many attributes.

Setting Windows file security

My problem is the opposite that most people have. I'm generating files locally in C#, but I want them to be marked as blocked. So when a user opens them in an application like Word or Excel it opens them in "Protected Mode".
I've read that this is set on "NTFS Alternate Data Streams". Does anyone know how I could mimick this in C#?
You can also use the PersistZoneIdentifier object instead of writing the alternative data stream directly.
More information here: http://blogs.msdn.com/b/oldnewthing/archive/2013/11/04/10463035.aspx
and here: https://github.com/citizenmatt/UnblockZoneIdentifier
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace ConsoleApplication3
{
public enum URLZONE : uint
{
URLZONE_LOCAL_MACHINE = 0,
URLZONE_INTRANET = 1,
URLZONE_TRUSTED = 2,
URLZONE_INTERNET = 3,
URLZONE_UNTRUSTED = 4,
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("cd45f185-1b21-48e2-967b-ead743a8914e")]
public interface IZoneIdentifier
{
URLZONE GetId();
void SetId(URLZONE zone);
void Remove();
}
class Program
{
static void Main(string[] args)
{
object persistZoneId = Activator.CreateInstance(Type.GetTypeFromCLSID(Guid.Parse("0968e258-16c7-4dba-aa86-462dd61e31a3")));
IZoneIdentifier zoneIdentifier = (IZoneIdentifier)persistZoneId;
IPersistFile persisteFile = (IPersistFile)persistZoneId;
zoneIdentifier.SetId(URLZONE.URLZONE_UNTRUSTED);
persisteFile.Save(#"c:\temp\test.txt", false);
}
}
}
You need to write the alternate data stream yourself.
To do this, open the file with CreateFile and write the text using FileStream.
Here is a simple exemple that works (tried on my computer).
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern SafeFileHandle CreateFile(
string name, FileAccess access, FileShare share,
IntPtr security,
FileMode mode, FileAttributes flags,
IntPtr template);
public static void Main()
{
// Opens the ":Zone.Identifier" alternate data stream that blocks the file
using (SafeFileHandle handle = CreateFile(#"\\?\C:\Temp\a.txt:Zone.Identifier", FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.OpenOrCreate, FileAttributes.Normal, IntPtr.Zero))
{
// Here add test of CreateFile return code
// Then :
using (StreamWriter writer = new StreamWriter(new FileStream(handle, FileAccess.ReadWrite), Encoding.ASCII))
{
writer.WriteLine("[ZoneTransfer]");
writer.WriteLine("ZoneId=3");
}
}

invoking WinApi when using long path

Can anyone help me please?
I tried to P/Invoke the WINAPI method from managed .net code.
CreateFile() method is always returning false. If I make the given path less than 256 it just works fine but not if greater than 256. I might be doing something wrong .
According to this link I should be able to use long path file that is greater than 256 in length.
Below is the code that I tried:
static void Main(string[] args)
{
string path = #"c:\tttttttttttaaaaaaaaaaaaaaatttttttttttttttaaaaaaaaaaaaaaatttttttttttttttttttttttttttttttaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaattttttttttttttttttaaaaaaaaaaaaaaaaatttttttttttaaaaaaaaaaatttttttaaaaaaaaaaaaaaaaattttttttttttttttttaaaaaaaaaaaaaaaaattttttttttttttaaaaaaaaaaaaaaaaatttttt";
LongPath.TestCreateAndWrite(path);
}
// This code snippet is provided under the Microsoft Permissive License.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern SafeFileHandle CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
public static void TestCreateAndWrite(string fileName) {
string formattedName = #"\\?\" + fileName;
//string formattedName = #"\\?\UNC" + fileName;
// Create a file with generic write access
SafeFileHandle fileHandle = CreateFile(formattedName, EFileAccess.GenericWrite,
EFileShare.None, IntPtr.Zero, ECreationDisposition.CreateAlways, 0, IntPtr.Zero);
// Check for errors
int lastWin32Error = Marshal.GetLastWin32Error();
if (fileHandle.IsInvalid) {
throw new System.ComponentModel.Win32Exception(lastWin32Error);
}
// Pass the file handle to FileStream. FileStream will close the
// handle
using (FileStream fs = new FileStream(fileHandle,
FileAccess.Write)) {
fs.WriteByte(80);
fs.WriteByte(81);
fs.WriteByte(83);
fs.WriteByte(84);
}
}
This method throws error code 3 which is file path not specified according to System Error Codes (0-499) (Windows).
Any help would be highly appreciable.
While the \\?\ notation allows you to use paths whose total length is longer than MAX_PATH, you still have to respect the per-component limit reported by GetVolumeInformation. For NTFS, the per-component limit is 255, which means you are not allowed to go more than 255 characters without a backslash.

create file with FileStream and apply FileAttributes

Is it possible while creating the file with FileStream also apply FileAttributes at the same time? I would like to create file for stream writing with FileAttributes.Temporary file attribute.
You can use FileOptions.DeleteOnClose as one of parameters. File will be automatically removed after you finish your operations and dispose a stream.
Ya, surely you can apply FileAttributes also by using File.SetAttributes Method
Why do you need to do it all at once?
Just create the file (using File.Create or, if its a temporary file, use GetTempFileName.)
Set the attributes on the newly created file
Open the file using whatever method suits you
You can do this if you use the Win32 CreateFile method
uint readAccess = 0x00000001;
uint writeAccess = 0x00000002;
uint readShare = 0x00000001;
uint createAlways = 2;
uint tempAttribute = 0x100;
uint deleteOnClose = 0x04000000;
new FileStream(new SafeFileHandle(NativeMethods.CreateFile("filename",
readAccess | writeAccess,
readShare,
IntPtr.Zero,
createAlways,
tempAttribute | deleteOnClose,
IntPtr.Zero),
true),
FileAccess.ReadWrite, 4096, true);
private static class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern IntPtr CreateFile(string name, uint accessMode, uint shareMode, IntPtr security, uint createMode, uint flags, IntPtr template);
}
For more information, see the MSDN documentation of CreateFile

Send print commands directly to LPT parallel port using C#.net [duplicate]

This question already has answers here:
Printing in (Parallel Port) Dot Matrix over C#
(4 answers)
Closed 6 years ago.
As in DOS we can do:
ECHO MESSAGE>LPT1
How can we achieve same thing in C# .NET?
Sending information to COM1 seems to be easy using C# .NET.
What about LPT1 ports?
I want to send Escape commands to the thermal printer.
In C# 4.0 and later its possible, first you need to connect to that port using the CreateFile method then open a filestream to that port to finally write to it.
Here is a sample class that writes two lines to the printer on LPT1.
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace YourNamespace
{
public static class Print2LPT
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileHandle CreateFile(string lpFileName, FileAccess dwDesiredAccess,uint dwShareMode, IntPtr lpSecurityAttributes, FileMode dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
public static bool Print()
{
string nl = Convert.ToChar(13).ToString() + Convert.ToChar(10).ToString();
bool IsConnected= false;
string sampleText ="Hello World!" + nl +
"Enjoy Printing...";
try
{
Byte[] buffer = new byte[sampleText.Length];
buffer = System.Text.Encoding.ASCII.GetBytes(sampleText);
SafeFileHandle fh = CreateFile("LPT1:", FileAccess.Write, 0, IntPtr.Zero, FileMode.OpenOrCreate, 0, IntPtr.Zero);
if (!fh.IsInvalid)
{
IsConnected= true;
FileStream lpt1 = new FileStream(fh,FileAccess.ReadWrite);
lpt1.Write(buffer, 0, buffer.Length);
lpt1.Close();
}
}
catch (Exception ex)
{
string message = ex.Message;
}
return IsConnected;
}
}
}
Assuming your printer is connected on the LPT1 port, if not you will need to adjust the CreateFile method to match the port you are using.
you can call the method anywhere in your program with the following line
Print2LPT.Print();
I think this is the shortest and most efficient solution to your problem.

Categories

Resources