メイン | 2006年12月 »


2006年11月 記事一覧

2006年11月23日

WAVファイルを再生する

wavファイルを再生したいときはPlaySound()を利用する。

#include "mmsystem.h"

#pragma	comment(lib,"winmm.lib")

void	Test(void)
{
	::PlaySound(_T("C:\\WINDOWS\\Media\\chimes.wav"),NULL,SND_FILENAME | SND_ASYNC); 
}

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

マウスカーソルを動かす

マウスカーソルを動かすにはmouse_eventを使う。この関数は引数として使う座標値がスクリーン座標ではないので、GetSystemMetricsで変換して渡す。
Test用の関数を実行すると現在位置から右斜め下に向かってマウスカーソルが勝手に移動する。

//
//	マウスカーソル移動関数
//
void	MoveMouse(UINT nX,UINT nY)
{
	DWORD	dwX;
	DWORD	dwY;

	//スクリーン座標をmouse_event()用の座標に変換
	dwX = nX * 65535 / ::GetSystemMetrics(SM_CXSCREEN);
	dwY = nY * 65535 / ::GetSystemMetrics(SM_CYSCREEN);

	//マウスカーソルを移動
	::mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,dwX,dwY,NULL,NULL);
}


void	Test(void)
{
	int		i;
	POINT	pt;

	//現在のマウスカーソル位置取得
	::GetCursorPos(&pt);

	for(i = 0; i < 30; i++)
	{
		//マウス位置を右斜め下にちょっとずらす
		pt.x += 10;
		pt.y += 10;

		//マウスカーソルを移動
		MoveMouse(pt.x,pt.y);

		//50ミリ秒待機
		::Sleep(50);
	}
}

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

ファイルの存在を確認する

指定したファイルがあるかどうかを調べるにはCreateFileを使ってファイルにアクセスできるかどうかを調べる方法と、今回使っているFindFirstFileで検索できるかどうかを使う方法がある。
FindFirstFileの場合は少し改良すればフォルダの存在確認にも使えるので便利だ。

//
//	ファイルの存在を確認する
//
bool	IsFileExist(LPCTSTR pszFile)
{
	HANDLE			hFind;
	WIN32_FIND_DATA	FindFileData;

	hFind = ::FindFirstFile(pszFile,&FindFileData);
	if(hFind == INVALID_HANDLE_VALUE)
		return	false;
	::FindClose(hFind);
	if(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		return	false;		//同名のフォルダがある(ファイルではない)

	return	true;
}

void	Test(void)
{
	bool	ret;

	ret = IsFileExist(_T("c:\\IO.SYS"));

	if(ret)
		::MessageBox(NULL,_T("ファイルは存在します"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("ファイルは存在しません"),_T(""),MB_OK);
}

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

ファイルがショートカットやリンクかどうかを調べる

指定されたパスがショートカットファイルやリンクファイルかどうかを調べる。

独自にファイルを開くアプリケーションを作るときは先にショートカットかどうかを調べて、ショートカットファイルであればそのリンク先を取得して開く方がいい場合もある。この手の実装漏れはリリース直前のテスト作業でも発見できないことがあるので要注意かもしれない。

#include "shobjidl.h"
#include "shellapi.h"

//
//	ファイルがショートカットかどうか
//
//	正常な.lnkと.urlならtrueが返る
//
bool	IsShortcut(LPCTSTR pszFile)
{
	DWORD_PTR	dwRet;
	SHFILEINFO	info;

	dwRet = ::SHGetFileInfo(pszFile,0,&info, sizeof(SHFILEINFO),SHGFI_ATTRIBUTES);
	if(dwRet == 0)
		return	false;

	return	(info.dwAttributes & SFGAO_LINK) ? true : false;
}



void	Test(void)
{
	bool	ret;

	ret = IsShortcut(_T("c:\\IO.SYS"));

	if(ret)
		::MessageBox(NULL,_T("ショートカットファイルです"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("ショートカットファイルではありません"),_T(""),MB_OK);
}

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

2006年11月24日

URLの表示回数を取得する

ブラウザーのお気に入りを右クリックしてプロパティを見ると、そのURLを表示した回数を知ることができる。この表示回数をプログラム的に取得するにはGetUrlCacheEntryInfoを利用する。
エラーのときやURLを表示したことがなければ関数はfalseを返す。

googlevideo.gif

#include "wininet.h"
#pragma	comment(lib,"wininet.lib")


//
//	URL表示回数の取得
//
bool	GetVisitsCount(LPCTSTR pszURL,DWORD* pdwCount)
{
	BOOL	fRet;
	BYTE*	pData;
	DWORD	cbInfo;
	LPINTERNET_CACHE_ENTRY_INFO	pInfo;

	if(pdwCount == NULL)
		return	false;

	*pdwCount = 0;
	cbInfo = 0;
	fRet = ::GetUrlCacheEntryInfo(pszURL,NULL,&cbInfo);
	if(cbInfo <= 0)		//fRetはFALSEが返る
		return	false;

	pData = new BYTE[cbInfo];
	if(pData == NULL)
		return	false;

	//データ取得
	pInfo = (LPINTERNET_CACHE_ENTRY_INFO)pData;
	fRet = ::GetUrlCacheEntryInfo(pszURL,pInfo,&cbInfo);
	if(fRet)
		*pdwCount = pInfo->dwHitRate;

	delete	pData;

	return	fRet ? true : false;
}




void	Test(void)
{
	bool	ret;
	DWORD	dwCount;
	TCHAR	pszMessage[1024];

	ret = GetVisitsCount(_T("http://www.google.co.jp/"),&dwCount);

	if(ret)
	{
		_stprintf_s(pszMessage,1024,_T("閲覧数は %d 回です"),dwCount);
		::MessageBox(NULL,pszMessage,_T(""),MB_OK);
	}
	else
		::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}

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

2006年11月25日

NEC PC-98シリーズをチェックする

最近のパソコンはほとんどがAT互換機でWindowsが動いている。ソフト開発をするときもWindowsとInternet Explorerのバージョンにさえ気を配れば問題ない。
とは言うものの今でも古いパソコンを使っているユーザーは少なからずいる。古いパソコンであってもAT互換機であれば問題が生じることはほとんどない。しかしそのようなユーザーの中にはNEC製のPC-98シリーズを使っている人もいる。
アプリケーションのパッケージに動作保証は「AT互換機のみ」と書いておくだけでなく、インストーラーなどでPC-98しりーずであることをチェックして動作保証がないことを警告しておくといいかもしれない。ちなみに古いマシンと言うとMIPSやAlpha、Power PCチップのWindowsパソコンなんてものも存在するが...これらは当時でさえユーザー数が少なかったから考えなくてもいいだろう。

//
//	NEC PC-98シリーズかAT互換機かのチェック
//
bool	IsNEC98(void)
{
	return ((::GetKeyboardType(0) == 7) && ((::GetKeyboardType(1) & 0xff00) == 0x0d00)) ? true : false;
}



void	Test(void)
{
	if(IsNEC98())
		::MessageBox(NULL,_T("このパソコンはNECのPC-98シリーズです"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("このパソコンはNECのPC-98シリーズではありません"),_T(""),MB_OK);
}

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

実行ファイルのあるフォルダを取得する

「オプションの設定項目をiniファイルとして実行ファイルの存在するフォルダ内に保存したい」というようなときに使えるのがGetModuleFileName関数。この関数では実行ファイルへのフルパスを取得できる。しかし必要なのはフォルダ名の部分までだ。ここではフォルダ名までを取得できる関数を用意した。

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

//
//	実行ファイルの存在するフォルダとファイル名を取得する
//
//実行ファイルが"c:\test\aaa.exe"の場合は、pstrFolderに"c:\test\"、pstrFileに"aaa.exe"が帰る
//pstrFolderの末尾は'\'になる
//
bool	GetExecDir(CAtlString* pstrFolder,CAtlString* pstrFile)
{
	int			nLen;
	errno_t		err;
	TCHAR		pcbPath[_MAX_PATH*2];	//あまり意味はないが2倍値を利用
	TCHAR		pszDrive[_MAX_DRIVE];
	TCHAR		pszFolder[_MAX_DIR];
	TCHAR		pszFile[_MAX_FNAME];
	TCHAR		pszExtent[_MAX_EXT ];

	if(pstrFolder == NULL && pstrFile == NULL)
		return	true;
	if(pstrFolder)
		*pstrFolder = _T("");
	if(pstrFile)
		*pstrFile = _T("");

	//起動ディレクトリ名、ファイル名取得
	nLen = ::GetModuleFileName(NULL,pcbPath,_MAX_PATH*2);
	if(nLen == 0)
		return	false;

	//パスの分解
	err = ::_tsplitpath_s(pcbPath,pszDrive,_MAX_DRIVE,pszFolder,_MAX_DIR,pszFile,_MAX_FNAME,pszExtent,_MAX_EXT);
	if(err != 0)
		return	false;

	//結果保存
	if(pstrFolder)
	{
		*pstrFolder = pszDrive;
		*pstrFolder += pszFolder;
	}
	if(pstrFile)
	{
		*pstrFile = pszFile;
		*pstrFile += pszExtent;
	}

	return	true;
}



void	Test(void)
{
	CAtlString	strMessage;
	CAtlString	strFolder;
	CAtlString	strExec;

	GetExecDir(&strFolder,&strExec);

	strMessage.Format(_T("フォルダは「%s」、ファイル名は「%s」です"),strFolder,strExec);
	::MessageBox(NULL,strMessage,_T(""),MB_OK);

}

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

2006年11月26日

日本語版WTL 7.5のアプリケーションウィザード

■はじめに
WTLはライセンスがCPLのオープンソースプロジェクトとして公開されている。そのためおそらく勝手に日本語化したものを配布しても問題ないと思い用意しました。ライセンス的に問題があるようでしたらお知らせください。削除します。

■メリット
従来の英語版WTLではプロジェクト作成後にリソースエディタでメニューなどの言語を日本語に切り替えて、表示文字列を編集する必要がありました。しかしこの日本語版WTL 7.5ではプロジェクト生成直後から日本語リソースが利用可能です。

■日本語化内容:
 ・WTL 7.5のreadme.html(の一部)
 ・Windowsアプリケーション用アプリケーションウィザード
 ・Windowsアプリケーション用アプリケーションウィザードのセットアップ
 ・アプリケーションウィザードによって生成されるソースコード
 ・アプリケーションウィザードによって生成されるソースコードのコメント

※WTL 7.5のサンプルやWindows CE用のアプリケーションウィザードは日本語化していません。また、ダウンロード可能なファイルにも含まれません。
※WTL 7.5のincldueファイルも含みますが日本語化されていません。
※短時間で翻訳処理したため変な日本語になっている部分があります。


■ダウンロード
日本語版WTL 7.5をダウンロード

■以下は画面例です
wtl75jpmod001_img01.gif

wtl75jpmod001_img02.gif

wtl75jpmod001_img03.gif

wtl75jpmod001_img04.gif

ショートカットを作成する

アプリケーションをインストールするときにプログラムメニューやデスクトップ上にショートカットを生成したいことがある。そのような場合はCOMインターフェースのIShellLinkを通じて作成する。ショートカットファイルの拡張子には「.lnk」を指定する。
また、この方法でURLへのショートカット作成もできるが一般的ではない。作成したい場合はIUniformResourceLocatorを利用するべきだ。

#include <shlobj.h>


//
//	ショートカットの作成
//
//	pszLinkにショートカットを保存します。拡張子に「.lnk」を指定してください。
//	ショートカットのターゲットはpszFile、説明はpszDescription...とオプションを設定できます。
//
bool	CreateShortcut(LPCTSTR pszLink,LPCTSTR pszFile,LPCTSTR pszDescription=NULL,LPCTSTR pszArgs=NULL,LPCTSTR pszWorkingDir=NULL,LPCTSTR pszIconPath=NULL,int nIcon=0,int nShowCmd=SW_SHOWNORMAL)
{
	HRESULT			hr;
	IShellLink*		pIShellLink;
	IPersistFile*	pIPersistFile;

	//IShellLinkの作成
	pIShellLink = NULL;
	hr = ::CoCreateInstance(CLSID_ShellLink,NULL,CLSCTX_INPROC_SERVER,IID_IShellLink,(void**)&pIShellLink);
	if(pIShellLink == NULL || FAILED(hr))
		return	false;

	//IPersistFileの取得
	pIPersistFile = NULL;
	hr = pIShellLink->QueryInterface(IID_IPersistFile,(void**)&pIPersistFile);
	if(pIPersistFile == NULL || FAILED(hr))
	{
		pIShellLink->Release();
		return	false;
	}

	//ショートカット詳細設定
	hr = pIShellLink->SetPath(pszFile);
	if(SUCCEEDED(hr) && pszDescription)
		hr = pIShellLink->SetDescription(pszDescription);
	if(SUCCEEDED(hr) && pszArgs)
		hr = pIShellLink->SetArguments(pszArgs);
	if(SUCCEEDED(hr) && pszWorkingDir)
		hr = pIShellLink->SetWorkingDirectory(pszWorkingDir);
	if(SUCCEEDED(hr) && pszIconPath)
		hr = pIShellLink->SetIconLocation(pszIconPath,nIcon);
	if(SUCCEEDED(hr))
		hr = pIShellLink->SetShowCmd(nShowCmd);


#ifndef UNICODE
	WCHAR*	pwszUnicode;
	int		nLen;

	//Unicode変換
	nLen = ::MultiByteToWideChar(CP_ACP,0,pszLink,-1,NULL,0);
	pwszUnicode = new WCHAR[nLen + 1];
	if(pwszUnicode == NULL)
	{
		pIPersistFile->Release();
		pIShellLink->Release();
		return	false;
	}
	nLen = ::MultiByteToWideChar(CP_ACP,0,pszLink,-1,pwszUnicode,nLen + 1);
	if(nLen == 0)
		hr = E_FAIL;

	//ショートカットの保存
	if(SUCCEEDED(hr))
		hr = pIPersistFile->Save(pwszUnicode,TRUE);

	delete	pwszUnicode;

#else
	//ショートカットの保存
	if(SUCCEEDED(hr))
		hr = pIPersistFile->Save(pszLink,TRUE);
#endif

	pIPersistFile->Release();
	pIShellLink->Release();

	return	SUCCEEDED(hr) ? true : false;
}




void	Test(void)
{
	bool	ret;

	//COM初期化
	::CoInitialize(NULL);

	ret = CreateShortcut(_T("c:\\test.lnk"),_T("c:\\windows\\notepad.exe"),_T("メモ帳"));
	if(ret)
		::MessageBox(NULL,_T("ショートカットの作成に成功しました"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("ショートカットの作成に失敗しました"),_T(""),MB_OK);

	//COM開放
	::CoUninitialize();
}

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

URLショートカットを作成する

アプリケーションをインストールするときにお気に入りにURLを追加したいことがある。そのような場合はCOMインターフェースのIUniformResourceLocatorを通じて作成する。ショートカットファイルの拡張子には「.url」を指定する。

#include <shlobj.h>
#include <intshcut.h>


//
//	URLショートカットの作成
//
//	pszLinkにショートカットを保存します。拡張子に「.url」を指定してください。
//
bool	CreateInternetShortcut(LPCTSTR pszFile,LPCTSTR pszURL)
{
	HRESULT			hr;
	IPersistFile*	pIPersistFile;
	IUniformResourceLocator*	pIUniformResourceLocator;

	pIUniformResourceLocator = NULL;
	hr = ::CoCreateInstance(CLSID_InternetShortcut,NULL,CLSCTX_INPROC_SERVER,IID_IUniformResourceLocator,(void**)&pIUniformResourceLocator);
	if(pIUniformResourceLocator == NULL || FAILED(hr))
		return	false;

	hr = pIUniformResourceLocator->QueryInterface(IID_IPersistFile,(void**)&pIPersistFile);
	if(pIPersistFile == NULL || FAILED(hr))
	{
		pIUniformResourceLocator->Release();
		return	false;
	}

	hr = pIUniformResourceLocator->SetURL(pszURL,0);

#ifdef  _UNICODE
	if(SUCCEEDED(hr))
		hr = pIPersistFile->Save(pszFile,TRUE);
#else
	WCHAR*	pwszUnicode;
	int		nLen;

	//Unicode変換
	nLen = ::MultiByteToWideChar(CP_ACP,0,pszFile,-1,NULL,0);
	pwszUnicode = new WCHAR[nLen + 1];
	if(pwszUnicode == NULL)
	{
		pIPersistFile->Release();
		pIUniformResourceLocator->Release();
		return	false;
	}
	nLen = ::MultiByteToWideChar(CP_ACP,0,pszFile,-1,pwszUnicode,nLen + 1);
	if(nLen == 0)
		hr = E_FAIL;

	//ショートカットの保存
	if(SUCCEEDED(hr))
		hr = pIPersistFile->Save(pwszUnicode,TRUE);

	delete	pwszUnicode;
#endif

	pIPersistFile->Release();
	pIUniformResourceLocator->Release();

	return	SUCCEEDED(hr) ? true : false;
}




void	Test(void)
{
	bool	ret;

	//COM初期化
	::CoInitialize(NULL);

	ret = CreateInternetShortcut(_T("c:\\test.url"),_T("http://www.google.co.jp/"));
	if(ret)
		::MessageBox(NULL,_T("ショートカットの作成に成功しました"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("ショートカットの作成に失敗しました"),_T(""),MB_OK);

	//COM開放
	::CoUninitialize();
}

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

URLショートカットからURLを読み出す

お気に入りなどに登録されているインターネットショートカットからURLを取り出したいことがある。そのような場合はCOMインターフェースのIUniformResourceLocatorを通じてアクセスする。ショートカットファイルの拡張子には「.url」を指定する。

IUniformResourceLocator::GetURLはURLが取得できていないにも関わらず成功を示すS_FALSEを返すことがあるので実装に注意する。

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

#include <shlobj.h>
#include <intshcut.h>



//
//	URLショートカットからのURL読み込み
//
//	pszFileに指定されたURLショートカットを読み込みます。拡張子に「.url」を指定してください。
//
bool	GetInternetShortcut(LPCTSTR pszFile,CAtlString* pstrURL)
{
	HRESULT	hr;
	LPTSTR	pszURL;
	IMalloc*					pIMalloc;
	IPersistFile*				pIPersistFile;
	IUniformResourceLocator*	pIUniformResourceLocator;

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

	pIUniformResourceLocator = NULL;
	hr = ::CoCreateInstance(CLSID_InternetShortcut,NULL,CLSCTX_INPROC_SERVER,IID_IUniformResourceLocator,(void**)&pIUniformResourceLocator);
	if(pIUniformResourceLocator == NULL || FAILED(hr))
		return	false;

	hr = pIUniformResourceLocator->QueryInterface(IID_IPersistFile,(void**)&pIPersistFile);

	if(pIPersistFile)
	{
	#ifdef  _UNICODE
		hr = pIPersistFile->Load(pszFile,STGM_READ | STGM_SHARE_DENY_NONE);
	#else
		WCHAR*	pwszUnicode;
		int		nLen;

		//Unicode変換
		nLen = ::MultiByteToWideChar(CP_ACP,0,pszFile,-1,NULL,0);
		pwszUnicode = new WCHAR[nLen + 1];
		if(pwszUnicode == NULL)
		{
			pIPersistFile->Release();
			pIUniformResourceLocator->Release();
			return	false;
		}
		nLen = ::MultiByteToWideChar(CP_ACP,0,pszFile,-1,pwszUnicode,nLen + 1);
		if(nLen == 0)
			hr = E_FAIL;

		hr = pIPersistFile->Load(pwszUnicode,STGM_READ | STGM_SHARE_DENY_NONE);

		delete	pwszUnicode;
	#endif
	}

	if(pIUniformResourceLocator)
		hr = pIUniformResourceLocator->GetURL(&pszURL);
	if(hr == S_FALSE)		//S_FALSEが返ることがある!これはSUCCEEDED(hr)なので注意!
		hr = E_FAIL;
	if(SUCCEEDED(hr))
		*pstrURL = pszURL;

	pIMalloc = NULL;
	if(SUCCEEDED(hr))
		hr = ::SHGetMalloc(&pIMalloc); 
	if(pIMalloc && SUCCEEDED(hr))
		pIMalloc->Free(pszURL);

	if(pIMalloc)
		pIMalloc->Release();
	if(pIPersistFile)
		pIPersistFile->Release();
	if(pIUniformResourceLocator)
		pIUniformResourceLocator->Release();

	return	SUCCEEDED(hr) ? true : false;
}



void	Test(void)
{
	bool		ret;
	CAtlString	strURL;

	//COM初期化
	::CoInitialize(NULL);

	ret = GetInternetShortcut(_T("c:\\test.url"),&strURL);
	if(ret)
		::MessageBox(NULL,strURL,_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("URLの読み込みに失敗しました"),_T(""),MB_OK);

	//COM開放
	::CoUninitialize();
}

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

ファイル名として禁止されている文字を置き換える

例えば「現在アクセスしているホームページへのショートカットを自動的にお気に入りに登録するアプリケーションを作りたい」などの場合、ホームページのタイトルをそのままファイル名として使うと、タイトル内にファイル名として禁止されている文字が入っている場合に誤動作する危険がある。
このようなときにファイル名に使いたい文字列をこの関数に通すと自動的に禁止された文字列が全角文字に置換されるので誤動作の可能性を減らすことができる。

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

//
//ファイル名として禁止されている文字を変換
//
void	TranslateToSafeFileName(CAtlString& strSafeFileName)
{
	//禁止半角を全角文字に変換
	strSafeFileName.Replace(_T("\\"),_T("¥"));
	strSafeFileName.Replace(_T("/"),_T("/"));
	strSafeFileName.Replace(_T("*"),_T("*"));
	strSafeFileName.Replace(_T(":"),_T(":"));
	strSafeFileName.Replace(_T("?"),_T("?"));
	strSafeFileName.Replace(_T("<"),_T("<"));
	strSafeFileName.Replace(_T(">"),_T(">"));
	strSafeFileName.Replace(_T("\""),_T("”"));
	strSafeFileName.Replace(_T("|"),_T("|"));
}



void	Test(void)
{
	CAtlString	strFile;

	strFile = _T("Googleへのショートカット (http://www.google.co.jp/)");
	TranslateToSafeFileName(strFile);

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

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

拡張子に関連付けられた実行ファイルを取得する

エクスプローラーでファイルをダブルクリックしたときに使われる実行ファイルを取得するには
AssocQueryStringを利用する。ただし取得結果は必ずしもexeファイルになるとは限らない。拡張子によってはdllファイルなどに関連づいていることもある。(例:jpegファイルでshimgvw.dllが取得されるなど)

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

#include "shlwapi.h"

#pragma	comment(lib,"shlwapi.lib")


//
//	拡張子に関連付けられた実行ファイル取得
//
//	pszExtensionは".txt"のようにする("txt"でも構わない)
//	".bat"を取得すると"%1"になるなど実際のコマンドとならないことがある!
//	拡張子によってはdllファイル名が返ることがある!
//
bool	GetExecutable(LPCTSTR pszExtension,CAtlString* pstrCommand)
{
	DWORD		dwOut;
	HRESULT		hr;
	TCHAR		pszFile[MAX_PATH*2];
	CAtlString	strExt;

	if(pstrCommand == NULL)
		return	false;
	*pstrCommand = _T("");
	if(pszExtension == NULL)
		return	false;

	strExt = pszExtension;
	if(strExt.Left(1) != _T("."))
		strExt = _T(".") + strExt;
	if(strExt.GetLength() == 1)
		return	false;

	//pszFileの代わりにNULLを渡せば必要なバッファーが取得できるはずだが...うまく動かない
	//ことがあるのでMAX_MAX*2の固定長で文字列を取得している
	dwOut = MAX_PATH*2;
	hr = ::AssocQueryString(ASSOCF_NOTRUNCATE,ASSOCSTR_EXECUTABLE,strExt,_T("open"),pszFile,&dwOut);
	if(FAILED(hr) || dwOut == 0)
		return	false;

	*pstrCommand = pszFile;

	return	true;
}




void	Test(void)
{
	bool		ret;
	CAtlString	strCommand;

	ret = GetExecutable(_T(".jpg"),&strCommand);

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

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

Internet Explorerの「お気に入りの整理」ウィンドウを開く

Internet Explorerの「お気に入り」メニューにある「お気に入りの整理」を選択すると表示されるウインドウはshdocvw.dllの中に実装されている。単独でも動作する関数なので自分のアプリケーションから利用することができる。

test13.gif

//
//	Internet Explorerの「お気に入りの整理」ウィンドウを開く
//
bool	ShowDoOrganizeFavoritesDialog(HWND hWnd,LPTSTR pszFolder=NULL)
{
	BOOL	ret;
	HMODULE	hDLL;

	BOOL (WINAPI* pfnDoOrganizeFavDlg)(HWND hWnd,LPTSTR pszFolder);

	hDLL = ::LoadLibrary(_T("shdocvw.dll"));
	if(hDLL == NULL)
		return	false;
#ifdef _UNICODE
	(FARPROC&)pfnDoOrganizeFavDlg = ::GetProcAddress(hDLL,"DoOrganizeFavDlgW");
#else
	(FARPROC&)pfnDoOrganizeFavDlg = ::GetProcAddress(hDLL,"DoOrganizeFavDlg");
#endif
	ret = FALSE;
	if(pfnDoOrganizeFavDlg)
		ret = pfnDoOrganizeFavDlg(hWnd,pszFolder);

	::FreeLibrary(hDLL);

	return	ret ? true : false;
}



void	Test(void)
{
	bool	ret;

	ret = ShowDoOrganizeFavoritesDialog(NULL,NULL);

	if(ret == false)
		::MessageBox(NULL,_T("表示に失敗しました"),_T(""),MB_OK);
}

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

全角半角変換、ひらがなカタカナ変換などの文字列変換をする

LCMapStringを利用すると、全角文字を半角文字に変換したり、ひらがなをカタカナに置き換えることができる(これらの処理はLCMapStringを使わずとも簡単にできるのだが...)。

文字列操作はユニコードビルド時でも非ユニコード文字を扱わなければいけなかったり、その逆も多い。つまりTCHAR系で実装すると使い勝手が悪くなる。そのためここではユニコード用と非ユニコード用、両用の3種類を全ての変換処理に用意した。

依存環境:ATL
#pragma	once

#include "atlstr.h"


class	CDnpStrConv
{
public:

	//
	//	LCMapStringを利用したロケール依存の文字列変換)
	//
	bool	StrConv(LPCTSTR pszSrc,CAtlString* pstrDest,DWORD dwFlags)
	{
	#ifdef	_UNICODE
		return	StrConvW(pszSrc,pstrDest,dwFlags);
	#else
		return	StrConvA(pszSrc,pstrDest,dwFlags);
	#endif
	}


	//
	//	LCMapStringを利用したロケール依存の文字列変換)(非ユニコード用)
	//
	bool	StrConvA(LPCSTR pszSrc,CAtlStringA* pstrDest,DWORD dwFlags)
	{
		char*	pszBuff;
		int		nLen;
		int		ret;
		LCID	dwLocale;

		if(pstrDest == NULL)
			return	false;
		*pstrDest = _T("");
		if(pszSrc == NULL)
			return	false;

		//dwLocale = ::GetUserDefaultLCID();	//このクラス内では日本語用の変換処理しか利用しないので日本用の1041に固定している!
		dwLocale = 1041;						//中国語など多言語用の変換処理を利用するにはそのロケールもしくはUserDefaultを指定すること
		nLen = ::LCMapStringA(dwLocale,dwFlags,pszSrc,-1,NULL,0);
		nLen += 10;
		pszBuff = new char[nLen];

		ret = ::LCMapStringA(dwLocale,dwFlags,pszSrc,-1,pszBuff,nLen);
		if(ret == 0)
		{
			delete	pszBuff;
			return	false;
		}

		*pstrDest = pszBuff;
		delete	pszBuff;

		return	true;
	}


	//
	//	LCMapStringを利用したロケール依存の文字列変換)(ユニコード用)
	//
	bool	StrConvW(LPCWSTR pszSrc,CAtlStringW* pstrDest,DWORD dwFlags)
	{
		WCHAR*	pszBuff;
		int		nLen;
		int		ret;
		LCID	dwLocale;

		if(pstrDest == NULL)
			return	false;
		*pstrDest = _T("");
		if(pszSrc == NULL)
			return	false;

		//dwLocale = ::GetUserDefaultLCID();	//このクラス内では日本語用の変換処理しか利用しないので日本用の1041に固定している!
		dwLocale = 1041;						//中国語など多言語用の変換処理を利用するにはそのロケールもしくはUserDefaultを指定すること
		nLen = ::LCMapStringW(dwLocale,dwFlags,pszSrc,-1,NULL,0);
		nLen += 10;
		pszBuff = new WCHAR[nLen];

		ret = ::LCMapStringW(dwLocale,dwFlags,pszSrc,-1,pszBuff,nLen);
		if(ret == 0)
		{
			delete	pszBuff;
			return	false;
		}

		*pstrDest = pszBuff;
		delete	pszBuff;

		return	true;
	}


	//
	//	メンバー関数定義用一時マクロ
	//
	#define	FN_STRCONV_EXTEND(func_dualchar,func_charA,func_chcarW,dwFlags)	\
		bool	func_dualchar(LPCTSTR pszSrc,CAtlString* pstrDest)			\
		{																	\
			return	StrConv(pszSrc,pstrDest,dwFlags);						\
		}																	\
		bool	func_charA(LPCSTR pszSrc,CAtlStringA* pstrDest)				\
		{																	\
			return	StrConvA(pszSrc,pstrDest,dwFlags);						\
		}																	\
		bool	func_chcarW(LPCWSTR pszSrc,CAtlStringW* pstrDest)			\
		{																	\
			return	StrConvW(pszSrc,pstrDest,dwFlags);						\
		}																	\
		bool	func_dualchar(CAtlString& strText)							\
		{																	\
			return	func_dualchar((CAtlString)strText,&strText);			\
		}																	\
		bool	func_charA(CAtlStringA& strText)							\
		{																	\
			return	func_charA((CAtlStringA)strText,&strText);				\
		}																	\
		bool	func_chcarW(CAtlStringW& strText)							\
		{																	\
			return	func_chcarW((CAtlStringW)strText,&strText);				\
		}


	//	半角全角変換
	FN_STRCONV_EXTEND(Zen2Han,Zen2HanA,Zen2HanW,LCMAP_HALFWIDTH)

	//全角半角変換
	FN_STRCONV_EXTEND(Han2Zen,Han2ZenA,Han2ZenW,LCMAP_FULLWIDTH)

	//カタカナひらがな変換
	FN_STRCONV_EXTEND(Katakana2Hiragana,Katakana2HiraganaA,Katakana2HiraganaW,LCMAP_HIRAGANA)
	FN_STRCONV_EXTEND(Kana2Hira,Kana2HiraA,Kana2HiraW,LCMAP_HIRAGANA)

	//ひらがなカタカナ変換
	FN_STRCONV_EXTEND(Hiragana2Katakana,Hiragana2KatakanaA,Hiragana2KatakanaW,LCMAP_KATAKANA)
	FN_STRCONV_EXTEND(Hira2Kana,Hira2KanaA,Hira2KanaW,LCMAP_KATAKANA)

	//小文字大文字変換
	FN_STRCONV_EXTEND(MakeUpper,MakeUpperA,MakeUpperW,LCMAP_UPPERCASE)

	//大文字小文字変換
	FN_STRCONV_EXTEND(MakeLower,MakeLowerA,MakeLowerW,LCMAP_LOWERCASE)

	//マクロ定義の削除
	#undef	FN_STRCONV_EXTEND
};





void	Test(void)
{
	bool		ret;
	CAtlString	strMessage;
	CDnpStrConv	cCnv;

	ret = cCnv.MakeLower(_T("あいうABCDXYZ123"),&strMessage);
	if(ret)
		::MessageBox(NULL,strMessage,_T(""),MB_OK);

	ret = cCnv.Zen2Han(strMessage);
	if(ret)
		::MessageBox(NULL,strMessage,_T(""),MB_OK);

	ret = cCnv.MakeUpper(strMessage);
	if(ret)
		::MessageBox(NULL,strMessage,_T(""),MB_OK);

	ret = cCnv.Hira2Kana(strMessage);
	if(ret)
		::MessageBox(NULL,strMessage,_T(""),MB_OK);

	ret = cCnv.Kana2Hira(strMessage);
	if(ret)
		::MessageBox(NULL,strMessage,_T(""),MB_OK);
}

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

ネットワークドライブ接続・切断ダイアログを表示する

エクスプローラの「ツール」メニューにある「ネットワークドライブの割り当て」メニューや「ネットワークドライブの切断」メニューを選択したときに表示されるダイアログはWNetConnectionDialogやWNetDisconnectDialog関数から利用できる。

test15_01.gif

test15_02.gif

#pragma comment(lib,"mpr.lib ")

//
//	ネットワークドライブの割り当てダイアログ表示
//
bool	ShowNetDriveConnectionDialog(HWND hParent)
{
	DWORD	dwRet;

	dwRet = ::WNetConnectionDialog(hParent,RESOURCETYPE_DISK);

	return	(dwRet == NO_ERROR) ? true : false;
}


//
//	ネットワークドライブの切断ダイアログの表示
//
bool	ShowNetDriveDissconnectDialog(HWND hParent)
{
	DWORD	dwRet;

	dwRet = ::WNetDisconnectDialog(hParent,RESOURCETYPE_DISK);

	return	(dwRet == NO_ERROR) ? true : false;
}




void	Test(void)
{
	ShowNetDriveConnectionDialog(NULL);

	ShowNetDriveDissconnectDialog(NULL);
}

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

ネットワークドライブへ接続する

LAN上のパソコンやNASなどから公開されているフォルダをドライブとして接続するにはWNetAddConnection2を使う。この関数はネットワークプリンタの接続にも利用できるものだが今回は使い方をネットワークドライブに限定している。

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


//
//	ネットワークドライブの割り当て
//
//bConnectionKeepはfalseだとWindowsを再起動すると接続は回復しない
//
bool	NetDriveConnect(LPCTSTR pszLocalName,LPCTSTR pszRemoteName,bool bConnectionKeep=false)
{
	DWORD		dwRet;
	DWORD		dwFlags;
	NETRESOURCE	sNetResource;
	CAtlStringA	strLocal;
	CAtlStringA	strRemote;

	strLocal	= pszLocalName;
	strRemote	= pszRemoteName;

	//pszLocalNameは「z:」のようにする。末尾は「:」にする
	if(strLocal.Right(1) != ':')
		strLocal += ":";

	//	pszRemoteNameは「\\\\192.168.11.151\\share」のようにする。末尾を「\\」にするのはNG
	if(strRemote.Right(1) == '\\')
		strRemote = strRemote.Left(strRemote.GetLength() - 1);

	ZeroMemory(&sNetResource,sizeof(NETRESOURCE));
	sNetResource.dwType			= RESOURCETYPE_DISK;
	sNetResource.lpLocalName	= strLocal.GetBuffer(0);
	sNetResource.lpRemoteName	= strRemote.GetBuffer(0);

	dwFlags = (bConnectionKeep) ? CONNECT_UPDATE_PROFILE : 0;
	dwRet = ::WNetAddConnection2(&sNetResource,NULL,NULL,dwFlags);

	return	(dwRet == NO_ERROR) ? true : false;
}






void	Test(void)
{
	bool	ret;

	ret = NetDriveConnect(_T("r:"),_T("\\\\192.168.11.151\\share"));

	if(ret)
		::MessageBox(NULL,_T("接続しました"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("失敗しました"),_T(""),MB_OK);
}

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

ネットワークドライブを切断する

ネットワークドライブとして使われているLAN上のパソコンやNASなどから公開されているフォルダから切断するにはWNetDisconnectDialog1を利用する。

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



//
//	ネットワークドライブの切断
//
//bDisconnectKeepはfalseだとWindowsを再起動すると接続が回復する
//
bool	NetDriveDisconnect(LPCTSTR pszLocalName,LPCTSTR pszRemoteName,bool bDisconnectKeep=false)
{
	DWORD			dwRet;
	DISCDLGSTRUCT	sDiscdlgStruct;
	CAtlStringA		strLocal;
	CAtlStringA		strRemote;

	strLocal	= pszLocalName;
	strRemote	= pszRemoteName;

	//pszLocalNameは「z:」のようにする。末尾は「:」にする
	if(strLocal.Right(1) != ':')
		strLocal += ":";

	//	pszRemoteNameは「\\\\192.168.11.151\\share」のようにする。末尾を「\\」にするのはNG
	if(strRemote.Right(1) == '\\')
		strRemote = strRemote.Left(strRemote.GetLength() - 1);

	ZeroMemory(&sDiscdlgStruct,sizeof(DISCDLGSTRUCT));
	sDiscdlgStruct.cbStructure	= sizeof(DISCDLGSTRUCT);
	sDiscdlgStruct.hwndOwner	= NULL;
	sDiscdlgStruct.lpLocalName	= strLocal.GetBuffer(0);
	sDiscdlgStruct.lpRemoteName	= strRemote.GetBuffer(0);
	sDiscdlgStruct.dwFlags		= (bDisconnectKeep) ? DISC_UPDATE_PROFILE | DISC_NO_FORCE : DISC_NO_FORCE;

	dwRet = ::WNetDisconnectDialog1(&sDiscdlgStruct);

	return	(dwRet == NO_ERROR) ? true : false;
}






void	Test(void)
{
	bool	ret;

	ret = NetDriveDisconnect(_T("r:"),_T("\\\\192.168.11.151\\share"));

	if(ret)
		::MessageBox(NULL,_T("切断しました"),_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("失敗しました"),_T(""),MB_OK);
}

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

使用中のドライブレターを取得する

ネットワークドライブの割り当てや切断などをするときに、どのドライブレターが使われているのかを調べるときはGetLogicalDrivesを利用する。これにより使われているものとそうでないものとを簡単に判別できる。

//
//	使用中のドライブレター取得
//
//ドライブ名が文字列として列挙されて返る。例: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;
}



void	Test(void)
{
	bool	ret;
	TCHAR	pszLetters[256];

	ret = GetUsedDriveLetter(pszLetters,256);

	if(ret)
		::MessageBox(NULL,pszLetters,_T(""),MB_OK);
	else
		::MessageBox(NULL,_T("失敗しました"),_T(""),MB_OK);
}

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

2006年11月27日

NICのMACアドレス、接続速度などを取得する

GetIfTableを利用するとパソコンに認識されている全てのネットワークカードのMACアドレス、役割、接続速度などを調べてることができる。この関数は残念ながらWindows 95はサポートしていないため、動作はWindows 98以降となる。2007年に発売のWindows Vistaでもサポートされている。
このクラスを作るに当たって初めはWindows 95でも動作するように実装を進めたのだが...そうすんなりといかず、NetBIOSやSNMPでも環境によってはMACアドレスが取得できなかったため諦めた。

test19.gif

依存環境:ATL
#pragma	once

#include "atlstr.h"
#include "iphlpapi.h"
#pragma comment(lib,"iphlpapi.lib") 


class	CDnpNetworkInterface
{
	_MIB_IFTABLE*	_pInfoBuffer;


protected:



	//	Platform SDK: Internet Protocol Helper
	//
	//	GetIfTable() によるMACアドレスの取得
	//
	//		Windows98以降に対応(Windows 95 非対応!:Windows NT4.0はSP4以降に対応)
	//
	//※Windows 95はWinSock1を利用する。WinSock2やNetBiosだとNGなこともある
	//※Windows VistaからはGetIfTable2も利用可能
	//
	bool	GetNicInformation(void)
	{
		DWORD	ret;
		ULONG	dwSize;

		dwSize = 0;
		ret = ::GetIfTable(NULL,&dwSize,TRUE);
		if(ret != NO_ERROR && ret != ERROR_INSUFFICIENT_BUFFER)
			return	false;

		if(_pInfoBuffer)
			delete	(BYTE*)_pInfoBuffer;

		_pInfoBuffer = (_MIB_IFTABLE*)new BYTE[dwSize];
		if(_pInfoBuffer == NULL)
			return	false;

		ZeroMemory(_pInfoBuffer,dwSize);
		ret = ::GetIfTable(_pInfoBuffer,&dwSize,TRUE);
		if(ret != NO_ERROR)
		{
			delete	(BYTE*)_pInfoBuffer;
			_pInfoBuffer = NULL;
			return	false;
		}

		return	true;
	}


	//
	//	クラス内にNICデータを取得済みかチェック
	//
	bool	IsValidInfo(void)
	{
		if(_pInfoBuffer)
			return	true;

		//なければ取得する
		return	GetNicInformation();
	}

public:

	//
	//	コンストラクタ
	//
	CDnpNetworkInterface()
	{
		_pInfoBuffer = NULL;
	}

	//
	//	デストラクタ
	//
	~CDnpNetworkInterface()
	{
		if(_pInfoBuffer)
			delete	(BYTE*)_pInfoBuffer;
	}


	//
	//	検出されたNIC数取得
	//
	DWORD	GetNicCount(void)
	{
		if(IsValidInfo() == false)
			return	0;

		return	_pInfoBuffer->dwNumEntries;
	}


	//
	//	NIC名取得
	//
	bool	GetNicName(DWORD dwIndex,CAtlString* pstrName)
	{
		if(pstrName == NULL)
			return	false;
		*pstrName = _T("");
		if(dwIndex >= GetNicCount())
			return	false;

		*pstrName = (char*)_pInfoBuffer->table[dwIndex].bDescr;

		return	true;
	}


	//
	//	NICのMACアドレス取得
	//
	bool	GetNicMacAddress(DWORD dwIndex,CAtlString* pstrMacAddress,int nFormat=0)
	{
		if(pstrMacAddress == NULL)
			return	false;
		*pstrMacAddress = _T("");
		if(dwIndex >= GetNicCount())
			return	false;

		switch(nFormat)
		{
		case	1:
			pstrMacAddress->Format(_T("%02X %02X %02X %02X %02X %02X")
					,_pInfoBuffer->table[dwIndex].bPhysAddr[0]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[1]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[2]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[3]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[4]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[5]);
			break;

		case	2:
			pstrMacAddress->Format(_T("%02X%02X%02X%02X%02X%02X")
					,_pInfoBuffer->table[dwIndex].bPhysAddr[0]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[1]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[2]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[3]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[4]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[5]);
			break;

		case	0:
		default:
			pstrMacAddress->Format(_T("%02X-%02X-%02X-%02X-%02X-%02X")
					,_pInfoBuffer->table[dwIndex].bPhysAddr[0]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[1]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[2]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[3]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[4]
					,_pInfoBuffer->table[dwIndex].bPhysAddr[5]);
			break;
		}

		return	true;
	}


	//
	//	NICの接続速度取得
	//
	DWORD	GetNicConnectionSpeed(DWORD dwIndex)
	{
		if(dwIndex >= GetNicCount())
			return	0;

		return	_pInfoBuffer->table[dwIndex].dwSpeed;
	}


	//
	//	NICが正常に動いているかチェック(ディスコネクトされていないかなどをチェック)
	//
	bool	IsNicWorking(DWORD dwIndex)
	{
		if(dwIndex >= GetNicCount())
			return	false;

		return	(_pInfoBuffer->table[dwIndex].dwOperStatus == MIB_IF_OPER_STATUS_OPERATIONAL) ? true : false;
	}

};



void	Test(void)
{
	bool		ret;
	DWORD		i;
	DWORD		dwSize;
	DWORD		dwSpeed;
	CAtlString	strName;
	CAtlString	strMacAddress;
	CAtlString	strBuff;
	CAtlString	strMessage;
	CDnpNetworkInterface	cDevice;

	dwSize = cDevice.GetNicCount();
	for(i = 0; i < dwSize; i++)
	{
		ret = cDevice.GetNicName(i,&strName);
		if(ret == false)
			continue;

		ret = cDevice.GetNicMacAddress(i,&strMacAddress);
		if(ret == false)
			continue;

		dwSpeed = cDevice.GetNicConnectionSpeed(i);

		strBuff.Format(_T("概要:%s\nMACアドレス:%s\n接続速度:%dKbps\n"),strName,strMacAddress,dwSpeed/1000);
		strMessage += strBuff;
		if(cDevice.IsNicWorking(i))
			strMessage += _T("動作:正常\n");
		else
			strMessage += _T("動作:停止中\n");

		strMessage += _T("\n");
	}

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

ファイルをダウンロード

2つのパス間の相対パスを取得する(非API)

例えば画像ファイルを一覧できるHTMLファイルを書き出したい場合など、HTMLファイルの位置から見た画像ファイルへの相対パスを知りたいことがある。このようなときはPathRelativePathToを利用する。

...が、この関数は引数として相対パスを格納するバッファサイズが指定できないなど今後変更されそうな雰囲気を匂わせているAPIでもある。そのためここでは手作業で変換する関数を作成した。と、もっともらしく書きたいが実際はコードを書いたときにPathRelativePathToの存在を知らなかっただけだったりもする。開発で利用するならMicrosoftが用意しているので誤動作の心配がないPathRelativePathToを利用した方がいいだろう。

ちなみにこの関数もPathRelativePathToも、相対パスで表現できないパスが与えられた場合は処理に失敗する。

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


//
//	相対パスの取得
//
//Aから見たBへの相対パスを返す。
//ドライブが異なるなど相対パス化できないときは関数は失敗する。
//パスはフルパスで渡すこと!
//一般的にはAPIとして用意されているPathRelativePathToを利用する
//
bool	GetRelatedPath(CAtlString* pstrRelatedPath,LPCTSTR pszPathA,bool bPathAisFolder,LPCTSTR pszPathB,bool bPathBisFolder)
{
	int			nFind;
	CAtlString	strPathA;
	CAtlString	strPathB;

	if(pstrRelatedPath == NULL)
		return	false;
	*pstrRelatedPath = _T("");
	if(pszPathB == NULL || pszPathB == NULL)
		return	false;

	strPathA = pszPathA;
	strPathB = pszPathB;
	if(strPathA.GetLength() == 0 || strPathA.GetLength() == 0)
		return	false;

	if(bPathAisFolder == false)
	{
		//パスAからファイル名部分を取り除き、フォルダ名にする(末尾は「¥」ではない)
		nFind = strPathA.ReverseFind(_T('\\'));
		if(nFind < 0)
			return	false;			//どんなファイルパスでも最低1つは「¥」があるはず
		strPathA = strPathA.Left(nFind);
	}

	if(bPathBisFolder == false)
	{
		//パスBからファイル名部分を取り除き、フォルダ名にする(末尾は「¥」ではない)
		nFind = strPathB.ReverseFind(_T('\\'));
		if(nFind < 0)
			return	false;			//どんなファイルパスでも最低1つは「¥」があるはず
		strPathB = strPathB.Left(nFind);
	}

	if(strPathA.GetLength() == 0 || strPathA.GetLength() == 0)
		return	false;

	nFind = strPathB.Find(strPathA);
	if(nFind == 0)
	{
		//以下のような関係だった場合
		//PATHA=c:\\aaa\\bbb\\aa.txt
		//PATHB=c:\\aaa\\bbb\\ccc\\aa.txt
		//結果=ccc/aa.txt

		*pstrRelatedPath = pszPathB;
		*pstrRelatedPath = pstrRelatedPath->Right(pstrRelatedPath->GetLength() - strPathA.GetLength() - 1);
		return	true;
	}

	while(1)
	{
		//以下のような関係だった場合
		//PATHA=c:\\aaa\\bbb\\ccc\\aa.txt
		//PATHB=c:\\aaa\\bbb\\aa.txt
		//結果=../aa.txt
		// ↑この場合はwhileの1度目のループで結果が取得できる
		//
		//PATHA=c:\\aaa\\bbb\\ccc\\aa.txt
		//PATHB=c:\\aaa\\bbb\\ddd\\aa.txt
		//結果=../ddd/aa.txt
		// ↑この場合はwhileの2度目のループで結果が取得できる

		nFind = strPathA.Find(strPathB);
		if(nFind == 0)
		{
			*pstrRelatedPath = pszPathB;
			*pstrRelatedPath = pstrRelatedPath->Right(pstrRelatedPath->GetLength() - strPathB.GetLength() - 1);

			strPathA = pszPathA;
			strPathA = strPathA.Right(strPathA.GetLength() - strPathB.GetLength() - 1);
			nFind = strPathA.Replace(_T("\\"),_T("\\"));

			int		i;
			for(i = 0; i < nFind; i++)
				*pstrRelatedPath = _T("..\\") + *pstrRelatedPath;

			return	true;
		}

		nFind = strPathB.ReverseFind(_T('\\'));
		if(nFind < 0)
			break;
		strPathB = strPathB.Left(nFind);
	}

	return	false;
}





void	Test(void)
{
	bool		ret;
	CAtlString	strPath;
	ret = GetRelatedPath(&strPath,_T("c:\\aaa\\bbb\\ccc\\aa.txt"),false,_T("c:\\aaa\\bbb\\ddd\\aa.txt"),false);

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

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

2つのパス間の相対パスを取得する

例えば画像ファイルを一覧できるHTMLファイルを書き出したい場合など、HTMLファイルの位置から見た画像ファイルへの相対パスを知りたいことがある。このようなときはPathRelativePathToを利用する。

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


//
//	相対パスの取得
//
//Aから見たBへの相対パスを返す。
//ドライブが異なるなど相対パス化できないときは関数は失敗する。
//
bool	GetRelatedPath(CAtlString* pstrRelatedPath,LPCTSTR pszPathA,bool bPathAisFolder,LPCTSTR pszPathB,bool bPathBisFolder)
{
	size_t	nLen;
	BOOL	ret;
	TCHAR*	pszPath;

	if(pstrRelatedPath == NULL)
		return	false;
	*pstrRelatedPath = _T("");
	if(pszPathA == NULL || pszPathB == NULL)
		return	false;

	nLen = max(::_tcslen(pszPathA),::_tcslen(pszPathB));
	nLen *= 2;		//変換結果の方が長くなる可能性があるので×2している

	pszPath = new TCHAR[nLen];
	if(pszPath == NULL)
		return	false;

	//本当はFILE_ATTRIBUTE_DIRECTORYかそうでないかで引数を渡さなければいけない
	ret = ::PathRelativePathTo(pszPath,pszPathA,(bPathAisFolder ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL),pszPathB,(bPathBisFolder ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL));

	if(ret)
		*pstrRelatedPath = pszPath;

	delete	pszPath;

	return	ret ? true : false;
}





void	Test(void)
{
	bool		ret;
	CAtlString	strPath;
	ret = GetRelatedPath(&strPath,_T("c:\\aaa\\bbb\\ccc\\aa.txt"),false,_T("c:\\aaa\\bbb\\ddd\\aa.txt"),false);

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

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

2006年11月28日

開発対象OSはWindows Vista/XP/2000で十分

仕事でソフト開発をしていると必ず出てくるのが動作環境。特にWindowsのバージョンをどれくらい古いものまでサポートするかだ。結論から言うとWindows XP(SP2)とWindows Vistaで動けば問題ない。余裕があればWindows 2000も動作保証するかな?という程度で十分なはずだ。

企業向けソフトの開発の現場では「企業、特に市場規模が大きい中小企業じゃWindows 98を使っているところもまだまだ多いからWindows 9x系はサポートしないとダメだよ。」と耳にすることがある。確かに中小企業ではWindows 98は多く使われている。また潜在的な市場も大きい。

しかしどうだろう?いまだにWindows 98を使っているような企業が新しく開発したソフトに興味を持つだろうか?ソフトに投資するだろうか?
よっぽどそのソフトに魅力があるか、開発後の売込みがうまくない限り無理だろう。開発したソフトに興味を持つのは新しい技術などの"変化"を求める企業であり、そういう企業では今の時代最低でもWindows XPは導入していると考えられるからだ。
またWindows 9x系を動作保証に入れる場合は、該当するOSで動作をテストする必要がある。今はVirtual PCも無料化されWindows XP上でWindows 95や98を走らせることも簡単にでき、テストもはかどる環境は整っている。しかしサービスパックの有無、OSR1、2、各種あるInternet Explorerのバージョン、アクティブデスクトップ使用の有無...確認しなければいけないケースが非常に多く、「Windows 9x系対応」という一言をパッケージに印刷したいがためにかかるコストはかなりなものになる。


ちなみに私のホームページへのアクセス履歴(2006年10月)を見てみると...

■WIndows系プラットフォーム
os_access01.gif

■OS全体
os_access02.gif

※履歴はGoogle Analyticsで取得したものです。この分析はかなり解析もれが生じていることがあります

となっていた。

Windowsの中で見るとWindows 98を含めたWindows 9x系は1300アクセスにも届かない程度。パーセンテージにして3%しかない。この数は全体のアクセスでみたときのMacintoshとほぼ同じ割合になっている。やはりダントツに多いのは80%を占めるWindows XP、そして13%のWindows 2000となっている。

このようなOSの割合から見て「Windows XPが多いからWindows 98の使用率はもうすでに非常に低い!」とは言えない。このデータはInternet上で集計したものだからであり、Windows 98はどちらかというとInternetやLANなどのネットワークに接続せず、企業内部で利用されていることが多いからだ。また、「私のホームページへのアクセス」という偏った母体集団に基づいていることもある。
とは言うもののWindows XP系(NT系)への移行は確実に起きている。今後、新たにソフトを開発する場合は、特別な理由がない限りWindows 9x系は切り捨て、Windows 2000やXP、そして新しいVistaへの動作保証をするだけでいいだろう。

Windows 9x系を利用しない場合は比較的容易にソフトを多言語化できる。Windows 98系サポートへ開発時間を注ぐよりも、むしろ多言語化に力を注ぎ「世界中で使えるソフト」をめざした方が潜在的な市場規模も大きいのではないだろうか?

TCHARとかLPCTSTR、LPTSTRって何???

Windowsプログラミングからは切っても切り離せないのが、TCHARと、LPCTSTR、LPTSTRなどのTCHAR系列の型。結論から書くとこれはソースコードをユニコードと非ユニコード両対応にするための型だ(何のことだかサッパリ分からないって?こういうプログラミングの授業じゃ習わない意味不明な拡張が多いからWindowsプログラミングはややこしいんですよね)。

何も考えずに

・TCHAR = char
・LPCTSTR = const char*
・LPTSTR = char*

と頭の中で置き換えて使っている人も多いのではないだろうか?実際、この置き換え方はそう大きく間違えたものではない。

CHARは文字型を意味しているし、STRは文字列型を、LPは*(ポインタ)、LPCのCはconstを示している。問題は残った「T」という文字だ。これは...私にも分かりません(かなり無責任ですが本当に分かりません)。

分かっていることは、例えばTCHARは以下のように定義されているということです。

#ifdef UNICODE
	typedef WCHAR    TCHAR;
#else
	typedef char     TCHAR;
#endif

このことから「UNICODE」が定義されているとTCHARはWCHAR、定義されていないと「char」と同一になります。つまり、前に挙げた

・TCHAR = char
・LPCTSTR = const char*
・LPTSTR = char*

というのは「UNICODE」が定義されていないときの置き換えということになり、「UNICODE」が定義されているときには

・TCHAR = WCHAR
・LPCTSTR = const WCHAR*
・LPTSTR = WCHAR*

という関係になります。

くどいようですが「UNICODE」を定義しなければ、TCHARはcharと同義なわけです。よく分からないTCHARやWCHARという型の意味は放っておいて、charであれば馴染みやすい型であり、扱いやすいと思う人も多いでしょう。

では、「UNICODE」を定義しないようにするにはどうすればいいかと言うと...

例えば「MFCアプリケーションウィザード」では、「アプリケーションの種類」の設定項目の中に
「ユニコードライブラリを使用する」というチェックボックスがあります。このチェックを外せばOKです。
tchar01.gif


既にプロジェクトができている場合は、「プロジェクト」メニューの「プロパティ」からプロジェクトの設定画面を開き、左側のツリーで「構成プロパティ」の「全般」を選択します。そして「文字セット」を「マルチバイト文字セットを使用する」に変更すればOKです。
tchar02.gif

これらの設定を行えば、

・TCHAR = char
・LPCTSTR = const char*
・LPTSTR = char*

というように置き換えて利用して問題ありません。

本当はTCHARとchar、WCHARの違いをきちんと把握してプログラミングするべきです。しかし上のように置き換えてTCHAR=char!と考えても上記のプロジェクトの設定さえきちんとしていれば困ることはまずありません。「多言語プログラミング」や「ユニコード」などに興味を覚えたら「TCHARは「UNICODE」を定義するとWCHARになる」ということを思い出して再びTCHARについて勉強してみてください(今後きちんとTCHARの使い方などについての説明をしたいと思います)。
TCHARなどのことをきちんと知っている方がこの文章を読んでいたら...ごめんなさい。あまりお怒りにならず「変なことを書いてるなぁ」と笑い飛ばしていただければと思います。