How to open volume by name - c#

I need to open volume by name(get volume handle). The target volume name is "\?\Volume{A25CF44F-8CB3-46E7-B3A7-931385FDF8CB}\" and this should works. According to CreateFile function
You can also open a volume by referring to its volume name.
The C# code:
public static class Wrapper
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
}
static void Main(string[] args)
{
string sourceVol = #"\\?\Volume{A25CF44F-8CB3-46E7-B3A7-931385FDF8CB}\";
SafeFileHandle sourceVolHandle = Wrapper.CreateFile(sourceVol, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (sourceVolHandle.IsInvalid)
throw new Win32Exception(); //Here I got "The system cannot find the path specified"
So how to open volume by name? (I know that I can open volume using drive letter "\\.\C:" but that is not accepteble)

To open volume we need to remove trailing slash, so:
string sourceVol = #"\\?\Volume{A25CF44F-8CB3-46E7-B3A7-931385FDF8CB}";

Related

How to get the target location of a Junction if the target directory does not exist?

I have a junction from A -> B.
B does not exist, but I want to read from A the target of the junction (B).
However I can't make this work (filename = A):
// directoryHandle.isInvalid == true
var directoryHandle = CreateFile(filename, EFileAccess.GenericRead, EFileShare.Read | EFileShare.Write, IntPtr.Zero, ECreationDisposition.OpenExisting, EFileAttributes.BackupSemantics, IntPtr.Zero);
StringBuilder path = new StringBuilder(1024);
// path is empty string
var res = GetFinalPathNameByHandle(directoryHandle, path, path.Capacity, 0);
GetFinalPathNameByHandle returns the location of the junction if B exists. But if B does not exist, I receive an invalid file handle so that the call to GetFinalPathNameByHandle also fails.
Question: How do I get the target of a junction if the target directory does not exist?
Here are the method definitions:
[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);
[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int GetFinalPathNameByHandle([In] SafeFileHandle hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder path, int bufLen, int flags);
Ask the junction directly.
To determine the type, use FindFirstFile (or GetFileInformationByHandleEx(FileAttributeTagInfo) if you already have a handle). The FILE_ATTRIBUTE_REPARSE_POINT bit must be set in the file attributes and the tag must be IO_REPARSE_TAG_MOUNT_POINT or IO_REPARSE_TAG_SYMLINK.
If all checks pass, call CreateFile with the FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS flags. Now call DeviceIoControl(FSCTL_GET_REPARSE_POINT) to read the REPARSE_DATA_BUFFER (allocate MAXIMUM_REPARSE_DATA_BUFFER_SIZE bytes for it). Look at the tag type to find PathBuffer and SubstituteNameOffset+SubstituteNameLength to find where in the buffer the path is.
You can find C# example code on Codeproject...

FileSystemWatcher locks parent folder, is there a fix or an alternative?

Whether in .NET Core or .NET Framework, the use of a FileSystemWatcher on a folder makes it impossible to delete any parent folder with File Explorer.
File Explorer will ask you for admin rights. Launching File Explorer as admin doesn't fix the issue.
The only way that I can make it work is to set the FileSystemWatcher to the root drive, and include subdirectories, which could lead to big performance issues, and a buffer overflow on the FSW.
FileSystemWatcher fsw = new FileSystemWatcher(#"C:\One\Two\Three");
fsw.EnableRaisingEvents = true;
You will not be able to delete folders 'One' or 'Two'.
The problem starts as soon as set 'EnableRaisingEvents' to true, but if you don't, you will not get any notification.
Is there a way to overcome this problem, or an alternative to FileSystemWatcher?
#EricP gave the root cause: The FSW internally makes a call to CreateFileW to open a handle to the monitored directory Three, which tells Windows to prevent deleting the parent folders One and Two.
But the CreateFileW call is made with read+write+delete sharing mode, meaning Windows should allow any other processes to delete the monitored folder Three.
So there's no direct fix for your problem, but there is a workaround:
Delete Three first, which should release the handle, then delete Two and/or One, which should be allowed.
Here's a code sample reproing this without any FileSystemWatcher code, just direct Windows P/Invokes:
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MyNamespace
{
public static class MyClass
{
static void Main()
{
string path = #"C:\Users\YOURUSERNAME\Desktop\parent\child";
if (!Directory.Exists(path))
{
throw new Exception("Make sure to create the directories before testing.");
}
// This will lock the directory
SafeFileHandle _directoryHandle = CreateFile(
lpFileName: path,
dwDesiredAccess: 0x0001, //FILE_LIST_DIRECTORY
dwShareMode: FileShare.Read | FileShare.Delete | FileShare.Write,
dwCreationDisposition: FileMode.Open,
dwFlagsAndAttributes:
0x02000000 /*FILE_FLAG_BACKUP_SEMANTICS*/ |
0x40000000 /*FILE_FLAG_OVERLAPPED*/
);
Console.WriteLine("Try to manually delete the 'parent' directory using File Explorer. It should fail.");
Console.WriteLine("Now try to manually delete the 'child' directory, then the 'parent' directory, using File Explorer. It should succeed.");
Console.WriteLine("Press enter to exit.");
System.Diagnostics.Debugger.Break();
_directoryHandle.Dispose();
}
internal enum BOOL : int
{
FALSE = 0,
TRUE = 1,
}
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_ATTRIBUTES
{
internal uint nLength;
internal IntPtr lpSecurityDescriptor;
internal BOOL bInheritHandle;
}
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)]
private static extern unsafe SafeFileHandle CreateFilePrivate(
string lpFileName,
int dwDesiredAccess,
FileShare dwShareMode,
SECURITY_ATTRIBUTES* lpSecurityAttributes,
FileMode dwCreationDisposition,
int dwFlagsAndAttributes,
IntPtr hTemplateFile);
internal static unsafe SafeFileHandle CreateFile(
string lpFileName,
int dwDesiredAccess,
FileShare dwShareMode,
FileMode dwCreationDisposition,
int dwFlagsAndAttributes)
{
return CreateFilePrivate(lpFileName, dwDesiredAccess, dwShareMode, null, dwCreationDisposition, dwFlagsAndAttributes, IntPtr.Zero);
}
}
}

Get Size of Disk and/or Drive With Unknown File Format, C# .NET Framework?

I want to be able to programmatically get the size of a physical disk, or the logical drives that are part of the physical disk.
Under normal conditions, getting logical drive size is easy. But, for the disk I am using, I intentionally have written raw data to it, and thus it is expected to have no known drive format.
Because of the drive format being unknown to Windows, Windows drive properties and .NET Framework's DriveInfo cannot tell me the size of the logical drive(s) that make up this physical disk. In DriveInfo foo, by format I mean foo.DriveFormat, foo.IsReady is false, and foo.TotalSize triggers an exception. In just right clicking the drive and getting the size manually via Windows' properties option, it shows 0 bytes for the drive size.
Preferably using C#, using .NET Framework 4.0 or earlier, on a Windows 7 system, how do I learn the size of a physical disk, or the logical drives that make up the physical disk, if the logical drives associated with it have no known file structure?
Specifically physical disk size is preferable for my purposes.
I know there is a way in general using Windows 7, as I have seen applications give an accurate physical disk size of the same disk.
The solution ended up being fairly simple, and an extension from the existing question Getting Disk Size Properly
Posting my solution here, as perhaps the full code will help someone in the future:
(FYI: P/Invoke definitions obtained and minimally modified from http://pinvoke.net)
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
IntPtr lpInBuffer, uint nInBufferSize,
IntPtr lpOutBuffer, uint nOutBufferSize,
out uint lpBytesReturned, IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
struct GET_LENGTH_INFORMATION
{
public long Length;
};
long GetPhysDiskSize(string physDeviceID)
{
uint IOCTL_DISK_GET_LENGTH_INFO = 0x0007405C;
uint dwBytesReturned;
//Example, physDeviceID == #"\\.\PHYSICALDRIVE1"
IntPtr hVolume = CreateFile(physDeviceID, FileAccess.ReadWrite,
FileShare.None, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
GET_LENGTH_INFORMATION outputInfo = new GET_LENGTH_INFORMATION();
outputInfo.Length = 0;
IntPtr outBuff = Marshal.AllocHGlobal(Marshal.SizeOf(outputInfo));
bool devIOPass = DeviceIoControl(hVolume,
IOCTL_DISK_GET_LENGTH_INFO,
IntPtr.Zero, 0,
outBuff, (uint)Marshal.SizeOf(outputInfo),
out dwBytesReturned,
IntPtr.Zero);
CloseHandle(hVolume);
outputInfo = (GET_LENGTH_INFORMATION)Marshal.PtrToStructure(outBuff, typeof(GET_LENGTH_INFORMATION));
Marshal.FreeHGlobal(hVolume);
Marshal.FreeHGlobal(outBuff);
return outputInfo.Length;
}

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.

PathTooLongException C# 4.5

I having trouble of copying some folder 260+ chars (for example: F:\NNNNNNNNNNNNNNNN\NNNNNNNNNNN\ROOT\$RECYCLE.BIN\S-1-5-21-3299053755-4209892151-505108915-1000\$RMSL3U8\NNNNNNNNN NNNNNNNN\NNNNNNNNNNN\NNNNNNNNNN\NNNNNNNNNN\publish\Application Files\TNNNNNNNNNNNN_1_0_0_0\NNNNNNNNNNNN.exe.manifest) to some other place with standart DrectoryInfo.Create(); adding \?\ or \?\UNC\ (like "\\?\UNC\") just throw another ArgumentException.
What am i doing wrong? What else i can do without using Directory.SetCurrentDirectory() ?
Actually you need to call win32 from c#. We have done this
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public static class LongPath
{
static class Win32Native
{
[StructLayout(LayoutKind.Sequential)]
public class SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr pSecurityDescriptor;
public int bInheritHandle;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CreateDirectory(string lpPathName, SECURITY_ATTRIBUTES lpSecurityAttributes);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, FileShare dwShareMode, SECURITY_ATTRIBUTES securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
}
public static bool CreateDirectory(string path)
{
return Win32Native.CreateDirectory(String.Concat(#"\\?\", path), null);
}
public static FileStream Open(string path, FileMode mode, FileAccess access)
{
SafeFileHandle handle = Win32Native.CreateFile(String.Concat(#"\\?\", path), (int)0x10000000, FileShare.None, null, mode, (int)0x00000080, IntPtr.Zero);
if (handle.IsInvalid)
{
throw new System.ComponentModel.Win32Exception();
}
return new FileStream(handle, access);
}
}
A sample code:
string path = #"c:\".PadRight(255, 'a');
LongPath.CreateDirectory(path);
path = String.Concat(path, #"\", "".PadRight(255, 'a'));
LongPath.CreateDirectory(path);
string filename = Path.Combine(path, "test.txt");
FileStream fs = LongPath.Open(filename, FileMode.CreateNew, FileAccess.Write);
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine("abc");
}
There's a great library on Microsoft TechNet for overcoming the long filenames problem, it's called Delimon.Win32.I​O Library (V4.0) and it has its own versions of key methods from System.IO
For example, you would replace:
System.IO.Directory.GetFiles
with
Delimon.Win32.IO.Directory.GetFiles
which will let you handle long files and folders.
From the website:
Delimon.Win32.IO replaces basic file functions of System.IO and
supports File & Folder names up to up to 32,767 Characters.
This Library is written on .NET Framework 4.0 and can be used either
on x86 & x64 systems. The File & Folder limitations of the standard
System.IO namespace can work with files that have 260 characters in a
filename and 240 characters in a folder name (MAX_PATH is usually
configured as 260 characters). Typically you run into the
System.IO.PathTooLongException Error with the Standard .NET Library.
Yes, using the standard APIs will give you this kind of limitations (255 chars IIRC).
From .NET you can use the AlphaFS project which lets you use very long paths (using the "\\?\" style) and mimics the System.IO namespace.
You will probably be able to use this library just as if you were using System.IO, for example : AlphaFS.Win32.Filesystem.File.Copy() instead System.IO.File.Copy()
If you don't want or cannot use AlphaFS you'll have to pinvoke the Win32 API

Categories

Resources