diff --git a/Saviour Backup System/Saviour Backup System.csproj b/Saviour Backup System/Saviour Backup System.csproj index 6c05cf6..bdc52b4 100644 --- a/Saviour Backup System/Saviour Backup System.csproj +++ b/Saviour Backup System/Saviour Backup System.csproj @@ -51,6 +51,7 @@ + Form diff --git a/Saviour Backup System/ejectDrive.cs b/Saviour Backup System/ejectDrive.cs new file mode 100644 index 0000000..535193e --- /dev/null +++ b/Saviour Backup System/ejectDrive.cs @@ -0,0 +1,349 @@ +using System; +using System.Text; +using System.Runtime.InteropServices; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.ComponentModel; + +namespace Saviour_Backup_System +{ + + public class RemoveDriveTools //Taken from: http://www.codeproject.com/Articles/375916/How-to-Prepare-a-USB-Drive-for-Safe-Removal + { + [StructLayout(LayoutKind.Sequential)] + struct STORAGE_DEVICE_NUMBER + { + public int DeviceType; + public int DeviceNumber; + public int PartitionNumber; + }; + + enum DriveType : uint + { + /// The drive type cannot be determined. + DRIVE_UNKNOWN = 0, //DRIVE_UNKNOWN + /// The root path is invalid, for example, no volume is mounted at the path. + DRIVE_NO_ROOT_DIR = 1, //DRIVE_NO_ROOT_DIR + /// The drive is a type that has removable media, for example, a floppy drive or removable hard disk. + DRIVE_REMOVABLE = 2, //DRIVE_REMOVABLE + /// The drive is a type that cannot be removed, for example, a fixed hard drive. + DRIVE_FIXED = 3, //DRIVE_FIXED + /// The drive is a remote (network) drive. + DRIVE_REMOTE = 4, //DRIVE_REMOTE + /// The drive is a CD-ROM drive. + DRIVE_CDROM = 5, //DRIVE_CDROM + /// The drive is a RAM disk. + DRIVE_RAMDISK = 6 //DRIVE_RAMDISK + } + + const string GUID_DEVINTERFACE_VOLUME = "53f5630d-b6bf-11d0-94f2-00a0c91efb8b"; + const string GUID_DEVINTERFACE_DISK = "53f56307-b6bf-11d0-94f2-00a0c91efb8b"; + const string GUID_DEVINTERFACE_FLOPPY = "53f56311-b6bf-11d0-94f2-00a0c91efb8b"; + const string GUID_DEVINTERFACE_CDROM = "53f56308-b6bf-11d0-94f2-00a0c91efb8b"; + + const int INVALID_HANDLE_VALUE = -1; + const int GENERIC_READ = unchecked((int)0x80000000); + const int GENERIC_WRITE = unchecked((int)0x40000000); + const int FILE_SHARE_READ = unchecked((int)0x00000001); + const int FILE_SHARE_WRITE = unchecked((int)0x00000002); + const int OPEN_EXISTING = unchecked((int)3); + const int FSCTL_LOCK_VOLUME = unchecked((int)0x00090018); + const int FSCTL_DISMOUNT_VOLUME = unchecked((int)0x00090020); + const int IOCTL_STORAGE_EJECT_MEDIA = unchecked((int)0x002D4808); + const int IOCTL_STORAGE_MEDIA_REMOVAL = unchecked((int)0x002D4804); + const int IOCTL_STORAGE_GET_DEVICE_NUMBER = unchecked((int)0x002D1080); + + const int ERROR_NO_MORE_ITEMS = 259; + const int ERROR_INSUFFICIENT_BUFFER = 122; + const int ERROR_INVALID_DATA = 13; + + [DllImport("kernel32.dll")] + static extern DriveType GetDriveType([MarshalAs(UnmanagedType.LPStr)] string lpRootPathName); + + [DllImport("kernel32.dll")] + static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax); + + [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)] + static extern IntPtr CreateFile( + string lpFileName, + int dwDesiredAccess, + int dwShareMode, + IntPtr lpSecurityAttributes, + int dwCreationDisposition, + int dwFlagsAndAttributes, + IntPtr hTemplateFile); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + static extern bool CloseHandle(IntPtr handle); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] + static extern bool DeviceIoControl( + IntPtr hDevice, + int dwIoControlCode, + IntPtr lpInBuffer, + int nInBufferSize, + IntPtr lpOutBuffer, + int nOutBufferSize, + out int lpBytesReturned, + IntPtr lpOverlapped); + + // from setupapi.h + const int DIGCF_PRESENT = (0x00000002); + const int DIGCF_DEVICEINTERFACE = (0x00000010); + + [StructLayout(LayoutKind.Sequential)] + class SP_DEVINFO_DATA + { + public int cbSize = Marshal.SizeOf(typeof(SP_DEVINFO_DATA)); + public Guid classGuid = Guid.Empty; // temp + public int devInst = 0; // dumy + public int reserved = 0; + } + + [StructLayout(LayoutKind.Sequential, Pack = 2)] + struct SP_DEVICE_INTERFACE_DETAIL_DATA + { + public int cbSize; + public short devicePath; + } + + [StructLayout(LayoutKind.Sequential)] + class SP_DEVICE_INTERFACE_DATA + { + public int cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA)); + public Guid interfaceClassGuid = Guid.Empty; // temp + public int flags = 0; + public int reserved = 0; + } + + [DllImport("setupapi.dll")] + static extern IntPtr SetupDiGetClassDevs( + ref Guid classGuid, + int enumerator, + IntPtr hwndParent, + int flags); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiEnumDeviceInterfaces( + IntPtr deviceInfoSet, + SP_DEVINFO_DATA deviceInfoData, + ref Guid interfaceClassGuid, + int memberIndex, + SP_DEVICE_INTERFACE_DATA deviceInterfaceData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiGetDeviceInterfaceDetail( + IntPtr deviceInfoSet, + SP_DEVICE_INTERFACE_DATA deviceInterfaceData, + IntPtr deviceInterfaceDetailData, + int deviceInterfaceDetailDataSize, + ref int requiredSize, + SP_DEVINFO_DATA deviceInfoData); + + [DllImport("setupapi.dll")] + static extern uint SetupDiDestroyDeviceInfoList( + IntPtr deviceInfoSet); + + [DllImport("setupapi.dll")] + static extern int CM_Get_Parent( + ref int pdnDevInst, + int dnDevInst, + int ulFlags); + + [DllImport("setupapi.dll")] + static extern int CM_Request_Device_Eject( + int dnDevInst, + out PNP_VETO_TYPE pVetoType, + StringBuilder pszVetoName, + int ulNameLength, + int ulFlags); + + [DllImport("setupapi.dll", EntryPoint = "CM_Request_Device_Eject")] + static extern int CM_Request_Device_Eject_NoUi( + int dnDevInst, + IntPtr pVetoType, + StringBuilder pszVetoName, + int ulNameLength, + int ulFlags); + + enum PNP_VETO_TYPE + { + Ok, + TypeUnknown, + LegacyDevice, + PendingClose, + WindowsApp, + WindowsService, + OutstandingOpen, + Device, + Driver, + IllegalDeviceRequest, + InsufficientPower, + NonDisableable, + LegacyDriver, + InsufficientRights + } + + /// + /// Call with "X:" or similar + /// + /// + /// + public static bool RemoveDrive(string driveCharWithColon) + { + // open the storage volume + IntPtr hVolume = CreateFile(@"\\.\" + driveCharWithColon, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); + if (hVolume.ToInt32() == -1) return false; + + // get the volume's device number + long DeviceNumber = GetDeviceNumber(hVolume); + if (DeviceNumber == -1) return false; + + // get the drive type which is required to match the device numbers correctely + string rootPath = driveCharWithColon + "\\"; + DriveType driveType = GetDriveType(rootPath); + + // get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way? + StringBuilder pathInformation = new StringBuilder(250); + uint res = QueryDosDevice(driveCharWithColon, pathInformation, 250); + if (res == 0) return false; + + // get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number + long DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, driveType, pathInformation.ToString()); + if (DevInst == 0) return false; + + // get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives! + int DevInstParent = 0; + CM_Get_Parent(ref DevInstParent, (int)DevInst, 0); + + for (int tries = 1; tries <= 3; tries++) // sometimes we need some tries... + { + int r = CM_Request_Device_Eject_NoUi(DevInstParent, IntPtr.Zero, null, 0, 0); + if (r == 0) return true; + Thread.Sleep(500); + } + return false; + } + + static long GetDeviceNumber(IntPtr handle) + { + // get the volume's device number + long DeviceNumber = -1; + int size = 0x400; // some big size + IntPtr buffer = Marshal.AllocHGlobal(size); + int bytesReturned = 0; + + try + { + DeviceIoControl(handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, buffer, size, out bytesReturned, IntPtr.Zero); + } + finally + { + CloseHandle(handle); + } + + if (bytesReturned > 0) + { + STORAGE_DEVICE_NUMBER sdn = (STORAGE_DEVICE_NUMBER)Marshal.PtrToStructure(buffer, typeof(STORAGE_DEVICE_NUMBER)); + DeviceNumber = sdn.DeviceNumber; + } + Marshal.FreeHGlobal(buffer); + + return DeviceNumber; + } + + + //---------------------------------------------------------------------- + // returns the device instance handle of a storage volume or 0 on error + //---------------------------------------------------------------------- + static long GetDrivesDevInstByDeviceNumber(long DeviceNumber, DriveType DriveType, string dosDeviceName) + { + bool IsFloppy = dosDeviceName.Contains("\\Floppy"); // who knows a better way? + Guid guid; + + switch (DriveType) + { + case DriveType.DRIVE_REMOVABLE: + if (IsFloppy) guid = new Guid(GUID_DEVINTERFACE_FLOPPY); + else guid = new Guid(GUID_DEVINTERFACE_DISK); + break; + case DriveType.DRIVE_FIXED: + guid = new Guid(GUID_DEVINTERFACE_DISK); + break; + case DriveType.DRIVE_CDROM: + guid = new Guid(GUID_DEVINTERFACE_CDROM); + break; + default: + return 0; + } + + // Get device interface info set handle for all devices attached to system + IntPtr hDevInfo = SetupDiGetClassDevs(ref guid, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if (hDevInfo.ToInt32() == INVALID_HANDLE_VALUE) throw new Win32Exception(Marshal.GetLastWin32Error()); + + // Retrieve a context structure for a device interface of a device information set + int dwIndex = 0; + + while (true) + { + SP_DEVICE_INTERFACE_DATA interfaceData = new SP_DEVICE_INTERFACE_DATA(); + if (!SetupDiEnumDeviceInterfaces(hDevInfo, null, ref guid, dwIndex, interfaceData)) + { + int error = Marshal.GetLastWin32Error(); + if (error != ERROR_NO_MORE_ITEMS) throw new Win32Exception(error); + break; + } + + SP_DEVINFO_DATA devData = new SP_DEVINFO_DATA(); + int size = 0; + if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, interfaceData, IntPtr.Zero, 0, ref size, devData)) + { + int error = Marshal.GetLastWin32Error(); + if (error != ERROR_INSUFFICIENT_BUFFER) throw new Win32Exception(error); + } + + IntPtr buffer = Marshal.AllocHGlobal(size); + SP_DEVICE_INTERFACE_DETAIL_DATA detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA(); + detailData.cbSize = Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DETAIL_DATA)); + Marshal.StructureToPtr(detailData, buffer, false); + + if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, interfaceData, buffer, size, ref size, devData)) + { + Marshal.FreeHGlobal(buffer); + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + IntPtr pDevicePath = (IntPtr)((int)buffer + Marshal.SizeOf(typeof(int))); + string devicePath = Marshal.PtrToStringAuto(pDevicePath); + Marshal.FreeHGlobal(buffer); + + // open the disk or cdrom or floppy + IntPtr hDrive = CreateFile(devicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); + if (hDrive.ToInt32() != INVALID_HANDLE_VALUE) + { + // get its device number + long driveDeviceNumber = GetDeviceNumber(hDrive); + if (DeviceNumber == driveDeviceNumber) // match the given device number with the one of the current device + { + // CloseHandle( hDrive ); // handle hDrive was closed inside GetDeviceNumber(..) + SetupDiDestroyDeviceInfoList(hDevInfo); + return devData.devInst; + } + // CloseHandle( hDrive ); // handle hDrive was closed inside GetDeviceNumber(..) + + } + dwIndex++; + } + + SetupDiDestroyDeviceInfoList(hDevInfo); + return 0; + } + + static void Test() + { + bool ok = RemoveDrive("H:"); + } + } +}