Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование для Windows NT

© Александр Фролов, Григорий Фролов
Том 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр.

[Назад] [Содеожание] [Дальше]

Приложение DiskInfo

Если ваше приложение просто открывает файлы или создает новые, возможно, для выбора файлов вам будет достаточно использовать стандартные диалоговые панели, которые мы создавали в предыдущем приложении. Однако во многих случаях вам необходимо предоставить пользователю детальную информацию о дисковых устройствах, такую, например, как тип файловой системы, общий объем диска, размер свободного пространства на диске, а также тип диска (локальный, сетевой, со сменным или несменным носителем данных, устройство чтения CD-ROM XE "CD-ROM" и так далее).

В этой главе мы приведем исходные тексты приложения DiskInfo, которое получает и отображает подробную информацию о всех дисковых устройствах, имеющихся в системе, как локальных, так и удаленных (сетевых). Информация отображается в табличном виде с помощью органа управления List View, который мы подробно описали в 22 томе “Библиотеки системного программиста”, посвященному операционной системе Microsoft Windows 95.

Внешний вид главного окна приложения DiskInfo, запущенного в одном из режимов отображения, показан на рис. 1.6.

Рис. 1.6. Просмотр информации о дисках в табличном виде

В столбце Drive отображаются пиктограммы и названия дисковых устройств, имеющихся в системе. Для каждого типа устройства используется своя пиктограмма.

В столбце Volume name для каждого устройства располагается метка тома (если она есть). В столбце File system мы отображаем имя файловой системы.

Так как максимальная длина имени файлов и каталогов разная для различных файловых систем, то в столбце File name length мы отображаем этй длину для каждого дискового устройства.

В столбцах Total Space и Free Space выводится, соответственно, емкость диска в байтах и размер свободного пространства на диске (также в байтах).

Заметим, что при первом запуске приложение DiskInfo не пытается определить параметры для устройств со сменными носителями данных (устройства НГМД, устройства чтения CD-ROM XE "CD-ROM" , магнитооптические накопители и так далее). Это связано с тем, что при попытке определить параметры устройства операционная система Microsoft Windows NT выполняет обращение к соответствующему накопителю. В том случае, если в накопителе нет носителя данных, на экране появится предупреждающее сообщение.

Тем не менее, наше приложение может определить параметры устройств со сменными носителями данных. Для этого вы должны вставить носитель (дискету, компакт-диск и так далее) в устройство, а затем сделать двойной щелчок левой клавишей мыши по пиктограмме устройства. После этого соответствующая строка таблицы будет заполнена, как это показано на рис. 1.7.

Рис. 1.7. Полностью заполненная таблица параметров дисковых устройств

Кроме того, после двойного щелчка по пиктограмме любого дискового устройства на экране появляется диалоговая панель Logical Drive Information, в которой отображается имя устройства, имя файловой системы, серийный номер, а также системные флаги (рис. 1.8 - 1.11). Системные флаги мы описали в последней главе предыдущего тома “Библиотеки системного программиста”.

Рис. 1.8. Просмотр дополнительной информации о диске FAT

Рис. 1.9. Просмотр дополнительной информации о диске NTFS

Рис. 1.10. Просмотр дополнительной информации о диске HPFS

Рис. 1.11. Просмотр дополнительной информации о диске CDFS

С помощью меню Options, показанного на рис. 1.12, вы можете изменить режим отображения списка дисковых устройств.

Рис. 1.12. Меню Options, предназначенное для изменения режима отображения списка дисковых устройств

Если выбрать из меню Options строку Icon view, внешний вид главного окна приложения изменится. Этот список будет отображаться в виде набора пиктограмм стандартного размера с подписью (рис. 1.13).

Рис. 1.13. Просмотр дисковых устройств в виде пиктограмм стандартного размера

Если же из этого меню выбрать строку Small icon view, для отображения списка устройств будут использованы маленькие пиктограммы (рис. 1.14).

Рис. 1.14. Просмотр дисковых устройств в виде пиктограмм маленького размера

Есть и еще один вариант, показанный на рис. 1.15. Он используется при выборе из меню Options строки List View.

Рис. 1.15. Просмотр дисковых устройств в виде списка

Если же из меню Options выбрать строку Report view, список дисковых устройств будет отображаться в виде таблицы, как это было показано на рис. 1.6 и 1.7.

Приложение DiskInfo может работать и в среде операционной системы Microsoft Windows 95 (рис. 1.16).

Рис. 1.16. Просмотр информации о дисковых устройствах в среде операционной системы Microsoft Windows 95

Компьютер, на котором была запущена программа в этом случае, был оборудован 3,5 дюймовым НГМД (устройство A:), 5,25 дюймовым НГМД (устройство B:), обычным жестким диском (устройство C:), магнитооптическим накопителем MaxOptix с емкостью 1,3 Гбайт (устройство D:), и устройством чтения CD-ROM XE "CD-ROM" (устройство E:).

Кроме того, этот компьютер был подключен к сети, в которой есть серверы на базе операционных систем Microsoft Windows NT Server и Novell NetWare. Устройства F: и G: отображаются на сетевые тома сервера Microsoft Windows NT Server, а устройства S:, T: и U: - на сетевые тома сервера Novell NetWare.

Заметим, что в отличие от сервера Microsoft Windows NT Server, сервер Novell NetWare не был настроен таким образом, чтобы можно было работать с длинными именами файлов и каталогов.

Исходные тексты приложения

Главный файл исходных текстов приложения DiskInfo приведен в листинге 1.5.

Заметим, что так как в приложении используется орган управления List View, в файле проекта вы должны подключить библиотеку comctl32.lib. В противном случае редактор связей выдаст сообщения об ошибках.

Листинг 1.5. Файл DiskInfo/DiskInfo.c


// ==================================================
// Приложение DiskInfo
// Получение и отображение информации о дисковых
// устройствах, имеющихся в системе
//
// (С) Фролов А.В., 1996
// Email: frolov@glas.apc.org
// ==================================================

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
#include "afxres.h"
#include "diskinfo.h"

// Структура для хранения информации о 
// логическом диске
typedef struct tagDISKINFO
{
  char  szDriveName[10];      // имя диска
  UINT  nDriveType;           // тип диска
  char  szVolumeName[30];     // имя тома
  DWORD dwVolumeSerialNumber; // серийный номер
  DWORD dwMaxFileNameLength;  // длина имени
  DWORD dwFileSystemFlags;    // системные флаги
  char  szFileSystemName[10]; // имя файловой системы
  int   iImage;               // номер пиктограммы
  DWORD dwFreeSpace;          // свободное пространство
  DWORD dwTotalSpace;         // общий объем диска
} DISKINFO;

// -----------------------------------------------------
// Глобальные переменные
// -----------------------------------------------------

HINSTANCE hInst;
char szAppName[]  = "DriveInfoApp";
char szAppTitle[] = "Logical Drive Information";
HWND hwndList;

// Указатель на область памяти, в которую будет
// записан массив строк имен дисков
LPSTR lpLogicalDriveStrings;

// Указатель на массив структур типа DISKINFO,
// в котором будет хранится информация о дисках
DISKINFO *pdi;

// Количество логических дисков в системе
int nNumDirves;

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    if(IsIconic(hWnd))
      ShowWindow(hWnd, SW_RESTORE);
    SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
      return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle, 
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
    NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  // Отображаем окно и запускаем цикл обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_CREATE,     WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY,    WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_COMMAND,    WndProc_OnCommand);
    HANDLE_MSG(hWnd, WM_NOTIFY,     WndProc_OnNotify);
    HANDLE_MSG(hWnd, WM_SIZE,       WndProc_OnSize);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, 
                      LPCREATESTRUCT lpCreateStruct)
{
  int i;
  RECT rc;
  HIMAGELIST himlSmall;
  HIMAGELIST himlLarge;
  HICON hIcon;
  LV_COLUMN lvc;
  LV_ITEM lvi;

  // Получаем информацию о логических 
  // дисковых устройствах
  GetDiskInfo();
  
  // Определяем размеры внутренней области главного окна
  GetClientRect(hWnd, &rc);

  // Инициализируем библиотеку стандартных 
  // органов управления
  InitCommonControls();

  // Создаем орган управления List View
  hwndList = CreateWindowEx(0L, WC_LISTVIEW, "",
    WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT,
    0, 0, rc.right - rc.left, rc.bottom - rc.top,
    hWnd, (HMENU) IDC_LISTVIEW, hInst, NULL);

  if(hwndList == NULL)
    return FALSE;

  // Создаем списки изображений
  himlSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), 
    GetSystemMetrics(SM_CYSMICON), ILC_MASK, 8, 1);
  himlLarge = ImageList_Create(
    GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
    ILC_MASK, 8, 1);

  // Добавляем в списки пиктограммы 
  // изображений дисковых устройств
  // Изображения с номером 0
  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DREMOVE));
  ImageList_AddIcon(himlLarge, hIcon);

  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DREMOVSM));
  ImageList_AddIcon(himlSmall, hIcon);
  
  // Изображения с номером 1
  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DFIXED));
  ImageList_AddIcon(himlLarge, hIcon);

  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DFIXEDSM));
  ImageList_AddIcon(himlSmall, hIcon);
  
  // Изображения с номером 2
  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DRCD));
  ImageList_AddIcon(himlLarge, hIcon);

  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DRCDSM));
  ImageList_AddIcon(himlSmall, hIcon);
  
  // Изображения с номером 3
  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DNET));
  ImageList_AddIcon(himlLarge, hIcon);

  hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_DNETSM));
  ImageList_AddIcon(himlSmall, hIcon);

  // Добавляем списки изображений
  ListView_SetImageList(hwndList, himlSmall, LVSIL_SMALL);
  ListView_SetImageList(hwndList, himlLarge, LVSIL_NORMAL);

  // Вставляем столбцы
  memset(&lvc, 0, sizeof(lvc));

  lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  lvc.fmt = LVCFMT_LEFT;
  lvc.cx = (rc.right - rc.left) / 10;
  
  lvc.iSubItem = 0;
  lvc.pszText = "Drive";
  ListView_InsertColumn(hwndList, 0, &lvc);

  lvc.iSubItem = 1;
  lvc.pszText = "Volume name";
  ListView_InsertColumn(hwndList, 1, &lvc);

  lvc.iSubItem = 2;
  lvc.pszText = "File system";
  ListView_InsertColumn(hwndList, 2, &lvc);

  lvc.fmt = LVCFMT_RIGHT;

  lvc.iSubItem = 3;
  lvc.pszText = "File name length";
  ListView_InsertColumn(hwndList, 3, &lvc);

  lvc.iSubItem = 4;
  lvc.pszText = "Total Space";
  ListView_InsertColumn(hwndList, 4, &lvc);

  lvc.iSubItem = 5;
  lvc.pszText = "Free Space";
  ListView_InsertColumn(hwndList, 5, &lvc);

  // Вставляем строки
  memset(&lvi, 0, sizeof(lvi));

  lvi.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_PARAM;
  lvi.pszText = LPSTR_TEXTCALLBACK;
  
  // Цикл по всем имеющимся логическим устройствам
  for(i=0; i<nNumDirves; i++)
  {
    lvi.iItem = i;
    lvi.iSubItem = 0;
    lvi.cchTextMax = 40;
    lvi.lParam = (LPARAM)(pdi + i)->szDriveName;
	
    lvi.iImage = (pdi + i)->iImage;
    ListView_InsertItem(hwndList, &lvi);

    lvi.iItem = i;
    lvi.iSubItem = 1;
    ListView_InsertItem(hwndList, &lvi);

    lvi.iItem = i;
    lvi.iSubItem = 2;
    ListView_InsertItem(hwndList, &lvi);

    lvi.iItem = i;
    lvi.iSubItem = 3;
    ListView_InsertItem(hwndList, &lvi);

    lvi.iItem = i;
    lvi.iSubItem = 4;
    ListView_InsertItem(hwndList, &lvi);

    lvi.iItem = i;
    lvi.iSubItem = 5;
    ListView_InsertItem(hwndList, &lvi);
  }

  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  DestroyWindow(hwndList);
  
  // Освобождаем память
  free(lpLogicalDriveStrings);
  free(pdi);
  
  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  DWORD dwStyle = 0;

  switch (id)
  {
    case ID_OPTIONS_ICONVIEW:
    {
      dwStyle = GetWindowLong(hwndList, GWL_STYLE);
	  
      if((dwStyle & LVS_TYPEMASK) != LVS_ICON)
        SetWindowLong(hwndList, GWL_STYLE, 
          (dwStyle & ~LVS_TYPEMASK) | LVS_ICON);
      break;
    }

    case ID_OPTIONS_SMALLICONVIEW:
    {
      dwStyle = GetWindowLong(hwndList, GWL_STYLE);
	  
      if((dwStyle & LVS_TYPEMASK) != LVS_SMALLICON)
        SetWindowLong(hwndList, GWL_STYLE, 
          (dwStyle & ~LVS_TYPEMASK) | LVS_SMALLICON);
      break;
    }
  	
    case ID_OPTIONS_LISTVIEW:
    {
      dwStyle = GetWindowLong(hwndList, GWL_STYLE);
	  
      if((dwStyle & LVS_TYPEMASK) != LVS_LIST)
        SetWindowLong(hwndList, GWL_STYLE, 
          (dwStyle & ~LVS_TYPEMASK) | LVS_LIST);
      break;
    }
  	
    case ID_OPTIONS_REPORTVIEW:
    {
      dwStyle = GetWindowLong(hwndList, GWL_STYLE);
	  
      if((dwStyle & LVS_TYPEMASK) != LVS_REPORT)
        SetWindowLong(hwndList, GWL_STYLE, 
          (dwStyle & ~LVS_TYPEMASK) | LVS_REPORT);
      break;
    }

    case ID_FILE_EXIT:
      PostQuitMessage(0);
      return 0L;
    break;
	  
    case ID_HELP_ABOUT:
      MessageBox(hWnd, 
        "Disk Information Browser\n"
        "(C) Alexandr Frolov, 1996\n"
        "Email: frolov@glas.apc.org",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
      break;

    default:
      break;
  }
  return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify,
    DefWindowProc);
}

// -----------------------------------------------------
// Функция WndProc_OnNotify
// -----------------------------------------------------
LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, NMHDR* pnmhdr)
{
  LV_DISPINFO * lpLvdi = (LV_DISPINFO *)pnmhdr;
  DISKINFO * lpDiskInfo = (DISKINFO *)(lpLvdi->item.lParam);
  static char szBuf[20];
  DWORD dwSectors, dwClusters, dwFreeClusters, dwBytes;

  NM_LISTVIEW *lpNm = (NM_LISTVIEW *)pnmhdr;

  if(idFrom != IDC_LISTVIEW)
    return 0L;

  switch(pnmhdr->code)
  {
    case LVN_GETDISPINFO:
    {
      if(lpLvdi->item.mask & LVIF_TEXT)
      {
        switch(lpLvdi->item.iSubItem)
        {
          case 0:
            lpLvdi->item.pszText = lpDiskInfo->szDriveName;
            break;
          case 1:
            lpLvdi->item.pszText = lpDiskInfo->szVolumeName;
	     break;
          case 2:
            lpLvdi->item.pszText = 
              lpDiskInfo->szFileSystemName;
	     break;
          case 3:
            ltoa(lpDiskInfo->dwMaxFileNameLength, szBuf, 10);
            lpLvdi->item.pszText = szBuf;
            break;
          case 4:
            ltoa(lpDiskInfo->dwTotalSpace, szBuf, 10);
            lpLvdi->item.pszText = szBuf;
            break;
          case 5:
            ltoa(lpDiskInfo->dwFreeSpace, szBuf, 10);
            lpLvdi->item.pszText = szBuf;
            break;
          default:
	     break;
        }
        break;
      }
    }

    case NM_DBLCLK:
    {
      int index;
      char szBuf[256];

      // Определяем номер выбранного элемента списка
      index = ListView_GetNextItem(hwndList,
        -1, LVNI_ALL | LVNI_SELECTED);
      if(index == -1)
  	    return 0;

      // Получаем информацию о выбранном устройстве
      GetVolumeInformation((pdi + index)->szDriveName, 
        (pdi + index)->szVolumeName, 30,
        &((pdi + index)->dwVolumeSerialNumber),
        &((pdi + index)->dwMaxFileNameLength),
        &((pdi + index)->dwFileSystemFlags),
        (pdi + index)->szFileSystemName, 10);

      // Определяем объем свободного пространства 
      // на диске и общую емкость диска
      GetDiskFreeSpace((pdi + index)->szDriveName, 
        &dwSectors, &dwBytes, &dwFreeClusters, &dwClusters);

      (pdi + index)->dwFreeSpace = 
        dwSectors * dwBytes * dwFreeClusters;

      (pdi + index)->dwTotalSpace = 
        dwSectors * dwBytes * dwClusters;

      // Подготавливаем для отображения имя диска,
      // имя файловой системы, серийный номер и
      // список системных флагов
      sprintf(szBuf, "System flags for drive %s (%s)\n"
        "Serial number: %lX\n",
        (pdi + index)->szDriveName,
        (pdi + index)->szFileSystemName,
        (pdi + index)->dwVolumeSerialNumber);

      if((pdi + index)->dwFileSystemFlags 
        & FS_CASE_IS_PRESERVED)
        strcat(szBuf, "\nFS_CASE_IS_PRESERVED");

      if((pdi + index)->dwFileSystemFlags 
        & FS_CASE_SENSITIVE)
        strcat(szBuf, "\nFS_CASE_SENSITIVE");

      if((pdi + index)->dwFileSystemFlags 
        & FS_UNICODE_STORED_ON_DISK)
        strcat(szBuf, "\nFS_UNICODE_STORED_ON_DISK");

      if((pdi + index)->dwFileSystemFlags 
        & FS_PERSISTENT_ACLS)
        strcat(szBuf, "\nFS_PERSISTENT_ACLS");

      if((pdi + index)->dwFileSystemFlags 
        & FS_FILE_COMPRESSION)
        strcat(szBuf, "\nFS_FILE_COMPRESSION");

      if((pdi + index)->dwFileSystemFlags 
        & FS_VOL_IS_COMPRESSED)
        strcat(szBuf, "\nFS_VOL_IS_COMPRESSED");

      // Перерисовываем главное окно приложения для
      // отражения изменений в окне списка
      InvalidateRect(hWnd, NULL, TRUE);

      MessageBox(hWnd, szBuf, szAppTitle, MB_OK);
      return 0L;
      break;
    }
  }
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnSize       
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
  MoveWindow(hwndList, 0, 0, cx, cy, TRUE);
  return FORWARD_WM_SIZE(hwnd, state, cx, cy, DefWindowProc);
}

// -----------------------------------------------------
// Функция GetDiskInfo
// -----------------------------------------------------
void GetDiskInfo(void)
{
  DWORD dwDriveStringsSpace;
  LPSTR lpTemp;
  int i;
  DWORD dwSectors, dwClusters, dwFreeClusters, dwBytes;

  // Определяем размер блока памяти, необходимый для
  // записи имен всех логических дисков
  dwDriveStringsSpace = GetLogicalDriveStrings(0, NULL);
  
  // Получаем память
  lpLogicalDriveStrings = malloc(dwDriveStringsSpace);
  
  // Заполняем полученный блок памяти именами дисков
  GetLogicalDriveStrings(dwDriveStringsSpace, 
    lpLogicalDriveStrings);

  // Подсчитываем количество дисков, сканируя список
  // имен, полученный на предыдущем шаге
  nNumDirves = 0;
  for(lpTemp = lpLogicalDriveStrings; 
    *lpTemp != 0; nNumDirves++)
  {
    lpTemp = strchr(lpTemp, 0) + 1;
  }

  // Заказываем память для хранения информации 
  // о всех дисках
  pdi = malloc(nNumDirves * sizeof(DISKINFO));

  // Заполняем массив структур DISKINFO информацией о дисках
  for(i = 0, lpTemp = lpLogicalDriveStrings; 
  i < nNumDirves; i ++)
  {
    // Получаем имя очередного диска
    strcpy((pdi + i)->szDriveName, lpTemp);
    
    // Определяем тип диска
    (pdi + i)->nDriveType = GetDriveType(lpTemp);
    
    // В зависимости от типа диска выбираем способ
    // заполнения соответствующей структуры DISKINFO
    switch ((pdi + i)->nDriveType)
    {
      // Для сменных устройств и для CD-ROM
      // записываем пустые значения
      case DRIVE_REMOVABLE:
      {
          // Выбираем пиктограмму с номером 0
          (pdi + i)->iImage               = 0;
          
          strcpy((pdi + i)->szVolumeName, "<Unknown>");
          (pdi + i)->dwVolumeSerialNumber = 0;
          (pdi + i)->dwMaxFileNameLength  = 0;
          (pdi + i)->dwFileSystemFlags    = 0;
          strcpy((pdi + i)->szFileSystemName, "?");
          (pdi + i)->dwFreeSpace          = 0;
          (pdi + i)->dwTotalSpace         = 0;

        break;
      }

      case DRIVE_CDROM:
      {
          (pdi + i)->iImage               = 2;
          strcpy((pdi + i)->szVolumeName, "<Unknown>");
          (pdi + i)->dwVolumeSerialNumber = 0;
          (pdi + i)->dwMaxFileNameLength  = 0;
          (pdi + i)->dwFileSystemFlags    = 0;
          strcpy((pdi + i)->szFileSystemName, "?");
          (pdi + i)->dwFreeSpace          = 0;
          (pdi + i)->dwTotalSpace         = 0;
        break;
      }

      // Получаем информацию для несменных устройств
      case DRIVE_FIXED:
      {
        (pdi + i)->iImage = 1;
        GetVolumeInformation(lpTemp, 
          (pdi + i)->szVolumeName, 30,
          &((pdi + i)->dwVolumeSerialNumber),
          &((pdi + i)->dwMaxFileNameLength),
          &((pdi + i)->dwFileSystemFlags),
          (pdi + i)->szFileSystemName, 10);
        
        GetDiskFreeSpace(lpTemp, &dwSectors, &dwBytes, 
          &dwFreeClusters, &dwClusters);

        (pdi + i)->dwFreeSpace = 
          dwSectors * dwBytes * dwFreeClusters;

        (pdi + i)->dwTotalSpace = 
          dwSectors * dwBytes * dwClusters;
        break;
      }

      // Получаем информацию для сетевых томов
      case DRIVE_REMOTE:
      {
        (pdi + i)->iImage = 3;
        GetVolumeInformation(lpTemp, 
          (pdi + i)->szVolumeName, 30,
          &((pdi + i)->dwVolumeSerialNumber),
          &((pdi + i)->dwMaxFileNameLength),
          &((pdi + i)->dwFileSystemFlags),
          (pdi + i)->szFileSystemName, 10);

        GetDiskFreeSpace(lpTemp, &dwSectors, &dwBytes, 
          &dwFreeClusters, &dwClusters);

        (pdi + i)->dwFreeSpace = 
          dwSectors * dwBytes * dwFreeClusters;

        (pdi + i)->dwTotalSpace = 
          dwSectors * dwBytes * dwClusters;
        break;
      }
     
      // Прочие дисковые устройства
      default:
      {
        (pdi + i)->iImage               = 1;
        GetVolumeInformation(lpTemp, 
          (pdi + i)->szVolumeName, 30,
          &((pdi + i)->dwVolumeSerialNumber),
          &((pdi + i)->dwMaxFileNameLength),
          &((pdi + i)->dwFileSystemFlags),
          (pdi + i)->szFileSystemName, 10);

        GetDiskFreeSpace(lpTemp, &dwSectors, &dwBytes, 
          &dwFreeClusters, &dwClusters);

        (pdi + i)->dwFreeSpace = 
          dwSectors * dwBytes * dwFreeClusters;

        (pdi + i)->dwTotalSpace = 
          dwSectors * dwBytes * dwClusters;
       break;
     }
    }

    // Переходим к следующей строке в списке
    // имен дисков
    lpTemp = strchr(lpTemp, 0) + 1;
  }
}

Файл diskinfo.h (листинг 1.6) содержит прототипы функций, определенных в приложении, а также определение идентификатора органа управления List View с именем IDC_LISTVIEW.

Листинг 1.6. Файл DiskInfo/diskinfo.h


#define IDC_LISTVIEW 1234
// -----------------------------------------------------
// Описание функций
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

BOOL WndProc_OnCreate(HWND hWnd, 
  LPCREATESTRUCT lpCreateStruct);
void WndProc_OnDestroy(HWND hWnd);
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);
LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, 
  NMHDR FAR* pnmhdr);
void WndProc_OnSize(HWND hwnd, UINT state, int cx, int cy);
void WndProc_OnDrawItem(HWND hwnd, 
  const DRAWITEMSTRUCT * lpDrawItem);
void GetDiskInfo(void);

Файл resource.h (листинг 1.7) создается автоматически и содержит определения констант для файла описания ресурсов приложения, который будет приведен ниже.

Листинг 1.7. Файл DiskInfo/resource.h


//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by DiskInfo.RC
//
#define IDR_APPMENU                     102
#define IDI_APPICON                     103
#define IDI_APPICONSM                   104
#define IDI_DREMOVE                     115
#define IDI_DREMOVSM                    116
#define IDI_DFIXED                      117
#define IDI_DFIXEDSM                    118
#define IDI_DRCD                        119
#define IDI_DRCDSM                      120
#define IDI_DNET                        121
#define IDI_DNETSM                      122
#define ID_FILE_EXIT                    40001
#define ID_HELP_ABOUT                   40003
#define ID_OPTIONS_ICONVIEW             40004
#define ID_OPTIONS_SMALLICONVIEW        40005
#define ID_OPTIONS_LISTVIEW             40006
#define ID_OPTIONS_REPORTVIEW           40007

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        123
#define _APS_NEXT_COMMAND_VALUE         40008
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

Файл описания ресурсов приложения diskinfo.rc представлен в листинге 1.8.

Листинг 1.8. Файл DiskInfo/diskinfo.rc


//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

//////////////////////////////////////////////////////////////
//
// Menu
//

IDR_APPMENU MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "E&xit",             ID_FILE_EXIT
    END
    POPUP "&Options"
    BEGIN
        MENUITEM "&Icon view",        ID_OPTIONS_ICONVIEW
        MENUITEM "&Small icon view",  ID_OPTIONS_SMALLICONVIEW
        MENUITEM "&List view",        ID_OPTIONS_LISTVIEW
        MENUITEM "&Report view",      ID_OPTIONS_REPORTVIEW
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",         ID_HELP_ABOUT
    END
END

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END
3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END
#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure 
// application icon
// remains consistent on all systems.
IDI_APPICON             ICON    DISCARDABLE     "diskinfo.ico"
IDI_APPICONSM           ICON    DISCARDABLE     "dinfosm.ico"
IDI_DREMOVE             ICON    DISCARDABLE     "DREMOV.ICO"
IDI_DREMOVSM            ICON    DISCARDABLE     "DREMOVSM.ICO"
IDI_DFIXED              ICON    DISCARDABLE     "DFIXED.ICO"
IDI_DFIXEDSM            ICON    DISCARDABLE     "DFIXEDSM.ICO"
IDI_DRCD                ICON    DISCARDABLE     "DRCD.ICO"
IDI_DRCDSM              ICON    DISCARDABLE     "DRCDSM.ICO"
IDI_DNET                ICON    DISCARDABLE     "DNET.ICO"
IDI_DNETSM              ICON    DISCARDABLE     "DNETSM.ICO"

//////////////////////////////////////////////////////////////
//
// String Table
//

STRINGTABLE DISCARDABLE 
BEGIN
    ID_FILE_EXIT            "Quits the application"
    ID_OPTIONS_ICONVIEW     "Each item appears as a full-sized icon"
    ID_OPTIONS_SMALLICONVIEW "Each item appears as a small icon"
    ID_OPTIONS_LISTVIEW     "Each item appears as a small icon arranged in columns"
    ID_OPTIONS_REPORTVIEW   "Each item appears with subitems arranged in columns"
END

#endif    // English (U.S.) resources
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Определения и глобальные переменные

В начале своей работы приложение DiskInfo получает список имен дисков в виде текстовых строк. Каждая такая строка закрыта двоичным нулем, а последняя - двумя нулевыми байтами. Адрес списка приложение записывает в глобальную переменную lpLogicalDriveStrings.

После получения списка имен приложение сканирует его с целью подсчета количества дисковых устройств. Это количество сохраняется в глобальной переменной nNumDirves.

Далее приложение заказывает память для массива структур типа DISKINFO:

typedef struct tagDISKINFO { char szDriveName[10]; // имя диска UINT nDriveType; // тип диска char szVolumeName[30]; // имя тома DWORD dwVolumeSerialNumber; // серийный номер DWORD dwMaxFileNameLength; // длина имени DWORD dwFileSystemFlags; // системные флаги char szFileSystemName[10]; // имя файловой системы int iImage; // номер пиктограммы DWORD dwFreeSpace; // свободное пространство DWORD dwTotalSpace; // общий объем диска } DISKINFO;

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

Адрес массива структур DISKINFO хранится в глобальной переменной pdi.

Описание функций

В этом разделе мы кратко расскажем о функциях, определенных в приложении DiskInfo.

Функция WinMain

Функция WinMain проверяет существование запущенной ранее копии приложения. Если такая копия будет обнаружена, окно работающей копии активизируется и выдвигается на передний план. Если нет - функция WinMain выполняет обычную инициализацию приложения, создавая его главное окно и запуская цикл обработки сообщений.

Функция WndProc

В задачу функции WndProc входит обработка сообщений WM_CREATE, WM_DESTROY, WM_COMMAND, WM_NOTIFY и WM_SIZE. Для обработки этих сообщений вызываются, соответственно, функции WndProc_OnCreate, WndProc_OnDestroy, WndProc_OnCommand, WndProc_OnNotify и WndProc_OnSize.

Все остальные сообщения передаются функции DefWindowProc, выполняющей обработку по умолчанию.

Функция WndProc_OnCreate

Эта функция обрабатывает сообщение WM_CREATE, поступающее в функцию окна при его создании.

Вначале функция WndProc_OnCreate вызывает функцию GetDiskInfo, которая получает информацию о логических дисках, имеющихс в системе, и сохраняет ее в массиве структур DISKINFO XE "DISKINFO" .

Далее обработчик сообщения WM_CREATE определяет размеры внутренней области главного окна приложения, инициализирует библиотеку стандартных органов управления и создает орган управления List View на базе предопределенного класса окна WC_LISTVIEW, вызывая для этого функцию CreateWindowEx.

На следующем этапе приложение создает два списка изображений. Первый из них (с идентификатором himlSmall) будет содержать пиктограммы дисковых устройств маленького размера, а второй (с идентификатором himlLarge) - эти же пиктограммы, но стандартного размера.

С помощью макрокоманды ImageList_AddIcon в эти списки добавляются пиктограммы с изображениями дисков. Каждое такое изображение хранится в списке под собственным номером. Например, под номером 0 хранятся пиктограммы с идентификатором IDI_DREMOVE и IDI_DREMOVSM (сетевые устройства), под номером 1 - пиктограммы с идентификатором IDI_DFIXED и IDI_DFIXEDSM (диск с несменным носителем данных) и так далее. Номера пиктограмм используются при формировании строк списка, отображаемого при помощи органа управления List View.

После добавления всех пиктограмм сформированные списки подключаются к органу управления List View с помощью макрокоманды ListView_SetImageList.

Далее обработчик сообщения WM_CREATE вставляет столбцы, задавая для них различное выравнивание текста. Текст в столбцах Drive, Volume name и File system выравнивается по левой границе, а текст в столбцах File name length, Total Space и Free Space - по правой.

Вставка строк выполняется в цикле с помощью макрокоманды ListView_InsertItem.

Более подробную информацию о работе с органом управления List View вы можете найти в 22 томе “Библиотеки системного программиста”, посвященному программированию для операционной системы Microsoft Windows 95.

Функция WndProc_OnDestroy

При уничтожении главного окна приложения обработчик сообщения WM_DESTROY удаляет орган управления List View и освобождает память, заказанную у операционной системы для списка строк с именами дисков и массива структур DISKINFO XE "DISKINFO" .

Далее обработчик вызывает функцию PostQuitMessage, инициируя завершения цикла обработки сообщений.

Функция WndProc_OnCommand

Эта функция обрабатывает сообщение WM_COMMAND, поступающее от главного меню приложения.

Выбирая строки меню Options, вы можете изменить внешний вид окна органа управления List View, выбрав один из четырех режимов отображения. Соответствующие процедуры мы описали в 22 томе “Библиотеки системного программиста”.

Функция WndProc_OnNotify

Функция WndProc_OnNotify обрабатывает извещения, поступающие от органа управления List View в главное окно приложения.

Обработчик извещения LVN_GETDISPINFO выбирает информацию из элементов массива структур DISKINFO, и предоставляет ее для отображения в окне органа управления List View.

Для нас сейчас больший интерес представляет обработчик извещения NM_DBLCLK, который получает управление, когда пользователь делает двойной щелчок левой клавишей мыши по пиктограмме дискового устройства в окне органа управления List View.

Вначале с помощью макрокоманды ListView_GetNextItem обработчик извещения NM_DBLCLK определяет номер выбранного элемента и записывает его в локальную переменную index.

Далее вызывается функция GetVolumeInformation, с помощью которой выполняется заполнение соответствующего элемента массива структур DISKINFO. Значение, записанное в переменную index служит при этом индексом в массиве структур.

Заполнение структуры завершается функцией GetDiskFreeSpace, с помощью которой определяется такая информация, как общий объем диска в байтах и объем свободного пространства на диске. Эта функция сохраняет в локальных переменных dwClusters и dwFreeClusters, соответственно, общее количество кластеров, имеющихся на диске, и количество свободных кластеров.

В локальные переменные dwSectors и dwBytes записывается количество секторов в одном кластере и размер сектора в байтах. Эти значения используются для вычисления общего и свободного объема диска исходя из общего количества кластеров и количества свободных кластеров.

После обновления элемента массива структур DISKINFO, соответтсвующего выбранному диску, обработчик извещения NM_DBLCLK отображает на экране диалоговую панель с такой информацией, как название диска, имя файловой системы, серийный номер диска и системные флаги.

Перед выводом указанной диалоговой панели мы перерисовываем содержимое окна органа управления List View, для чего вызываем функцию InvalidateRect.

Зачем мы это делаем?

При запуске приложения мы получаем список логических дисковых устройств и определяем их параметры, однако только для устройств с несменными носителями данных. В противном случае операционная система попытается выполнить обращение к устройству (например, к НГМД) и, если вы заранее не вставите в него носитель данных, на экране появится сообщение об ошибке.

Если же вы вначале вставите носитель в устройство, а затем в окне нашего приложения DiskInfo сделаете двойной щелчок левой клавишей мыши по пиктограмме устройства, обработчик извещения NM_DBLCLK получит параметры этого устройства и сохранит их в соответствующем элементе массива структур DISKINFO. Однако само по себе это не вызовет никаких изменений в списке параметров устройств, который отображается нашим приложением.

Вызывая функцию InvalidateRect, мы выполняем перерисовку главного окна приложения и его дочернего окна - окна органа управления List View. При этом обработчик извещения LVN_GETDISPINFO получает и отображает в окне обновленную информацию о параметрах устройства.

Функция WndProc_OnSize

В задачу функции WndProc_OnSize, обрабатывающей сообщение WM_SIZE, входит изменение размеров органа управления List View при изменении размеров главного окна приложения.

Функция GetDiskInfo

Функция GetDiskInfo вызывается обработчиком сообщения WM_CREATE при создании главного окна приложения. Она получает и сохраняет информацию о всех логических дисках, имеющихся в системе.

Из предыдущего тома “Библиотеки системного программиста” вы знаете, что с помощью функции GetLogicalDriveStrings можно получить список имен всех логических дисковых устройств. Через второй параметр этой функции необходимо передать адрес блока памяти, в который будет записан указанный выше список, а через первый - размер этого блока.

Однако заранее приложение не знает, сколько логических дисков имеется в системе и, соответственно, не знает, сколько места потребуется ему для сохранения списка логических устройств. Поэтому вначале мы вызываем функцию GetLogicalDriveStrings, передав ей значение 0 в качестве первого параметра и значение NULL - в качестве второго:


dwDriveStringsSpace = GetLogicalDriveStrings(0, NULL);

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

На следующем шаге наше приложение получает блок памяти необходимого размера, вызывая для этого функцию malloc. Адрес полученного блока и его размер затем передаются функции GetLogicalDriveStrings, которая в этом случае заполняет блок необходимой информацией:


GetLogicalDriveStrings(dwDriveStringsSpace,
  lpLogicalDriveStrings);

Если в компьютере имеются, например, логические диски A:, C: и D:, в блок памяти с адресом lpLogicalDriveStrings будут записаны следующие три строки:


A:\<0>
C:\<0>
D:\<0><0>

В конце каждой строки записывается байт с нулевым значением, а в конце последней строки - два нулевых байта.

Получив список имен устройств, наше приложение подсчитывает количество устройств. Для этого оно с помощью функции strchr сканирует список в цикле до тех пор, пока не будет найден его конец, подсчитывая количество проходов в глобальной переменной nNumDirves:


nNumDirves = 0;
for(lpTemp = lpLogicalDriveStrings; *lpTemp != 0; 
  nNumDirves++)
{
  lpTemp = strchr(lpTemp, 0) + 1;
}

Определив таким образом общее количество логических дисков, приложение заказывает память для массива структур типа DISKINFO, в котором будут хранится параметры логических дисков:


pdi = malloc(nNumDirves * sizeof(DISKINFO));

Заполнение массива структур DISKINFO выполняется в цикле.

Для каждого диска прежде всего выполняется копирование имени диска из соответствующей строки списка, полученного ранее при помощи функции GetLogicalDriveStrings.

Далее приложение определяет тип диска, вызывая для этого функцию GetDriveType (в локальной переменной lpTemp хранится имя диска):


(pdi + i)->nDriveType = GetDriveType(lpTemp);

Заполнение полей структуры DISKINFO выполняется по-разному в зависимости от типа диска.

Если устройство со сменным носителем данных, то в поле iImage, предназначенное для хранения номера пиктограммы диска, записывается нулевое значение. Именно под этим номером мы занесли пиктограмму диска со сменным носителем в список пиктограмм для органа управления List View.

В поле szVolumeName мы записываем строку <Unknown>, так как определение фактических параметров устройств со сменным носителем выполняется при обработке извещения NM_DBLCLK. Аналогичным образом заполняются и остальные поля структуры.

Заполнение структуры DISKINFO для устройств чтения CD-ROM выполняется точно так же, как и устройств со сменным носителем данных, за исключением того что в поле номера пиктограммы iImage записывается значение 2. Это номер пиктограммы с изображением накопителя CD-ROM в списке пиктограмм органа управления List View.

Если текущим устройством, для которого мы определяем параметры, является диск с несменным носителем данных, функция GetDiskInfo получает большинство этих параметров при помощи функции GetVolumeInformation, как это показано ниже:


GetVolumeInformation(lpTemp, (pdi + i)->szVolumeName, 30,
  &((pdi + i)->dwVolumeSerialNumber),
  &((pdi + i)->dwMaxFileNameLength),
  &((pdi + i)->dwFileSystemFlags), 
(pdi + i)->szFileSystemName, 10);

Для определения общего объема диска и объема свободного пространства дополнительно вызывается функция GetDiskFreeSpace:


GetDiskFreeSpace(lpTemp, &dwSectors, &dwBytes, &dwFreeClusters, 
  &dwClusters);

Значения, полученные от этой функции, обрабатываются следующим образом:


(pdi + i)->dwFreeSpace  = dwSectors * dwBytes*dwFreeClusters;
(pdi + i)->dwTotalSpace = dwSectors * dwBytes*dwClusters;

Объем свободного пространства в байтах записывается в поле dwFreeSpace. Он вычисляется как произведение сделующих величин: количества свободных кластеров на диске dwFreeClusters, количество секторов в одном кластере dwSectors и количества байт в одном сеткоре dwBytes.

Общий объем диска записывается в поле dwTotalSpace и подсчитывается аналогично, за исключением того что вместо количества свобдных кластеров используется общее количество кластеров на диске dwClusters.

В поле iImage записывается значение 1. Это номер пиктограммы с изображением диска с несменным носителем данных.

Получение и заполнение информации об удаленных (сетевых) дисковых устройствах выполняется аналогично, однако в поле iImage записывается значение 3.

Если тип устройства не поддается классификации, наше приложение получает информацию о его параметрах, а в поле iImage записывает значение 1.

[Назад] [Содеожание] [Дальше]