Pár tipů pro práci s CD mechanikou v C++

V tomto článku si ukážeme pár možností pro ovládání a detekci různých událostí CD mechaniky.

Ukážeme si, jak zjistit jednotky CD v počítači, jak vybranou jednotku programově otevřít a zavřít, jak detekovat otevření a zavření dvířek uživatelem nebo jiným programem a jak detekovat automatické spuštění programu z CD mechaniky. Ukázková aplikace je MFC aplikace založená na dialogovém okně.

Zjištění přítomných CD mechanik

Nejprve si ukážeme, jak zjistit písmenné označení mechanik přítomných v počítači. Vytvoříme si funkci, která nám (při inicializaci dialogu) naplní list-box označením nalezených jednotek. Použijeme funkci

DWORD GetLogicalDrives(VOID);

která nám vrátí bitovou masku reprezentující dostupné CD mechaniky, a dále funkci

UINT GetDriveType(
  LPCTSTR lpRootPathName  // kořenový adrersář
);

Její návratová hodnota určuje typ zařízení. Pro jednotky CD (ale i DVD nebo CD-RW) nám funkce vrátí hodnotu DRIVE_CDROM. Celá naše zmíněná funkce vypadá potom takto:

int CUkazkaDlg::GetCDDrives()
{
  int pos = 0;
  DWORD dwList = GetLogicalDrives();
  int index = 0;
  TCHAR chDrive[4];
  lstrcpy(chDrive, "x:\\");
  while ( dwList )
  {
    if ( dwList & 1 )
    {
      chDrive[0] = 0x41 + pos;
      if ( GetDriveType(chDrive) == DRIVE_CDROM )
      {
        SendDlgItemMessage(IDC_LIST_CD, LB_ADDSTRING, 0, (LPARAM)chDrive);
        index++;
      }
    }
    dwList >>= 1;
    pos++;
  }
  return index;
}

Otevírání a zavírání CD mechaniky

Nyní se podívejme, jak programově otvírat a zavírat vybranou mechaniku. Použijeme příkazů MCI (Media Kontrol Interface), konkrétně funkci

MCIERROR mciSendCommand(
  MCIDEVICEID IDDevice,
  UINT uMsg,
  DWORD fdwCommand,
  DWORD dwParam
);

Podrobný popis této funkce a jejích parametrů a možností naleznete v nápovědě, podívejme se na její použití v případě otevření CD mechaniky:

BOOL CUkazkaDlg::OpenCD(LPCTSTR lpDrive)
{
  TCHAR chDrive[4];
  MCI_OPEN_PARMS mop;
  DWORD dwFlags;
  strcpy(chDrive, "D:");
  ZeroMemory(&mop, sizeof(MCI_OPEN_PARMS));
  mop.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO;
  chDrive[0] = lpDrive[0];
  dwFlags = MCI_OPEN_TYPE |  MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE | MCI_OPEN_ELEMENT;
  mop.lpstrElementName = chDrive;
  if ( mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD)&mop) == 0 )
  {
    mciSendCommand(mop.wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0);
    mciSendCommand(mop.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
  }
  return TRUE;
}

Pro případ, že požadujeme zavření CD mechaniky, se celá funkce bude lišit pouze v jediném řádku

// …..
  if ( mciSendCommand(0, MCI_OPEN, dwFlags, (DWORD)&mop) == 0 )
  {
    mciSendCommand(mop.wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, 0);
    mciSendCommand(mop.wDeviceID, MCI_CLOSE, MCI_WAIT, 0);
  }
  return TRUE;

Jak tyto funkce použít? Jako parametr, který zadáme do prvku struktury MCI_OPEN_PARAMS, musíme zadat písmenné označeni jednotky CD. Naší funkci tedy v našem případě předáme obsah vybraného řádku list-boxu:

void CUkazkaDlg::OnOpenCD()
{
  TCHAR chDrive[6];
  SendDlgItemMessage(IDC_LIST_CD, LB_GETTEXT,
    SendDlgItemMessage(IDC_LIST_CD, LB_GETCURSEL, 0,0), (LPARAM)chDrive);
  OpenCD(chDrive);
}

Detekce otevření a zavření

Nyní si ukažme, jak zachytávat některé události CD mechaniky. Pro tento účel je důležité zachycení zprávy WM_DEVICECHANGE. Tato zpráva je posílána všem aplikacím jako oznámení o změně na některém zařízení v počítači. Parametr wParam této zprávy určuje, o jakou událost se jedná. Nás budou zajímat 2 hodnoty tohoto parametru:
  • DBT_DEVICEARRIVAL – došlo k vložení zařízení, tedy v našem případě CD disku do mechaniky
  • DBT_DEVICEREMOVECOMPLETE – zařízení (CD disk) bylo vyjmuto z mechaniky
Parametr lParam této zprávy je adresa struktury

typedef struct _DEV_BROADCAST_HDR {
  DWORD dbch_size;
  DWORD dbch_devicetype;
  DWORD dbch_reserved;
} DEV_BROADCAST_HDR;
typedef DEV_BROADCAST_HDR *PDEV_BROADCAST_HDR;

která obsahuje další informace o události na zařízení. Pokud prvek dbch_devicetype je roven DBT_DEVTYP_VOLUME (což je i v případě CD disku), tato struktura je pak

typedef struct _DEV_BROADCAST_VOLUME {
  DWORD dbcv_size;
  DWORD dbcv_devicetype;
  DWORD dbcv_reserved;
  DWORD dbcv_unitmask;
  WORD dbcv_flags;
} DEV_BROADCAST_VOLUME;
typedef DEV_BROADCAST_VOLUME *PDEV_BROADCAST_VOLUME;

Pokud pak prvek dbcv_flags obsahuje DBTF_MEDIA, jde o médium (v našem případě CD disk) v mechanice. Podívejme se nyní konkrétně, jak lze tuto zprávu zpracovat. Když přímo v proceduře okna vytvoříme „switch“ na zprávy, bude příslušná část funkce WindowProc vypadat třeba takto:

// …..
  switch ( message )
  {
    case WM_DEVICECHANGE:
      lpdb = (PDEV_BROADCAST_HDR)lParam;
      switch ( wParam )
      {
        case DBT_DEVICEARRIVAL:
          if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
          {
            lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
            if (lpdbv->dbcv_flags & DBTF_MEDIA)
            {
              if ( SendDlgItemMessage(IDC_NOTIFY_INSERT, BM_GETCHECK, 0, 0) == BST_CHECKED)
              {
                wsprintf(chMessage, "Do jednotky %c: byl vložen disk\n",
                  GetDriveFromMask(lpdbv->dbcv_unitmask));
                MessageBox(chMessage);
              }
            }
          }
          break;
        case DBT_DEVICEREMOVECOMPLETE:
          if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
          {
            lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
            if (lpdbv->dbcv_flags & DBTF_MEDIA)
            {
              if ( SendDlgItemMessage(IDC_NOTIFY_REMOVE, BM_GETCHECK, 0, 0) == BST_CHECKED)
              {
                wsprintf(chMessage, "Z jednotky %c: byl vyjmut disk\n",
                  GetDriveFromMask(lpdbv->dbcv_unitmask));
                MessageBox(chMessage);
              }
            }
          }
          break;
      }
      break;
// …..
  }

Funkce, která převádí získanou bitovou masku, určující zařízení, na písmenné označení CD mechaniky, vypadá takto:

TCHAR CUkazkaDlg::GetDriveFromMask(ULONG mask)
{
  CHAR i;
  for (i = 0; i < 26; ++i)
  {
    if (mask & 0x1)
      break;
    mask = mask >> 1;
  }
  return (i + `A`);
 
}

Detekce automatického spuštění

Nyní si ještě ukážeme, jak detekovat automatické spuštění programu z CD disku. Pro tento účel musíme nejdříve registrovat zprávu "QueryCancelAutoPlay" pomocí funkce

UINT RegisterWindowMessage(
  LPCTSTR lpString  // message string
);

Tuto zprávu pak systém posílá aplikaci na popředí. Znamená to tedy, že tímto způsobem můžeme autoplay detekovat pouze tehdy, když je naše aplikace na popředí.

Vytvoříme si tedy členskou proměnnou

UINT m_AutoPlayMessage;

a ve funkci InitDialog zaregistrujeme zprávu:

  m_AutoPlayMessage = RegisterWindowMessage(LPCTSTR("QueryCancelAutoPlay"));

Zbytek již zařídíme opět v proceduře okna WindowProc:

switch ( message )
{
// ….  Tady jsou handlery ostatních zpráv

  default:
    if ( message == m_AutoPlayMessage )
      if ( SendDlgItemMessage(IDC_NOTIFY_AUTORUN, BM_GETCHECK, 0, 0) == BST_CHECKED)
          MessageBox("CD disk byl automaticky spuštěn.");
    break;
}

Zde si můžete stáhnout ukázkový projekt cd_drive.zip (30 kB).

Diskuze (10) Další článek: Transmeta vyhodila svého CEO

Témata článku: , , , , , , , , , ,