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);
}
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);
}
ブラウザーのお気に入りを右クリックしてプロパティを見ると、そのURLを表示した回数を知ることができる。この表示回数をプログラム的に取得するにはGetUrlCacheEntryInfoを利用する。
エラーのときやURLを表示したことがなければ関数はfalseを返す。
#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);
}
最近のパソコンはほとんどが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);
}
■はじめに
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をダウンロード
■以下は画面例です




アプリケーションをインストールするときにプログラムメニューやデスクトップ上にショートカットを生成したいことがある。そのような場合は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を追加したいことがある。そのような場合は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を取り出したいことがある。そのような場合は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();
}
例えば「現在アクセスしているホームページへのショートカットを自動的にお気に入りに登録するアプリケーションを作りたい」などの場合、ホームページのタイトルをそのままファイル名として使うと、タイトル内にファイル名として禁止されている文字が入っている場合に誤動作する危険がある。
このようなときにファイル名に使いたい文字列をこの関数に通すと自動的に禁止された文字列が全角文字に置換されるので誤動作の可能性を減らすことができる。
#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が取得されるなど)
#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の「お気に入り」メニューにある「お気に入りの整理」を選択すると表示されるウインドウはshdocvw.dllの中に実装されている。単独でも動作する関数なので自分のアプリケーションから利用することができる。
//
// 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関数から利用できる。
#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);
}
GetIfTableを利用するとパソコンに認識されている全てのネットワークカードのMACアドレス、役割、接続速度などを調べてることができる。この関数は残念ながらWindows 95はサポートしていないため、動作はWindows 98以降となる。2007年に発売のWindows Vistaでもサポートされている。
このクラスを作るに当たって初めはWindows 95でも動作するように実装を進めたのだが...そうすんなりといかず、NetBIOSやSNMPでも環境によってはMACアドレスが取得できなかったため諦めた。
#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);
}
例えば画像ファイルを一覧できる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);
}
例えば画像ファイルを一覧できる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);
}
仕事でソフト開発をしていると必ず出てくるのが動作環境。特に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全体

※履歴は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系サポートへ開発時間を注ぐよりも、むしろ多言語化に力を注ぎ「世界中で使えるソフト」をめざした方が潜在的な市場規模も大きいのではないだろうか?
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です。
![]()
既にプロジェクトができている場合は、「プロジェクト」メニューの「プロパティ」からプロジェクトの設定画面を開き、左側のツリーで「構成プロパティ」の「全般」を選択します。そして「文字セット」を「マルチバイト文字セットを使用する」に変更すればOKです。
![]()
これらの設定を行えば、
・TCHAR = char
・LPCTSTR = const char*
・LPTSTR = char*
というように置き換えて利用して問題ありません。
本当はTCHARとchar、WCHARの違いをきちんと把握してプログラミングするべきです。しかし上のように置き換えてTCHAR=char!と考えても上記のプロジェクトの設定さえきちんとしていれば困ることはまずありません。「多言語プログラミング」や「ユニコード」などに興味を覚えたら「TCHARは「UNICODE」を定義するとWCHARになる」ということを思い出して再びTCHARについて勉強してみてください(今後きちんとTCHARの使い方などについての説明をしたいと思います)。
TCHARなどのことをきちんと知っている方がこの文章を読んでいたら...ごめんなさい。あまりお怒りにならず「変なことを書いてるなぁ」と笑い飛ばしていただければと思います。