前回までにATL/WTLのアプリケーションウイザードのタブ型ビューMDIをひな形にタブブラウザーを作ってきた。だんだんと機能も増えて少しずつ形になってきた。
しかし機能が増えるに従ってソースコードがごちゃごちゃとしてきて大変なことになっている。作り上げる目標もなく、思いつきのままに実装をしているため初期に実装したものの今では不要なものまででてきている。
今回は前回までに作成したソースコードを少し整理する。
stdafx.cppへの追い出し
ソースコードの中に「インターネットショートカットを作る」「インターネットショートカットからURLを取り出す」というような汎用的に使える関数がいくつかある。それらをコピペでstdafx.cppへ移動する。
これらは一度作ったら編集する可能性も少ないので関数宣言をstdafx.hに置いた。#include "atlmisc.h" #include "atlstr.h" bool CreateInternetShortcut(LPCTSTR pszURL, LPCTSTR pszFileName); HRESULT IUnknown_Exec(IUnknown* pUnk,const GUID *pguidCmdGroup,DWORD nCmdID,DWORD nCmdExecOpt,VARIANTARG *pvaIn,VARIANTARG *pvaOut); BOOL SafeSHGetSpecialFolderPath(HWND hwndOwner,LPTSTR lpszPath,int nFolder,BOOL fCreate); bool GetInternetShortcut(LPCTSTR pszFile,CAtlString& strURL); HBITMAP GetHBitmapFromFile(LPCTSTR pszFile); bool DoOrganizeFavDlg(HWND hWnd,LPTSTR pszFolder=NULL);
重複コードの関数化(その1)ページビューの取得
MainFrm.h内では、
{
int nPage;
CTabBrowser100View* pView = NULL;
nPage = m_view.GetActivePage();
if(nPage >= 0)
pView = (CTabBrowser100View*)m_view.GetPageData(nPage);
if(pView)
{
....
}
}
というようにビュークラスへのポインタを取得する処理がいたるところに散在している。
同じようなソースをその都度書いていると、処理を変更したいときに変更漏れが生じたり、処理そのものを間違えてバグを生み出したりする。もちろん重複コードがたくさんあるとそれだけでソースコードの可読性も落ちる。
そのためこの部分を、、、
//指定されたタブのビュークラスを取得
CTabBrowser100View* GetPageView(int nPage)
{
if(nPage < 0)
return NULL;
return (CTabBrowser100View*)m_view.GetPageData(nPage);
}
//アクティブページのビュークラスを取得
CTabBrowser100View* GetActivePageView(void)
{
return GetPageView(m_view.GetActivePage());
}
という2つの関数を用意して、
{
CTabBrowser100View* pView;
pView = GetActivePageView();
if(pView)
{
....
}
}
と簡潔に書けるようにする。これで重複するソースコードをいくらか削除できる。
重複コードの関数化(その2)タブの新規作成
タブが開いていない状態で「お気に入り」が選択されたとき、リンクを「新しいウインドウで開く」ときなどでタブを作る処理は
pView = new CTabBrowser100View(&m_view); //タブビューのポインタを渡しておく
pView->Create(m_view, rcDefault, _T("about:blank"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
m_view.AddPage(pView->m_hWnd, _T("Document"),-1,pView);
というようなコードをその都度書いてきた。これを関数化して重複コードを減らす。
//タブの新規作成
//nPosはタブを追加する場所。nPos<0(もしくはnPos=m_view.GetPageCount())で一番後ろ、
//そのほかの数値はCTabView::InsertPageにそのまま渡す
//
//戻ったポインタは自動削除されるため、deleteの必要なし
//
CTabBrowser100View* CreateNewTab(LPCTSTR pszURL,LPCTSTR pszTitle,int nPos=-1,int nImage=-1)
{
bool ret;
CTabBrowser100View* pView;
pView = new CTabBrowser100View(&m_view); //タブビューのポインタを渡しておく
if(pView == NULL)
return NULL;
//ビューウインドウ生成
if(pszURL == NULL || *pszURL == NULL)
pView->Create(m_view, rcDefault, _T("about:blank"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
else
pView->Create(m_view, rcDefault, pszURL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
if(pView->IsWindow() == FALSE)
{
delete pView;
return NULL; //ビューウインドウ生成失敗
}
//タブにビューを割り当てる
if(nPos < 0)
nPos = m_view.GetPageCount();
if(pszTitle)
ret = m_view.InsertPage(nPos,pView->m_hWnd,pszTitle,nImage,pView);
else
ret = m_view.InsertPage(nPos,pView->m_hWnd,_T(""),nImage,pView);
if(ret == false)
{
delete pView;
return NULL; //タブへの追加失敗
}
return pView;
}
この関数の実装によりCMainFrame内であれば、簡単にタブを作れるようになった。しかし、ビュークラスからタブを作りたい場合はこのままでは直接CreateNewTabを呼びだせない。
そのためWM_DNP_CREATENEWTABという独自メッセージを利用してビューからのメッセージによりタブを作れるようにする。今回は、、、
class CTabBrowser100View;
// CMainFrame::nDnpCreateNewTab() 用の情報クラス
class CDnpCreateNewTabInfo
{
public:
CAtlString strURL;
CAtlString strTitle;
int nImage;
int nPos;
CTabBrowser100View* pView;
CDnpCreateNewTabInfo()
{
strURL = _T("about:blank");
pView = NULL;
nImage = -1;
nPos = -1;
}
};
//WM_DNP_CREATENEWTABによりタブの作成
//WPARAMはCDnpCreateNewTabInfoへのポインタを渡す
LRESULT OnDnpCreateNewTab(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CDnpCreateNewTabInfo* pInfo = (CDnpCreateNewTabInfo*)wParam;
if(pInfo == NULL)
return 0;
pInfo->pView = CreateNewTab(pInfo->strURL,pInfo->strTitle,pInfo->nPos,pInfo->nImage);
return 0;
}
というような雰囲気で実装した。
ビュークラスの単純機能をほかのクラスに追い出す
CTabBrowser100View の中には
void Back(void)
{
if(_pIWebBrowser2)
_pIWebBrowser2->GoBack();
}
void Next(void)
{
if(_pIWebBrowser2)
_pIWebBrowser2->GoForward();
}
のようにIWebBrowser2のメソッドを呼び出すだけの単純な関数が多くある(また今後も増えるだろう)。
それらをCIEUtilityというクラスに追い出して、CTabBrowser100View派生元に追加する。
class CIEUtility
{
protected:
CComPtr<IWebBrowser2> _pIWebBrowser2;
public:
//URLを開く
bool Navigate(LPCTSTR pszURL)
{
HRESULT hr;
if(_pIWebBrowser2 == NULL || pszURL == NULL || *pszURL == NULL)
return false;
hr = _pIWebBrowser2->Navigate(CComBSTR(pszURL),&CComVariant(),&CComVariant(),&CComVariant(),&CComVariant());
return SUCCEEDED(hr) ? true : false;
}
void Back(void)
{
if(_pIWebBrowser2)
_pIWebBrowser2->GoBack();
}
(・・・省略・・・)
//「戻る」や「進む」ための履歴がIEにあるかどうかのチェック
bool IsButtonEnable(bool& bBack,bool& bNext)
{
HRESULT hr;
DWORD dwCount;
CComPtr<ITravelLogStg> pITravelLogStg;
CComPtr<IServiceProvider> pIServiceProvider;
bBack = false;
bNext = false;
if(_pIWebBrowser2)
_pIWebBrowser2->QueryInterface(&pIServiceProvider);
if(pIServiceProvider)
pIServiceProvider->QueryService(SID_STravelLogCursor,IID_ITravelLogStg,(void**)&pITravelLogStg);
if(pITravelLogStg == NULL)
return false;
hr = pITravelLogStg->GetCount(TLEF_RELATIVE_BACK,&dwCount);
if(dwCount)
bBack = true;
if(FAILED(hr))
return false;
hr = pITravelLogStg->GetCount(TLEF_RELATIVE_FORE,&dwCount);
if(FAILED(hr))
return false;
if(dwCount)
bNext = true;
return true;
}
};
外部からビュークラスのIWebBrowser2に直接アクセスしないようにする
現状のCMainFrame::OnDnpChangeFocus() では
//フォーカス変更時処理
LRESULT OnDnpChangeFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
BOOL bHasFocus = (BOOL)wParam; //アドレスバーなどがフォーカスを受け取っていたらTRUE
CTabBrowser100View* pView;
pView = GetActivePageView();
if(pView == NULL)
return 0;
if(bHasFocus)
{
//アドレスバーなどIE以外がフォーカスを受け取った
CComPtr<IOleInPlaceObject> pIOleInPlaceObject;
pView->_pIWebBrowser2->QueryInterface(&pIOleInPlaceObject);
if(pIOleInPlaceObject)
pIOleInPlaceObject->UIDeactivate(); //IEのUIを無効化
}
return 0;
}
のようにビュークラス内の_pIWebBrowser2に直接アクセスしている。このような実装は行儀がわるいので修正する。
ここではCIEUtility::SetFocusChange()を作り、それを呼ぶようにした。
必要ないコードを削除
ビュークラス内のCTabBrowser100View::GetBrowserCtrlとCTabBrowser100View::PreTranslateMessage()は必要ないので削除する。
GetBrowserCtrlの削除に伴ってCTabBrowser100View::_pIWebBrowser2のインターフェースを確保する部分がなくなったのでしその処理をOnCreate()内で行っておく。
//Create関数のオーバーライド。内部でIEの取得、接続などを行う
HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
DWORD dwStyle = 0, DWORD dwExStyle = 0,
_U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
{
HWND hWnd;
hWnd = __super::Create(hWndParent,rect,szWindowName,dwStyle,dwExStyle,MenuOrID,lpCreateParam);
if(hWnd == NULL)
return NULL; //Create失敗
QueryControl(&_pIWebBrowser2); //_pIWebBrowser2にこのビューに関連づいているIEをセットする
if(_pIWebBrowser2 == NULL)
return hWnd; //WebBrowser取得失敗
Advise(_pIWebBrowser2); //IEとの接続
return hWnd;
}
各クラス用のヘッダーファイルを作成する
今まではMainFrm.h内に複数のクラスを記述するなど、どんどん増やしていた。これをCFavoritesMenuは「FavoritesMenu.h」内に、、、というようにヘッダーファイルに分ける。
(その他。。。?)
現状ではクラス内のpublic、private、protectedの分類やコメントが全然ない。
そこまで整理しておこうと思ったのだがだんだん面倒になってきたのでこのくらいでソースコードの整理は終わりにした。
