본문 바로가기

MS/C++

파일 시스템 정보 얻기 (WM_DEVICECHANGE)

출처 :  http://seobby.tistory.com/48



2007/03/05 - [코딩인생] - WM_DEVICECHANGE 메시지 이용하여 SD Card 탈착 및 USB 탈착 여부 알아오기

그런 연유로 내용을 보강하여 단말기의 오브젝트 스토어 및 파티션 정보를 얻어오는 부분에 대해 포스팅 하려 합니다.

이것은 물론 제가 나중에 써먹기 위해 올리는 것입니다. ^^

1. WM_DEVICECHANGE에서 장치의 착탈 유무를 판단하자.
SD 혹은 USB 장치 등이 단말기로부터 분리되면, 시스템은 모든 윈도우에 WM_DEVICECHANGE 메시지를 전달합니다. MFC의 경우 이 메시지는 PreTranslateMessage에서는 잡히지 않습니다. DefWinProc에서 잡아주시거나 혹은 ON_WM_DEVICECHANGE() 매크로를 이용하여 잡아야 합니다. 비주얼 스튜디오에서는 위의 메시지 및 매크로를 바로 사용할 수 있으나, EVC 에서는 메시지가 정의되어 있지 않으므로 상수 값을 직접 쓰던가 따로 정의해 두어야 합니다.

BOOL XXXXXXXXX::OnDeviceChange(UINT nEventType, DWORD_PTR dwData)
{
 // Device 종류를 통해 Driver의 이름을 얻어올 수 있다.
 DEV_BROADCAST_HDR *pDevBroadcastHdr = (DEV_BROADCAST_HDR*)dwData;
 DEV_BROADCAST_PORT *pDevBroadcastPort = NULL;
 CString sStorage = _T("");
 switch (pDevBroadcastHdr->dbch_devicetype) {
 case DBT_DEVTYP_DEVNODE:
 break;
 case DBT_DEVTYP_OEM:
 break;
 case DBT_DEVTYP_PORT:
 pDevBroadcastPort = (DEV_BROADCAST_PORT*)dwData;
 sStorage = pDevBroadcastPort->dbcp_name;
 break;
 case DBT_DEVTYP_VOLUME:
 break;
 }
 switch (nEventType) {
 case DBT_DEVICEARRIVAL:
 case DBT_DEVICEREMOVECOMPLETE:
  break;
 }
 return TRUE;
}

간단히 작성한 WM_DEVICECHANGE 메시지 핸들러입니다.
실제 아무일은 하지 않지만, 이벤트가 발생한 장치, 그리고 이벤트의 종류가 어떤 것인지 판단할 수 있습니다.
sStorage에는 "DSK1:" 등의 이름이 들어갑니다. CE에서는 드라이버의 이름이 보통 Prefix 3자에 순서대로 번호가 붙어서 정해집니다. 우리는 이제 착탈된 장치 드라이버의 이름을 얻었습니다. 하지만 이것 만으로는 어플리케이션에서 이용하기 힘듭니다. 어플리케이션에서는 보통 "\StorageDisk" 등의 이름을 쓰지요. 때문에 우리는 디바이스 드라이버의 이름을 통해서 "\StorageDisk" 등의 볼륨 이름을 얻어와야 합니다.

2. 디바이스의 이름을 통해 볼륨 이름을 얻자.

아래에 그 코드를 제시합니다.

INT XXXXXXXXX::GetPartitions( LPCTSTR lpszDeviceName )
{
 if(lpszDeviceName == NULL) return -1;
 HANDLE hOpenStore = NULL;
 HANDLE hFindPart = NULL;
 PARTINFO partInfo;
 partInfo.cbSize = sizeof(PARTINFO);
 
 hOpenStore = OpenStore(lpszDeviceName);
 hFindPart = FindFirstPartition(hOpenStore, &partInfo);
 if (hFindPart != INVALID_HANDLE_VALUE) {
  do {
// 여기에서 일을 하자.
  } while (FindNextPartition(hFindPart, &partInfo) == TRUE);
  FindClosePartition(hFindPart);
 }
 CloseHandle(hOpenStore);
 return 0;
}

윈도우 프로그래밍을 좀 해본 사람들이라면 위의 코드 구조가 쉽게 이해할 것입니다. FindFirstXXX ~ FindNextXXX 등등으로 이어지는 구조입니다.

위의 함수의 인자로 WM_DEVICECHANGE 이벤트 핸들러에서 얻었던 디바이스 이름 (예. "DSK1:")을 넘겨주면 이에 해당하는 스토어를 열고, 파티션 정보를 모두 얻어옵니다. PARTINFO 구조체에는 szVolumeName 멤버가 있고, 여기에 우리가 필요로 하는 볼륨 이름 (예. "\StorageDisk")이 들어 있기 때문에 이 값을 통해 실제 파일 시스템에 접근할 수 있습니다. 위 함수의 주석 부분에 필요로 하는 코드를 추가하면 될 것입니다.

3. 오브젝트 스토어 정보 얻어오기

이왕 코드 작성해 본 김에, 시스템의 오브젝트 스토어를 모두 검색하는 루틴을 작성해 보겠습니다. 구조는 위의 GetPartitions 함수와 같습니다.


DWORD XXXXXXXXX::FindAllStorage()
{
 DWORD dwError = ERROR_SUCCESS;
 STOREINFO storeInfo;
 storeInfo.cbSize = sizeof(STOREINFO);
 // 최초 스토어 찾기
 HANDLE hFindStore = ::FindFirstStore(&storeInfo);
 if (hFindStore != INVALID_HANDLE_VALUE) {
  //.. 스토어에서 첫번째 볼륨 구하기
  do {
   //.. 스토어에서 첫번째 볼륨 구하기
   if ((storeInfo.sdi.dwDeviceType & STORAGE_DEVICE_TYPE_REMOVABLE_DRIVE) != 0)
   {
// .. 스토어 정보는 storeinfo 변수에 저장됨.
// 여기에서 사용하자.
   }
  } while (FindNextStore(hFindStore, &storeInfo) == TRUE);
  FindCloseStore(hFindStore);
  dwError = GetLastError();
  if (dwError != ERROR_NO_MORE_ITEMS) {
   TRACE(_T("FindNextStore Error\r\n"));
  }
  else {
   dwError = ERROR_SUCCESS;
  }
 }
 else {
  dwError = GetLastError();
  if (dwError != ERROR_NO_MORE_ITEMS) {
   TRACE(_T("FindNextStore Error\r\n"));
  }
  else
   dwError = ERROR_SUCCESS;
 }
 return dwError;
}


이렇게 작성한 코드는 필요한 곳에서 자유롭게 쓸 수 있습니다. 단말기에서 개발을 하다 보면 의외로 많이 필요한 코드입니다. 파일 탐색기에서 디바이스 장치가 착탈될 때 이를 감지해서 새로 로드한다거나, 혹은 파일을 재생 중에 갑자기 사용자가 SD 카드 및 USB 장치를 제거했을 경우의 예외 처리를 수행하는 데 아주 중요하게 사용됩니다.

그래서 나중에 쉽게 이용할 수 있도록 클래스로 만들어 두었습니다.
위에서 언급한 코드들이 작성되어 있으며, 보기 편하도록 샘플 프로그램을 작성하여 올렸습니다. 샘플 코드는 스탠다드 SDK를 이용하였는데, 비주얼 스튜디오에서는 CE 5.0 에뮬레이터가 안 보이네요. 때문에 PC에서 바로 확인하기는 힘들 것 같고, 단말기에 올려서 테스트 해 보아야 할 듯 합니다.

데모 실행의 결과는 다음과 같습니다.
사용자 삽입 이미지

프로그램이 실행되면, 전체 오브젝트 스토어를 검색하고 이를 콤보 박스에 채웁니다. 콤보 박스에서 오브젝트 스토어를 선택하면 선택된 오브젝트의 정보가 아래 리스트에 출력되며, 해당 오브젝트의 파티션 정보가 오른쪽 콤보 박스에 채워집니다. 같은 방법으로 파티션을 선택하면 해당 파티션에 대한 정보가 아래 리스트에 출력됩니다.

또한 WM_DEVICECHANGE 메시지를 처리하고 있기 때문에 장치 착탈 시, 자동으로 장치 정보를 재검색 합니다.