예제 #1
0
        /// <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;
                }
            }
        }
예제 #3
0
        /// <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);
        }