вторник, 14 апреля 2015 г.

Индикатор активности винчестера

В одном из прошлых материалов вы узнали как с помощью API SMART измерять температуру винчестера и диагностировать множество других параметров нашего «железного друга». Если вспомнить, что современные винчестеры еще и сверхтихие, в отличие от старых «шуршащих» моделей, то контроль за их работой еще более затрудняется. Сегодня мы организуем наблюдение за частотой обращения к жесткому диску непосредственно перед вашими глазами, а если точнее - руками...

Материал из учебного цикла РЛ, 2011, №5, с.22 (автора данного блога)

Краткий экскурс… 

Это не «очепятка». Именно, под руками. А под руками у нас... клавиатура :). На классической клавиатуре расположено три индикатора. А какой самый бесполезный индикатор на ней? Правильно, ScrollLock**. Можно конечно использовать и готовое решение. Но цель наших с вами практических занятий – научиться создавать самому. Тем более, что ничего сложного. Да и функциональность можно любую организовать. 
** В данном материале мы рулим индикатором через отсылку нажатий клавиш, меняя их состояние. Но индикаторы на клавиатуре можно зажигать независимо от нажатий, не посылая клавиатурных нажатий. Через DeviceIoControl(), к примеру. Пример реализации подобного подхода приведен в данном материале.
Предпосылки реализации ПО 

Прежде всего, заглянем в MSDN. Нам понадобятся следующие WinAPI функции: keybd_event (для посылки нажатий и управления состоянием индикатора на клавиатуре), MapVirtualKey (получение виртуальных скан-кодов клавиш), GetKeyState (получение состояния клавиш), CreateFile (для доступа к устройствам, файлам), DeviceIoControl (для обмена с устройствами, в нашем случае с жестким диском). 

Синтаксис перечисленных нами функций выглядит следующим образом... 

DISK_PERFORMANCE Structure [2]



type DISK_PERFORMANCE = record

BytesRead,

BytesWritten,

ReadTime,

WriteTime : longint;

ReadCount,

WriteCount,

QueueDepth : dword;

End;



Keybd_event Function [3]



VOID WINAPI keybd_event(

__in BYTE bVk,

__in BYTE bScan,

__in DWORD dwFlags,

__in ULONG_PTR dwExtraInfo

);



Пример вызова: procedure keybd_event; external user32 name 'keybd_event';



MapVirtualKey Function [4]



UINT WINAPI MapVirtualKey(

__in UINT uCode,

__in UINT uMapType

);



function MapVirtualKey; external user32 name 'MapVirtualKeyA';



GetKeyState Function [5]



SHORT WINAPI GetKeyState(

__in int nVirtKey

);



Пример вызова: function GetKeyState; external user32 name 'GetKeyState';



CreateFile Function [6]



HANDLE WINAPI CreateFile(

__in LPCTSTR lpFileName,

__in DWORD dwDesiredAccess,

__in DWORD dwShareMode,

__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,

__in DWORD dwCreationDisposition,

__in DWORD dwFlagsAndAttributes,

__in_opt HANDLE hTemplateFile

);



Пример вызова: CreateFile('\\.\PhysicalDrive0',0,0,0,OPEN_EXISTING, 0, 0);



DeviceIoControl Function [7]



BOOL WINAPI DeviceIoControl(

__in HANDLE hDevice,

__in DWORD dwIoControlCode,

__in_opt LPVOID lpInBuffer,

__in DWORD nInBufferSize,

__out_opt LPVOID lpOutBuffer,

__in DWORD nOutBufferSize,

__out_opt LPDWORD lpBytesReturned,

__inout_opt LPOVERLAPPED lpOverlapped

);



Пример вызова: DeviceIoControl(handle, IOCTL_DISK_PERFORMANCE, 0, 0, @dp, SizeOf(DISK_PERFORMANCE), lBytesReturned, 0).



В общем, более ничего и не нужно. Интерфейс нам не нужен окна у программы не будет. Исходя из вышеизложенного, определим основные требования к индикатору активности HDD:

  1. Возможность индикации активности (обращения к винчестеру) на клавиатуре;
  2. Приложение на WinAPI без форм;
  3. Открытые исходники.
Практика. Приступим к кодингу



Итак, для работы нам понадобится следующее:

  1. TurboDelphi-Lite портабле* (флешечная версия) [8];
  2. Любой винчестер для диагностики, с интерфейсом SATA или IDE.


Прежде всего, запустите IDE и создайте пустой проект. Разработку приложения осуществим на WinAPI без форм. Для того, чтобы однозначно управлять состоянием индикатора ScrollLock, ведь его состояние заранее нам неизвестно (он может «гореть», а может и нет), нужно считать его состояние. Это легко осуществить функцией GetKeyState(). А управление состоянием индикатора будем осуществлять эмуляцией нажатия на клавишу <ScrollLock>, как будто вы это делаете сами. Для этого используем функцию Keybd_event():
// проверка горит-ли индикатор ScrollLock и управление
...
procedure scroll(p: boolean);
begin
 if ((GetKeyState(145) = 1) and (not p)) or // включен и погасить
    ((GetKeyState(145) <> 1) and (p))         // выключен и зажечь
  then begin
   // нажать и отпустить ScrollLock
   keybd_event(145, MapVirtualKey(145, 0) , 0, 0);
   keybd_event(145, MapVirtualKey(145, 0), 38, 0)
 end
end;
...


Далее осуществим создание пустого класса TF, методом AllocateHWnd() создадим «пустое окно» и назначим обработчик очереди сообщений приложения. Для получения хэндла физического устройства, используем уже известную нам функцию CreateFile():
// скелет приложения: инициализация доступа к HDD,
// создание очереди сообщений к пустому окну
...
constructor TF.Create;
begin
 inherited Create;
 FWnd:= AllocateHWnd(WndProcc);
 timer_create
end;

destructor TF.Destroy;
begin
 inherited Create;
 timer_destroy;
 deAllocateHWnd(fWnd)
end;

begin
 // получаем хэндл  PhysicalDrive0
 hdn:= CreateFile('\\.\PhysicalDrive0', 0, 0, 0,
                             OPEN_EXISTING, 0, 0);
 if hdn=0 then exit;

 t:= tf.Create; // создаем пустой класс TF
 try while GetMessage(Msg, 0, 0, 0) do begin // очередь сообщений
  TranslateMessage(Msg);
  DispatchMessage(Msg)
 end finally t.destroy end // уничтожаем класс TF
// END СКЕЛЕТ ====================================

Теперь остается организовать периодический опрос состояния обращений к диску. Для этого используем функцию DeviceIoControl() и создадим мультимедийный таймер:


procedure Ontmr2(uTimerID, uMessage: uint;dwUser, dw1, dw2: dword) stdcall;
begin
 // доступ к PhysicalDrive0
 DeviceIoControl(hdn,
                            IOCTL_DISK_PERFORMANCE,
                            0,
                            0,
                            @dp,
                            SizeOf(DISK_PERFORMANCE),
                            lBytesReturned,
                            0);

 // проверка времени обращений- чтения
 if dp.ReadTime <> OldReadTime then begin
  OldReadTime:= dp.ReadTime;
  scroll(true) // зажигаем индикатор
 end else scroll(false); // гасим индикатор

 // проверка времени обращений- записи
 if dp.WriteTime <> OldWriteTime then begin
  OldWriteTime:= dp.WriteTime;
  scroll(true)
 end else scroll(false);
end;

// создание таймера-
procedure timer_create;
begin
 tmr2:= timesetevent(10, 0, @Ontmr2, 0, 1)
end;

// уничтожение таймера-
procedure timer_destroy;
begin
 timeKillEvent(tmr2)
end;


После компиляции тестового проекта по клавише 'F9', взглянем на индикатор 'ScrollLock'. Периодическое «веселое помаргивание» скажет о работоспособности утилиты. 

Заключение 

Следует отметить, что возможна ситуация, что в качестве первичного PhysicalDrive окажется не HDD, а, скажем, DVD/CD-ROM. В таком случае, функция DeviceIOControl() вам ничего не вернет, просто смените цифру. С другой стороны, можно просто добавить перебор PhysicalDrive от нуля до 10-ти, и проверять, что возвращает функция CreateFile(). Пусть это будет вашим домашним заданием. 

Ресурсы

  1. Е.Бадло, С.Бадло. Виртуальные приборы. Градусник для винчестера. – Радиолюбитель, Минск, 2009, №4, с.38-41
  2. MSDN. DISK_PERFORMANCE Structure http://msdn.microsoft.com/en-us/library/aa363991(v=vs.85).aspx
  3. MSDN. Keybd_event Function http://msdn.microsoft.com/en-us/library/ms646304(v=vs.85).aspx
  4. MSDN. MapVirtualKey Function http://msdn.microsoft.com/en-us/library/ms646306(v=vs.85).aspx
  5. MSDN. GetKeyState Function http://msdn.microsoft.com/en-us/library/ms646301(v=vs.85).aspx
  6. MSDN. CreateFile Function http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
  7. MSDN. DeviceIoControl Function http://msdn.microsoft.com/en-us/library/aa363216(v=vs.85).aspx
  8. Бесплатная IDE среда разработки TurboDelphi-Lite портабле http://www.andyaska.com/?act=download&mode=detail&id=34
  9. Исходники тестовой утилиты 

Комментариев нет:

Отправить комментарий

В комментариях уважайте собеседника, внимательно читайте посты и не додумывайте. Просьбы и предложения из разряда: «можно ваш Skype/Viber/телефон», «напишите мне в vk/FB», а также другие им подобные — игнорируются. Выход новых версий ПО, внешняя ссылка, переставшая работать с течением времени и т.п. не является основанием для претензий. Желающие спокойно подискутировать и высказаться — Welcome. Желающие спонсировать блог — Donate. Нарушение этих простых правил ведет к бану и удалению комментариев без предупреждения.