« 2006年11月 | メイン | 2007年01月 »

前の10件 1  2  3  4  5  6  7  8  9  10

2006年12月 記事一覧

Windows XP SP2のVirtual PCイメージが無料ダウンロードできるけど...

vpc00.gif
これまた知らないうちにVirtual PC 2004用のWindows XP SP2のイメージファイル(VHD)がダウンロードできるようになっていた。
ダウンロードは「IE6 App Compat Test Image」から可能。

開発者などのためにInternet Explorer 6やWindows Internet Explorer 7での動作確認環境の提供用というのがダウンロードを始めた目的らしい。Windows XPのライセンスなしで利用できる。

vpc01.gif
ということでさっそくダウンロードして、Virtual PC 2004SP1上に読み込んで起動。ちなみにVirtual PC 2004SP1自体も無料ダウンロード可能。まぁ2007年になったらVirtual PC 2007の無料ダウンロードが始まるからいまさらな感じだけど。

vpc02.gif
起動時の画面をよく見ると「Windows XP Professional」「Evaluation copy. Build2600.xpsp_sp2_gdr.050301-1519(Service Pack 2)」となっている。このことからも分かるように今回無料ダウンロードが始まったWindows XPのイメージは期間限定の評価版。2007年4月1日以降は起動しなくなるらしい。

vpc03.gif
ようやく起動終了。「スタート」メニューの中を見てみるとものすごくシンプル。「マイコンピュータ」もなければ何もない。最小限のものだけ。

vpc04.gif
そして使っているうちにエラー発生。エクスプローラが落ちた。こうなると何もできなくなりVHDを初期状態に戻すことに。再び起動しても同じようにエラーが起きる。結局Windowsが起動してから20秒以上操作できたことがなかったぐらいの頻度で落ちまくる。

まったく使い物にならなかった。

自分で構築したWindows XPやWindows 2000などは日本語版でも英語版でも正常に動いているから配布されているイメージ側の問題のような気もする。まぁ仮に動いたとしても期限も機能制限も大きいから実用性にはとぼしいかもしれない。

※ちなみにvhdファイルをVirtual PCのほかのマシンからアクセスすることができた


Virtual Hard Disk Image (VHD)の基本情報を取得する

Virtual PCで利用されている拡張子VHDのハードディスクイメージファイルのフッター情報を読み込む。このファイルのフォーマットはMicrosoftが公開しているのでそれを参考にした。

ここではVHDファイルに共通に含まれるフッター情報のみを読み込む。
フッター情報はVHDファイルの末尾512Bytesのデータで、VHDファイルの固有識別子、容量、セクター数、VHD種別(固定容量か可変容量か)などの基本情報が格納されている。

サンプルコードはvhdファイルを開いて、ファイル末尾512bytesを読み込む。そしてチェックサムなどのチェックなどが正しければVHD種別などを表示する。

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


//
//	バイト配列をひっくり返す
//
//例:0x1234 -> 0x3412
//
#define	SWAP_BYTE_ALIGNMENT(data)	\
{									\
	int		i;						\
	int		nSize;					\
	int		nLoop;					\
	BYTE*	pData;					\
	BYTE	cbTmp;					\
									\
	pData = (BYTE*)&data;			\
	nSize = sizeof(data);			\
	nLoop = nSize / 2;				\
	for(i = 0; i < nLoop; i++)		\
	{								\
		cbTmp = *(pData + i);		\
		*(pData + i) = *(pData + nSize - i - 1);	\
		*(pData + nSize - i - 1) = cbTmp;			\
	}								\
}



class	CDnpVHDFooter
{
	//
	//	VHDのフッター内で使用される構造体
	//
	#pragma pack(push,1)
	struct	VHD_DISK_GEOMETRY
	{
		SHORT	nCylinder;
		BYTE	nHeads;
		BYTE	nSectorsPerTrackCylinder;
	};
	#pragma pack(pop)


	//
	//	VHDのフッター構造体
	//
	#pragma pack(push,1)
	struct	VHD_FOOTER
	{
		BYTE		pcbCookie[8];				//"conectix"
		DWORD		dwFeatures;					//
		DWORD		dwFileFormatVersion;		//=0x00010000
		ULONGLONG	nDataOffset;
		DWORD		dwTimeStamp;				//2000/1/1/12:00:00UTCからの秒数
		BYTE		pcbCreatorApplication[4];
		DWORD		dwCreatorVersion;
		DWORD		dwCreatorHostOS;
		ULONGLONG	nOriginalSize;
		ULONGLONG	nCurrentSize;
		VHD_DISK_GEOMETRY	sDiskGeometry;
		DWORD		dwDiskType;
		DWORD		dwChecksum;
		BYTE		pcbUniqueId[16];
		BYTE		cbSavedState;
		BYTE		pcbReserved[427];
	};
	#pragma pack(pop)

public:

	//フッター情報
	VHD_FOOTER		_sFooter;


	CDnpVHDFooter()
	{
		::ZeroMemory(&_sFooter,sizeof(VHD_FOOTER));
	}


	//
	//	指定されたファイルハンドルからVirtual Hard Driveのフッター情報を読み込む
	//
	bool	LoadFooter(HANDLE hFile)
	{
		BOOL	ret;
		DWORD	dwRead;

		::SetFilePointer(hFile,-512,NULL,FILE_END);
		ret = ::ReadFile(hFile,&_sFooter,sizeof(VHD_FOOTER),&dwRead,NULL);
		if(ret == FALSE || dwRead != sizeof(VHD_FOOTER))
		{
			::ZeroMemory(&_sFooter,sizeof(VHD_FOOTER));
			return	false;
		}

		//バイト配列をintel形式に変更
		SWAP_BYTE_ALIGNMENT(_sFooter.dwChecksum);
		SWAP_BYTE_ALIGNMENT(_sFooter.dwCreatorHostOS);
		SWAP_BYTE_ALIGNMENT(_sFooter.dwCreatorVersion);
		SWAP_BYTE_ALIGNMENT(_sFooter.dwDiskType);
		SWAP_BYTE_ALIGNMENT(_sFooter.dwFeatures);
		SWAP_BYTE_ALIGNMENT(_sFooter.dwFileFormatVersion);
		SWAP_BYTE_ALIGNMENT(_sFooter.dwTimeStamp);
		SWAP_BYTE_ALIGNMENT(_sFooter.nCurrentSize);
		SWAP_BYTE_ALIGNMENT(_sFooter.nDataOffset);
		SWAP_BYTE_ALIGNMENT(_sFooter.nOriginalSize);
		SWAP_BYTE_ALIGNMENT(_sFooter.sDiskGeometry.nCylinder);

		return	IsValidFooter();
	}



	//
	//	フッター情報が正しいかのチェック
	//
	//完全にはチェックしていない!
	//
	bool	IsValidFooter(void)
	{
		//cookieは"conectix"
		if(::memcmp(_sFooter.pcbCookie,"conectix",8))
		{
			::ZeroMemory(&_sFooter,sizeof(VHD_FOOTER));
			return	false;
		}

		//ファイルフォーマットバージョンは0x00010000
		if(_sFooter.dwFileFormatVersion != 0x00010000)
		{
			::ZeroMemory(&_sFooter,sizeof(VHD_FOOTER));
			return	false;
		}

		//作成OSはWindowsかMac
		if(_sFooter.dwCreatorHostOS != 0x5769326b && _sFooter.dwCreatorHostOS != 0x4d616320)
		{
			::ZeroMemory(&_sFooter,sizeof(VHD_FOOTER));
			return	false;
		}

		//チェックサムのチェック
		DWORD	i;
		DWORD	dwChecksum;
		DWORD	dwCurrent;

		dwCurrent = _sFooter.dwChecksum;
		_sFooter.dwChecksum = 0;
		dwChecksum = 0;
		for(i = 0; i < sizeof(VHD_FOOTER); i++)
			dwChecksum += ((BYTE*)&_sFooter)[i];
		_sFooter.dwChecksum = dwCurrent;
		if(dwCurrent != ~dwChecksum)
		{
			::ZeroMemory(&_sFooter,sizeof(VHD_FOOTER));
			return	false;
		}

		return	true;
	}


	//
	//	イメージファイルが固定サイズかどうか
	//
	bool	IsFixedVHD(void)
	{
		if(IsValidFooter() == false)
			return	false;
		if(_sFooter.dwDiskType == 2)
			return	true;
		return	false;
	}

	//
	//	イメージファイルが可変サイズかどうか
	//
	bool	IsDynamicVHD(void)
	{
		if(IsValidFooter() == false)
			return	false;
		if(_sFooter.dwDiskType == 3)
			return	true;
		return	false;
	}

	//
	//	イメージファイルが差分かどうか
	//
	bool	IsDifferencingVHD(void)
	{
		if(IsValidFooter() == false)
			return	false;
		if(_sFooter.dwDiskType == 4)
			return	true;
		return	false;
	}
};




bool	LoadOnlyFooter(LPCTSTR pszVHDFile)
{
	bool			ret;
	HANDLE			hFile;
	CDnpVHDFooter	cFooter;
	CAtlString		strTmp;
	CAtlString		strMessage;

	hFile = ::CreateFile(pszVHDFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	if(hFile == INVALID_HANDLE_VALUE)
		return	false;

	ret = cFooter.LoadFooter(hFile);
	::CloseHandle(hFile);


	if(ret == false)
		return	false;

	if(cFooter.IsDifferencingVHD())
		strMessage = _T("差分VHD\n");
	else if(cFooter.IsDynamicVHD())
		strMessage = _T("可変容量VHD\n");
	else if(cFooter.IsFixedVHD())
		strMessage = _T("固定容量VHD\n");

	strTmp.Format(_T("シリンダー数:%d\nヘッダー数:%d\nセクター数/シリンダー:%d\n容量:%dbytes"),cFooter._sFooter.sDiskGeometry.nCylinder,cFooter._sFooter.sDiskGeometry.nHeads,cFooter._sFooter.sDiskGeometry.nSectorsPerTrackCylinder,cFooter._sFooter.nOriginalSize);
	strMessage += strTmp;

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

	return	true;
}


#undef	SWAP_BYTE_ALIGNMENT




void	Test(void)
{
	bool	ret;

	ret = LoadOnlyFooter(_T("Fixed3MB.vhd"));
	if(ret == false)
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);

	ret = LoadOnlyFooter(_T("Dynamic3MB.vhd"));
	if(ret == false)
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}

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

Virtual Hard Disk Image (VHD)のヘッダー情報も取得する

Virtual PCで利用されている拡張子VHDのハードディスクイメージファイルのヘッダー情報を読み込む。このファイルのフォーマットはMicrosoftが公開しているのでそれを参考にした。

ここでは前回作成した「Virtual Hard Disk Image (VHD)の基本情報を取得する」にヘッダー情報読み込み用のソースコードを追加して、可変容量VHDファイルと差分VHDファイルのみに含まれるヘッダー情報を読み込む。

ヘッダー情報はファイル先頭512bytesの位置から512bytesのサイズ書き込まれている。ただし固定容量VHDの場合はヘッダー情報がない。そのため、先にフッター情報を参照して固定容量VHDではないことを確認する必要がある。

ここでは前回からの追加部分のみを掲載する。ダウンロード可能なプロジェクトには全ソースコードを含んでいる。

依存環境:ATL
class	CDnpVHDHeader
{

	//
	//	VHDのヘッダー内で利用される構造体
	//
	#pragma pack(push,1)
	struct	PARENT_LOCATOR_ENTRY
	{
		DWORD		dwPlatformCode;
		DWORD		dwPlatformDataSpace;
		DWORD		dwPlatformDataLength;
		BYTE		pcbReserved[4];
		LONGLONG	nPlatformDataOffset;
	};
	#pragma pack(pop)


	//
	//	VHDのヘッダー構造体
	//
	//容量可変VHD、差分VHDでのみ使われる
	//
	#pragma pack(push,1)
	struct	VHD_HEADER_DYNAMIC
	{
		BYTE		pcbCookie[8];
		LONGLONG	nDataOffset;
		LONGLONG	nTableOffset;
		DWORD		dwHeaderVersion;
		DWORD		dwMaxTableEntries;
		DWORD		dwBlockSize;
		DWORD		dwChecksum;
		BYTE		pcbParentUniqueId[16];
		DWORD		dwParentTimeStamp;
		BYTE		pcbReserved1[4];
		WCHAR		pwszParentUnicodeName[512/2];
		PARENT_LOCATOR_ENTRY	psParentLocatorEntry[8];
		BYTE		pcbReserved2[256];
	};
	#pragma pack(pop)
public:

public:

	//ヘッダー情報
	VHD_HEADER_DYNAMIC		_sHeader;


	CDnpVHDHeader()
	{
		::ZeroMemory(&_sHeader,sizeof(VHD_HEADER_DYNAMIC));
	}


	//
	//	指定されたファイルハンドルからVirtual Hard Driveのヘッダー情報を読み込む
	//
	bool	LoadHeader(HANDLE hFile)
	{
		int		j;
		BOOL	ret;
		DWORD	dwRead;

		::SetFilePointer(hFile,512,NULL,FILE_BEGIN);
		ret = ::ReadFile(hFile,&_sHeader,sizeof(VHD_HEADER_DYNAMIC),&dwRead,NULL);
		if(ret == FALSE || dwRead != sizeof(VHD_HEADER_DYNAMIC))
		{
			::ZeroMemory(&_sHeader,sizeof(VHD_HEADER_DYNAMIC));
			return	false;
		}

		//バイト配列をintel形式に変更
		SWAP_BYTE_ALIGNMENT(_sHeader.dwBlockSize);
		SWAP_BYTE_ALIGNMENT(_sHeader.dwChecksum);
		SWAP_BYTE_ALIGNMENT(_sHeader.dwHeaderVersion);
		SWAP_BYTE_ALIGNMENT(_sHeader.dwMaxTableEntries);
		SWAP_BYTE_ALIGNMENT(_sHeader.dwParentTimeStamp);
		SWAP_BYTE_ALIGNMENT(_sHeader.nDataOffset);
		SWAP_BYTE_ALIGNMENT(_sHeader.nTableOffset);

		for(j = 0; j < 8; j++)
		{
			SWAP_BYTE_ALIGNMENT(_sHeader.psParentLocatorEntry[j].dwPlatformCode);
			SWAP_BYTE_ALIGNMENT(_sHeader.psParentLocatorEntry[j].dwPlatformDataLength);
			SWAP_BYTE_ALIGNMENT(_sHeader.psParentLocatorEntry[j].dwPlatformDataSpace);
			SWAP_BYTE_ALIGNMENT(_sHeader.psParentLocatorEntry[j].nPlatformDataOffset);
		}

		return	IsValidHeader();
	}


	//
	//	ヘッダー情報が正しいかのチェック
	//
	//完全にはチェックしていない!
	//
	bool	IsValidHeader(void)
	{
		//cookieは"conectix"
		if(::memcmp(_sHeader.pcbCookie,"cxsparse",8))
		{
			::ZeroMemory(&_sHeader,sizeof(VHD_HEADER_DYNAMIC));
			return	false;
		}

		if(_sHeader.nDataOffset != -1)
		{
			::ZeroMemory(&_sHeader,sizeof(VHD_HEADER_DYNAMIC));
			return	false;
		}

		//作成OSはWindowsかMac
		if(_sHeader.dwHeaderVersion != 0x00010000)
		{
			::ZeroMemory(&_sHeader,sizeof(VHD_HEADER_DYNAMIC));
			return	false;
		}

		//チェックサムのチェック
		DWORD	i;
		DWORD	dwChecksum;
		DWORD	dwCurrent;

		dwCurrent = _sHeader.dwChecksum;
		_sHeader.dwChecksum = 0;
		dwChecksum = 0;
		for(i = 0; i < sizeof(VHD_HEADER_DYNAMIC); i++)
			dwChecksum += ((BYTE*)&_sHeader)[i];
		_sHeader.dwChecksum = dwCurrent;
		if(dwCurrent != ~dwChecksum)
		{
			::ZeroMemory(&_sHeader,sizeof(VHD_HEADER_DYNAMIC));
			return	false;
		}

		return	true;
	}


};





bool	LoadFooterAndHeader(LPCTSTR pszVHDFile)
{
	bool			ret;
	bool			bHasHeader;
	HANDLE			hFile;
	CDnpVHDFooter	cFooter;
	CDnpVHDHeader	cHeader;
	CAtlString		strTmp;
	CAtlString		strMessage;

	hFile = ::CreateFile(pszVHDFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	if(hFile == INVALID_HANDLE_VALUE)
		return	false;

	ret = cFooter.LoadFooter(hFile);
	if(ret == false)
	{
		::CloseHandle(hFile);
		return	false;
	}

	bHasHeader = true;
	if(cFooter.IsDifferencingVHD())
		strMessage = _T("差分VHD\n");
	else if(cFooter.IsDynamicVHD())
		strMessage = _T("可変容量VHD\n");
	else if(cFooter.IsFixedVHD())
	{
		strMessage = _T("固定容量VHD\n");
		bHasHeader = false;
	}

	if(bHasHeader)
	{
		bHasHeader = cHeader.LoadHeader(hFile);
		if(bHasHeader == false)
			strMessage += _T("(ヘッダー情報の取得に失敗しました)\n");
	}

	::CloseHandle(hFile);

	strTmp.Format(_T("シリンダー数:%d\nヘッダー数:%d\nセクター数/シリンダー:%d\n容量:%dbytes"),cFooter._sFooter.sDiskGeometry.nCylinder,cFooter._sFooter.sDiskGeometry.nHeads,cFooter._sFooter.sDiskGeometry.nSectorsPerTrackCylinder,cFooter._sFooter.nOriginalSize);
	strMessage += strTmp;

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

	return	true;
}



void	Test(void)
{
	bool	ret;

	ret = LoadFooterAndHeader(_T("Fixed3MB.vhd"));
	if(ret == false)
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);

	ret = LoadFooterAndHeader(_T("Dynamic3MB.vhd"));
	if(ret == false)
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);

	ret = LoadFooterAndHeader(_T("Differncial.vhd"));
	if(ret == false)
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}

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

2006年12月07日

ウインドウ内容を別のHDCに出力する

test45.gif
APIのPrintWindowを利用すると任意のウインドウに対して、自分が保持しているHDCへ描画させることができる。
HDCからBMPなどの画像を保存するように作れば画面キャプチャ用途にも利用できる。

ここではタスクバーのウインドウに対してPrintWindowを実行。デスクトップ画面の上部に描画させた。

void	Test(void)
{
	HWND	hWnd;
	HDC		hDC;

	hWnd = ::FindWindow(_T("Shell_TrayWnd"),NULL);
	hDC = ::GetWindowDC(NULL);

	::PrintWindow(hWnd,hDC,0);

	::ReleaseDC(NULL,hDC);
}

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

CHSなど物理ドライブ情報を取得する

DeviceIoControlにIOCTL_DISK_GET_DRIVE_GEOMETRY_EXを指定して実行するとシリンダー数などのディスク情報を取得できる。ただしデバイスによってはIOCTL_DISK_GET_DRIVE_GEOMETRY_EXをサポートしていないので失敗した場合は従来から備わるIOCTL_DISK_GET_DRIVE_GEOMETRYで取得する。

対応OS:Windows NT 4.0以降 依存環境:ATL
#include "atlstr.h"

#include "winioctl.h"


//
//	ディスク情報の取得
//
//nDriveNoは0~の数値で指定
//
//IOCTL_DISK_GET_DRIVE_GEOMETRY_EX、IOCTL_DISK_GET_DRIVE_GEOMETRYにより
//ディスク情報を取得する
//
bool	GetDiskGeometry(int nDriveNo,DISK_GEOMETRY_EX* psDiskGeometryEx)
{
	DWORD	dwWritten;
	BOOL	ret;
	HANDLE	hDevice;
	TCHAR	pszDevice[256];

	if(psDiskGeometryEx == NULL)
		return	false;
	::_stprintf_s (pszDevice,256,_T("\\\\.\\PHYSICALDRIVE%d"),nDriveNo);
	::ZeroMemory(psDiskGeometryEx,sizeof(DISK_GEOMETRY_EX));
	hDevice = ::CreateFile(pszDevice,GENERIC_READ,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,NULL,NULL);
	if(hDevice == INVALID_HANDLE_VALUE)
		return	false;

	ret = ::DeviceIoControl(hDevice,IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,NULL,0,psDiskGeometryEx,sizeof(DISK_GEOMETRY_EX),&dwWritten,NULL);
	if(ret == FALSE || dwWritten != sizeof(DISK_GEOMETRY_EX))
	{
		ret = ::DeviceIoControl(hDevice,IOCTL_DISK_GET_DRIVE_GEOMETRY,NULL,0,&psDiskGeometryEx->Geometry,sizeof(DISK_GEOMETRY),&dwWritten,NULL);
		if(ret == FALSE || dwWritten != sizeof(DISK_GEOMETRY))
		{
			::ZeroMemory(psDiskGeometryEx,sizeof(DISK_GEOMETRY_EX));
			ret = FALSE;
		}
		else
		{
			psDiskGeometryEx->DiskSize.QuadPart = psDiskGeometryEx->Geometry.Cylinders.QuadPart;
			psDiskGeometryEx->DiskSize.QuadPart *= psDiskGeometryEx->Geometry.TracksPerCylinder;
			psDiskGeometryEx->DiskSize.QuadPart *= psDiskGeometryEx->Geometry.SectorsPerTrack;
			psDiskGeometryEx->DiskSize.QuadPart *= psDiskGeometryEx->Geometry.BytesPerSector;
		}
	}
	::CloseHandle(hDevice);

	return	(ret) ? true : false;
}



void	Test(void)
{
	bool				ret;
	DISK_GEOMETRY_EX	sDiskGeometryEx;

	ret = GetDiskGeometry(0,&sDiskGeometryEx);

	if(ret)
	{
		CAtlString	strMessage;

		strMessage.Format(_T("シリンダー数:%d %d\nトラック/シリンダー:%d\nセクター/トラック:%d\nバイト/セクター:%d\nディスク容量:%ld")
			,sDiskGeometryEx.Geometry.Cylinders.HighPart,sDiskGeometryEx.Geometry.Cylinders.LowPart,sDiskGeometryEx.Geometry.TracksPerCylinder,sDiskGeometryEx.Geometry.SectorsPerTrack,sDiskGeometryEx.Geometry.BytesPerSector,sDiskGeometryEx.DiskSize);
		::MessageBox(NULL,strMessage,_T(""),MB_OK);
	}
	else
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}

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

Windows Driver Kitをインストールする

wdk_00a.gif
Windows Vistaもサポートした新しいDDK「Windows Driver Kit」の正式版が知らないうちに公開されていた。初めはダウンロードできないものだと思っていたが、無料でダウンロードできた。



Microsoft Connect Web site」からダウンロード可能。ただしユーザー登録(?)をしなければいけない。


wdk_00b.gif
登録を済ませて「Windows Driver Kit (WDK) and Windows Driver Framework (WDF)」の項目を見ると2006年12月5日付けで「Windows Driver Kit RTM」(RTM=製品版)のダウンロードが開始されていた。

wdk_00c.gif
ダウンロードサイズは...2.3GB。かなり大きい。ファイル名は「6000.16386.061101-2205-LRMWDK.A.ISO」。

wdk_01.gif
ダウンロードしたisoファイルをマウントすると自動的にインストーラーが起動した。「Microsoft Automated Software Installer (ASI)」というのをインストールしなければいけない感じだが無視して「WDK build Environments, Samples, and Documentation (Development Kit)」のインストールを開始。

wdk_02.gif
一瞬スプラッシュウインドウが表示された。スプラッシュウインドウにしては珍しくタイトルバーが付いている。

wdk_03.gif
そしてインストーラーが起動。

wdk_04.gif
ライセンスアグリーメントを読まなければいけないが面倒なので無視して次へ。

wdk_05.gif
Windows Driver Kitのインストールのみであれば1.6GBとなるようだ。

wdk_06.gif
そしてインストール開始。

wdk_07.gif

wdk_08.gif
セットアップ終了。DDKのインストールのときは20分以上かかったが今回は数分で済んだ。

wdk_09.gif
WDKのインストールが終わったので「WDK Driver Test manager (DTM) Controller」がインストール可能になるのかな?と思ったがそうでもないらしい。ASIをインストールしていないからかな?

wdk_10.gif
実際にインストールされたファイルを見てみると...IFS Kit(Windows Installable File System Kit )に含まれていた「filesys」などのフォルダができていた。


ドライブの書き込みキャッシュが有効かどうか調べる

物理ドライブの書き込みキャッシュや読み込みキャッシュが有効になっているかどうかを取得する。この情報はIOCTL_DISK_GET_CACHE_INFORMATIONにより取得できる。ただし対応OSはWindows 2000以降。

#include "atlstr.h"

#include "winioctl.h"


//
//	ライト/リードキャッシュ設定状態の取得
//
//nDriveNoは0~の数値で指定
//
//IOCTL_DISK_GET_CACHE_INFORMATIONによりディスク情報を取得する
//
//対応OS:Windows 2000以降
//
bool	GetDiskCacheInformation(int nDriveNo,bool* pbWriteChachEnable,bool* pbReadChachEnable)
{
	DWORD	dwWritten;
	BOOL	ret;
	HANDLE	hDevice;
	TCHAR	pszDevice[256];

	if(pbWriteChachEnable)
		*pbWriteChachEnable = false;
	if(pbReadChachEnable)
		*pbReadChachEnable = false;

	::_stprintf_s (pszDevice,256,_T("\\\\.\\PHYSICALDRIVE%d"),nDriveNo);
	hDevice = ::CreateFile(pszDevice,GENERIC_READ,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,NULL,NULL);
	if(hDevice == INVALID_HANDLE_VALUE)
		return	false;

	DISK_CACHE_INFORMATION	sInfo;

	::ZeroMemory(&sInfo,sizeof(DISK_CACHE_INFORMATION));
	ret = ::DeviceIoControl(hDevice,IOCTL_DISK_GET_CACHE_INFORMATION,NULL,0,&sInfo,sizeof(DISK_CACHE_INFORMATION),&dwWritten,NULL);
	::CloseHandle(hDevice);

	if(ret == FALSE || dwWritten != sizeof(DISK_CACHE_INFORMATION))
		return	false;

	if(pbWriteChachEnable)
		*pbWriteChachEnable = (sInfo.WriteCacheEnabled) ? true : false;
	if(pbReadChachEnable)
		*pbReadChachEnable = (sInfo.ReadCacheEnabled) ? true : false;

	return	true;
}



void	Test(void)
{
	bool	ret;
	bool	bWriteChachEnable;
	bool	bReadChachEnable;

	ret = GetDiskCacheInformation(0,&bWriteChachEnable,&bReadChachEnable);

	if(ret)
	{
		CAtlString	strMessage;

		if(bWriteChachEnable)
			strMessage += _T("ライトキャッシュが有効です。\n");
		else
			strMessage += _T("ライトキャッシュが無効です。\n");

		if(bReadChachEnable)
			strMessage += _T("リードキャッシュが有効です。\n");
		else
			strMessage += _T("リードキャッシュが無効です。\n");

		::MessageBox(NULL,strMessage,_T(""),MB_OK);
	}
	else
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}

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

ドライブ書き込み可能かどうかを調べる

物理ドライブが書き込み可能かどうか調べる。CD-ROMドライブは常に書き込み不可、SDカードなどはライトプロテクト状態によって結果がする。情報取得にはIOCTL_DISK_IS_WRITABLEを利用する。

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

#include "winioctl.h"


//
//	ディスクに書き込めるかを調べる
//
//nDriveNoは0~の数値で指定
//
//IOCTL_DISK_GET_CACHE_INFORMATIONによりディスク情報を取得する
//
//対応OS:Windows 2000以降
//
bool	GetDiskWriteProtectStatus(int nDriveNo,bool* pbWriteProtected)
{
	DWORD	dwWritten;
	BOOL	ret;
	HANDLE	hDevice;
	TCHAR	pszDevice[256];

	if(pbWriteProtected == NULL)
		return	false;

	::_stprintf_s (pszDevice,256,_T("\\\\.\\PHYSICALDRIVE%d"),nDriveNo);
	hDevice = ::CreateFile(pszDevice,GENERIC_READ,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,NULL,NULL);
	if(hDevice == INVALID_HANDLE_VALUE)
		return	false;

	ret = ::DeviceIoControl(hDevice,IOCTL_DISK_IS_WRITABLE,NULL,0,NULL,0,&dwWritten,NULL);
	::CloseHandle(hDevice);
	if(ret == FALSE && GetLastError () != ERROR_WRITE_PROTECT)
		return	false;

	*pbWriteProtected = (ret) ? false : true;

	return	true;
}



void	Test(void)
{
	bool	ret;
	bool	bWriteProtected;

	ret = GetDiskWriteProtectStatus(0,&bWriteProtected);

	if(ret)
	{
		CAtlString	strMessage;

		if(bWriteProtected)
			strMessage += _T("ディスクに書き込みません。\n");
		else
			strMessage += _T("ディスクに書き込めます。\n");

		::MessageBox(NULL,strMessage,_T(""),MB_OK);
	}
	else
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}

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

2006年12月08日

LARGE_INTEGERやULARGE_INTEGERを使う

最近、LARGE_INTEGERやULARGE_INTEGERといった型を見かけることが増えてきた。これらはGetDiskFreeSpaceExなど大きな数値を扱うAPIを中心に使われている。この型がどのように定義されているのかを見てみると、以下のようになっていた。

typedef union _LARGE_INTEGER {
    struct {
        DWORD LowPart;
        LONG HighPart;
    };
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER;

typedef LARGE_INTEGER *PLARGE_INTEGER;


typedef union _ULARGE_INTEGER {
    struct {
        DWORD LowPart;
        DWORD HighPart;
    };
    struct {
        DWORD LowPart;
        DWORD HighPart;
    } u;
    ULONGLONG QuadPart;
} ULARGE_INTEGER;

typedef ULARGE_INTEGER *PULARGE_INTEGER;


つまりLARGE_INTEGER=LONGLONG、ULARGE_INTEGER=ULONGLONGということがわかる。すなわちLARGE_INTEGERは符号付の数値型で、ULARGE_INTEGERは符号なしの数値型ということになる。
LONGLONGやULONGLONG型は普段よく目にする4bytesの大きさのLONGやULONG、DWORD型の2倍で8bytesもの大きさがある。大きすぎて扱いづらいため上位ダブルワードをHighPart、下位ダブルワードをLowPart、全体をQuadPartとして定義されている。

そのためプログラムで使うときには以下のようにできる。

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

void	Test(void)
{
	LARGE_INTEGER	lnTest;
	CAtlString		strMessage;

	int		nSize = sizeof(LARGE_INTEGER);		// nSize == 8bytes

	lnTest.QuadPart = -1;

	strMessage.Format(_T(" sizeof(LARGE_INTEGER) = %d \n HighPart = %X \n LowPart = %X \n QuadPart = %I64X \n QuadPart(signed) = %I64d \n QuadPart(unsigned) = %I64u"),nSize,lnTest.HighPart,lnTest.LowPart,lnTest.QuadPart,lnTest.QuadPart,lnTest.QuadPart);
	::MessageBox(NULL,strMessage,_T(""),MB_OK);


	lnTest.HighPart = 1;
	lnTest.LowPart = 2;

	strMessage.Format(_T(" sizeof(LARGE_INTEGER) = %d \n HighPart = %X \n LowPart = %X \n QuadPart = %I64X \n QuadPart(signed) = %I64d \n QuadPart(unsigned) = %I64u"),nSize,lnTest.HighPart,lnTest.LowPart,lnTest.QuadPart,lnTest.QuadPart,lnTest.QuadPart);
	::MessageBox(NULL,strMessage,_T(""),MB_OK);
}
プロジェクトファイルをダウンロード


test49_01.giftest49_02.gif



要はLARGE_INTEGER(ULARGE_INTEGER)に数値を代入したいときはQuadPartに代入すればいい。もしも上位ダブルワードと下位ダブルワードを区別して代入したいときはそれぞれHighPartとLowPartを利用する。

ただしprintfやsprintf、TRACE、CString::Formatなどの関数で数値から文字列に変換したい場合は注意が必要だ。
QuadPartは8bytes(64bits)もの大きさがある。そのためprintf("%d",xx.QuadPart);のように「%d」や「%x」を利用することはできない。64bitsを処理できるように「%I64d」や「%I64x」のように「I64」指定をしなければいけない。

メモリ内容を確実に消すときはSecureZeroMemoryを使うべき

これはよく知られた問題だ。パスワードなど流出しては困る重要なデータを扱うときは、使い終わった後のメモリ内容を消さないと、そこから流出する可能性がある。そのため以下のようなソースコードにして使い終わったパスワード内容をメモリから消す。

■うまく動かない例
void	Test(void)
{
	char	pszPassword[256];

	//パスワード入力
	::strcpy_s(pszPassword,256 * sizeof(char),"password-string");

	//パスワード表示
	::MessageBoxA(NULL,pszPassword,"",MB_OK);

	//メモリ上からパスワード消去
	::ZeroMemory(&pszPassword,256);
}
プロジェクトファイルをダウンロード


test50_01.gif
上のソースコード自体には間違えた部分はどこにもない。デバッグビルドで動かすときちんとメモリは消されてパスワード流出の危険性は少ない(この例ではMessageBoxを利用してパスワードを表示しているなどなどの理由でバレバレだが、本質的な部分、つまりメモリ内容からの流出の危険性は少ない)。

実際生成される実行ファイルの中を逆アセンブラしてみると、ZeroMemoryが実行されているのがわかる(図はVisual Studio 2005が生成したソースコード付きのアセンブラコード)。

test50_02.gif
しかし、まったく同じソースコードをリリースビルドに切り替えてみると...ZeroMemoryに相当するアセンブラコードが出力されていない。

これは、リリースビルドではデフォルトで最適化オプションが有効になっているためだ。最適化オプションではそれ以下のコード部分でメモリを参照していない変数への代入などの処理を省くように作られている。この場合であればZeroMemory実行後に一度もpszPassword変数を利用する命令がないため、ZeroMemoryは不要な命令と判断し、ビルド時に省略された。


ではどうすればZeroMemoryがきちんと動作するのかというと2つの方法がある。1つはpszPasswordにゼロを代入した後に、その値を読みだすように作ること。しかしこの場合も読み出し方を間違えると、その読み出しも必要ないものだと最適化ルーチンに判断されてZeroMemoryもろとも省略されてしまう恐れがある。もう1つの方法はSecureZeroMemoryを利用する方法だ。

void	Test(void)
{
	char	pszPassword[256];

	//パスワード入力
	::strcpy_s(pszPassword,256 * sizeof(char),"password-string");

	//パスワード表示
	::MessageBoxA(NULL,pszPassword,"",MB_OK);

	//メモリ上からパスワード消去
	::SecureZeroMemory(&pszPassword,256 * sizeof(char));
}
プロジェクトファイルをダウンロード

test50_04.gif
使い方はZeroMemoryと同じなので、単純に置き換えるだけでいい。

このSecureZeroMemoryは名前が示すとおり安全にメモリをクリアするために用意されている。そのためリリースビルドした場合でも左図のアセンブラ出力のように最適化によって省略されることなく命令が実行されて、メモリ内容が消去される。


「重要なデータを格納した変数は必要なくなったらメモリ内容まで消去する」。このような細かいところまできちんと考えてプログラミングしている開発者は少ないと思う。そのためメモリ内容を確実にクリアするためのSecureZeroMemory関数の存在など知らなくてもさほど問題ではない(と、少なくとも私は思う)。
しかしながらリリースビルドに切り替えたとたんにプログラムが動かなくなったときなどは、今回のZeroMemoryのように「最適化によって省略されてしまう命令がある」ということが頭の片隅にあるかどうかで原因究明までの時間に及ぼす影響は大きい。そのため時間があったら最適化について調べておくといいだろう。

前の10件 1  2  3  4  5  6  7  8  9  10





usefullcode@gmail.com

About 2006年12月

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

前の記事は2006年11月です。

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

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