/// <summary> /// Get disk information by P/Invoke. /// </summary> /// <param name="physicalDrive">Index number of physical drive</param> /// <returns>Disk information</returns> internal static DiskInfo GetDiskInfo(int physicalDrive) { var info = new DiskInfo { PhysicalDrive = physicalDrive }; SafeFileHandle hFile = null; try { hFile = NativeMethod.CreateFile( String.Format(@"\\.\PhysicalDrive{0}", physicalDrive), NativeMethod.GENERIC_READ | NativeMethod.GENERIC_WRITE, // Administrative privilege is required. GENERIC_WRITE is for IOCTL_ATA_PASS_THROUGH. NativeMethod.FILE_SHARE_READ | NativeMethod.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethod.OPEN_EXISTING, NativeMethod.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero); if (hFile == null || hFile.IsInvalid) { // This is normal when this application is not run by administrator. throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get handle to disk."); } // --------------------------------- // Use IOCTL_STORAGE_QUERY_PROPERTY. // --------------------------------- var storageQuery = new NativeMethod.STORAGE_PROPERTY_QUERY(); storageQuery.PropertyId = NativeMethod.StorageDeviceProperty; storageQuery.QueryType = NativeMethod.PropertyStandardQuery; NativeMethod.STORAGE_DEVICE_DESCRIPTOR storageDescriptor; uint bytesReturned1; var result1 = NativeMethod.DeviceIoControl( hFile, NativeMethod.IOCTL_STORAGE_QUERY_PROPERTY, ref storageQuery, (uint)Marshal.SizeOf(typeof(NativeMethod.STORAGE_PROPERTY_QUERY)), out storageDescriptor, (uint)Marshal.SizeOf(typeof(NativeMethod.STORAGE_DEVICE_DESCRIPTOR)), out bytesReturned1, IntPtr.Zero); if (result1 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get disk information."); } // Convert to byte array. var size = Marshal.SizeOf(storageDescriptor); var ptr = Marshal.AllocHGlobal(size); var bytes = new byte[size]; Marshal.StructureToPtr(storageDescriptor, ptr, true); Marshal.Copy(ptr, bytes, 0, size); Marshal.FreeHGlobal(ptr); // Set values. info.Vendor = ConvertBytesToString(bytes, (int)storageDescriptor.VendorIdOffset).Trim(); info.Product = ConvertBytesToString(bytes, (int)storageDescriptor.ProductIdOffset).Trim(); info.IsRemovable = storageDescriptor.RemovableMedia; info.BusType = ConvertBusTypeToString(storageDescriptor.BusType); // ------------------------------- // Use IOCTL_DISK_GET_LENGTH_INFO. // ------------------------------- long diskSize; uint bytesReturned2; var result2 = NativeMethod.DeviceIoControl( hFile, NativeMethod.IOCTL_DISK_GET_LENGTH_INFO, IntPtr.Zero, 0, out diskSize, (uint)Marshal.SizeOf(typeof(long)), out bytesReturned2, IntPtr.Zero); if (result2 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get disk size."); } // Set value. info.SizePInvoke = diskSize; // --------------------------- // Use IOCTL_ATA_PASS_THROUGH. // --------------------------- var ataQuery = new NativeMethod.ATAIdentifyDeviceQuery(); ataQuery.data = new ushort[256]; ataQuery.header.Length = (ushort)Marshal.SizeOf(typeof(NativeMethod.ATA_PASS_THROUGH_EX)); ataQuery.header.AtaFlags = NativeMethod.ATA_FLAGS.ATA_FLAGS_DATA_IN; ataQuery.header.DataTransferLength = (uint)ataQuery.data.Length * 2; // Size of "data" in bytes ataQuery.header.TimeOutValue = 3; // Sec ataQuery.header.DataBufferOffset = Marshal.OffsetOf(typeof(NativeMethod.ATAIdentifyDeviceQuery), "data"); ataQuery.header.PreviousTaskFile = new byte[8]; ataQuery.header.CurrentTaskFile = new byte[8]; ataQuery.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE uint bytesReturned3; var result3 = NativeMethod.DeviceIoControl( hFile, NativeMethod.IOCTL_ATA_PASS_THROUGH, ref ataQuery, (uint)Marshal.SizeOf(typeof(NativeMethod.ATAIdentifyDeviceQuery)), ref ataQuery, (uint)Marshal.SizeOf(typeof(NativeMethod.ATAIdentifyDeviceQuery)), out bytesReturned3, IntPtr.Zero); if (result3) { const int index = 217; // Word index of nominal media rotation rate (1 means non-rotating media.) info.NominalMediaRotationRate = ataQuery.data[index]; } else { // None. It is normal that IOCTL_ATA_PASS_THROUGH fails when used to external or removable media. } } catch (Win32Exception ex) { Debug.WriteLine("{0} (Code: {1}).", ex.Message.Substring(0, ex.Message.Length - 1), ex.ErrorCode); throw; } catch (Exception ex) { Debug.WriteLine(ex.Message); throw; } finally { if (hFile != null) { // CloseHandle is inappropriate to close SafeFileHandle. // Dispose method is not necessary because Close method will call it internally. hFile.Close(); } } return(info); }
public static void Run() { SafePipeHandle hNamedPipe = null; try { // ---------------------------------------------------------------------------------------------------------------- // Try to open the named pipe identified by the pipe name. while (true) { hNamedPipe = NativeMethod.CreateFile( Program.FullPipeName, // Pipe name FileDesiredAccess.GENERIC_READ | // Read access FileDesiredAccess.GENERIC_WRITE, // Write access FileShareMode.Zero, // No sharing null, // Default security attributes FileCreationDisposition.OPEN_EXISTING, // Opens existing pipe 0, // Default attributes IntPtr.Zero // No template file ); // If the pipe handle is opened successfully. if (!hNamedPipe.IsInvalid) { Console.WriteLine("[IPC Client Connected] Named Pipe: \"{0}\"", Program.FullPipeName); break; } // Exit if an error other than ERROR_PIPE_BUSY occurs. if (Marshal.GetLastWin32Error() != ERROR_PIPE_BUSY) { throw new Win32Exception(); } // All pipe instances are busy, so wait for 5 seconds. if (!NativeMethod.WaitNamedPipe(Program.FullPipeName, 5000)) { throw new Win32Exception(); } } // ---------------------------------------------------------------------------------------------------------------- // Set the read mode and the blocking mode of the named pipe PipeMode mode = PipeMode.PIPE_READMODE_MESSAGE; if (!NativeMethod.SetNamedPipeHandleState(hNamedPipe, ref mode, IntPtr.Zero, IntPtr.Zero)) { throw new Win32Exception(); } // ---------------------------------------------------------------------------------------------------------------- // Send a request from client to server. string message = Program.RequestMessage; byte[] bRequest = Encoding.UTF8.GetBytes(message); int cbRequest = bRequest.Length, cbWritten; if (!NativeMethod.WriteFile( hNamedPipe, // Handle of the pipe bRequest, // Message to be written cbRequest, // Number of bytes to write out cbWritten, // Number of bytes written IntPtr.Zero // Not overlapped )) { throw new Win32Exception(); } Console.WriteLine("[IPC Client Sent {0} bytes] Message - {1}", cbWritten, message.Replace("\0", "")); // ---------------------------------------------------------------------------------------------------------------- // Receive a response from server. bool finishRead = false; do { byte[] bResponse = new byte[Program.BufferSize]; int cbResponse = bResponse.Length, cbRead; finishRead = NativeMethod.ReadFile( hNamedPipe, // Handle of the pipe bResponse, // Buffer to receive data cbResponse, // Size of buffer in bytes out cbRead, // Number of bytes read IntPtr.Zero // Not overlapped ); if (!finishRead && Marshal.GetLastWin32Error() != ERROR_MORE_DATA) { throw new Win32Exception(); } // ---------------------------------------------------------------------------------------------------------------- // UTF8-encode the received byte array and trim all the '\0' characters at the end. message = Encoding.UTF8.GetString(bResponse).Replace("\0", ""); Console.WriteLine("[IPC Client Received {0} bytes] Message - {1}", cbRead, message); }while (!finishRead); // Repeat loop if ERROR_MORE_DATA } catch (Exception ex) { Console.WriteLine("[IPC Client ERROR] - {0}", ex.Message); } finally { if (hNamedPipe != null) { hNamedPipe.Close(); hNamedPipe = null; } } }
/// <summary> /// Read disk by P/Invoke (synchronously and with cancellation). /// </summary> /// <param name="rawData">Raw data</param> /// <param name="cancellationToken">Cancellation token</param> /// <returns>Result data</returns> internal static RawData ReadDiskPInvoke(RawData rawData, CancellationToken cancellationToken) { var blockOffsetMultiple = rawData.BlockOffsetMultiple; SafeFileHandle hFile = null; try { // ---------- // Read disk. // ---------- // This section is based on sequential read test of CrystalDiskMark (3.0.2) // created by hiyohiyo (http://crystalmark.info/). // Get handle to disk. hFile = NativeMethod.CreateFile( String.Format(@"\\.\PhysicalDrive{0}", Settings.Current.PhysicalDrive), NativeMethod.GENERIC_READ, // Administrative privilege is required. 0, IntPtr.Zero, NativeMethod.OPEN_EXISTING, NativeMethod.FILE_ATTRIBUTE_NORMAL | NativeMethod.FILE_FLAG_NO_BUFFERING | NativeMethod.FILE_FLAG_SEQUENTIAL_SCAN, IntPtr.Zero); if (hFile == null || hFile.IsInvalid) { // This is normal when this application is not run by administrator. throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get handle to disk."); } // Prepare parameters. var areaSizeActual = Settings.Current.AreaSize; // Area size for actual reading (MiB) if (0 < Settings.Current.BlockOffset) { areaSizeActual -= 1; // 1 is for the last MiB of area. If offset, it may exceed disk size. } int readNum = (areaSizeActual * 1024) / Settings.Current.BlockSize; // The number of reads int loopOuter = 1; // The number of outer loops int loopInner = readNum; // The number of inner loops if (Settings.Current.AreaRatioInner < Settings.Current.AreaRatioOuter) { loopOuter = (areaSizeActual * 1024) / (Settings.Current.BlockSize * Settings.Current.AreaRatioOuter); loopInner = Settings.Current.AreaRatioInner; readNum = loopInner * loopOuter; } var areaLocationBytes = (long)Settings.Current.AreaLocation * 1024L * 1024L; // Bytes var blockOffsetBytes = (long)Settings.Current.BlockOffset * (long)blockOffsetMultiple * 1024L; // Bytes var jumpBytes = (long)Settings.Current.BlockSize * (long)Settings.Current.AreaRatioOuter * 1024L; // Bytes areaLocationBytes += blockOffsetBytes; var buffSize = (uint)Settings.Current.BlockSize * 1024U; // Buffer size (Bytes) var buff = new byte[buffSize]; // Buffer uint readSize = 0U; var sw = new Stopwatch(); var lapTime = new TimeSpan[readNum + 1]; // 1 is for leading zero time. lapTime[0] = TimeSpan.Zero; // Leading zero time for (int i = 0; i < loopOuter; i++) { if (0 < i) { areaLocationBytes += jumpBytes; } // Move pointer. var result1 = NativeMethod.SetFilePointerEx( hFile, areaLocationBytes, IntPtr.Zero, NativeMethod.FILE_BEGIN); if (result1 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to move pointer."); } // Measure disk transfer rate (sequential read). for (int j = 1; j <= loopInner; j++) { cancellationToken.ThrowIfCancellationRequested(); sw.Start(); var result2 = NativeMethod.ReadFile( hFile, buff, buffSize, ref readSize, IntPtr.Zero); if (result2 == false) { throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to measure disk transfer rate."); } sw.Stop(); lapTime[i * loopInner + j] = sw.Elapsed; } } cancellationToken.ThrowIfCancellationRequested(); // ---------------- // Process results. // ---------------- // Calculate each transfer rate. var data = new double[readNum]; for (int i = 1; i <= readNum; i++) { var timeEach = (lapTime[i] - lapTime[i - 1]).TotalSeconds; // Second var scoreEach = Math.Floor(buffSize / timeEach) / 1000000D; // MB/s data[i - 1] = scoreEach; } // Calculate total transfer rate (just for reference). var totalTime = lapTime[readNum].TotalSeconds; // Second var totalRead = (double)Settings.Current.BlockSize * (double)readNum * 1024D; // Bytes var totalScore = Math.Floor(totalRead / totalTime) / 1000000D; // MB/s // Compose outcome. var outcome = "[Start data]" + Environment.NewLine; int k = 0; for (int i = 0; i < readNum; i++) { outcome += String.Format("{0:f6} ", data[i]); // Data have 6 decimal places. k++; if ((k == 6) | (i == readNum - 1)) { k = 0; outcome += Environment.NewLine; } } outcome += "[End data]" + Environment.NewLine; outcome += String.Format("Total {0:f6} MB/s", totalScore); rawData.Result = ReadResult.Success; rawData.Outcome = outcome; rawData.Data = data; } catch (Win32Exception ex) { rawData.Result = ReadResult.Failure; rawData.Message = String.Format("{0} (Code: {1}).", ex.Message.Substring(0, ex.Message.Length - 1), ex.ErrorCode); } catch (Exception ex) // Including OperationCanceledException { rawData.Result = ReadResult.Failure; rawData.Message = ex.Message; } finally { if (hFile != null) { // CloseHandle is inappropriate to close SafeFileHandle. // Dispose method is not necessary because Close method will call it internally. hFile.Close(); } } return(rawData); }