#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;
  }
}