« 2007年01月 | メイン | 2007年03月 »

1  2  3

2007年02月 記事一覧

2007年02月04日

HDDのシリアル番号や型番などを取得する

test102.gif
HDDのシリアルナンバーを取得するにはDeviceIoControlを利用して、HDDへ直接ATA/ATAPIのコマンドを送る。

シリアル番号以外にも取得できる情報はたくさんある。詳しくはATA/ATAPIの仕様書を参照するといいだろう。
http://www.t13.org/内で「ATAPI」と検索すれば見つかり、PDF形式やWORD形式で無料ダウンロードできる。

依存環境:ATL
#include "winioctl.h"
#include "atlstr.h"

//
//	ATA/ATAPIデバイス情報取得用クラス
//
//以下の情報を参照しています。
//
//・ATA/ATAPI-8コマンドセット(ドラフト案)
//	http://www.t13.org/
//	上のHPのトップページから「ATAPI」で検索。検索結果の「ATA8-ACS」から「1699Dr0-ATA8-ACS.pdf」
//
//・Microsoftの用意しているサンプルアプリケーション(ソースコード)
//	http://download.microsoft.com/download/winddk/sample3/9x/W9X/EN-US/SmartApp.exe
//
class	CDnpATAPI
{
protected:

	enum	IO_CONTROL_CODE
	{
		DFP_RECEIVE_DRIVE_DATA = 0x0007c088,
	};

	enum	ATAPI_COMMAND_CODE
	{
		ATAPI_IDENTIFY_DEVICE	= 0xEC,
	};


#pragma pack(push,1)

	struct	IDENTIFY_DEVICE_OUTDATA
	{
		SENDCMDOUTPARAMS	sSendCmdOutParam;
		BYTE	pData[IDENTIFY_BUFFER_SIZE - 1];
	};

public:
	//
	//	IDENTIFY_DEVICEの取得結果構造体
	//
	struct IDENTIFY_DEVICE
	{
		USHORT	wGeneralConfiguration;		//0			//15bit:0=ATAデバイス、7bit:1=リムーバブルメディア・デバイス
		USHORT	wObsolute1;					//1
		USHORT	wSpecificConfiguration;		//2
		USHORT	wObsolute2;					//3
		USHORT	wRetired1[2];				//4-5
		USHORT	wObsolute3;					//6
		ULONG	ulReservedForCompactFlash;	//7-8
		USHORT	wRetired2;					//9
		CHAR	pszSerialNumber[20];		//10-19		//シリアルナンバー
		ULONG	ulRetired3;					//20-21
		USHORT	wObsolute4;					//22
		CHAR	pszFirmwareRev[8];			//23-26		//ファームウエア・バージョン
		CHAR	pszModelNumber[40];			//27-46		//モデル名
		USHORT	wMaxNumPerInterupt;			//47
		USHORT	wReserved1;					//48
		USHORT	wCapabilities1;				//49		//8bit:1=DMA対応 9bit:1=LBA対応 10bit:1=IORDY無効 11bit:1=IORDY対応/0=不明
		USHORT	wCapabilities2;				//50
		ULONG	ulObsolute5;				//51-52
		USHORT	wField88and7063;			//53		//2bit:1=88ワード目情報利用可能 1bit:1=70:64ワード目情報利用可能
		USHORT	wObsolute6[5];				//54-58
		USHORT	wMultSectorStuff;			//59
		ULONG	ulTotalAddressableSectors;	//60-61
		USHORT	wObsolute7;					//62
		USHORT	wMultiWordDMA;				//63		//0bit:1=mode0対応 1bit:1=mode1対応 2bit:1=mode2対応 8bit:1=mode0選択 9bit:mode1選択 10bit:mode2選択
		USHORT	wPIOMode;					//64
		USHORT	wMinMultiwordDMACycleTime;	//65
		USHORT	wRecommendedMultiwordDMACycleTime;	//66
		USHORT	wMinPIOCycleTimewoFlowCtrl;	//67
		USHORT	wMinPIOCycleTimeWithFlowCtrl;	//68
		USHORT	wReserved2[6];				//69-74
		USHORT	wQueueDepth;				//75
		USHORT	wReserved3[4];				//76-79
		USHORT	wMajorVersion;				//80		//4bit:1=ATA/ATAPI-4対応 5bit:1=ATA/ATAPI-5対応 6bit:1=ATA/ATAPI-6対応 7bit:1=ATA/ATAPI-7対応
		USHORT	wMinorVersion;				//81
		USHORT	wCommandSetSupported1;		//82		//0bit:1=SMART対応 1bit:SecurityMode対応 ....
		USHORT	wCommandSetSupported2;		//83		//0bit:1=DOWNLOAD Microcode対応 ....
		USHORT	wCommandSetSupported3;		//84		//0bit:1=SMART error logging対応 1bit:1=SMART self-test対応 ....
		USHORT	wCommandSetEnable1;			//85		//0bit:1=SMART利用可能 ....
		USHORT	wCommandSetEnable2;			//86		//0bit:1=DOWNLOAD Microcode利用可能 ....
		USHORT	wCommandSetDefault;			//87
		USHORT	wUltraDMAMode;				//88		//0bit:1=UltraDMAmode0対応 1bit:1=UltraDMAmode1対応 ....
		USHORT	wTimeReqForSecurityErase;	//89
		USHORT	wTimeReqForEnhancedSecure;	//90
		USHORT	wCurrentPowerManagement;	//91
		USHORT	wMasterPasswordRevision;	//92
		USHORT	wHardwareResetResult;		//93
		USHORT	wAcoustricmanagement;		//94
		USHORT	wStreamMinRequestSize;		//95
		USHORT	wStreamingTimeDMA;			//96
		USHORT	wStreamingAccessLatency;	//97
		ULONG	ulStreamingPerformance;		//98-99
		USHORT	pwMaxUserLBA[4];			//100-103
		USHORT	wStremingTimePIO;			//104
		USHORT	wReserved4;					//105
		USHORT	wSectorSize;				//106
		USHORT	wInterSeekDelay;			//107
		USHORT	wIEEEOUI;					//108
		USHORT	wUniqueID3;					//109
		USHORT	wUniqueID2;					//110
		USHORT	wUniqueID1;					//111
		USHORT	wReserved5[4];				//112-115
		USHORT	wReserved6;					//116
		ULONG	ulWordsPerLogicalSector;	//117-118
		USHORT	wReserved7[8];				//119-126
		USHORT	wRemovableMediaStatus;		//127
		USHORT	wSecurityStatus;			//128
		USHORT	pwVendorSpecific[31];		//129-159
		USHORT	wCFAPowerMode1;				//160
		USHORT	wReserved8[15];				//161-175
		CHAR	pszCurrentMediaSerialNo[60];//176-205
		USHORT	wReserved9[49];				//206-254
		USHORT	wIntegrityWord;				//255		//0-7bit:Signature 8-15bit:Checksum
	};
#pragma	pack(pop)


protected:

	//
	//	文字列並びの整形
	//
	VOID ChangeByteOrder(PCHAR szString, USHORT uscStrSize)
	{
		USHORT	i;
		CHAR	temp;

		for(i = 0; i < uscStrSize; i+=2)
		{
			temp = szString[i];
			szString[i] = szString[i+1];
			szString[i+1] = temp;
		}
	}


	//
	//	DeviceIoControlへ渡すハンドルの取得
	//
	bool	GetIoCtrlHandle(HANDLE* phIoCtrl,int nDeviceNo)
	{
		BOOL	ret;
		HANDLE	hIoCtrl;
		OSVERSIONINFO	sVerInfo;

		if(phIoCtrl == NULL)
			return	false;
		*phIoCtrl = INVALID_HANDLE_VALUE;

		::ZeroMemory(&sVerInfo,sizeof(OSVERSIONINFO));
		sVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		ret = ::GetVersionEx(&sVerInfo);
		if(ret == FALSE)
			return	false;

		//OSによってCreateFile処理を変える
		if(sVerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
			hIoCtrl = ::CreateFile(_T("\\\\.\\SMARTVSD"), 0,0,0,CREATE_NEW, 0, 0);		//Windows 9x
		else
		{
			CAtlString	strDevice;
			strDevice.Format(_T("\\\\.\\PhysicalDrive%d"),nDeviceNo);
			hIoCtrl = ::CreateFile(strDevice,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
		}
		if(hIoCtrl == INVALID_HANDLE_VALUE)
			return	false;

		*phIoCtrl = hIoCtrl;

		return	true;
	}


public:

	class	CModelInfo
	{
	public:
		CAtlString	_strModel;
		CAtlString	_strFirmware;
		CAtlString	_strSerialNo;
	};



	//
	//	デバイス情報取得
	//
	bool	GetIdentifyDevice(int nDeviceNo,IDENTIFY_DEVICE* pData,CModelInfo* pInfo=NULL)
	{
		bool	ret;
		BOOL	bRet;
		HANDLE	hIoCtrl;
		DWORD	dwReturned;
		IDENTIFY_DEVICE_OUTDATA	sSendCmdOutParam;
		SENDCMDINPARAMS	sSendCmd;

		if(pData == NULL)
			return	false;
		::ZeroMemory(pData,sizeof(IDENTIFY_DEVICE));
		if(pInfo)
			*pInfo = CModelInfo();

		//ハンドル取得
		ret = GetIoCtrlHandle(&hIoCtrl,nDeviceNo);
		if(ret == false)
			return	false;

		::ZeroMemory(&sSendCmdOutParam,sizeof(IDENTIFY_DEVICE_OUTDATA));
		::ZeroMemory(&sSendCmd,sizeof(SENDCMDINPARAMS));
		sSendCmd.irDriveRegs.bCommandReg		= ATAPI_IDENTIFY_DEVICE;			//IDENTIFY DEVICEコマンド番号
		sSendCmd.irDriveRegs.bDriveHeadReg		= 0xA0 | ((nDeviceNo & 1) << 4);	//ドライブ番号
		sSendCmd.cBufferSize	= IDENTIFY_BUFFER_SIZE;
		sSendCmd.bDriveNumber	= nDeviceNo;			//ドライブ番号

		//IDENTIFY DEVICEコマンドの実行
		bRet = ::DeviceIoControl(hIoCtrl,DFP_RECEIVE_DRIVE_DATA,&sSendCmd,sizeof(SENDCMDINPARAMS),&sSendCmdOutParam,sizeof(IDENTIFY_DEVICE_OUTDATA),&dwReturned,NULL);
		::CloseHandle(hIoCtrl);
		if(ret == FALSE || dwReturned != sizeof(IDENTIFY_DEVICE_OUTDATA))
			return	false;

		memcpy_s(pData,sizeof(IDENTIFY_DEVICE),sSendCmdOutParam.sSendCmdOutParam.bBuffer,sizeof(IDENTIFY_DEVICE));

		if(pInfo)
		{
			char	pszBuff[256];
			IDENTIFY_DEVICE*	pData;

			pData = (IDENTIFY_DEVICE*)sSendCmdOutParam.sSendCmdOutParam.bBuffer;
			ChangeByteOrder(pData->pszModelNumber,sizeof(pData->pszModelNumber));
			ChangeByteOrder(pData->pszFirmwareRev,sizeof(pData->pszFirmwareRev));
			ChangeByteOrder(pData->pszSerialNumber,sizeof(pData->pszSerialNumber));

			strncpy_s(pszBuff,256 * sizeof(char),pData->pszModelNumber,sizeof(pData->pszModelNumber));
			pInfo->_strModel = pszBuff;
			strncpy_s(pszBuff,256 * sizeof(char),pData->pszFirmwareRev,sizeof(pData->pszFirmwareRev));
			pInfo->_strFirmware = pszBuff;
			strncpy_s(pszBuff,256 * sizeof(char),pData->pszSerialNumber,sizeof(pData->pszSerialNumber));
			pInfo->_strSerialNo = pszBuff;
		}

		return	true;
	}






};






bool	Test()
{
	int		i;

	//PCに接続されているHDDドライブのうち最初の4つの情報を取得&表示する
	for(i = 0; i < 4; i++)
	{
		bool		ret;
		CDnpATAPI	cIDE;
		CDnpATAPI::CModelInfo		cInfo;
		CDnpATAPI::IDENTIFY_DEVICE	sInfo;

		ret = cIDE.GetIdentifyDevice(i,&sInfo,&cInfo);
		if(ret == false)
			continue;

		CAtlString	strMessage;

		strMessage = _T("型番:");
		strMessage += cInfo._strModel;
		strMessage += _T("\n");

		strMessage += _T("シリアル番号:");
		strMessage += cInfo._strSerialNo;
		strMessage += _T("\n");

		strMessage += _T("ファームウエア:");
		strMessage += cInfo._strFirmware;
		strMessage += _T("\n");
		strMessage += _T("\n");

		if(sInfo.wGeneralConfiguration & 0x8000)
			strMessage += _T("NOT ATA Device\n");
		else
			strMessage += _T("ATA Device\n");

		if(sInfo.wGeneralConfiguration & 0x0008)
			strMessage += _T("Removable Media Device\n");

		if(sInfo.wCapabilities1 & 0x0100)
			strMessage += _T("DMA対応\n");
		if(sInfo.wCapabilities1 & 0x0200)
			strMessage += _T("LBA対応\n");
		if(sInfo.wCapabilities1 & 0x0800)
			strMessage += _T("IORDY対応\n");

		if(sInfo.wMultiWordDMA & 0x0400)
			strMessage += _T("Multiword DMA mode 2利用中\n");
		else if(sInfo.wMultiWordDMA & 0x0200)
			strMessage += _T("Multiword DMA mode 1利用中\n");
		else if(sInfo.wMultiWordDMA & 0x0100)
			strMessage += _T("Multiword DMA mode 0利用中\n");
		if(sInfo.wMultiWordDMA & 0x0004)
			strMessage += _T("Multiword DMA mode 2対応\n");
		else if(sInfo.wMultiWordDMA & 0x0002)
			strMessage += _T("Multiword DMA mode 1対応\n");
		else if(sInfo.wMultiWordDMA & 0x0001)
			strMessage += _T("Multiword DMA mode 0対応\n");

		if(sInfo.wMajorVersion & 0x0080)
			strMessage += _T("ATA/ATAPI-7対応\n");
		else if(sInfo.wMajorVersion & 0x0040)
			strMessage += _T("ATA/ATAPI-6対応\n");
		else if(sInfo.wMajorVersion & 0x0020)
			strMessage += _T("ATA/ATAPI-5対応\n");
		else if(sInfo.wMajorVersion & 0x0010)
			strMessage += _T("ATA/ATAPI-4対応\n");

		if(sInfo.wCommandSetEnable1 & 0x0001)
			strMessage += _T("S.M.A.R.T.利用可能\n");
		if(sInfo.wCommandSetEnable1 & 0x0004)
			strMessage += _T("Removable Media機能利用可能\n");
		if(sInfo.wCommandSetEnable1 & 0x0020)
			strMessage += _T("ライトキャッシュ利用可能\n");

		if(sInfo.wUltraDMAMode & 0x4000)
			strMessage += _T("Ultra DMA mode 6利用中\n");
		else if(sInfo.wUltraDMAMode & 0x2000)
			strMessage += _T("Ultra DMA mode 5利用中\n");
		else if(sInfo.wUltraDMAMode & 0x1000)
			strMessage += _T("Ultra DMA mode 4利用中\n");
		else if(sInfo.wUltraDMAMode & 0x0800)
			strMessage += _T("Ultra DMA mode 3利用中\n");
		else if(sInfo.wUltraDMAMode & 0x0400)
			strMessage += _T("Ultra DMA mode 2利用中\n");
		else if(sInfo.wUltraDMAMode & 0x0200)
			strMessage += _T("Ultra DMA mode 1利用中\n");
		else if(sInfo.wUltraDMAMode & 0x0100)
			strMessage += _T("Ultra DMA mode 0利用中\n");
		if(sInfo.wUltraDMAMode & 0x0040)
			strMessage += _T("Ultra DMA mode 6対応\n");
		else if(sInfo.wUltraDMAMode & 0x0020)
			strMessage += _T("Ultra DMA mode 5対応\n");
		else if(sInfo.wUltraDMAMode & 0x0010)
			strMessage += _T("Ultra DMA mode 4対応\n");
		else if(sInfo.wUltraDMAMode & 0x0008)
			strMessage += _T("Ultra DMA mode 3対応\n");
		else if(sInfo.wUltraDMAMode & 0x0004)
			strMessage += _T("Ultra DMA mode 2対応\n");
		else if(sInfo.wUltraDMAMode & 0x0002)
			strMessage += _T("Ultra DMA mode 1対応\n");
		else if(sInfo.wUltraDMAMode & 0x0001)
			strMessage += _T("Ultra DMA mode 0対応\n");

		::MessageBox(NULL,strMessage,_T(""),MB_OK);
	}

	return	true;
}

プロジェクトファイルをダウンロード

HDDのS.M.A.R.T情報を取得する

test103.gif
HDDの動作時の温度などのSMART情報はDeviceIoControlを利用してHDDへ直接ATA/ATAPIコマンドを送ることで取得できる。

SMART情報の取得自体は比較的簡単にできる。しかし、取得した値の"意味"をきちんと解説している文章を見つけることは出来なかった。そのため取得した情報を元に想像するしかないようだ。

例えばhttp://www.t13.org/のトップページから「SMART」と検索して見つかる情報によると、HDDの温度はSMART情報のIDが0xC2の情報として取得できるとある。実際の取得結果のRAWデータ部では「26 00 0E 00 2A 00」となっている。ここから想像すると最後の2バイトが摂氏の温度を示していることが分かる。この場合は42度(0x002A)だ。しかしそのほかの「26 00 0E 00」が何を示しているのかは謎のままとなり、本当にこれであっているのかと不安を感じながらの利用となる。

依存環境:ATL
#include "winioctl.h"
#include "atlstr.h"

//
//	ATA/ATAPIデバイス情報取得用クラス
//
//以下の情報を参照しています。
//
//・ATA/ATAPI-8コマンドセット(ドラフト案)
//	http://www.t13.org/
//	上のHPのトップページから「ATAPI」で検索。検索結果の「ATA8-ACS」から「1699Dr0-ATA8-ACS.pdf」
//
//・SMART情報
//	http://www.t13.org/
//	上のHPのトップページから「SMART」で検索。
//
//・Microsoftの用意しているサンプルアプリケーション(ソースコード)
//	http://download.microsoft.com/download/winddk/sample3/9x/W9X/EN-US/SmartApp.exe
//
class	CDnpATAPI
{
protected:

	enum	IO_CONTROL_CODE
	{
		DFP_RECEIVE_DRIVE_DATA	= 0x0007c088,
	};

	enum	ATAPI_COMMAND_CODE
	{
		ATAPI_IDENTIFY_DEVICE	= 0xEC,
		ATAPI_SMART_READ_DATA	= 0xB0,
	};


#pragma pack(push,1)

	struct	IDENTIFY_DEVICE_OUTDATA
	{
		SENDCMDOUTPARAMS	sSendCmdOutParam;
		BYTE	pData[IDENTIFY_BUFFER_SIZE - 1];
	};

	struct	SMART_READ_DATA_OUTDATA
	{
		SENDCMDOUTPARAMS	sSendCmdOutParam;
		BYTE	pData[READ_ATTRIBUTE_BUFFER_SIZE - 1];
	};


	typedef	struct	_DRIVEATTRIBUTE {
		BYTE	bAttrID;		// Identifies which attribute
		WORD	wStatusFlags;	// see bit definitions below
		BYTE	bAttrValue;		// Current normalized value
		BYTE	bWorstValue;	// How bad has it ever been?
		BYTE	bRawValue[6];	// Un-normalized value
		BYTE	bReserved;		// ...
	} DRIVEATTRIBUTE, *PDRIVEATTRIBUTE, *LPDRIVEATTRIBUTE;

public:
	//
	//	IDENTIFY_DEVICEの取得結果構造体
	//
	struct IDENTIFY_DEVICE
	{
		USHORT	wGeneralConfiguration;		//0			//15bit:0=ATAデバイス、7bit:1=リムーバブルメディア・デバイス
		USHORT	wObsolute1;					//1
		USHORT	wSpecificConfiguration;		//2
		USHORT	wObsolute2;					//3
		USHORT	wRetired1[2];				//4-5
		USHORT	wObsolute3;					//6
		ULONG	ulReservedForCompactFlash;	//7-8
		USHORT	wRetired2;					//9
		CHAR	pszSerialNumber[20];		//10-19		//シリアルナンバー
		ULONG	ulRetired3;					//20-21
		USHORT	wObsolute4;					//22
		CHAR	pszFirmwareRev[8];			//23-26		//ファームウエア・バージョン
		CHAR	pszModelNumber[40];			//27-46		//モデル名
		USHORT	wMaxNumPerInterupt;			//47
		USHORT	wReserved1;					//48
		USHORT	wCapabilities1;				//49		//8bit:1=DMA対応 9bit:1=LBA対応 10bit:1=IORDY無効 11bit:1=IORDY対応/0=不明
		USHORT	wCapabilities2;				//50
		ULONG	ulObsolute5;				//51-52
		USHORT	wField88and7063;			//53		//2bit:1=88ワード目情報利用可能 1bit:1=70:64ワード目情報利用可能
		USHORT	wObsolute6[5];				//54-58
		USHORT	wMultSectorStuff;			//59
		ULONG	ulTotalAddressableSectors;	//60-61
		USHORT	wObsolute7;					//62
		USHORT	wMultiWordDMA;				//63		//0bit:1=mode0対応 1bit:1=mode1対応 2bit:1=mode2対応 8bit:1=mode0選択 9bit:mode1選択 10bit:mode2選択
		USHORT	wPIOMode;					//64
		USHORT	wMinMultiwordDMACycleTime;	//65
		USHORT	wRecommendedMultiwordDMACycleTime;	//66
		USHORT	wMinPIOCycleTimewoFlowCtrl;	//67
		USHORT	wMinPIOCycleTimeWithFlowCtrl;	//68
		USHORT	wReserved2[6];				//69-74
		USHORT	wQueueDepth;				//75
		USHORT	wReserved3[4];				//76-79
		USHORT	wMajorVersion;				//80		//4bit:1=ATA/ATAPI-4対応 5bit:1=ATA/ATAPI-5対応 6bit:1=ATA/ATAPI-6対応 7bit:1=ATA/ATAPI-7対応
		USHORT	wMinorVersion;				//81
		USHORT	wCommandSetSupported1;		//82		//0bit:1=SMART対応 1bit:SecurityMode対応 ....
		USHORT	wCommandSetSupported2;		//83		//0bit:1=DOWNLOAD Microcode対応 ....
		USHORT	wCommandSetSupported3;		//84		//0bit:1=SMART error logging対応 1bit:1=SMART self-test対応 ....
		USHORT	wCommandSetEnable1;			//85		//0bit:1=SMART利用可能 ....
		USHORT	wCommandSetEnable2;			//86		//0bit:1=DOWNLOAD Microcode利用可能 ....
		USHORT	wCommandSetDefault;			//87
		USHORT	wUltraDMAMode;				//88		//0bit:1=UltraDMAmode0対応 1bit:1=UltraDMAmode1対応 ....
		USHORT	wTimeReqForSecurityErase;	//89
		USHORT	wTimeReqForEnhancedSecure;	//90
		USHORT	wCurrentPowerManagement;	//91
		USHORT	wMasterPasswordRevision;	//92
		USHORT	wHardwareResetResult;		//93
		USHORT	wAcoustricmanagement;		//94
		USHORT	wStreamMinRequestSize;		//95
		USHORT	wStreamingTimeDMA;			//96
		USHORT	wStreamingAccessLatency;	//97
		ULONG	ulStreamingPerformance;		//98-99
		USHORT	pwMaxUserLBA[4];			//100-103
		USHORT	wStremingTimePIO;			//104
		USHORT	wReserved4;					//105
		USHORT	wSectorSize;				//106
		USHORT	wInterSeekDelay;			//107
		USHORT	wIEEEOUI;					//108
		USHORT	wUniqueID3;					//109
		USHORT	wUniqueID2;					//110
		USHORT	wUniqueID1;					//111
		USHORT	wReserved5[4];				//112-115
		USHORT	wReserved6;					//116
		ULONG	ulWordsPerLogicalSector;	//117-118
		USHORT	wReserved7[8];				//119-126
		USHORT	wRemovableMediaStatus;		//127
		USHORT	wSecurityStatus;			//128
		USHORT	pwVendorSpecific[31];		//129-159
		USHORT	wCFAPowerMode1;				//160
		USHORT	wReserved8[15];				//161-175
		CHAR	pszCurrentMediaSerialNo[60];//176-205
		USHORT	wReserved9[49];				//206-254
		USHORT	wIntegrityWord;				//255		//0-7bit:Signature 8-15bit:Checksum
	};
#pragma	pack(pop)


protected:

	//
	//	文字列並びの整形
	//
	VOID ChangeByteOrder(PCHAR szString, USHORT uscStrSize)
	{
		USHORT	i;
		CHAR	temp;

		for(i = 0; i < uscStrSize; i+=2)
		{
			temp = szString[i];
			szString[i] = szString[i+1];
			szString[i+1] = temp;
		}
	}


	//
	//	DeviceIoControlへ渡すハンドルの取得
	//
	bool	GetIoCtrlHandle(HANDLE* phIoCtrl,int nDeviceNo)
	{
		BOOL	ret;
		HANDLE	hIoCtrl;
		OSVERSIONINFO	sVerInfo;

		if(phIoCtrl == NULL)
			return	false;
		*phIoCtrl = INVALID_HANDLE_VALUE;

		::ZeroMemory(&sVerInfo,sizeof(OSVERSIONINFO));
		sVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		ret = ::GetVersionEx(&sVerInfo);
		if(ret == FALSE)
			return	false;

		//OSによってCreateFile処理を変える
		if(sVerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
			hIoCtrl = ::CreateFile(_T("\\\\.\\SMARTVSD"), 0,0,0,CREATE_NEW, 0, 0);		//Windows 9x
		else
		{
			CAtlString	strDevice;
			strDevice.Format(_T("\\\\.\\PhysicalDrive%d"),nDeviceNo);
			hIoCtrl = ::CreateFile(strDevice,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
		}
		if(hIoCtrl == INVALID_HANDLE_VALUE)
			return	false;

		*phIoCtrl = hIoCtrl;

		return	true;
	}



public:


	class	CSmartAttribute
	{
		BYTE	_pAttributeData[READ_ATTRIBUTE_BUFFER_SIZE];

		DRIVEATTRIBUTE*	_ppInfo[30];


	public:
		CSmartAttribute()
		{
			Init();
		}

		void	Init(void)
		{
			::ZeroMemory(_pAttributeData,READ_ATTRIBUTE_BUFFER_SIZE);
			::ZeroMemory(_ppInfo,sizeof(DRIVEATTRIBUTE*) * 30);
		}

		bool	SetData(const BYTE* pData,int cbSize)
		{
			if(cbSize != READ_ATTRIBUTE_BUFFER_SIZE)
				return	false;

			Init();
			memcpy(_pAttributeData,pData,READ_ATTRIBUTE_BUFFER_SIZE);

			int		i;
			DRIVEATTRIBUTE**	ppInfo;
			DRIVEATTRIBUTE*		pAttribute;

			ppInfo = _ppInfo;
			pAttribute = (DRIVEATTRIBUTE*)&(_pAttributeData[2]);
			for(i = 0; i < 30; i++)
			{
				if(pAttribute->bAttrID)
				{
					*ppInfo = pAttribute;
					ppInfo++;
				}
				pAttribute++;
			}

			return	true;
		}


		bool	GetAttributeCount(int* pnCount)
		{
			int		i;

			if(pnCount == NULL)
				return	false;

			*pnCount = 0;
			for(i = 0; i < 30; i++)
			{
				if(_ppInfo[i] && _ppInfo[i]->bAttrID == 0)
					break;
				(*pnCount)++;
			}

			return	true;
		}

		bool	GetAttribute(int nIndex,BYTE* pcbAttrID,WORD* pwFlags,BYTE* pcbValue,BYTE* pcbWorstValue,BYTE pcbRawValue[6],BYTE* pcbReserved)
		{
			if(nIndex < 0 || nIndex > 30 || _ppInfo[nIndex] == NULL || _ppInfo[nIndex]->bAttrID == 0)
				return	false;

			if(pcbAttrID)
				*pcbAttrID = _ppInfo[nIndex]->bAttrID;
			if(pwFlags)
				*pwFlags = _ppInfo[nIndex]->wStatusFlags;
			if(pcbValue)
				*pcbValue = _ppInfo[nIndex]->bAttrValue;
			if(pcbWorstValue)
				*pcbWorstValue = _ppInfo[nIndex]->bWorstValue;
			if(pcbRawValue)
				memcpy(pcbRawValue,_ppInfo[nIndex]->bRawValue,6);
			if(pcbReserved)
				*pcbReserved = _ppInfo[nIndex]->bReserved;

			return	true;
		}

		//
		//	SMARTの属性IDの説明文字列を取得する
		//
		//TODO:※日本語訳や意味に間違えがある可能性があります!
		//
		bool	GetAttributeIdString(BYTE cbAttrID,CAtlString* pstrName,CAtlString* pstrNameJp,CAtlString* pstrDesctiption)
		{
			switch(cbAttrID)
			{
			case	0x01:
				if(pstrName)
					*pstrName		= _T("Raw read error rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("読み込みエラー率");
				if(pstrDesctiption)
					*pstrDesctiption= _T("読み込み時に発生したエラーの割合です。");
				break;

			case	0x02:
				if(pstrName)
					*pstrName		= _T("Throughput performance");
				if(pstrNameJp)
					*pstrNameJp		= _T("スループット");
				if(pstrDesctiption)
					*pstrDesctiption= _T("HDDの効率です。");
				break;

			case	0x03:
				if(pstrName)
					*pstrName		= _T("Spinup time");
				if(pstrNameJp)
					*pstrNameJp		= _T("スピンアップ時間");
				if(pstrDesctiption)
					*pstrDesctiption= _T("回転開始に必要な時間です。");
				break;

			case	0x04:
				if(pstrName)
					*pstrName		= _T("Start/Stop count");
				if(pstrNameJp)
					*pstrNameJp		= _T("回転/停止数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("モーターの回転開始/停止の回数です。");
				break;

			case	0x05:
				if(pstrName)
					*pstrName		= _T("Reallocated sector count");
				if(pstrNameJp)
					*pstrNameJp		= _T("再割り当てセクター数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("セクター不良などで再割り当てされたセクター数です。");
				break;

			case	0x06:
				if(pstrName)
					*pstrName		= _T("Read channel margin");
				if(pstrNameJp)
					*pstrNameJp		= _T("リード・チャンネル・マージン");
				if(pstrDesctiption)
					*pstrDesctiption= _T("読み込み時に割り当てたチャンネル数です。");
				break;

			case	0x07:
				if(pstrName)
					*pstrName		= _T("Seek error rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("シークエラー率");
				if(pstrDesctiption)
					*pstrDesctiption= _T("シークの際に生じたエラーの割合です。");
				break;

			case	0x08:
				if(pstrName)
					*pstrName		= _T("Seek timer performance");
				if(pstrNameJp)
					*pstrNameJp		= _T("シークタイム性能");
				if(pstrDesctiption)
					*pstrDesctiption= _T("シークの平均効率です。");
				break;

			case	0x09:
				if(pstrName)
					*pstrName		= _T("Power-on hours count");
				if(pstrNameJp)
					*pstrNameJp		= _T("合計利用時間");
				if(pstrDesctiption)
					*pstrDesctiption= _T("電源を入れていた時間の合計です。");
				break;

			case	0x0A:
				if(pstrName)
					*pstrName		= _T("Spinup retry count");
				if(pstrNameJp)
					*pstrNameJp		= _T("スピンアップ再試行回数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("スピンアップを繰り返した回数です。");
				break;

			case	0x0B:
				if(pstrName)
					*pstrName		= _T("Calibration retry count");
				if(pstrNameJp)
					*pstrNameJp		= _T("キャリブレーション再試行回数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("デバイス初期化を繰り返した回数です。");
				break;

			case	0x0C:
				if(pstrName)
					*pstrName		= _T("Power cycle count");
				if(pstrNameJp)
					*pstrNameJp		= _T("電源投入回数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("電源投入イベントの実行された回数です。");
				break;

			case	0x0D:
			case	0xC9:
				if(pstrName)
					*pstrName		= _T("Soft read error rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("論理読み込みエラー率");
				if(pstrDesctiption)
					*pstrDesctiption= _T("読み込み中に生じたプログラム実行エラーの割合です。");
				break;

			case	0xBB:
			case	0xBD:
			case	0xBE:
				if(pstrName)
					*pstrName		= _T("vendor-specific");
				if(pstrNameJp)
					*pstrNameJp		= _T("ベンダー独自情報");
				if(pstrDesctiption)
					*pstrDesctiption= _T("製造メーカーの独自情報です。");
				break;

			case	0xBF:
				if(pstrName)
					*pstrName		= _T("G-sense error rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("加速度センサー検出エラー率");
				if(pstrDesctiption)
					*pstrDesctiption= _T("物理衝撃によるエラーの割合です。");
				break;

			case	0xC0:
				if(pstrName)
					*pstrName		= _T("Power-off retract count");
				if(pstrNameJp)
					*pstrNameJp		= _T("電源切断回避数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("電源切断もしくは緊急時のヘッド回避回数です。");
				break;

			case	0xC1:
				if(pstrName)
					*pstrName		= _T("Load/Unload cycle count");
				if(pstrNameJp)
					*pstrNameJp		= _T("ロード/アンロード・サイクル数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ヘッド回避位置へ移動した回数です。");
				break;

			case	0xC2:
				if(pstrName)
					*pstrName		= _T("HDA temperature");
				if(pstrNameJp)
					*pstrNameJp		= _T("温度");
				if(pstrDesctiption)
					*pstrDesctiption= _T("HDDの内部温度です。");
				break;

			case	0xC3:
				if(pstrName)
					*pstrName		= _T("Hardware ECC recovered");
				if(pstrNameJp)
					*pstrNameJp		= _T("ハードウエアECC復旧");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ECCによるエラー検出数です。");
				break;

			case	0xC4:
				if(pstrName)
					*pstrName		= _T("Reallocation count");
				if(pstrNameJp)
					*pstrNameJp		= _T("再割り当て数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("割り当て命令の実行数です。");
				break;

			case	0xC5:
				if(pstrName)
					*pstrName		= _T("Current pending sector count");
				if(pstrNameJp)
					*pstrNameJp		= _T("不安定セクター数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("再割り当て待ちの不安定なセクター数です。");
				break;

			case	0xC6:
				if(pstrName)
					*pstrName		= _T("Offline scan uncorrectable count");
				if(pstrNameJp)
					*pstrNameJp		= _T("未訂正エラー数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("訂正できなかったエラー数です。");
				break;

			case	0xC7:
				if(pstrName)
					*pstrName		= _T("UDMA CRC error rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("UltraDMA CRCエラー");
				if(pstrDesctiption)
					*pstrDesctiption= _T("UltraDMAモードにおけるCRCエラー数です。");
				break;

			case	0xC8:
				if(pstrName)
					*pstrName		= _T("Write error rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("書き込みエラー率");
				if(pstrDesctiption)
					*pstrDesctiption= _T("書き込み時に生じたエラーの割合です。");
				break;

			//case	0xC9:

			case	0xCA:
				if(pstrName)
					*pstrName		= _T("Data Address Mark errors");
				if(pstrNameJp)
					*pstrNameJp		= _T("DAM(Data Address Mark)エラー");
				if(pstrDesctiption)
					*pstrDesctiption= _T("DAMエラーの生じた回数もしくはベンダー独自情報です。");
				break;

			case	0xCB:
				if(pstrName)
					*pstrName		= _T("Run out cancel");
				if(pstrNameJp)
					*pstrNameJp		= _T("ECCエラー");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ECCエラーの生じた回数です。");
				break;

			case	0xCC:
				if(pstrName)
					*pstrName		= _T("Soft ECC correction");
				if(pstrNameJp)
					*pstrNameJp		= _T("論理ECC訂正");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ソフトウエアECCによるエラー訂正数です。");
				break;

			case	0xCD:
				if(pstrName)
					*pstrName		= _T("Thermal asperity rate(TAR)");
				if(pstrNameJp)
					*pstrNameJp		= _T("熱エラー率(TAR)");
				if(pstrDesctiption)
					*pstrDesctiption= _T("TARエラーの生じた回数です。");
				break;

			case	0xCE:
				if(pstrName)
					*pstrName		= _T("Flying height");
				if(pstrNameJp)
					*pstrNameJp		= _T("ヘッド高");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ディスク面とヘッドとの隙間です。");
				break;

			case	0xCF:
				if(pstrName)
					*pstrName		= _T("Spin high current");
				if(pstrNameJp)
					*pstrNameJp		= _T("最大回転電流");
				if(pstrDesctiption)
					*pstrDesctiption= _T("モーターに流れた最大電流です。");
				break;

			case	0xD0:
				if(pstrName)
					*pstrName		= _T("Spin buzz");
				if(pstrNameJp)
					*pstrNameJp		= _T("ヘッド制御");
				if(pstrDesctiption)
					*pstrDesctiption= _T("スピンアップ時のヘッド制御処理の数です。");
				break;

			case	0xD1:
				if(pstrName)
					*pstrName		= _T("Offline seek performance");
				if(pstrNameJp)
					*pstrNameJp		= _T("オフライン・シーク性能");
				if(pstrDesctiption)
					*pstrDesctiption= _T("オフライン時のシーク性能です。");
				break;

			case	0xDC:
				if(pstrName)
					*pstrName		= _T("Disk shift");
				if(pstrNameJp)
					*pstrNameJp		= _T("ディスク交換");
				if(pstrDesctiption)
					*pstrDesctiption= _T("(内容不明)");
				break;

			case	0xDD:
				if(pstrName)
					*pstrName		= _T("G-sense error rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("加速度センサー検出エラー率");
				if(pstrDesctiption)
					*pstrDesctiption= _T("衝撃によるエラーの検出割合です。");
				break;

			case	0xDE:
				if(pstrName)
					*pstrName		= _T("Loaded hours");
				if(pstrNameJp)
					*pstrNameJp		= _T("利用時間");
				if(pstrDesctiption)
					*pstrDesctiption= _T("利用可能状態にある時間数です。");
				break;

			case	0xDF:
				if(pstrName)
					*pstrName		= _T("Load/unload retry count");
				if(pstrNameJp)
					*pstrNameJp		= _T("ロード/アンロード再試行回数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ロードを繰り返した回数です。");
				break;

			case	0xE0:
				if(pstrName)
					*pstrName		= _T("Load friction");
				if(pstrNameJp)
					*pstrNameJp		= _T("ロード抵抗");
				if(pstrDesctiption)
					*pstrDesctiption= _T("物理的摩擦で生じたロード数です。");
				break;

			case	0xE1:
				if(pstrName)
					*pstrName		= _T("Load/Unload cycle count");
				if(pstrNameJp)
					*pstrNameJp		= _T("ロード/アンロード・サイクル数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ロードの合計数です。");
				break;

			case	0xE2:
				if(pstrName)
					*pstrName		= _T("Load-in time");
				if(pstrNameJp)
					*pstrNameJp		= _T("ロードイン時間");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ドライブのロードにかかる時間です。");
				break;

			case	0xE3:
				if(pstrName)
					*pstrName		= _T("Torque amplification count");
				if(pstrNameJp)
					*pstrNameJp		= _T("トルク増幅数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("回転モーメントの影響量です。");
				break;

			case	0xE4:
				if(pstrName)
					*pstrName		= _T("Power-off retract count");
				if(pstrNameJp)
					*pstrNameJp		= _T("電源切断退避数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("電源切断によるヘッドの退避回数です。");
				break;

			case	0xE6:
				if(pstrName)
					*pstrName		= _T("GMR head amplitude");
				if(pstrNameJp)
					*pstrNameJp		= _T("GMRヘッド振幅");
				if(pstrDesctiption)
					*pstrDesctiption= _T("GMRヘッドの振動の振幅です。");
				break;

			case	0xE7:
				if(pstrName)
					*pstrName		= _T("Temperature");
				if(pstrNameJp)
					*pstrNameJp		= _T("温度");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ドライブの温度です。");
				break;

			case	0xF0:
				if(pstrName)
					*pstrName		= _T("Head flying hours");
				if(pstrNameJp)
					*pstrNameJp		= _T("シーク移動時間");
				if(pstrDesctiption)
					*pstrDesctiption= _T("ヘッドが動いている時間です。");
				break;

			case	0xFA:
				if(pstrName)
					*pstrName		= _T("Read error retry rate");
				if(pstrNameJp)
					*pstrNameJp		= _T("読み込みエラー再試行数");
				if(pstrDesctiption)
					*pstrDesctiption= _T("読み込みエラーの回数です。");
				break;

			default:
				if(pstrName)
					*pstrName		= _T("Unknown");
				if(pstrNameJp)
					*pstrNameJp		= _T("不明");
				if(pstrDesctiption)
					*pstrDesctiption= _T("用途不明です。");
				return	false;
			}

			return	true;
		}

	};







	//
	//	SMART情報取得
	//
	bool	GetSmartReadData(int nDeviceNo,CSmartAttribute* pInfo)
	{
		bool	ret;
		BOOL	bRet;
		HANDLE	hIoCtrl;
		DWORD	dwReturned;
		SENDCMDINPARAMS	sSendCmd;
		SMART_READ_DATA_OUTDATA	sSendCmdOutParam;

		if(pInfo == NULL)
			return	false;
		pInfo->Init();

		//ハンドル取得
		ret = GetIoCtrlHandle(&hIoCtrl,nDeviceNo);
		if(ret == false)
			return	false;

		::ZeroMemory(&sSendCmdOutParam,sizeof(SMART_READ_DATA_OUTDATA));
		::ZeroMemory(&sSendCmd,sizeof(SENDCMDINPARAMS));
		sSendCmd.irDriveRegs.bFeaturesReg	= 0xd0;
		sSendCmd.irDriveRegs.bCylLowReg		= 0x4f;
		sSendCmd.irDriveRegs.bCylHighReg	= 0xc2;
		sSendCmd.irDriveRegs.bDriveHeadReg	= 0xA0 | ((nDeviceNo & 1) << 4);	//ドライブ番号
		sSendCmd.irDriveRegs.bCommandReg	= ATAPI_SMART_READ_DATA;			//SMART READ DATAコマンド番号
		sSendCmd.cBufferSize	= READ_ATTRIBUTE_BUFFER_SIZE;
		sSendCmd.bDriveNumber	= nDeviceNo;			//ドライブ番号

		//コマンドの実行
		bRet = ::DeviceIoControl(hIoCtrl,DFP_RECEIVE_DRIVE_DATA,&sSendCmd,sizeof(SENDCMDINPARAMS),&sSendCmdOutParam,sizeof(SMART_READ_DATA_OUTDATA),&dwReturned,NULL);
		::CloseHandle(hIoCtrl);
		if(ret == FALSE || dwReturned != sizeof(SMART_READ_DATA_OUTDATA))
			return	false;

		return	pInfo->SetData(sSendCmdOutParam.sSendCmdOutParam.bBuffer,READ_ATTRIBUTE_BUFFER_SIZE);
	}





	class	CModelInfo
	{
	public:
		CAtlString	_strModel;
		CAtlString	_strFirmware;
		CAtlString	_strSerialNo;
	};



	//
	//	デバイス情報取得
	//
	bool	GetIdentifyDevice(int nDeviceNo,IDENTIFY_DEVICE* pData,CModelInfo* pInfo=NULL)
	{
		bool	ret;
		BOOL	bRet;
		HANDLE	hIoCtrl;
		DWORD	dwReturned;
		IDENTIFY_DEVICE_OUTDATA	sSendCmdOutParam;
		SENDCMDINPARAMS	sSendCmd;

		if(pData == NULL)
			return	false;
		::ZeroMemory(pData,sizeof(IDENTIFY_DEVICE));
		if(pInfo)
			*pInfo = CModelInfo();

		//ハンドル取得
		ret = GetIoCtrlHandle(&hIoCtrl,nDeviceNo);
		if(ret == false)
			return	false;

		::ZeroMemory(&sSendCmdOutParam,sizeof(IDENTIFY_DEVICE_OUTDATA));
		::ZeroMemory(&sSendCmd,sizeof(SENDCMDINPARAMS));
		sSendCmd.irDriveRegs.bCommandReg		= ATAPI_IDENTIFY_DEVICE;			//IDENTIFY DEVICEコマンド番号
		sSendCmd.irDriveRegs.bDriveHeadReg		= 0xA0 | ((nDeviceNo & 1) << 4);	//ドライブ番号
		sSendCmd.cBufferSize	= IDENTIFY_BUFFER_SIZE;
		sSendCmd.bDriveNumber	= nDeviceNo;			//ドライブ番号

		//IDENTIFY DEVICEコマンドの実行
		bRet = ::DeviceIoControl(hIoCtrl,DFP_RECEIVE_DRIVE_DATA,&sSendCmd,sizeof(SENDCMDINPARAMS),&sSendCmdOutParam,sizeof(IDENTIFY_DEVICE_OUTDATA),&dwReturned,NULL);
		::CloseHandle(hIoCtrl);
		if(ret == FALSE || dwReturned != sizeof(IDENTIFY_DEVICE_OUTDATA))
			return	false;

		memcpy_s(pData,sizeof(IDENTIFY_DEVICE),sSendCmdOutParam.sSendCmdOutParam.bBuffer,sizeof(IDENTIFY_DEVICE));

		if(pInfo)
		{
			char	pszBuff[256];
			IDENTIFY_DEVICE*	pData;

			pData = (IDENTIFY_DEVICE*)sSendCmdOutParam.sSendCmdOutParam.bBuffer;
			ChangeByteOrder(pData->pszModelNumber,sizeof(pData->pszModelNumber));
			ChangeByteOrder(pData->pszFirmwareRev,sizeof(pData->pszFirmwareRev));
			ChangeByteOrder(pData->pszSerialNumber,sizeof(pData->pszSerialNumber));

			strncpy_s(pszBuff,256,pData->pszModelNumber,sizeof(pData->pszModelNumber));
			pInfo->_strModel = pszBuff;
			strncpy_s(pszBuff,256,pData->pszFirmwareRev,sizeof(pData->pszFirmwareRev));
			pInfo->_strFirmware = pszBuff;
			strncpy_s(pszBuff,256,pData->pszSerialNumber,sizeof(pData->pszSerialNumber));
			pInfo->_strSerialNo = pszBuff;
		}

		return	true;
	}


};






bool	Test()
{
	int		j;

	//PCに接続されているATAドライブのうち最初の4つの情報を取得&表示する
	for(j = 0; j < 4; j++)
	{
		bool	ret;
		int		i;
		int		nSize;
		CAtlString	strMessage;
		CDnpATAPI	cIDE;
		CDnpATAPI::CSmartAttribute	cAttrInfo;

		ret = cIDE.GetSmartReadData(j,&cAttrInfo);
		if(ret == false)
			continue;

		ret = cAttrInfo.GetAttributeCount(&nSize);
		if(ret == false)
			continue;

		for(i = 0; i < nSize; i++)
		{
			BYTE	cbAttrID;
			BYTE	cbValue;
			BYTE	pcbRawData[6];
			CAtlString	strName;
			CAtlString	strNameJp;
			CAtlString	strDescription;
			CAtlString	strBuff;

			ret = cAttrInfo.GetAttribute(i,&cbAttrID,NULL,&cbValue,NULL,pcbRawData,NULL);
			if(ret == false)
				continue;

			ret = cAttrInfo.GetAttributeIdString(cbAttrID,&strName,&strNameJp,&strDescription);
			if(ret == false)
				continue;

			strBuff.Format(_T("%s(%s)  %s\n"),strNameJp,strName,strDescription);
			strMessage += strBuff;
			strBuff.Format(_T("%02X %03d  %02X%02X %02X%02X %02X%02X\n\n"),cbAttrID,cbValue,pcbRawData[0],pcbRawData[1],pcbRawData[2],pcbRawData[3],pcbRawData[4],pcbRawData[5]);
			strMessage += strBuff;
		}

		::MessageBox(NULL,strMessage,_T(""),MB_OK);
	}

	return	true;
}

プロジェクトファイルをダウンロード

2007年02月06日

ドライブのモデル名や接続I/F情報などを取得する

test104.gif
情報を取得したドライブに対してDeviceIoControlによりIOCTL_STORAGE_QUERY_PROPERTYコマンドを実行すると、そのドライブの情報を取得できる。

とは言うものの実際に利用してみると、情報を取得できないドライブが少なからずある。また取得できても例えばシリアル番号などは値の格納され方がドライブによって異なっている。図でCとDドライブのシリアル番号は本来0x20 0x20 ... 0x45 0x32というバイト配列(文字列)のシリアル番号だが、二重にアスキーエンコードされている。逆にFドライブの場合はシリアル番号がアスキー文字列になっていない。情報がきちんと取得できない原因はドライブ側のためどうしようもないのだが...

結局、信頼できる情報はリムーバブルメディアかどうかと接続インターフェースぐらいだろうか?

#include "winioctl.h"
#include "atlstr.h"


class	CDnpStorageProperty
{
public:

	//
	//	使用中のドライブレター取得
	//
	//ドライブ名が文字列として列挙されて返る。例:AとC、Dドライブが使われているなら"ACD"
	//
	bool	GetUsedDriveLetter(TCHAR* pszLetter,int nSize)
	{
		int		i;
		int		nIndex;
		TCHAR	cbDrive;
		DWORD	dwDrives;

		if(pszLetter == NULL || nSize <= 0)
			return	false;

		dwDrives = ::GetLogicalDrives();

		ZeroMemory(pszLetter,sizeof(TCHAR) * nSize);
		nIndex = 0;
		cbDrive = _T('A');
		for(i = 0; i <= 'Z' - 'A'; i++)
		{
			if(! ((1 << i) & dwDrives))
				continue;

			pszLetter[nIndex++] = cbDrive + i;
			if(nIndex < nSize)
				continue;

			return	(dwDrives << (i+1)) ? false : true;
		}

		return	true;
	}



	class	CInfo
	{
	public:
		CAtlString	_strSerialNo;
		CAtlString	_strProductID;
		CAtlString	_strVendorID;
		CAtlString	_strRevision;
		bool		_bRemovableMedia;
		STORAGE_BUS_TYPE	_enumBusType;

		CInfo()
		{
			_bRemovableMedia = false;
			_enumBusType = BusTypeUnknown;
		}
	};






	//
	//	ドライブのプロパティ情報を取得する
	//
	bool	GetStorageProperty(TCHAR cbDrive,CInfo* pInfo)
	{
		BOOL	ret;
		HANDLE	hDrive;
		DWORD	dwRet;
		DWORD	dwLen;
		BYTE*	pcbData;
		CAtlString	strDrive;
		STORAGE_DEVICE_DESCRIPTOR*	pDescriptor;
		STORAGE_PROPERTY_QUERY		sQuery;

		if(pInfo == NULL)
			return	false;
		*pInfo = CInfo();

		strDrive.Format(_T("\\\\.\\%c:"),cbDrive);
		hDrive = CreateFile(strDrive,GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		if(hDrive == INVALID_HANDLE_VALUE)
			return	false;

		dwLen = 4096;		//十分なバッファー領域を確保
		pcbData = new BYTE[dwLen];
		if(pcbData == NULL)
		{
			::CloseHandle(hDrive);
			return	false;
		}
		ZeroMemory(pcbData,dwLen);

		sQuery.PropertyId	= StorageDeviceProperty;
		sQuery.QueryType	= PropertyStandardQuery;
		sQuery.AdditionalParameters[0] = NULL;
		ret = ::DeviceIoControl(hDrive,IOCTL_STORAGE_QUERY_PROPERTY,&sQuery,sizeof(STORAGE_PROPERTY_QUERY),pcbData,dwLen,&dwRet,NULL);
		::CloseHandle(hDrive);
		if(ret == FALSE)
		{
			delete	pcbData;
			return	false;
		}

		pDescriptor = (STORAGE_DEVICE_DESCRIPTOR*)pcbData;

		if(pDescriptor->SerialNumberOffset)
			pInfo->_strSerialNo		= (char*)pDescriptor + pDescriptor->SerialNumberOffset;
		if(pDescriptor->ProductIdOffset)
			pInfo->_strProductID	= (char*)pDescriptor + pDescriptor->ProductIdOffset;
		if(pDescriptor->ProductRevisionOffset)
			pInfo->_strRevision		= (char*)pDescriptor + pDescriptor->ProductRevisionOffset;
		if(pDescriptor->VendorIdOffset)
			pInfo->_strVendorID		= (char*)pDescriptor + pDescriptor->VendorIdOffset;
		pInfo->_enumBusType		= pDescriptor->BusType;
		pInfo->_bRemovableMedia	= pDescriptor->RemovableMedia ? true : false;

		delete	pcbData;

		return	true;
	}




};






bool	Test()
{
	bool	ret;
	size_t	i;
	size_t	nSize;
	TCHAR	pszDrives[256];

	CAtlString	strBuff;
	CAtlString	strMessage;
	CDnpStorageProperty			cStorage;
	CDnpStorageProperty::CInfo	cInfo;

	ret = cStorage.GetUsedDriveLetter(pszDrives,256);
	if(ret == false)
		return	false;


	nSize = ::_tcslen(pszDrives);
	for(i = 0; i < nSize; i++)
	{
		ret = cStorage.GetStorageProperty(pszDrives[i],&cInfo);
		if(ret == false)
			continue;

		strBuff.Format(_T("\nドライブ:%c"),pszDrives[i]);
		strMessage += strBuff;
		strMessage += _T("\nベンダーID:");
		strMessage += cInfo._strVendorID;
		strMessage += _T("\nプロダクトID:");
		strMessage += cInfo._strProductID;
		strMessage += _T("\nシリアル番号:");
		strMessage += cInfo._strSerialNo;
		strMessage += _T("\nリビジョン番号:");
		strMessage += cInfo._strRevision;

		strMessage += _T("\nメディアタイプ:");
		strMessage += (cInfo._bRemovableMedia) ? _T("リムーバブルメディア") : _T("固定メディア");

		strMessage += _T("\n接続インターフェース:");
		switch(cInfo._enumBusType)
		{
		case	BusTypeScsi:
			strMessage += _T("SCSI");
			break;

		case	BusTypeAtapi:
			strMessage += _T("ATAPI");
			break;

		case	BusTypeAta:
			strMessage += _T("ATA");
			break;

		case	BusType1394:
			strMessage += _T("IEEE1394");
			break;

		case	BusTypeSsa:
			strMessage += _T("BusTypeSsa");
			break;

		case	BusTypeFibre:
			strMessage += _T("BusTypeFibre");
			break;

		case	BusTypeUsb:
			strMessage += _T("USB");
			break;

		case	BusTypeRAID:
			strMessage += _T("RAID");
			break;

		case	BusTypeiScsi:
			strMessage += _T("iSCSI");
			break;

		case	BusTypeSas:
			strMessage += _T("SAS");
			break;

		case	BusTypeSata:
			strMessage += _T("SerialATA");
			break;

		case	BusTypeSd:
			strMessage += _T("SD");
			break;

		case	BusTypeMmc:
			strMessage += _T("MMC");
			break;

		default:
		case	BusTypeUnknown:
			strMessage += _T("不明");
			break;
		}
		strMessage += _T("\n");
	}
	::MessageBox(NULL,strMessage,_T(""),MB_OK);

	return	true;
}

プロジェクトファイルをダウンロード

2007年02月07日

ドライブ名からデバイス情報を取得する

test105.gif
「C:」などのドライブ名からSetup APIを利用して「汎用ボリューム」といったデバイス名などを取得する。「こんな処理何に使うの?」と聞かれると答えに窮するが...

ドライブ名とHDEVINFO系を関連付けるためにQueryDosDeviceを利用している。

依存環境:ATL
#include <winioctl.h>
#include <setupapi.h>
#pragma	comment(lib,"setupapi.lib")

#include "atlstr.h"


//
//	使用中のドライブレター取得
//
//ドライブ名が文字列として列挙されて返る。例:AとC、Dドライブが使われているなら"ACD"
//
bool	GetUsedDriveLetter(TCHAR* pszLetter,int nSize)
{
	int		i;
	int		nIndex;
	TCHAR	cbDrive;
	DWORD	dwDrives;

	if(pszLetter == NULL || nSize <= 0)
		return	false;

	dwDrives = ::GetLogicalDrives();

	ZeroMemory(pszLetter,sizeof(TCHAR) * nSize);
	nIndex = 0;
	cbDrive = _T('A');
	for(i = 0; i <= 'Z' - 'A'; i++)
	{
		if(! ((1 << i) & dwDrives))
			continue;

		pszLetter[nIndex++] = cbDrive + i;
		if(nIndex < nSize)
			continue;

		return	(dwDrives << (i+1)) ? false : true;
	}

	return	true;
}



//
//	HDEVINFO(SP_DEVINFO_DATA)のプロパティ文字列取得
//
//SetupDiGetDeviceRegistryPropertyにより文字列データを取得する
//
bool	GetDeviceRegistryPropertyString(HDEVINFO hDeviceInfoSet,PSP_DEVINFO_DATA pDeviceInfoData,DWORD dwProperty,CAtlString* pstrText)
{
	BOOL	ret;
	DWORD	dwDataType;
	DWORD	dwDataSize;
	BYTE*	pData;

	if(pstrText == NULL)
		return	false;
	*pstrText = _T("");

	pData = NULL;
	dwDataSize = 0;
	while(1)
	{
		ret = ::SetupDiGetDeviceRegistryProperty(hDeviceInfoSet,pDeviceInfoData,dwProperty,&dwDataType,pData,dwDataSize,&dwDataSize);
		if(ret || ::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
			break;

		if(pData)
			delete	pData;
		pData = new BYTE[dwDataSize];
	}
	if(ret && pData)
	{
		//TODO:REG_MULTI_SZも1つの文字列として扱っている!
		if(dwDataType == REG_EXPAND_SZ || dwDataType == REG_MULTI_SZ || dwDataType == REG_SZ)
			*pstrText = (TCHAR*)pData;
	}
	if(pData)
		delete	pData;

	return	(ret && pData) ? true : false;
}



//
//	HDEVINFOデバイス検索例
//
//「C:」などのドライブレターからQueryDosDeviceで取得できる文字列と同じ場所を示すHDEVINFOを探す
//
bool	SearchHDEVINFODevice(LPCTSTR pszPhysicalDeviceName)
{
	bool		bRet;
	BOOL		ret;
	DWORD		i;
	HDEVINFO	hDevInfo;
	CAtlString	strProperty;
	SP_DEVINFO_DATA	sDeviceInfoData;

	hDevInfo = ::SetupDiGetClassDevs(NULL,0,0,DIGCF_PRESENT | DIGCF_ALLCLASSES);
	if(hDevInfo == INVALID_HANDLE_VALUE)
		return	false;

	i = 0;
	sDeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
	while(1)
	{
		ret = ::SetupDiEnumDeviceInfo(hDevInfo,i,&sDeviceInfoData);
		if(ret == FALSE)
			break;
		i++;

		bRet = GetDeviceRegistryPropertyString(hDevInfo,&sDeviceInfoData,SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,&strProperty);
		if(bRet == false)
			continue;

		//一致するデバイスかどうかチェック
		if(strProperty != pszPhysicalDeviceName)
			continue;

		//デバイスが見つかった!
		//ここではいくつかのプロパティをメッセージボックスで表示
		{
			CAtlString	strBuff;
			CAtlString	strMessage;

			bRet = GetDeviceRegistryPropertyString(hDevInfo,&sDeviceInfoData,SPDRP_DEVICEDESC,&strProperty);
			if(bRet)
			{
				strBuff.Format(_T("SPDRP_DEVICEDESC:%s\n"),strProperty);
				strMessage += strBuff;
			}
			bRet = GetDeviceRegistryPropertyString(hDevInfo,&sDeviceInfoData,SPDRP_HARDWAREID,&strProperty);
			if(bRet)
			{
				strBuff.Format(_T("SPDRP_HARDWAREID:%s\n"),strProperty);
				strMessage += strBuff;
			}
			bRet = GetDeviceRegistryPropertyString(hDevInfo,&sDeviceInfoData,SPDRP_CLASS,&strProperty);
			if(bRet)
			{
				strBuff.Format(_T("SPDRP_CLASS:%s\n"),strProperty);
				strMessage += strBuff;
			}
			bRet = GetDeviceRegistryPropertyString(hDevInfo,&sDeviceInfoData,SPDRP_DRIVER,&strProperty);
			if(bRet)
			{
				strBuff.Format(_T("SPDRP_DRIVER:%s\n"),strProperty);
				strMessage += strBuff;
			}
			bRet = GetDeviceRegistryPropertyString(hDevInfo,&sDeviceInfoData,SPDRP_PHYSICAL_DEVICE_OBJECT_NAME,&strProperty);
			if(bRet)
			{
				strBuff.Format(_T("SPDRP_PHYSICAL_DEVICE_OBJECT_NAME:%s\n"),strProperty);
				strMessage += strBuff;
			}
			bRet = GetDeviceRegistryPropertyString(hDevInfo,&sDeviceInfoData,SPDRP_DEVTYPE,&strProperty);
			if(bRet)
			{
				strBuff.Format(_T("SPDRP_DEVTYPE:%s\n"),strProperty);
				strMessage += strBuff;
			}
			::MessageBox(NULL,strMessage,_T(""),MB_OK);
		}
	}
	::SetupDiDestroyDeviceInfoList(hDevInfo);

	return	true;;
}





bool	Test()
{
	bool	ret;
	size_t	i;
	size_t	nSize;
	TCHAR	pszDrives[256];

	//使われている全ドライブを取得
	ret = GetUsedDriveLetter(pszDrives,256);
	if(ret == false)
		return	false;

	//ドライブごとに処理
	nSize = ::_tcslen(pszDrives);
	for(i = 0; i < nSize; i++)
	{
		DWORD		dwRet;
		TCHAR		pszTargetPath[MAX_PATH];
		CAtlString	strDrive;

		strDrive.Format(_T("%c:"),pszDrives[i]);
		dwRet = ::QueryDosDevice(strDrive,pszTargetPath,MAX_PATH);
		if(dwRet == 0)
			continue;

		ret = SearchHDEVINFODevice(pszTargetPath);
	}

	return	true;
}

プロジェクトファイルをダウンロード

ドライブ名からデバイス情報を取得する(DEVINST系)

test106.gif
「C:」などのドライブレターと接続されているデバイスとを関連付ける方法にはいくつかある。ここではDEVINSTを返すCM_系APIを利用した。この方法を利用するとDeviceIoControlに渡すためのハンドルをCreateFileで取得できる。

依存環境:ATL
#include "winioctl.h"
#include "cfgmgr32.h"
#pragma	comment(lib,"setupapi.lib")

#include "atlstr.h"


//
//	使用中のドライブレター取得
//
//ドライブ名が文字列として列挙されて返る。例:AとC、Dドライブが使われているなら"ACD"
//
bool	GetUsedDriveLetter(TCHAR* pszLetter,int nSize)
{
	int		i;
	int		nIndex;
	TCHAR	cbDrive;
	DWORD	dwDrives;

	if(pszLetter == NULL || nSize <= 0)
		return	false;

	dwDrives = ::GetLogicalDrives();

	ZeroMemory(pszLetter,sizeof(TCHAR) * nSize);
	nIndex = 0;
	cbDrive = _T('A');
	for(i = 0; i <= 'Z' - 'A'; i++)
	{
		if(! ((1 << i) & dwDrives))
			continue;

		pszLetter[nIndex++] = cbDrive + i;
		if(nIndex < nSize)
			continue;

		return	(dwDrives << (i+1)) ? false : true;
	}

	return	true;
}





//
//	DEVINSTのプロパティ文字列取得
//
//CM_Get_DevNode_Registry_Propertyにより文字列データを取得する
//
bool	GetDevNodeRegistryProperty(DEVINST devInst,ULONG nProperty,CAtlString* pstrText)
{
	ULONG	nDataSize;
	BYTE*	pData;
	CONFIGRET	ret;

	if(pstrText == NULL)
		return	false;
	*pstrText = _T("");

	nDataSize = 0;
	ret = ::CM_Get_DevNode_Registry_Property(devInst,nProperty,NULL,NULL,&nDataSize,0);
	pData = new BYTE[nDataSize];
	ret = ::CM_Get_DevNode_Registry_Property(devInst,nProperty,NULL,pData,&nDataSize,0);
	if(ret == CR_SUCCESS)
		*pstrText = (TCHAR*)pData;
	if(pData)
		delete	pData;

	return	(ret == CR_SUCCESS) ? true : false;
}




//
//	DEVINSTデバイス検索例
//
//「C:」などのドライブレターからQueryDosDeviceで取得できる文字列と同じ場所を示すDEVINSTを探す
//
bool	SearchDEVINSTDevice(LPCTSTR pszPhysicalDeviceName)
{
	bool		ret;
	DEVINST     devInst;
	DEVINST     devInstNext;
	CONFIGRET   cRet;
	CAtlString	strText;

	cRet = ::CM_Locate_DevNode(&devInst,NULL,0);
	if(cRet != CR_SUCCESS)
		return	false;

	while(1)
	{
		ret = GetDevNodeRegistryProperty(devInst,CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME,&strText);
		if(ret == false || strText != pszPhysicalDeviceName)
		{
			cRet = ::CM_Get_Child(&devInstNext,devInst,0);
			if(cRet == CR_SUCCESS)
			{
				devInst = devInstNext;
				continue;
			}

			while(1)
			{
				cRet = ::CM_Get_Sibling(&devInstNext,devInst,0);
				if(cRet == CR_SUCCESS)
				{
					devInst = devInstNext;
					break;
				}

				cRet = ::CM_Get_Parent(&devInstNext,devInst,0);
				if(cRet != CR_SUCCESS)
					return	false;

				devInst = devInstNext;
			}
			continue;
		}


		//デバイスが見つかったときの処理例(ここから)
		{
			CAtlString	strBuff;
			CAtlString	strMessage;

			//プロパティをいくつか取得してメッセージボックスに表示
			ret = GetDevNodeRegistryProperty(devInst,CM_DRP_DEVICEDESC,&strText);
			if(ret)
			{
				strBuff.Format(_T("CM_DRP_DEVICEDESC:%s\n"),strText);
				strMessage += strBuff;
			}
			ret = GetDevNodeRegistryProperty(devInst,CM_DRP_HARDWAREID,&strText);
			if(ret)
			{
				strBuff.Format(_T("CM_DRP_HARDWAREID:%s\n"),strText);
				strMessage += strBuff;
			}
			ret = GetDevNodeRegistryProperty(devInst,CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME,&strText);
			if(ret)
			{
				strBuff.Format(_T("CM_DRP_PHYSICAL_DEVICE_OBJECT_NAME:%s\n"),strText);
				strMessage += strBuff;
			}

			{
				//デバイスIDを取得してCreateFileで開く
				TCHAR	pszDeviceID[2048];
				cRet = ::CM_Get_Device_ID(devInst,pszDeviceID,2048,0);
				if(cRet == CR_SUCCESS)
				{
					strBuff.Format(_T("CM_Get_Device_ID:%s\n"),pszDeviceID);
					strMessage += strBuff;
				}

				if(cRet == CR_SUCCESS)
				{
					int		nFind;
					HANDLE	hDevice;
					CAtlString	strDevice;

					//開けるのデバイスIDに_??_が含まれているもののみ?
					//_??_以降を取り出して先頭に\\\\?\\を付加してCreateFileで開く
					strDevice = pszDeviceID;
					nFind = strDevice.Find(_T("_??_"));
					if(nFind > 0)
					{
						strDevice = strDevice.Right(strDevice.GetLength() - nFind - 4);
						strDevice = _T("\\\\?\\") + strDevice;

						hDevice = CreateFile(strDevice,GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
						if(hDevice != INVALID_HANDLE_VALUE)
						{
							//ハンドルを取得できた(DeviceIoControlで利用可能)
							::CloseHandle(hDevice);
						}
					}
				}

			}

			::MessageBox(NULL,strMessage,_T(""),MB_OK);
		}
		//デバイスが見つかったときの処理例(ここまで)
		return	true;
	}

	return false;
}






bool	Test()
{
	bool	ret;
	size_t	i;
	size_t	nSize;
	TCHAR	pszDrives[256];

	//使われている全ドライブを取得
	ret = GetUsedDriveLetter(pszDrives,256);
	if(ret == false)
		return	false;

	//ドライブごとに処理
	nSize = ::_tcslen(pszDrives);
	for(i = 0; i < nSize; i++)
	{
		DWORD		dwRet;
		TCHAR		pszTargetPath[MAX_PATH];
		CAtlString	strDrive;

		strDrive.Format(_T("%c:"),pszDrives[i]);
		dwRet = ::QueryDosDevice(strDrive,pszTargetPath,MAX_PATH);
		if(dwRet == 0)
			continue;

		ret = SearchDEVINSTDevice(pszTargetPath);
	}

	return	true;
}

プロジェクトファイルをダウンロード

2007年02月10日

USBデバイスやUSBハブの詳細情報を取得する

test107.gif
USBメモリーでPCをロックするソフトなど、近頃USBデバイス情報を利用したソフトウエアが増えてきた。とは言うものの肝心の取得方法に関する情報はあまり見当たらない。

USBデバイスの情報を取得する一番の情報源はDDKに含まれるサンプルコード「USBView」だ。しかしこのソースコードは非常に読みずらく、ほかに流用しずらい。ということでここではUSBViewを流用しやすい形に成型しなおした。



ちなみに一番興味持たれる可能性が高いUSBデバイスのシリアル番号「iSerialNumber」情報についてだが...この情報はかなり信用できないようだ。

図の実行結果ではコンパクトフラッシュやSDカード、HDDドライブなどいくつかのいわゆる「USB 大容量記憶装置」を接続した。
まず、「USB スマート カード リーダー」に挿入したSDカードに関する情報はiSerialNumberを含め、まったく取得できていない。
次に、Express Cardスロットにアダプターを介して挿入したCFカードに関する情報はiSerialNumberをっ含めて取得できた。しかし取得できた値は利用したCFカードアダプターのものであり、コンパクトフラッシュメディアの情報ではない。
巷にはこのiSerialNumberを利用したデータの暗号化ソフトやPCロックソフトなどが出回っているが、その利用には注意しなければいけない場合もありそうだ。



最新のWindows Vistaに対応した最新のDDK(Windows Driver Kit)の入手先などの情報はこちらのページです。

依存環境:ATL
//
//USBデバイス情報の取得
//
//ここではDDK/WDKに含まれるサンプルソースコード「USBView」を
//そのまま利用しています。流用の際にはライセンスに気をつけて
//ください。
//
//■Windows Driver Kitの入手先などの情報
//http://www.usefullcode.net/2006/12/windows_driver_kit.html
//




#include "winioctl.h"
#include "cfgmgr32.h"
#include "setupapi.h"
#pragma	comment(lib,"setupapi.lib")



//////////////////////////////////////////
//DDKに含まれるヘッダーファイルを2つ使用
//
//「C:\WINDDK\6000\inc\wxp,C:\WINDDK\6000\inc\crt,C:\WINDDK\6000\inc\api」
//をVisual Studioのincludeパス設定へ追加すること!
//※DDKのインストール先に合わせてパスを修正する必要があります
//
//ここではWindows Vistaに対応したWindows Driver Kitを利用しました。
//
#include <usbioctl.h>
#include <usbiodef.h>



#include "atlstr.h"
#include "atlcoll.h"

#include "DnpTreeData.h"



const	TCHAR* ConnectionStatuses[] =
{
	_T("NoDeviceConnected"),
	_T("DeviceConnected"),
	_T("DeviceFailedEnumeration"),
	_T("DeviceGeneralFailure"),
	_T("DeviceCausedOvercurrent"),
	_T("DeviceNotEnoughPower")
};


class	CUsbInfo
{
protected:

	class	CStringDescriptor
	{
	public:
		CStringDescriptor()
		{
			_nDescriptorIndex = 0;
			_nLanguageID = 0;
		}

		UCHAR		_nDescriptorType;
		UCHAR		_nDescriptorIndex;
		USHORT		_nLanguageID;
		CAtlString	_strDescriptor;

		CAtlArray<BYTE>	_acbRawString;
	};








	bool	DriverNameToDeviceDesc(LPCTSTR pszDriverName, BOOLEAN DeviceId,CAtlString& strDesc)
	{
		DEVINST     devInst;
		DEVINST     devInstNext;
		CONFIGRET   cr;
		ULONG       len;
		TCHAR		buf[MAX_DEVICE_ID_LEN];

		strDesc = _T("");

		// Get Root DevNode
		//
		cr = ::CM_Locate_DevNode(&devInst,NULL,0);
		if (cr != CR_SUCCESS)
			return false;

		// Do a depth first search for the DevNode with a matching
		// DriverName value
		//
		while(1)
		{
			// Get the DriverName value
			//
			len = MAX_DEVICE_ID_LEN;
			cr = ::CM_Get_DevNode_Registry_Property(devInst,CM_DRP_DRIVER,NULL,buf,&len,0);

			if(cr != CR_SUCCESS || _tcsicmp (pszDriverName,buf) != 0)
			{
				// This DevNode didn't match, go down a level to the first child.
				//
				cr = ::CM_Get_Child(&devInstNext,devInst,0);
				if (cr == CR_SUCCESS)
				{
					devInst = devInstNext;
					continue;
				}

				// Can't go down any further, go across to the next sibling.  If
				// there are no more siblings, go back up until there is a sibling.
				// If we can't go up any further, we're back at the root and we're
				// done.
				//
				while(1)
				{
					cr = ::CM_Get_Sibling(&devInstNext,devInst,0);
					if (cr == CR_SUCCESS)
					{
						devInst = devInstNext;
						break;
					}

					cr = ::CM_Get_Parent(&devInstNext,devInst,0);
					if (cr == CR_SUCCESS)
						devInst = devInstNext;
					else
						return	false;
				}
				continue;
			}


			// If the DriverName value matches, return the DeviceDescription
			//
			len = sizeof(buf);

			if (DeviceId)
				cr = ::CM_Get_Device_ID(devInst,buf,len,0);
			else
				cr = ::CM_Get_DevNode_Registry_Property(devInst,CM_DRP_DEVICEDESC,NULL,buf,&len,0);
			if (cr == CR_SUCCESS)
			{
				strDesc = buf;
				return true;
			}
			return false;
		}

		return false;
	}

	//*****************************************************************************
	//
	// GetHCDDriverKeyName()
	//
	//*****************************************************************************

	bool	GetHCDDriverKeyName (HANDLE HCD,CAtlString& strDriverKeyName)
	{
		BOOL	ret;
		ULONG	nBytes;
		USB_HCD_DRIVERKEY_NAME	sDriverKeyName;
		PUSB_HCD_DRIVERKEY_NAME	pDriverKeyName;

		strDriverKeyName = _T("");
		::ZeroMemory(&sDriverKeyName,sizeof(USB_HCD_DRIVERKEY_NAME));
		ret = ::DeviceIoControl(HCD,IOCTL_GET_HCD_DRIVERKEY_NAME,
								  &sDriverKeyName,sizeof(USB_HCD_DRIVERKEY_NAME),
								  &sDriverKeyName,sizeof(USB_HCD_DRIVERKEY_NAME),&nBytes,NULL);
		if(ret == FALSE)
			return	false;

		nBytes = sDriverKeyName.ActualLength;
		if (nBytes <= sizeof(USB_HCD_DRIVERKEY_NAME))
			return	false;

		pDriverKeyName = (PUSB_HCD_DRIVERKEY_NAME)new BYTE[nBytes];
		if (pDriverKeyName == NULL)
			return	false;

		::ZeroMemory(pDriverKeyName,nBytes);
		ret = ::DeviceIoControl(HCD,IOCTL_GET_HCD_DRIVERKEY_NAME,
								  pDriverKeyName,nBytes,
								  pDriverKeyName,nBytes,&nBytes,NULL);

		if(ret)
			strDriverKeyName = pDriverKeyName->DriverKeyName;

		delete	pDriverKeyName;

		return ret ? true : false;
	}






	//*****************************************************************************
	//
	// GetRootHubName()
	//
	//*****************************************************************************

	bool	GetRootHubName(HANDLE HostController,CAtlString& strRootHubName)
	{
		BOOL	ret;
		ULONG	nBytes;
		USB_ROOT_HUB_NAME	sRootHubName;
		PUSB_ROOT_HUB_NAME	pRootHubName;

		strRootHubName = _T("");
		ret = ::DeviceIoControl(HostController,IOCTL_USB_GET_ROOT_HUB_NAME,NULL,0,&sRootHubName,sizeof(USB_ROOT_HUB_NAME),&nBytes,NULL);
		if (!ret)
			return	false;

		nBytes = sRootHubName.ActualLength;
		pRootHubName = (PUSB_ROOT_HUB_NAME)new BYTE[nBytes];
		if (pRootHubName == NULL)
			return	false;

		ret = ::DeviceIoControl(HostController,IOCTL_USB_GET_ROOT_HUB_NAME,NULL,0,pRootHubName,nBytes,&nBytes,NULL);
		if(ret)
			strRootHubName = pRootHubName->RootHubName;

		delete	pRootHubName;

		return ret ? true : false;
	}




	//*****************************************************************************
	//
	// GetDriverKeyName()
	//
	//*****************************************************************************

	bool	GetDriverKeyName (HANDLE Hub,ULONG ConnectionIndex,CAtlString& strDriverKeyName)
	{
		BOOL	ret;
		ULONG	nBytes;
		USB_NODE_CONNECTION_DRIVERKEY_NAME	sDriverKeyName;
		PUSB_NODE_CONNECTION_DRIVERKEY_NAME	pDriverKeyName;

		strDriverKeyName = _T("");

		::ZeroMemory(&sDriverKeyName,sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME));
		sDriverKeyName.ConnectionIndex = ConnectionIndex;
		ret = ::DeviceIoControl(Hub,IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
								  &sDriverKeyName,sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME),
								  &sDriverKeyName,sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME),
								  &nBytes,NULL);
		if (!ret)
			return	false;

		nBytes = sDriverKeyName.ActualLength;
		if (nBytes <= sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME))
			return	false;

		pDriverKeyName = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)new BYTE[nBytes];
		if(pDriverKeyName == NULL)
			return	false;

		::ZeroMemory(pDriverKeyName,nBytes);
		pDriverKeyName->ConnectionIndex = ConnectionIndex;
		ret = ::DeviceIoControl(Hub,IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
								  pDriverKeyName,nBytes,
								  pDriverKeyName,nBytes,
								  &nBytes,NULL);
		if(ret)
			strDriverKeyName = pDriverKeyName->DriverKeyName;

		delete	pDriverKeyName;

		return ret ? true : false;
	}





	//*****************************************************************************
	//
	// GetConfigDescriptor()
	//
	// hHubDevice - Handle of the hub device containing the port from which the
	// Configuration Descriptor will be requested.
	//
	// ConnectionIndex - Identifies the port on the hub to which a device is
	// attached from which the Configuration Descriptor will be requested.
	//
	// DescriptorIndex - Configuration Descriptor index, zero based.
	//
	//*****************************************************************************

	PUSB_DESCRIPTOR_REQUEST
	GetConfigDescriptor (
		HANDLE  hHubDevice,
		ULONG   ConnectionIndex,
		UCHAR   DescriptorIndex
	)
	{
		BOOL    success;
		ULONG   nBytes;
		ULONG   nBytesReturned;

		UCHAR   configDescReqBuf[sizeof(USB_DESCRIPTOR_REQUEST) + sizeof(USB_CONFIGURATION_DESCRIPTOR)];

		PUSB_DESCRIPTOR_REQUEST         configDescReq;
		PUSB_CONFIGURATION_DESCRIPTOR   configDesc;


		// Request the Configuration Descriptor the first time using our
		// local buffer, which is just big enough for the Cofiguration
		// Descriptor itself.
		//
		nBytes = sizeof(configDescReqBuf);

		configDescReq = (PUSB_DESCRIPTOR_REQUEST)configDescReqBuf;
		configDesc = (PUSB_CONFIGURATION_DESCRIPTOR)(configDescReq+1);

		::ZeroMemory(configDescReq,nBytes);

		// Indicate the port from which the descriptor will be requested
		//
		configDescReq->ConnectionIndex = ConnectionIndex;

		//
		// USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
		// IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
		//
		// USBD will automatically initialize these fields:
		//     bmRequest = 0x80
		//     bRequest  = 0x06
		//
		// We must inititialize these fields:
		//     wValue    = Descriptor Type (high) and Descriptor Index (low byte)
		//     wIndex    = Zero (or Language ID for String Descriptors)
		//     wLength   = Length of descriptor buffer
		//
		configDescReq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | DescriptorIndex;
		configDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));

		success = DeviceIoControl(hHubDevice,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
								  configDescReq,nBytes,
								  configDescReq,nBytes,
								  &nBytesReturned,NULL);

		if(success == FALSE || nBytes != nBytesReturned)
			return	NULL;

		if (configDesc->wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))
			return NULL;

		// Now request the entire Configuration Descriptor using a dynamically
		// allocated buffer which is sized big enough to hold the entire descriptor
		//
		nBytes = sizeof(USB_DESCRIPTOR_REQUEST) + configDesc->wTotalLength;

		configDescReq = (PUSB_DESCRIPTOR_REQUEST)new BYTE[nBytes];
		if (configDescReq == NULL)
			return NULL;
		::ZeroMemory(configDescReq,nBytes);

		configDesc = (PUSB_CONFIGURATION_DESCRIPTOR)(configDescReq+1);

		// Indicate the port from which the descriptor will be requested
		//
		configDescReq->ConnectionIndex = ConnectionIndex;

		//
		// USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
		// IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
		//
		// USBD will automatically initialize these fields:
		//     bmRequest = 0x80
		//     bRequest  = 0x06
		//
		// We must inititialize these fields:
		//     wValue    = Descriptor Type (high) and Descriptor Index (low byte)
		//     wIndex    = Zero (or Language ID for String Descriptors)
		//     wLength   = Length of descriptor buffer
		//
		configDescReq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | DescriptorIndex;
		configDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));

		success = DeviceIoControl(hHubDevice,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
								  configDescReq,nBytes,
								  configDescReq,nBytes,
								  &nBytesReturned,NULL);

		if(success == FALSE || nBytes != nBytesReturned)
		{
			//OOPS();
			delete	(configDescReq);
			return NULL;
		}

		if (configDesc->wTotalLength != (nBytes - sizeof(USB_DESCRIPTOR_REQUEST)))
		{
			//OOPS();
			delete	(configDescReq);
			return NULL;
		}

		return configDescReq;
	}




	//*****************************************************************************
	//
	// GetStringDescriptor()
	//
	// hHubDevice - Handle of the hub device containing the port from which the
	// String Descriptor will be requested.
	//
	// ConnectionIndex - Identifies the port on the hub to which a device is
	// attached from which the String Descriptor will be requested.
	//
	// DescriptorIndex - String Descriptor index.
	//
	// LanguageID - Language in which the string should be requested.
	//
	//*****************************************************************************

	bool	GetStringDescriptor (
		HANDLE  hHubDevice,
		ULONG   ConnectionIndex,
		UCHAR   DescriptorIndex,
		USHORT  LanguageID,
		CAtlArray<CStringDescriptor>& acDescriptor
	)
	{
		BOOL    success;
		ULONG   nBytes;
		ULONG   nBytesReturned;

		UCHAR   stringDescReqBuf[sizeof(USB_DESCRIPTOR_REQUEST) + MAXIMUM_USB_STRING_LENGTH];

		PUSB_DESCRIPTOR_REQUEST stringDescReq;
		PUSB_STRING_DESCRIPTOR  stringDesc;

		nBytes = sizeof(stringDescReqBuf);

		stringDescReq = (PUSB_DESCRIPTOR_REQUEST)stringDescReqBuf;
		stringDesc = (PUSB_STRING_DESCRIPTOR)(stringDescReq+1);

		//
		// USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
		// IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
		//
		// USBD will automatically initialize these fields:
		//     bmRequest = 0x80
		//     bRequest  = 0x06
		//
		// We must inititialize these fields:
		//     wValue    = Descriptor Type (high) and Descriptor Index (low byte)
		//     wIndex    = Zero (or Language ID for String Descriptors)
		//     wLength   = Length of descriptor buffer
		//
		::ZeroMemory(stringDescReq,nBytes);
		stringDescReq->ConnectionIndex = ConnectionIndex;
		stringDescReq->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) | DescriptorIndex;
		stringDescReq->SetupPacket.wIndex = LanguageID;
		stringDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));

		success = DeviceIoControl(hHubDevice,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
								  stringDescReq,nBytes,
								  stringDescReq,nBytes,
								  &nBytesReturned,NULL);

		if (!success || nBytesReturned < 2)
			return false;

		if (stringDesc->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE)
			return false;

		if (stringDesc->bLength != nBytesReturned - sizeof(USB_DESCRIPTOR_REQUEST))
			return false;

		if (stringDesc->bLength % 2 != 0)
			return false;


		size_t	nIndex;

		nIndex = acDescriptor.GetCount();
		acDescriptor.SetCount(nIndex + 1);

		acDescriptor[nIndex]._nDescriptorType	= stringDesc->bDescriptorType;
		acDescriptor[nIndex]._nDescriptorIndex	= DescriptorIndex;
		acDescriptor[nIndex]._nLanguageID		= LanguageID;
		acDescriptor[nIndex]._strDescriptor		= stringDesc->bString;

		acDescriptor[nIndex]._acbRawString.SetCount(stringDesc->bLength);
		memcpy(acDescriptor[nIndex]._acbRawString.GetData(),stringDesc->bString,acDescriptor[nIndex]._acbRawString.GetCount());

		return true;
	}

	//*****************************************************************************
	//
	// GetStringDescriptors()
	//
	// hHubDevice - Handle of the hub device containing the port from which the
	// String Descriptor will be requested.
	//
	// ConnectionIndex - Identifies the port on the hub to which a device is
	// attached from which the String Descriptor will be requested.
	//
	// DescriptorIndex - String Descriptor index.
	//
	// NumLanguageIDs -  Number of languages in which the string should be
	// requested.
	//
	// LanguageIDs - Languages in which the string should be requested.
	//
	//*****************************************************************************

	bool	GetStringDescriptors (
		HANDLE  hHubDevice,
		ULONG   ConnectionIndex,
		UCHAR   DescriptorIndex,
		ULONG   NumLanguageIDs,
		USHORT  *LanguageIDs,
		CAtlArray<CStringDescriptor>& acDescriptor
	)
	{
		ULONG	i;
		bool	ret;

		for (i=0; i<NumLanguageIDs; i++)
		{
			ret = GetStringDescriptor(hHubDevice,ConnectionIndex,DescriptorIndex,*LanguageIDs,acDescriptor);
			LanguageIDs++;
		}

		return true;
	}


	//*****************************************************************************
	//
	// GetAllStringDescriptors()
	//
	// hHubDevice - Handle of the hub device containing the port from which the
	// String Descriptors will be requested.
	//
	// ConnectionIndex - Identifies the port on the hub to which a device is
	// attached from which the String Descriptors will be requested.
	//
	// DeviceDesc - Device Descriptor for which String Descriptors should be
	// requested.
	//
	// ConfigDesc - Configuration Descriptor (also containing Interface Descriptor)
	// for which String Descriptors should be requested.
	//
	//*****************************************************************************

	bool
	GetAllStringDescriptors (
		HANDLE                          hHubDevice,
		ULONG                           ConnectionIndex,
		PUSB_DEVICE_DESCRIPTOR          DeviceDesc,
		CAtlArray<CStringDescriptor>& acDescriptor
	)
	{
		bool	ret;

		ULONG	numLanguageIDs;
		USHORT*	languageIDs;
		PUCHAR	descEnd;

		PUSB_COMMON_DESCRIPTOR			commonDesc;
		PUSB_CONFIGURATION_DESCRIPTOR	ConfigDesc;

		CAtlArray<CStringDescriptor>	acLanguageIDDescriptor;

		////////////////////////////////
		//文字列情報が存在するかチェック
		//
		ConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR)GetConfigDescriptor(hHubDevice,ConnectionIndex,0);
		if(ConfigDesc == NULL)
		{
			//取得できるString Descriptorsがない
			delete	ConfigDesc;
			return	true;
		}
		//注意:上の処理を通っても確実に文字列情報があるとは限らない



		////////////////////////////////
		//文字列の言語種と言語数を取得
		//
		ret = GetStringDescriptor(hHubDevice,ConnectionIndex,0,0,acLanguageIDDescriptor);
		if (ret == false)
		{
			delete	ConfigDesc;
			return false;
		}

		numLanguageIDs = ((ULONG)acLanguageIDDescriptor[0]._acbRawString.GetCount() - 2) / 2;
		languageIDs = (USHORT*)new BYTE[acLanguageIDDescriptor[0]._acbRawString.GetCount()];
		if(languageIDs == NULL)
		{
			delete	ConfigDesc;
			return false;
		}
		memcpy(languageIDs,acLanguageIDDescriptor[0]._acbRawString.GetData(),acLanguageIDDescriptor[0]._acbRawString.GetCount());



		////////////////////////////////
		//文字列を取得
		//

		if (DeviceDesc->iManufacturer)
			ret = GetStringDescriptors(hHubDevice,ConnectionIndex,DeviceDesc->iManufacturer,numLanguageIDs,languageIDs,acDescriptor);

		if (DeviceDesc->iProduct)
			ret = GetStringDescriptors(hHubDevice,ConnectionIndex,DeviceDesc->iProduct,numLanguageIDs,languageIDs,acDescriptor);

		if (DeviceDesc->iSerialNumber)
			ret = GetStringDescriptors(hHubDevice,ConnectionIndex,DeviceDesc->iSerialNumber,numLanguageIDs,languageIDs,acDescriptor);


		descEnd = (PUCHAR)ConfigDesc + ConfigDesc->wTotalLength;

		commonDesc = (PUSB_COMMON_DESCRIPTOR)ConfigDesc;
		while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd && (PUCHAR)commonDesc + commonDesc->bLength <= descEnd)
		{
			switch (commonDesc->bDescriptorType)
			{
				case USB_CONFIGURATION_DESCRIPTOR_TYPE:
				case USB_INTERFACE_DESCRIPTOR_TYPE:
					if (commonDesc->bLength != sizeof(USB_CONFIGURATION_DESCRIPTOR))
					{
						//OOPS();
						break;
					}
					if(commonDesc->bDescriptorType == USB_CONFIGURATION_DESCRIPTOR_TYPE && ((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc)->iConfiguration)
					{
						ret = GetStringDescriptors(hHubDevice,ConnectionIndex,
												 ((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc)->iConfiguration,
												 numLanguageIDs,languageIDs,acDescriptor);
					}
					else if(commonDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE && ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->iInterface)
					{
						ret = GetStringDescriptors(hHubDevice,ConnectionIndex,
												 ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->iInterface,
												 numLanguageIDs,languageIDs,acDescriptor);
					}
					//ここにbreak;を配置しないこと!
				default:
					commonDesc = (PUSB_COMMON_DESCRIPTOR)((PUCHAR)commonDesc + commonDesc->bLength);
					continue;
			}
			break;
		}

		delete	languageIDs;
		delete	ConfigDesc;

		return true;
	}



	//*****************************************************************************
	//
	// GetExternalHubName()
	//
	//*****************************************************************************

	bool	GetExternalHubName (HANDLE Hub,ULONG ConnectionIndex,CAtlString& strExternalHubName)
	{
		BOOL	ret;
		ULONG	nBytes;
		USB_NODE_CONNECTION_NAME	extHubName;
		PUSB_NODE_CONNECTION_NAME	extHubNameW;

		strExternalHubName = _T("");

		::ZeroMemory(&extHubName,sizeof(USB_NODE_CONNECTION_NAME));
		extHubName.ConnectionIndex = ConnectionIndex;
		ret = ::DeviceIoControl(Hub,IOCTL_USB_GET_NODE_CONNECTION_NAME,
								  &extHubName,sizeof(USB_NODE_CONNECTION_NAME),
								  &extHubName,sizeof(USB_NODE_CONNECTION_NAME),
								  &nBytes,NULL);
		if(ret == FALSE)
			return	false;

		nBytes = extHubName.ActualLength;
		if (nBytes <= sizeof(USB_NODE_CONNECTION_NAME))
			return	false;

		extHubNameW = (PUSB_NODE_CONNECTION_NAME)new BYTE[nBytes];
		if(extHubNameW == NULL)
			return	false;

		::ZeroMemory(extHubNameW,nBytes);
		extHubNameW->ConnectionIndex = ConnectionIndex;
		ret = ::DeviceIoControl(Hub,IOCTL_USB_GET_NODE_CONNECTION_NAME,
								  extHubNameW,nBytes,
								  extHubNameW,nBytes,
								  &nBytes,NULL);

		if(ret)
			strExternalHubName = extHubNameW->NodeName;

		delete	extHubNameW;

		return ret ? true : false;
	}

















































	class	CInfo
	{
	public:
		CInfo()
		{
			_bNodeInfo = false;
			::ZeroMemory(&_sNodeInfo,sizeof(USB_NODE_INFORMATION));

			_bNodeConnectionInfoEx = false;
			//_pNodeConnectionInfoEx = (PUSB_NODE_CONNECTION_INFORMATION_EX)_pNodeConnectionInfoEx_buffer;
			::ZeroMemory(_pNodeConnectionInfoEx_buffer,sizeof(USB_NODE_CONNECTION_INFORMATION_EX) + sizeof(USB_PIPE_INFO) * 30);

			_nPort = 0;
		}

		int			_nPort;
		CAtlString	_strName;

		CAtlArray<CStringDescriptor>	acDescriptor;

		bool	_bNodeInfo;
		USB_NODE_INFORMATION	_sNodeInfo;

		bool	_bNodeConnectionInfoEx;
		PUSB_NODE_CONNECTION_INFORMATION_EX	Get_pNodeConnectionInfoEx()
		{
			return	(PUSB_NODE_CONNECTION_INFORMATION_EX)_pNodeConnectionInfoEx_buffer;
		}
		//PUSB_NODE_CONNECTION_INFORMATION_EX	_pNodeConnectionInfoEx;

	private:
		BYTE	_pNodeConnectionInfoEx_buffer[sizeof(USB_NODE_CONNECTION_INFORMATION_EX) + sizeof(USB_PIPE_INFO) * 30];

	};

	CAtlArray<CInfo>		_acDeviceInfo;

	CDnpTreeData<UINT>	_treeDeviceInfoIndex;







	//*****************************************************************************
	//
	// EnumerateHubPorts()
	//
	// hTreeParent - Handle of the TreeView item under which the hub port should
	// be added.
	//
	// hHubDevice - Handle of the hub device to enumerate.
	//
	// NumPorts - Number of ports on the hub.
	//
	//*****************************************************************************

	void EnumerateHubPorts(UINT nTreeIndex,HANDLE hHubDevice,ULONG NumPorts)
	{
		ULONG       index;
		BOOL        success;

		PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfoEx;

		CAtlString	strDeviceDesc;

		// Loop over all ports of the hub.
		//
		// Port indices are 1 based, not 0 based.
		//
		for (index=1; index <= NumPorts; index++)
		{
			ULONG	nBytesEx;
			BYTE	pBuff[sizeof(USB_NODE_CONNECTION_INFORMATION_EX) + sizeof(USB_PIPE_INFO) * 30];

			nBytesEx = sizeof(pBuff);
			connectionInfoEx = (PUSB_NODE_CONNECTION_INFORMATION_EX)pBuff;
			::ZeroMemory(connectionInfoEx,nBytesEx);


			//IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EXを取得
			connectionInfoEx->ConnectionIndex = index;
			success = DeviceIoControl(hHubDevice,IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
									  connectionInfoEx,nBytesEx,
									  connectionInfoEx,nBytesEx,
									  &nBytesEx,NULL);

			//IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EXの代わりにIOCTL_USB_GET_NODE_CONNECTION_INFORMATIONを取得
			if (!success)
			{
				PUSB_NODE_CONNECTION_INFORMATION    connectionInfo;
				ULONG                               nBytes;

				// Try using IOCTL_USB_GET_NODE_CONNECTION_INFORMATION
				// instead of IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX
				//
				nBytes = sizeof(USB_NODE_CONNECTION_INFORMATION) + sizeof(USB_PIPE_INFO) * 30;

				connectionInfo = (PUSB_NODE_CONNECTION_INFORMATION)new BYTE[nBytes];

				::ZeroMemory(connectionInfo,nBytes);
				connectionInfo->ConnectionIndex = index;
				success = DeviceIoControl(hHubDevice,IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
										  connectionInfo,nBytes,
										  connectionInfo,nBytes,
										  &nBytes,NULL);

				if (!success)
				{
					delete	(connectionInfo);
					continue;
				}

				// Copy IOCTL_USB_GET_NODE_CONNECTION_INFORMATION into
				// IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX structure.
				//
				connectionInfoEx->ConnectionIndex	= connectionInfo->ConnectionIndex;
				connectionInfoEx->DeviceDescriptor	= connectionInfo->DeviceDescriptor;
				connectionInfoEx->CurrentConfigurationValue = connectionInfo->CurrentConfigurationValue;
				connectionInfoEx->Speed				= connectionInfo->LowSpeed ? UsbLowSpeed : UsbFullSpeed;
				connectionInfoEx->DeviceIsHub		= connectionInfo->DeviceIsHub;
				connectionInfoEx->DeviceAddress		= connectionInfo->DeviceAddress;
				connectionInfoEx->NumberOfOpenPipes	= connectionInfo->NumberOfOpenPipes;
				connectionInfoEx->ConnectionStatus	= connectionInfo->ConnectionStatus;
				memcpy(&connectionInfoEx->PipeList[0],&connectionInfo->PipeList[0],sizeof(USB_PIPE_INFO) * 30);
				delete	(connectionInfo);
			}


			bool	ret;
			UINT	nChildTreeIndex;
			UINT	nIndex;


			//ツリーへUSBハブ/デバイスの情報を追加
			nIndex = (UINT)_acDeviceInfo.GetCount();
			_acDeviceInfo.SetCount(nIndex + 1);
			ret = _treeDeviceInfoIndex.AddData(nTreeIndex,&nChildTreeIndex,nIndex);		//子として追加
			if(ret == false)
				continue;

			_acDeviceInfo[nIndex]._bNodeConnectionInfoEx = true;
			::memcpy(_acDeviceInfo[nIndex].Get_pNodeConnectionInfoEx(),connectionInfoEx,nBytesEx);
			_acDeviceInfo[nIndex]._nPort = index;





			if(connectionInfoEx->ConnectionStatus == DeviceConnected)
				GetAllStringDescriptors(hHubDevice,index,&connectionInfoEx->DeviceDescriptor,_acDeviceInfo[nIndex].acDescriptor);




			strDeviceDesc = _T("");
			if(connectionInfoEx->ConnectionStatus != NoDeviceConnected)
			{
				bool		ret;
				CAtlString	strDriverKeyName;

				ret = GetDriverKeyName(hHubDevice,index,strDriverKeyName);
				if(ret)
					ret = DriverNameToDeviceDesc(strDriverKeyName,FALSE,strDeviceDesc);
				if(ret)
					_acDeviceInfo[nIndex]._strName = strDeviceDesc;

				//ATLTRACE(_T("%s\n"),strDeviceDesc);
			}




			if (connectionInfoEx->DeviceIsHub)
			{
				CAtlString	strExtHubName;

				ret = GetExternalHubName(hHubDevice,index,strExtHubName);
				if(ret && _acDeviceInfo[nIndex]._strName == _T(""))
					_acDeviceInfo[nIndex]._strName = strExtHubName;

				//次のUSBハブを列挙
				if(ret)
					EnumerateHub(nChildTreeIndex,strExtHubName);
			}
		}
	}








	bool	EnumerateHub(UINT nTreeIndex,LPCTSTR HubName)
	{
		HANDLE                  hHubDevice;
		BOOL                    ret;
		ULONG                   nBytes;
		USB_NODE_INFORMATION	sHubInfo;
		CAtlString	strHubName;
		CAtlString	strDeviceName;

		strDeviceName = _T("\\\\.\\");
		strDeviceName += HubName;
		hHubDevice = ::CreateFile(strDeviceName,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
		if (hHubDevice == INVALID_HANDLE_VALUE)
			return	false;

		//ハブの持つポート数取得
		::ZeroMemory(&sHubInfo,sizeof(USB_NODE_INFORMATION));
		ret = ::DeviceIoControl(hHubDevice,IOCTL_USB_GET_NODE_INFORMATION,
								  &sHubInfo,sizeof(USB_NODE_INFORMATION),
								  &sHubInfo,sizeof(USB_NODE_INFORMATION),&nBytes,NULL);

		if(ret && nTreeIndex < _acDeviceInfo.GetCount())
		{
			//USBハブ詳細情報の追加保存
			_acDeviceInfo[nTreeIndex]._bNodeInfo = true;
			::memcpy(&_acDeviceInfo[nTreeIndex]._sNodeInfo,&sHubInfo,sizeof(USB_NODE_INFORMATION));
		}

		if(ret)
			EnumerateHubPorts(nTreeIndex,hHubDevice,sHubInfo.u.HubInformation.HubDescriptor.bNumberOfPorts);

		::CloseHandle(hHubDevice);

		return	ret ? true : false;
	}



public:



	bool	Test()
	{
		BOOL		ret;
		HANDLE		hHCDev;
		ULONG		index;
		ULONG		nLen;
		HDEVINFO	hDevInfo;
		IID			iidUSBHostController;
		SP_DEVICE_INTERFACE_DATA			sDeviceInterfaceData;
		PSP_DEVICE_INTERFACE_DETAIL_DATA	pDeviceDetailData;


		//USBコントローラークラス用GUIDを文字列から取得
		::IIDFromString(L"{3ABF6F2D-71C4-462a-8A92-1E6861E6AF27}",&iidUSBHostController);

		//USBホストコントローラーの列挙開始
		hDevInfo = ::SetupDiGetClassDevs(&iidUSBHostController,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
		if(hDevInfo == INVALID_HANDLE_VALUE)
			return	false;

		sDeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

		index = 0;
		while(1)
		{
			//ホストコントローラーを開く
			ret = ::SetupDiEnumDeviceInterfaces(hDevInfo,0,&iidUSBHostController,index,&sDeviceInterfaceData);
			if(ret == FALSE)
				break;
			index++;

			//詳細情報サイズを取得
			ret = ::SetupDiGetDeviceInterfaceDetail(hDevInfo,&sDeviceInterfaceData,NULL,0,&nLen,NULL);

			pDeviceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) new BYTE[nLen];
			if(pDeviceDetailData == NULL)
				return	false;

			::ZeroMemory(pDeviceDetailData,nLen);
			pDeviceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

			//詳細情報を取得
			ret = ::SetupDiGetDeviceInterfaceDetail(hDevInfo,&sDeviceInterfaceData,pDeviceDetailData,nLen,&nLen,NULL);
			if(ret == FALSE)
			{
				delete	pDeviceDetailData;
				continue;
			}

			//ホストコントローラーをCreateFileで開く
			hHCDev = ::CreateFile(pDeviceDetailData->DevicePath,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
			if(hHCDev == INVALID_HANDLE_VALUE)
			{
				delete	pDeviceDetailData;
				continue;
			}


			bool		bRet;
			CAtlString	strRootHubName;
			CAtlString	strDeviceDesc;
			CAtlString	strDriverKeyName;

			bRet = GetHCDDriverKeyName(hHCDev,strDriverKeyName);
			if(bRet)
				bRet = GetRootHubName(hHCDev,strRootHubName);
			::CloseHandle(hHCDev);


			UINT	nTreeIndex;

			if(bRet)
				bRet = DriverNameToDeviceDesc(strDriverKeyName,FALSE,strDeviceDesc);
			if(bRet)
			{
				//ATLTRACE(_T("%s\n"),strDeviceDesc);

				//ツリーへホストコントローラーの情報を追加
				UINT	nIndex;

				nIndex = (UINT)_acDeviceInfo.GetCount();
				_acDeviceInfo.SetCount(nIndex + 1);
				_acDeviceInfo[nIndex]._strName = strDeviceDesc;

				bRet = _treeDeviceInfoIndex.AddData(-1,&nTreeIndex,nIndex);		//ルートへ追加
			}

			//USBハブの列挙開始
			if(bRet)
				EnumerateHub(nTreeIndex,strRootHubName);

			delete	pDeviceDetailData;
		}

		::SetupDiDestroyDeviceInfoList(hDevInfo);

		return	true;
	}



	bool	PrintTreeData(CAtlArray<UINT>* panIndex=NULL,CAtlString strPrefix=_T(""))
	{
		bool	ret;
		CAtlArray<UINT>	anRootIndex;

		if(panIndex == NULL)
		{
			ret = _treeDeviceInfoIndex.GetRootItemIndex(anRootIndex);
			if(ret == false)
				return	false;
			panIndex = &anRootIndex;
		}

		size_t	i;
		size_t	nSize;

		nSize = panIndex->GetCount();
		for(i = nSize - 1; i != -1; i--)
		{
			CInfo*	pInfo;

			if((*panIndex)[i] >= _acDeviceInfo.GetCount())
				continue;

			CAtlArray<UINT>	anNextIndex;

			pInfo = &_acDeviceInfo[(*panIndex)[i]];

			//デバイス名の表示
			{
				CAtlString	strName;

				if(pInfo->_nPort && pInfo->_bNodeConnectionInfoEx)
					strName.Format(_T("[Port%d] %s "),pInfo->_nPort,ConnectionStatuses[pInfo->Get_pNodeConnectionInfoEx()->ConnectionStatus]);

				if(pInfo->_strName != _T(""))
					strName += pInfo->_strName;

				ATLTRACE("%s %s\n",strPrefix,strName);
			}

			//USBハブ情報の表示
			if(pInfo->_bNodeInfo)
			{
				CAtlString	strMessage;
				CAtlString	strBuff;

				strBuff.Format(_T("%s    bNumberOfPorts: %d\n"),strPrefix,pInfo->_sNodeInfo.u.HubInformation.HubDescriptor.bNumberOfPorts);
				strMessage += strBuff;
				//strBuff.Format(_T("%s    bPowerOnToPowerGood: 0x%02X\n"),strPrefix,pInfo->_sNodeInfo.u.HubInformation.HubDescriptor.bPowerOnToPowerGood);
				//strMessage += strBuff;
				strBuff.Format(_T("%s    bHubControlCurrent: 0x%02X\n"),strPrefix,pInfo->_sNodeInfo.u.HubInformation.HubDescriptor.bHubControlCurrent);
				strMessage += strBuff;
				//strBuff.Format(_T("%s    bRemoveAndPowerMask: 0x%02X\n"),strPrefix,pInfo->_sNodeInfo.u.HubInformation.HubDescriptor.bRemoveAndPowerMask);
				//strMessage += strBuff;

				ATLTRACE(_T("%s"),strMessage);
			}


			//デバイス/ハブ詳細情報の表示
			if(pInfo->_bNodeConnectionInfoEx && pInfo->Get_pNodeConnectionInfoEx()->ConnectionStatus == DeviceConnected)
			{
				CAtlString	strMessage;
				CAtlString	strBuff;

				strBuff.Format(_T("%s    idVendor: 0x%04X\n"),strPrefix,pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.idVendor);
				strMessage += strBuff;

				BYTE	iData;

				iData = pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.iProduct;
				if(iData)
				{
					CAtlString	strDescriptor;
					{
						size_t	j;
						size_t	nSize;

						nSize = pInfo->acDescriptor.GetCount();
						for(j = 0; j < nSize; j++)
						{
							if(pInfo->acDescriptor[j]._nDescriptorIndex != iData)
								continue;

							strDescriptor = pInfo->acDescriptor[j]._strDescriptor;
							break;
						}
					}
					strBuff.Format(_T("%s    iProduct: 0x%02X(%s)\n"),strPrefix,iData,strDescriptor);
					strMessage += strBuff;
				}
				iData = pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.iManufacturer;
				if(iData)
				{
					CAtlString	strDescriptor;
					{
						size_t	j;
						size_t	nSize;

						nSize = pInfo->acDescriptor.GetCount();
						for(j = 0; j < nSize; j++)
						{
							if(pInfo->acDescriptor[j]._nDescriptorIndex != iData)
								continue;

							strDescriptor = pInfo->acDescriptor[j]._strDescriptor;
							break;
						}
					}
					strBuff.Format(_T("%s    iManufacturer: 0x%02X(%s)\n"),strPrefix,iData,strDescriptor);
					strMessage += strBuff;
				}
				iData = pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.iSerialNumber;
				if(iData)
				{
					CAtlString	strDescriptor;
					{
						size_t	j;
						size_t	nSize;

						nSize = pInfo->acDescriptor.GetCount();
						for(j = 0; j < nSize; j++)
						{
							if(pInfo->acDescriptor[j]._nDescriptorIndex != iData)
								continue;

							strDescriptor = pInfo->acDescriptor[j]._strDescriptor;
							break;
						}
					}
					strBuff.Format(_T("%s    iSerialNumber: 0x%02X(%s)\n"),strPrefix,iData,strDescriptor);
					strMessage += strBuff;
				}

				//strBuff.Format(_T("%s    bcdUSB: 0x%04X\n"),strPrefix,pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.bcdUSB);
				//strMessage += strBuff;
				//strBuff.Format(_T("%s    bcdDevice: 0x%04X\n"),strPrefix,pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.bcdDevice);
				//strMessage += strBuff;
				//strBuff.Format(_T("%s    bDeviceClass: 0x%02X\n"),strPrefix,pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.bDeviceClass);
				//strMessage += strBuff;
				//strBuff.Format(_T("%s    bDeviceProtocol: 0x%02X\n"),strPrefix,pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.bDeviceProtocol);
				//strMessage += strBuff;
				//strBuff.Format(_T("%s    bDeviceSubClass: 0x%02X\n"),strPrefix,pInfo->Get_pNodeConnectionInfoEx()->DeviceDescriptor.bDeviceSubClass);
				//strMessage += strBuff;

				ATLTRACE(_T("%s"),strMessage);
			}
			//else
			//{
			//	ATLTRACE(_T("\n"));
			//}





			ret = _treeDeviceInfoIndex.GetChildItemIndex((*panIndex)[i],anNextIndex);
			if(ret)
				PrintTreeData(&anNextIndex,strPrefix + _T("   "));
		}

		return	true;
	}


};


void	Test()
{
	CUsbInfo	cInfo;

	cInfo.Test();
	cInfo.PrintTreeData();
}

プロジェクトファイルをダウンロード

2007年02月11日

USBメモリーの挿入などを検出する

USBメモリーをUSBポートに挿したときや抜いたとき、DVDドライブにCD-ROMを入れたときなど、デバイスの状態が変化したときにはWM_DEVICECHANGEメッセージが届く。
残念ながらこのメッセージを通じて取得できるのは必要最低限の情報しかないため、続く処理を作るのはかなり面倒になる。しかし必要最低限の情報しかないため理解するのは楽だろう。
ここではメッセージを取得したときに得られる情報の一部をATLTRACEにて出力ウインドウに書き出している。


ATL/WTLアプリケーションウイザードでダイアログベースのプロジェクトを作成し、自動生成されたソースコードに変更を加える。

■stdafx.hを修正 WINVERの値を0x0500に変更する。
#define WINVER		0x0500
■MainDlg.hを修正 includeファイルを2つ追加する。
#include "atlstr.h"
#include "Dbt.h"
メッセージマップにWM_DEVICECHANGE用のハンドラーを追加、CMainDlg内にメンバークラスを追加する。
	BEGIN_MSG_MAP(CMainDlg)
		MESSAGE_HANDLER(WM_DEVICECHANGE, OnDeviceChange)
		
		(。。。省略。。。)
		
	END_MSG_MAP()

	//
	//この関数は以下のURLよりコピー&改変
	//http://support.microsoft.com/kb/163503/ja
	//
	TCHAR	FirstDriveFromMask(ULONG unitmask)
	{
		TCHAR i;

		for (i = 0; i < 26; ++i)
		{
			if (unitmask & 0x1)
				break;
			unitmask = unitmask >> 1;
		}

		return (i + _T('A'));
	}



	LRESULT OnDeviceChange(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		bool	bAddData;
		CAtlString	strMessage;

		bAddData = false;
		switch(wParam)
		{
		case	DBT_CONFIGCHANGECANCELED:
			strMessage = _T("DBT_CONFIGCHANGECANCELED:設定変更要求がキャンセルされました");
			break;

		case	DBT_CONFIGCHANGED:
			strMessage = _T("DBT_CONFIGCHANGED:設定が変更されました");
			break;

		case	DBT_CUSTOMEVENT:
			strMessage = _T("DBT_CUSTOMEVENT:ドライバー定義のカスタムイベントが発行されました");
			bAddData = true;
			break;

		case	DBT_DEVICEARRIVAL:
			strMessage = _T("DBT_DEVICEARRIVAL:デバイスが使用可能になりました");
			bAddData = true;
			break;

		case	DBT_DEVICEQUERYREMOVE:
			strMessage = _T("DBT_DEVICEQUERYREMOVE:デバイス停止要求が発行されました");
			bAddData = true;
			break;

		case	DBT_DEVICEQUERYREMOVEFAILED:
			strMessage = _T("DBT_DEVICEQUERYREMOVEFAILED:デバイス停止要求が失敗されました");
			bAddData = true;
			break;

		case	DBT_DEVICEREMOVECOMPLETE:
			strMessage = _T("DBT_DEVICEREMOVECOMPLETE:デバイスが停止されました");
			bAddData = true;
			break;

		case	DBT_DEVICEREMOVEPENDING:
			strMessage = _T("DBT_DEVICEREMOVEPENDING:デバイスを停止中です");
			bAddData = true;
			break;

		case	DBT_DEVICETYPESPECIFIC:
			strMessage = _T("DBT_DEVICETYPESPECIFIC:デバイスの独自イベントが発行されました");
			bAddData = true;
			break;

		case	DBT_DEVNODES_CHANGED:
			strMessage = _T("DBT_DEVNODES_CHANGED:システムのデバイス状態が変化しました");
			break;

		case	DBT_QUERYCHANGECONFIG:
			strMessage = _T("DBT_QUERYCHANGECONFIG:設定変更要求が発行されました");
			break;

		case	DBT_USERDEFINED:
			strMessage = _T("DBT_USERDEFINED");
			break;

		default:
			strMessage.Format(_T("unknown(%04X)"),wParam);
			{
				_DEV_BROADCAST_USERDEFINED*	pData = (_DEV_BROADCAST_USERDEFINED*)lParam;
				if(pData)
				{
					strMessage += _T("(");
					strMessage += pData->dbud_szName;
					strMessage += _T(")");

					lParam = (LPARAM)&pData->dbud_dbh;
					bAddData = true;
				}
			}
			break;
		} 


		if(bAddData)
		{
			DEV_BROADCAST_HDR*	pData = (DEV_BROADCAST_HDR*)lParam;
			if(pData)
			{
				CAtlString	strBuff;

				strBuff.Format(_T("(Size=%d DeviceType=%08d)"),pData->dbch_size,pData->dbch_devicetype);
				strMessage += strBuff;

				switch(pData->dbch_devicetype)
				{
				case	DBT_DEVTYP_OEM:
					{
						DEV_BROADCAST_OEM*	pExtend = (DEV_BROADCAST_OEM*)pData;
						if(pExtend)
						{
						}
					}
					break;

				case	DBT_DEVTYP_DEVNODE:
					{
						DEV_BROADCAST_DEVNODE*	pExtend = (DEV_BROADCAST_DEVNODE*)pData;
						if(pExtend)
						{
						}
					}
					break;

				case	DBT_DEVTYP_VOLUME:
					{
						DEV_BROADCAST_VOLUME*	pExtend = (DEV_BROADCAST_VOLUME*)pData;
						if(pExtend)
						{
							CAtlString	strBuff;

							strBuff.Format(_T("「%c:」"),FirstDriveFromMask(pExtend->dbcv_unitmask));
							strMessage += strBuff;
						}
					}
					break;

				case	DBT_DEVTYP_PORT:
					{
						DEV_BROADCAST_PORT*	pExtend = (DEV_BROADCAST_PORT*)pData;
						if(pExtend)
						{
							CAtlString	strBuff;

							strBuff.Format(_T("(PORT:%s)"),pExtend->dbcp_name);
							strMessage += strBuff;
						}
					}
					break;

				case	DBT_DEVTYP_NET:
					{
						DEV_BROADCAST_NET*	pExtend = (DEV_BROADCAST_NET*)pData;
						if(pExtend)
						{
						}
					}
					break;

				case	DBT_DEVTYP_DEVICEINTERFACE:
					{
						DEV_BROADCAST_DEVICEINTERFACE*	pExtend = (DEV_BROADCAST_DEVICEINTERFACE*)pData;
						if(pExtend)
						{
							CAtlString	strBuff;

							strBuff.Format(_T("(I/F:%s)"),pExtend->dbcc_name);
							strMessage += strBuff;
						}
					}
					break;

				case	DBT_DEVTYP_HANDLE:
					{
						DEV_BROADCAST_HANDLE*	pExtend = (DEV_BROADCAST_HANDLE*)pData;
						if(pExtend && pExtend->dbch_handle && pExtend->dbch_handle != INVALID_HANDLE_VALUE)
						{
						}
					}
					break;

				}
			}
		}

		ATLTRACE("%s\n",strMessage);

		return	0;
	}

プロジェクトファイルをダウンロード

タスクトレイ常駐プログラムを簡単に作る

画面右下のタスクトレイ(通知領域)にアイコンを登録するアプリケーションは多い。ここでは簡単にアイコンを登録するためのクラスとその使用例を作成した。

ここでは簡単にするためにInternet Explorerのバージョン判断処理やアイコンのID設定処理は省いた。またタスクトレイのアイコンを管理するクラス「CDnpTaskTrayIcon」内には右クリックメッセージ表示用やバルーンメッセージ表示用の処理も備えているが、ここで示した使用例では利用していない。

ダウンロードできるプロジェクトをビルド、実行すると初期状態でタスクトレイにアイコンが表示される。そのアイコンをダブルクリックを繰り返すとダイアログが表示したり消えたりする。


ATL/WTLアプリケーションウイザードでダイアログベースのプロジェクトを作成し、自動生成されたソースコードを編集する。

■以下のコードをDnpTaskTrayIcon.hとして保存
#pragma	once


//#include "dinoputy.h"
//#include "dinoputy.cpp"


//
//	タスクトレイアイコン管理クラス
//
// uIDはゼロ固定のため、複数のアイコン登録は未対応
//
//
//■使用例
//
////stdafx.hでIEバージョン値を次のように定義すること
////#define _WIN32_IE	0x0501
//
//#include "DnpTaskTrayIcon.h"
//
//#define	WM_TRAY_NOTIFY_ICON		WM_APP + 10
//
//class CMainFrame : public CFrameWindowImpl<CMainFr(....省略....)
//{
//protected:
//	//メンバ変数として1行追加
//	CDnpTaskTrayIcon	_cTrayIcon;
//public:
//
//	(....省略....)
//
//	//メッセージマップに2行追加\
//	BEGIN_MSG_MAP(CMainFrame)
//		(....省略....)
//		MESSAGE_HANDLER(WM_TRAY_NOTIFY_ICON, OnTrayNotify)
//		CHAIN_MSG_MAP_MEMBER(_cTrayIcon)			//タスクトレイにメッセージを回す
//	END_MSG_MAP()
//
//	//トレイの登録とバルーンメッセージの表示
//	//OnCreateに限らず好きな位置に行を追加
//	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
//	{
//		(....省略....)
//
//		//トレイアイコンの登録
//		_cTrayIcon.CreateTrayIcon(_T("テスト"),IDR_MAINFRAME,m_hWnd,WM_TRAY_NOTIFY_ICON);
//
//		//バルーンメッセージの表示
//		_cTrayIcon.ShowBaloonHelp(_T("バルーン"),_T("メッセージです"),15000);
//
//		(....省略....)
//	}
//
//
//	(....省略....)
//
//
//	//アイコンのクリック処理用関数を追加
//	LRESULT OnTrayNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
//	{
//		switch(lParam)
//		{
//		case	WM_LBUTTONDBLCLK:		//ダブルクリック処理
//			break;
//
//		case	WM_RBUTTONDOWN:			//右クリック処理
//			break;
//
//		case	NIN_BALLOONSHOW:		//バルーンが表示されたとき
//			break;
//
//		case	NIN_BALLOONTIMEOUT:		//バルーンがタイムアウトで消えたとき
//			break;
//
//		case	NIN_BALLOONHIDE:		//バルーンが×ボタンのクリックにより消えたとき
//			break;
//
//		case	NIN_BALLOONUSERCLICK:	//バルーンが通知領域のクリックで消えたとき
//			break;
//		}
//
//		return	0;
//	}
//};
//
class	CDnpTaskTrayIcon
{
protected:

	bool	_bCreateIcon;			//アイコンが登録されているかどうか
	HWND	_hWnd;					//通知先HWND
	bool	_bIsIE5orLater;			//IEが5.0以上かどうか

	UINT	_nWmTaskbarCreated;		//タスクバー再描画を知らせる登録メッセージのID

	NOTIFYICONDATA	_sData;			//アイコン登録時情報

public:

	BEGIN_MSG_MAP(CDnpTaskTrayIcon)
		MESSAGE_HANDLER(_nWmTaskbarCreated, OnTaskbarCreated)
		MESSAGE_HANDLER(WM_DESTROY,OnClose)
		MESSAGE_HANDLER(WM_CLOSE,OnClose)
	END_MSG_MAP()



	//
	//	コンストラクタ
	//
	CDnpTaskTrayIcon()
	{
		_hWnd = NULL;
		_bIsIE5orLater = true;//IsIEVerion5orLater();
		_nWmTaskbarCreated = ::RegisterWindowMessage(_T("TaskbarCreated"));
		_bCreateIcon = false;

		ZeroMemory(&_sData,sizeof(NOTIFYICONDATA));
	}


	//
	//	アイコンの登録
	//
	bool	CreateTrayIcon(LPCTSTR pszTitle,UINT nIconResource,HWND hWnd,UINT nNotifyMessage)
	{
		BOOL	ret;

		_hWnd = hWnd;

		ATLASSERT(_bCreateIcon == false);			//既にアイコンが登録されている

		ZeroMemory(&_sData,sizeof(NOTIFYICONDATA));
		_sData.cbSize	= sizeof(NOTIFYICONDATA);
		_sData.hWnd		= hWnd;
		_sData.uID		= 0;						//IDはゼロで固定している!
		_sData.uFlags	= NIF_ICON | NIF_MESSAGE | NIF_TIP;
		_sData.hIcon	= ::LoadIcon(_Module.m_hInst,MAKEINTRESOURCE(IDR_MAINFRAME));
		_sData.uCallbackMessage = nNotifyMessage;

		//IEのバージョンによって最大TIPS長が異なる!
		if(pszTitle)
		{
			if(_bIsIE5orLater)
				_tcsncpy_s(_sData.szTip,128 * sizeof(TCHAR),pszTitle,64 * sizeof(TCHAR));
			else
				_tcsncpy_s(_sData.szTip,128 * sizeof(TCHAR),pszTitle,128 * sizeof(TCHAR));
		}

		ret = Shell_NotifyIcon(NIM_ADD,&_sData);

		_bCreateIcon = (ret) ? true : false;

		return	(ret) ? true : false;
	}



	//
	//	アイコンの変更
	//
	bool	ChangeTrayIcon(UINT nIconResource)
	{
		BOOL	ret;

		ATLASSERT(_bCreateIcon);		//アイコンが登録されていない
		if(_bCreateIcon == false)
			return	false;

		ZeroMemory(&_sData,sizeof(NOTIFYICONDATA));
		_sData.cbSize	= sizeof(NOTIFYICONDATA);
		_sData.hWnd		= _hWnd;
		_sData.uID		= 0;						//IDはゼロで固定している!
		_sData.uFlags	= NIF_ICON;
		_sData.hIcon	= ::LoadIcon(_Module.m_hInst,MAKEINTRESOURCE(nIconResource));

		ret = Shell_NotifyIcon(NIM_MODIFY,&_sData);

		return	(ret) ? true : false;
	}





	//
	//	バルーンヘルプの表示
	//
	bool	ShowBaloonHelp(LPCTSTR pszTitle,LPCTSTR pszMessage,UINT nTimeOut)
	{
		BOOL			ret;
		NOTIFYICONDATA	sData;

		//■■OSのバージョンチェックもするべき
//		ATLASSERT(_bCreateIcon);		//アイコンが登録されていない
//		ATLASSERT(_bIsIE5orLater);		//IE5以上ではない!
		if(_bCreateIcon == false)
			return	false;

		ZeroMemory(&sData, sizeof(NOTIFYICONDATA));
		sData.cbSize		= sizeof(NOTIFYICONDATA);
		sData.uFlags		= NIF_INFO;
		sData.hWnd			= _hWnd;
		sData.dwInfoFlags	= NIIF_INFO;
		sData.uTimeout		= nTimeOut;

		if(_bIsIE5orLater)
		{
			_tcsncpy_s(sData.szInfoTitle,64 * sizeof(TCHAR),pszTitle,64 * sizeof(TCHAR));
			_tcsncpy_s(sData.szInfo,256 * sizeof(TCHAR),pszMessage,256 * sizeof(TCHAR));
		}

		ret = Shell_NotifyIcon(NIM_MODIFY,&sData);

		return	(ret) ? true : false;
	}


	//
	//	トレイアイコンの削除
	//
	bool	RemoveTrayIcon(void)
	{
		BOOL			ret;
		NOTIFYICONDATA	sData;

		ATLASSERT(_bCreateIcon);		//アイコンが登録されていない

		ZeroMemory(&sData,sizeof(NOTIFYICONDATA));
		sData.cbSize	= sizeof(NOTIFYICONDATA);
		sData.hWnd		= _hWnd;
		sData.uID		= 0;
		ret = Shell_NotifyIcon(NIM_DELETE,&sData);

		_bCreateIcon = false;

		return	(ret) ? true : false;
	}



	//
	//	ポップアップメニューの表示
	//
	BOOL	TrackPopupMenu(HMENU hMenu,UINT uFlags=TPM_LEFTALIGN | TPM_RIGHTBUTTON)
	{
		BOOL	ret;
		POINT	pt;

		if(hMenu == NULL)
		{
			ATLASSERT(0);
			return	FALSE;
		}

		//カーソル位置の取得
		::GetCursorPos(&pt);

		::SetForegroundWindow(_hWnd);					//これがないとメニューが消えない

		//メニューの表示
		ret = ::TrackPopupMenu(hMenu,uFlags,pt.x,pt.y,0,_hWnd,NULL);

		::PostMessage(_hWnd,WM_NULL,NULL,NULL);			//これがないとメニューが消えない可能性がある

		return	ret;
	}


	//
	//	ポップアップメニューの表示
	//
	bool	TrackPopupMenu(UINT nMenuResource,int nSubMenu)
	{
		bool	ret;
		HMENU	hMenu;
		HMENU	hPopMenu;

		hMenu = ::LoadMenu(_Module.m_hInst,MAKEINTRESOURCE(nMenuResource));
		ATLASSERT(hMenu);
		if(hMenu == NULL)
			return	false;

		hPopMenu = ::GetSubMenu(hMenu,nSubMenu);

		ret = TrackPopupMenu(hPopMenu) ? true : false;

		::DestroyMenu(hMenu);

		return	ret;
	}


protected:

	//
	//	アプリケーション終了時処理
	//
	LRESULT OnClose(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE;

		if(_bCreateIcon)
			RemoveTrayIcon();		//アイコンを削除する

		return	0;
	}

	//
	//	アイコン再描画処理
	//
	//タスクマネージャでexplorer.exeを強制終了させた場合などに消えたアイコンを再描画する
	//
	LRESULT OnTaskbarCreated(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE;

		if(_bCreateIcon)
			Shell_NotifyIcon(NIM_ADD,&_sData);	//前回の情報を用いてアイコンを登録

		return	0;
	}
};
■MainDlg.h内に定義を追加
#include "DnpTaskTrayIcon.h"

#define	WM_TRAY_NOTIFY_ICON		WM_APP + 10
■MainDlg.hのCMainDlgにメンバー変数を追加
	CDnpTaskTrayIcon	_cTrayIcon;
■MainDlg.hのCMainDlgにメッセージマップを追加
	BEGIN_MSG_MAP(CMainDlg)

(。。。省略。。。)

		MESSAGE_HANDLER(WM_TRAY_NOTIFY_ICON, OnTrayNotify)
		CHAIN_MSG_MAP_MEMBER(_cTrayIcon)			//タスクトレイにメッセージを回す
	END_MSG_MAP()
■MainDlg.hのOnInitDialogでアイコン登録処理を追加
		//トレイアイコンの登録
		_cTrayIcon.CreateTrayIcon(_T("テスト"),IDR_MAINFRAME,m_hWnd,WM_TRAY_NOTIFY_ICON);
■MainDlg.hのCMainDlgにメンバー関数を追加
	//アイコンのクリック処理用関数を追加
	LRESULT OnTrayNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		switch(lParam)
		{
		case	WM_LBUTTONDBLCLK:		//ダブルクリック処理
			if(IsWindowVisible())
				ShowWindow(SW_HIDE);
			else
				ShowWindow(SW_SHOWDEFAULT);
			break;

		case	WM_RBUTTONDOWN:			//右クリック処理
			break;

		case	NIN_BALLOONSHOW:		//バルーンが表示されたとき
			break;

		case	NIN_BALLOONTIMEOUT:		//バルーンがタイムアウトで消えたとき
			break;

		case	NIN_BALLOONHIDE:		//バルーンが×ボタンのクリックにより消えたとき
			break;

		case	NIN_BALLOONUSERCLICK:	//バルーンが通知領域のクリックで消えたとき
			break;
		}

		return	0;
	}


プロジェクトファイルをダウンロード

数値を素因数分解する

test110.gif
与えられた数値を素因数分解する効率的な方法は見つかっていない。そのため素数もしくは素数と思われる数値で小さい方から順々に除算をして割り切れるかを確認するしかない。ここでは2と奇数に絞って割り切れるか調べることで素因数分解を実現した。

依存環境:ATL
#include "math.h"
#include "atlcoll.h"
#include "atlstr.h"



class	CElementInfo
{
public:
	long	_nElement;
	ULONG	_nCount;

	CElementInfo()
	{
		_nElement = 0;
		_nCount = 0;
	}

	CElementInfo(long nElement,ULONG nCount)
	{
		_nElement = nElement;
		_nCount = nCount;
	}

	CElementInfo&	operator++()
	{
		_nCount++;
		return	*this;
	}
	CElementInfo&	operator++(int)
	{
		_nCount++;
		return	*this;
	}

	CElementInfo&	operator--()
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
	CElementInfo&	operator--(int)
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
};



//
//	素因数分解
//
bool	Digest(long nData,CAtlArray<CElementInfo>* pacElements)
{
	long	nElement;
	ULONG	nCount;
	long	nLoopMax;

	if(pacElements == NULL)
		return	false;
	pacElements->RemoveAll();

	//負の数なら素因数として-1を与える
	if(nData < 0)
	{
		pacElements->Add(CElementInfo(-1,1));
		nData *= -1;
	}
	if(nData == 0 || nData == 1)
		return	true;

	nLoopMax = (long)sqrt((double)nData) + 1;
	for(nElement = 2; nElement <= nLoopMax; nElement += 2)
	{
		if(nElement == 4)	//2で素因数分解を1回行った後は3,5,7...2n+1で処理を
			nElement--;	//行なうように4のときに1を減算

		nCount = 0;
		while(1)
		{
			if(nData % nElement)
				break;

			nCount++;
			nData /= nElement;
		}
		if(nCount > 0)
			pacElements->Add(CElementInfo(nElement,nCount));
		if(nData == 1)
			break;
	}
	if(nData > 1)
		pacElements->Add(CElementInfo(nData,1));

	return	true;
}




bool	Test()
{
	size_t	i;
	size_t	nSize;
	ULONG	j;
	long	n;
	CAtlString	strMessage;
	CAtlString	strBuff;
	CAtlArray<CElementInfo>	acElements;


	////////////////////////
	//素因数分解実行
	//
	n = 234;
	Digest(n,&acElements);


	////////////////////////
	//結果表示
	//
	strBuff.Format(_T("%d = "),n);
	strMessage += strBuff;

	nSize = acElements.GetCount();
	for(i = 0; i < nSize; i++)
	{
		if(i > 0)
			strMessage += _T("×");

		strBuff.Format(_T("%d"),acElements[i]._nElement);
		strMessage += strBuff;

		for(j = 0; j < acElements[i]._nCount -1; j++)
		{
			strBuff.Format(_T("×%d"),acElements[i]._nElement);
			strMessage += strBuff;
		}
	}

	::MessageBox(NULL,strMessage,_T("結果"),MB_OK);

	return	true;
}

プロジェクトファイルをダウンロード

分数を約分する

test111.gif
分数の約分をしたいときは最初に分母と分子から共通因子を取り出す。ここでは共通因子を取り出す方法として素因数分解を利用した。分母、分子を素因数分解し、共通して含まれる素因数を消去することで約分した。

約分の過程で用いた素因数分解処理用のクラスと関数は「数値を素因数分解する」と同じものを利用している。

依存環境:ATL
#include "math.h"
#include "atlcoll.h"
#include "atlstr.h"



class	CElementInfo
{
public:
	long	_nElement;
	ULONG	_nCount;

	CElementInfo()
	{
		_nElement = 0;
		_nCount = 0;
	}

	CElementInfo(long nElement,ULONG nCount)
	{
		_nElement = nElement;
		_nCount = nCount;
	}

	CElementInfo&	operator++()
	{
		_nCount++;
		return	*this;
	}
	CElementInfo&	operator++(int)
	{
		_nCount++;
		return	*this;
	}

	CElementInfo&	operator--()
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
	CElementInfo&	operator--(int)
	{
		if(_nCount)
			_nCount--;
		return	*this;
	}
};



//
//	素因数分解
//
bool	Digest(long nData,CAtlArray<CElementInfo>* pacElements)
{
	long	nElement;
	ULONG	nCount;
	long	nLoopMax;

	if(pacElements == NULL)
		return	false;
	pacElements->RemoveAll();

	//負の数なら素因数として-1を与える
	if(nData < 0)
	{
		pacElements->Add(CElementInfo(-1,1));
		nData *= -1;
	}
	if(nData == 0 || nData == 1)
		return	true;

	nLoopMax = (long)sqrt((double)nData) + 1;
	for(nElement = 2; nElement <= nLoopMax; nElement += 2)
	{
		if(nElement == 4)	//2で素因数分解を1回行った後は3,5,7...2n+1で処理を
			nElement--;	//行なうように4のときに1を減算

		nCount = 0;
		while(1)
		{
			if(nData % nElement)
				break;

			nCount++;
			nData /= nElement;
		}
		if(nCount > 0)
			pacElements->Add(CElementInfo(nElement,nCount));
		if(nData == 1)
			break;
	}
	if(nData > 1)
		pacElements->Add(CElementInfo(nData,1));

	return	true;
}


//
//	約分
//
//nDenominator	:分母
//nNumerator	:分子
//
bool	Reduction(long nDenominator,long nNumerator,CAtlArray<CElementInfo>* pacDenominatorElements,CAtlArray<CElementInfo>* pacNumeratorElements)
{
	CAtlArray<CElementInfo>*	pA;
	CAtlArray<CElementInfo>*	pB;

	if(pacDenominatorElements)
		pacDenominatorElements->RemoveAll();
	if(pacNumeratorElements)
		pacNumeratorElements->RemoveAll();
	if(pacNumeratorElements == NULL || pacDenominatorElements == NULL)
		return	false;

	if(nDenominator == 0 || nNumerator == 0)
		return	true;
	if(nDenominator == -1)
	{
		//分母が-1だったら、分子に-1をかけて終わり
		nDenominator = 1;
		nNumerator *= -1;
		return	true;
	}
	if(nDenominator == 1 || nNumerator == 1)
		return	true;


	///////////////////////////////
	//約分前処理
	//

	if(nDenominator < 0)
	{
		//分母がマイナスだったら分子にマイナス記号を移す
		nDenominator *= -1;
		nNumerator *= -1;
	}

	//分母・分子ともに素因数分解
	Digest(nDenominator,pacDenominatorElements);
	Digest(nNumerator,pacNumeratorElements);

	//素因数の数が少ない方をpA、多い方をpBにする
	if(pacDenominatorElements->GetCount() < pacNumeratorElements->GetCount())
	{
		pA = pacDenominatorElements;
		pB = pacNumeratorElements;
	}
	else
	{
		pA = pacNumeratorElements;
		pB = pacDenominatorElements;
	}


	///////////////////////////////
	//約分処理
	//
	size_t	i;
	size_t	j;
	size_t	nSizeA;
	size_t	nSizeB;

	i = 0;
	nSizeA = pA->GetCount();
	nSizeB = pB->GetCount();
	for(j = 0; j < nSizeB; j++)
	{
		for(; i < nSizeA; i++)
		{
			if((*pA)[i]._nElement < (*pB)[j]._nElement)
				continue;
			if((*pA)[i]._nElement > (*pB)[j]._nElement)
				break;

			//共通する素因数の数だけnCountを減らす
			if((*pA)[i]._nCount < (*pB)[j]._nCount)
			{
				(*pB)[j]._nCount -= (*pA)[i]._nCount;
				(*pA)[i]._nCount = 0;
			}
			else if((*pA)[i]._nCount > (*pB)[j]._nCount)
			{
				(*pA)[i]._nCount -= (*pB)[j]._nCount;
				(*pB)[j]._nCount = 0;
			}
			else
			{
				(*pA)[i]._nCount = 0;
				(*pB)[j]._nCount = 0;
			}
			break;
		}
	}

	return	true;
}


//
//	約分
//
//nDenominator	:分母
//nNumerator	:分子
//
bool	Reduction(long& nDenominator,long& nNumerator)
{
	bool	ret;
	CAtlArray<CElementInfo>		acElementsA;
	CAtlArray<CElementInfo>		acElementsB;

	///////////////////////////////
	//約分
	//
	ret = Reduction(nDenominator,nNumerator,&acElementsA,&acElementsB);
	if(ret == false)
		return	false;


	///////////////////////////////
	//約分結果の数値計算
	//
	size_t	i;
	size_t	j;
	size_t	nSizeI;
	size_t	nSizeJ;

	nDenominator = 1;
	nSizeI = acElementsA.GetCount();
	for(i = 0; i < nSizeI; i++)
	{
		nSizeJ = acElementsA[i]._nCount;
		for(j = 0; j < nSizeJ; j++)
			nDenominator *= acElementsA[i]._nElement;
	}

	nNumerator = 1;
	nSizeI = acElementsB.GetCount();
	for(i = 0; i < nSizeI; i++)
	{
		nSizeJ = acElementsB[i]._nCount;
		for(j = 0; j < nSizeJ; j++)
			nNumerator *= acElementsB[i]._nElement;
	}

	return	true;
}




bool	Test()
{
	long	nA;
	long	nB;

	CAtlString	strBuff;
	CAtlString	strMessage;

	nA = 384;
	nB = 96;

	strBuff.Format(_T("%d/%d = "),nB,nA);
	strMessage += strBuff;

	//約分
	Reduction(nA,nB);

	strBuff.Format(_T("%d/%d"),nB,nA);
	strMessage += strBuff;

	::MessageBox(NULL,strMessage,_T("結果"),MB_OK);

	return	true;
}

プロジェクトファイルをダウンロード

1  2  3





usefullcode@gmail.com

About 2007年02月

2007年02月にブログ「UsefullCode.net」に投稿されたすべてのエントリーです。

前の記事は2007年01月です。

次の記事は2007年03月です。

他にも多くのエントリーがあります。メインページ記事一覧も見てください。