Visual Studio 2010対応の日本語版WTL 8.1.9127 アプリケーションウィザード
WTLはライセンスがCPLのオープンソースプロジェクトとして公開されている。そのためおそらく勝手に日本語化したものを配布しても問題ないと思い用意しました。ライセンス的に問題があるようでしたらお知らせください。削除します。
先月リリースされたVisual Studio 2010に対応しました。
(2010年5月8日現在最新版のWTLを元にしています)
WTLはライセンスがCPLのオープンソースプロジェクトとして公開されている。そのためおそらく勝手に日本語化したものを配布しても問題ないと思い用意しました。ライセンス的に問題があるようでしたらお知らせください。削除します。
先月リリースされたVisual Studio 2010に対応しました。
(2010年5月8日現在最新版のWTLを元にしています)
![]()
前回の作業で、「タブ」をバックグラウンドを開けるようになった。しかしその副作用で、タブの開き方によって「Googleツールバー」などのIE用ツールバーが表示されなかったり、下端が欠けるなどの不具合が生じた。今回はそれらを修正する。
//hWndParentはCDummyWnd(Googleツールバーの親となるウインドウハンドル)
//clsidIDeskBandはGoogleツールバーのCLSIDを指定する
bool CreateIEToolbar(IWebBrowser2* pIWebBrowser2,HWND hWndParent,IDeskBand** ppIDeskBand,const IID& clsidIDeskBand,bool bVisible)
{
HRESULT hr;
if(ppIDeskBand == NULL || pIWebBrowser2 == NULL)
return false;
if(*ppIDeskBand)
{
ATLASSERT(0);
(*ppIDeskBand)->Release();
*ppIDeskBand = NULL;
}
hr = ::CoCreateInstance(clsidIDeskBand,NULL,CLSCTX_INPROC_SERVER,IID_IDeskBand,(void**)ppIDeskBand);
if(FAILED(hr) || *ppIDeskBand == NULL)
return false;
CComPtr<IObjectWithSite> pIObjectWithSite;
(*ppIDeskBand)->QueryInterface(IID_IObjectWithSite,(void**)&pIObjectWithSite);
//デスクバンドをホスト用クラスに割り当てる
{
CComPtr<IIEToolbar> pIIEToolbar;
pIIEToolbar = new CComObject<CIEToolbar>;
pIIEToolbar->put_hwnd(hWndParent);
if(pIWebBrowser2)
pIIEToolbar->put_IWebBrowser2(pIWebBrowser2);
if(pIObjectWithSite)
hr = pIObjectWithSite->SetSite(pIIEToolbar);
if(SUCCEEDED(hr) && bVisible)
hr = (*ppIDeskBand)->ShowDW(TRUE); //表示
}
DESKBANDINFO sDeskBandInfo;
//別に必要ないが情報をたくさん取得
::ZeroMemory(&sDeskBandInfo,sizeof(DESKBANDINFO));
sDeskBandInfo.dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL | DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR;
hr = (*ppIDeskBand)->GetBandInfo(0,DBIF_VIEWMODE_NORMAL,&sDeskBandInfo);
CRect rect; //■変更 不要なサイズ指定が残っていたので変更
//rebar情報の設定
{
HWND hWndRebar;
UINT nIndex;
hWndRebar = ::GetParent(hWndParent);
nIndex = -1;
::SendMessage(hWndParent,WM_DNP_GETRBINDEX,(WPARAM)&nIndex,NULL);
if(nIndex != -1 && hWndRebar)
{
//ShowDW(TRUE)を実行していないとGetBandInfoでサイズが
//取得できないので、bVisible=falseのときはリバーの大きさを
//調整しない
if(bVisible) //■条件を追加
{
REBARBANDINFO info;
//MinSizeのみをリバーへ設定。本来ならもっと情報を設定すべき
::ZeroMemory(&info,sizeof(REBARBANDINFO));
info.cbSize = sizeof(REBARBANDINFO);
info.fMask = RBBIM_CHILDSIZE;
::SendMessage(hWndRebar,RB_GETBANDINFO,nIndex,(LPARAM)&info);
info.cyMinChild = sDeskBandInfo.ptMinSize.y;
info.cyChild = sDeskBandInfo.ptMinSize.y;
info.cxMinChild = sDeskBandInfo.ptMinSize.x;
::SendMessage(hWndRebar,RB_SETBANDINFO,nIndex,(LPARAM)&info);
}
//位置調整
::SendMessage(hWndRebar,RB_GETRECT,nIndex,(LPARAM)&rect);
::SendMessage(hWndParent,WM_SIZE,SIZE_RESTORED,MAKELPARAM(rect.Width(),rect.Height()));
}
}
return true;
}
//タブの新規作成
//nPosはタブを追加する場所。nPos<0(もしくはnPos=m_view.GetPageCount())で一番後ろ、
//そのほかの数値はCTabView::InsertPageにそのまま渡す
//
//戻ったポインタは自動削除されるため、deleteの必要なし
//
//bOpenInBackground=trueならバックグラウンドでタブを開く
//
CTabBrowser100View* CreateNewTab(LPCTSTR pszURL,LPCTSTR pszTitle,int nPos=-1,int nImage=-1,bool bOpenInBackground=false)
{
bool ret;
CTabBrowser100View* pView;
//bOpenInBackgroundが指定されていても現在タブが1つもない状態ならbOpenInBackgroundをfalseに強制設定する
if(bOpenInBackground && m_view.GetPageCount() == 0)
bOpenInBackground = false;
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,bOpenInBackground);
else
ret = m_view.InsertPage(nPos,pView->m_hWnd,_T(""),nImage,pView,bOpenInBackground);
if(ret == false)
{
delete pView;
return NULL; //タブへの追加失敗
}
//IEツールバーの生成
{
size_t i;
size_t nSize;
nSize = _acAvailableIEToolbar.GetCount();
for(i = 0; i < nSize; i++)
{
if(_acAvailableIEToolbar[i].pwndDummy)
pView->AddIEToolbar(*_acAvailableIEToolbar[i].pwndDummy,! bOpenInBackground); //■変更 IEツールバー生成
}
}
return pView;
}
![]()
これでビルド/実行するとIE用ツールバーが再びきちんと表示されるようになった。
これまでの作業で
・「お気に入り」メニューを開いているときにメニューバーの「お気に入り」がハイライトにならない
・「お気に入り」メニューでフォーカスが変わるため、メニューを開きながらメニューバーを順に開けない
・「お気に入り」メニューを開くとステータスバーのデザインが壊れる
・「お気に入り」メニュー内の項目選択でステータスバーへ解説表示ができない
・「お気に入り」メニュー内の項目の有効/無効を切りかえれない
・「編集」メニュー内の「コピー」などの各項目をアドレスバーなどIEコントロール以外で利用できない
・スクリプトエラーダイアログの抑制がNavigate直後に働かない
・Navigate直後のタブ名が「about:blank」になる
・アドレスバーの表示URLが長い場合、先頭から表示されずに後方が表示される
・「戻る」「進む」ボタンにIE履歴を選択するためのドロップメニューがない
・IEツールバーを利用するのに毎回手動で表示する必要がある(終了時の状態を保存していない)
・IEツールバーを新規表示するときに必ずリバーが1行表示される
・リンクの右クリックで「新しいタブで開く」でタブを開き、「新しいウインドウで開く」では新しくウインドウを生成するようにすべき
・ページ読み込み中を示す表示がない
・アドレスバーがオートコンプリートによるURL入力に対応していない
・メニュー項目で利用できないものが多い
・実装上のバグからUNICODEビルドしかできない
などなどざっと挙げただけどもまだ不具合や未実装な必要最低限の機能が多く残っている。
しかしだんだん疲れてきたのでしばらくタブブラウザーからは離れて保留にします。お疲れ様でした。また気が向くか要望があれば再開するかもしれませんが。。。
![]()
今回は新しいページをバックグラウンドのタブで開けるようにする。
現状では「ファイルメニュー」の「新規作成」でも、リンクを右クリックして現れるメニューから「新しいウインドウで開く」を選択したときも、新しく開いたタブにフォーカスがあたり、それまで開いていたタブから新しいタブに切り替わってしまう。
それを
・「ファイルメニュー」の「新規作成」で開いたタブはアクティブで
・リンクを右クリックして現れるメニューから「新しいウインドウで開く」から開いたタブはバックグラウンドで
開くようにする。
//■追加
class CTabViewEx : public CTabView
{
public:
//WTLのCTabView::InsertPageからソースをコピー&改変
//
//bOpenInBackground=trueなら新しいタブにフォーカスを与えない(バックグラウンドで開く)
//
bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL,bool bOpenInBackground=false)
{
return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData, bOpenInBackground);
}
//WTLのCTabView::InsertPageからソースをコピー&改変
//
//bOpenInBackground=trueなら新しいタブにフォーカスを与えない(バックグラウンドで開く)
//
bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL,bool bOpenInBackground=false)
{
ATLASSERT(::IsWindow(m_hWnd));
ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage));
CTabViewEx* pT = static_cast<CTabViewEx*>(this); //■変更 CTabViewExでキャスト
int cchBuff = lstrlen(lpstrTitle) + 1;
LPTSTR lpstrBuff = NULL;
ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
if(lpstrBuff == NULL)
return false;
SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
if(lpstrTabText == NULL)
return false;
pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
SetRedraw(FALSE);
TCITEMEXTRA tcix = { 0 };
tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
tcix.tciheader.pszText = lpstrTabText;
tcix.tciheader.iImage = nImage;
tcix.tvpage.hWnd = hWndView;
tcix.tvpage.lpstrTitle = lpstrBuff;
tcix.tvpage.pData = pData;
int nItem = m_tab.InsertItem(nPage, tcix);
if(nItem == -1)
{
delete [] lpstrBuff;
SetRedraw(TRUE);
return false;
}
if(bOpenInBackground == false || GetPageCount() == 1) //■条件を追加
{
//新しいタブをアクティブにする
SetActivePage(nItem);
pT->OnPageActivated(m_nActivePage);
}
if(GetPageCount() == 1)
pT->ShowTabControl(true);
pT->UpdateLayout();
SetRedraw(TRUE);
RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
return true;
}
};
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler
,protected CIShellMenuPopup //「お気に入り」メニュー用クラス。publicの必要なし
,protected CIEToolbarManager //IEツールバー管理クラス。publicの必要なし
{
CAddressBarCtrl _wndAddressBar; // アドレスバー用コンボボックス
CTabViewEx m_view; //■変更
CCommandBarCtrl2 m_CmdBar;
public:
//■変更(bOpenInBackgroundへの対応)
//タブの新規作成
//nPosはタブを追加する場所。nPos<0(もしくはnPos=m_view.GetPageCount())で一番後ろ、
//そのほかの数値はCTabView::InsertPageにそのまま渡す
//
//戻ったポインタは自動削除されるため、deleteの必要なし
//
//bOpenInBackground=trueならバックグラウンドでタブを開く
//
CTabBrowser100View* CreateNewTab(LPCTSTR pszURL,LPCTSTR pszTitle,int nPos=-1,int nImage=-1,bool bOpenInBackground=false)
{
bool ret;
CTabBrowser100View* pView;
//■追加
//bOpenInBackgroundが指定されていても現在タブが1つもない状態ならbOpenInBackgroundをfalseに強制設定する
if(bOpenInBackground && m_view.GetPageCount() == 0)
bOpenInBackground = false;
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,bOpenInBackground); //■変更
else
ret = m_view.InsertPage(nPos,pView->m_hWnd,_T(""),nImage,pView,bOpenInBackground); //■変更
if(ret == false)
{
delete pView;
return NULL; //タブへの追加失敗
}
//IEツールバーの生成
{
size_t i;
size_t nSize;
nSize = _acAvailableIEToolbar.GetCount();
for(i = 0; i < nSize; i++)
{
if(_acAvailableIEToolbar[i].pwndDummy)
pView->AddIEToolbar(*_acAvailableIEToolbar[i].pwndDummy,bOpenInBackground); //IEツールバー生成
}
}
return pView;
}
#pragma once
class CTabBrowser100View;
// CMainFrame::nDnpCreateNewTab() 用の情報クラス
class CDnpCreateNewTabInfo
{
public:
CAtlString strURL;
CAtlString strTitle;
int nImage;
int nPos;
bool bOpenInBackground; //■追加
CTabBrowser100View* pView;
CDnpCreateNewTabInfo()
{
strURL = _T("about:blank");
pView = NULL;
nImage = -1;
nPos = -1;
bOpenInBackground = false; //■追加
}
};
//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->bOpenInBackgroundを追加
pInfo->pView = CreateNewTab(pInfo->strURL,pInfo->strTitle,pInfo->nPos,pInfo->nImage,pInfo->bOpenInBackground);
return 0;
}
//新しいウインドウで開く前に呼ばれる処理
//新しいタブで開く
void __stdcall OnNewWindow2(IDispatch **ppDisp,VARIANT_BOOL *Cancel)
{
CDnpCreateNewTabInfo cInfo;
cInfo.bOpenInBackground = true; //■追加 リンクを新しいタブで開くときはバックグラウンドで開く
//新しいタブを作る
GetTopLevelWindow().SendMessage(WM_DNP_CREATENEWTAB,(WPARAM)&cInfo);
if(cInfo.pView == NULL || cInfo.pView->_pIWebBrowser2 == NULL)
return;
//新しいタブで開くようにIEコントロールにポインタを渡す
cInfo.pView->_pIWebBrowser2->put_RegisterAsBrowser(VARIANT_TRUE); //これしなくても開くようになっているのはなぜ?IE8から?一応実行しておく
cInfo.pView->_pIWebBrowser2->get_Application(ppDisp);
}
しかし今回の修正によりリンクを新しいタブで開いたときに「Googleツールバー」の下端が欠けてしまい、きちんと表示されなくなった。次回はその問題を修正する。
これまでの作業でまたソースコードが煩雑化し、まったく利用されていない部分も多く残っている。今回は機能の追加などはせずに少しソースコードを整理する。
「お気に入り」メニューはIShellMenuにより表示しているが、IShellMenuによる実装の前にはフォルダからインターネットショートカットを検索して直にメニューを生成/表示していた。それらの処理はもう利用しなくなったので削除する。
まず「FavoritesMenu.h」と「DnpFolderMenu.h」の2つのファイルをプロジェクトから取り除く。プロジェクトからの削除に加えて、ファイル自体も削除してしまってかまわない。
そして「MainFrm.h」の中にある「#include "FavoritesMenu.h"」や「CHAIN_MSG_MAP_MEMBER(_cFovMenu)」、関数「CMainFrame::OnMenuSelect」や「CMainFrame::OnFavorite」を削除する。
「CShellMenuCallback」を独立したヘッダーファイルに移動する。
そしてCMainFrame::PopupMenuの引数に「HWND hWnd」を追加する。この関数の内部処理では「m_hWnd」を参照する部分を「hWnd」に変えて、ウインドウを引数で渡せるようにする。bool PopupMenu(int nX,int nY,HWND hWnd)
#define FAVORITE_MENU_POSITION 4 //メインウインドウ用リソースでの「お気に入り」メニューの位置
そして「CMainFrame::PopupMenu」、「CCommandBarCtrl2::DoPopupMenu」、「CMainFrame::OnMenuSelectForPopup」内の「4」というメニュー位置の指定をすべて「FAVORITE_MENU_POSITION」に置き換える。
最後に「お気に入り」関連処理を「CMainFrame」から離した場所に移動する。
独立したヘッダーファイルにクラス「CIShellMenuPopup」を新しく作り、
「CMainFrame::PopupMenu」、「_pIMenuBand」、
「CMainFrame::TranslatePopupMessage」をそのクラスの中に移動する。そして「CMainFrame」の派生元に「CIShellMenuPopup」を追加する。
「CMainFrame」内に実装しているインストールされているIEツールバーを検索する処理などを離した場所に移動する。
独立したヘッダーファイルにクラス「CIEToolbarManager」を新しく作り、CMainFrame内の以下のメンバ関数、クラス、変数をその中に移動する。
・CDummyWnd
・CAvailableIEToolbar
・_acAvailableIEToolbar、
・CLSIDtoUIString
・RegEnumKeyName
・EnumAvailableIEToolbar、
そして「CMainFrame」の派生元に「CIEToolbarManager」を追加する。
・CAmbientControl
・CTabBrowser100View::CIEToolbarInfo
を独立したヘッダーファイルへ移動する。
・CTabBrowser100View::GetZone
を「CIEUtility」へ移動する。
「CTabBrowser100View」と「CMainFrame」内のメンバ関数やメンバ変数にprotectedやpublicの指定を追加する。
・「CTabBrowser100View」内の変数の記述位置を前後に移動
・その他、少しだけコメントを追加
今回は以上の細かなソースコード整理をした。次回は現在のページを開いたままリンクをバックグラウンドのタブで開けるようにする。
「お気に入り」メニューの表示方法をIShellMenuに変えてから、「お気に入りに追加」などの項目が表示されなくなった。今回はこれらの項目を再び表示、使えるようにする。
IShellMenuへのメニュー追加はIShellMenu::SetMenuで行える。ここにリソースから読みだしたHMENUを渡すだけでいい。内部でDestroyMenuされるため、m_Cmdbar.GetMenu()などの引数は渡せない。
bool PopupMenu(int nX,int nY)
{
LPITEMIDLIST pidl;
CComPtr<IShellFolder> pIShellFolder;
if(_pIMenuBand)
{
//すでにメニューが開いていたら、まずはそのメニューを閉じる
CComPtr<IOleCommandTarget> pIOleCommandTarget;
_pIMenuBand->QueryInterface(&pIOleCommandTarget);
if(pIOleCommandTarget)
pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
}
//「お気に入り」フォルダのPIDLとIShellFolderを取得
{
CComPtr<IShellFolder> pIShellFolderDesktop;
::SHGetDesktopFolder(&pIShellFolderDesktop);
if(pIShellFolderDesktop == NULL)
return false;
::SHGetSpecialFolderLocation(NULL,CSIDL_FAVORITES,&pidl);
pIShellFolderDesktop->BindToObject(pidl,NULL,IID_IShellFolder,(void **)&pIShellFolder);
}
CComPtr<IDeskBand> pIDeskBand;
//PIDLとIShellFolderの示すIShellMenu(IDeskBand)を取得
{
HRESULT hr;
CRegKey cRegOrder;
CComPtr<IShellMenu> pIShellMenu;
CComPtr<IDeskBar> pIDeskBar;
CComPtr<IMenuPopup> pIMenuPopup;
//並び順用レジストリ。必要ならCreateしてもいいが今回は「お気に入り」のみなのでopen
cRegOrder.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Favorites"));
::CoCreateInstance(CLSID_MenuBand,NULL,CLSCTX_INPROC_SERVER,IID_IShellMenu,(void**)&pIShellMenu);
//■追加
//「お気に入りに追加」などのメニュー項目をメニューリソースから追加
//IShellMenu::Initializeよりも先にIShellMenu::SetMenuを使わないとだめなことに注意
if(pIShellMenu)
{
HMENU hMenu;
HMENU hSubMenu = NULL;
hMenu = ::LoadMenu(::_Module.m_hInst,MAKEINTRESOURCE(IDR_MAINFRAME));
if(hMenu)
hSubMenu = ::GetSubMenu(hMenu,4); //■メニュー位置固定
if(hSubMenu)
pIShellMenu->SetMenu(hSubMenu,m_hWnd,SMSET_TOP);
//if(hMenu)
// ::DestroyMenu(hMenu); //不要
}
//普通のメニュー項目に関する情報
HRESULT OnSMC_GETINFO(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
SMINFO* pSmInfo = (SMINFO*)lParam;
if(pSmInfo->dwMask & SMIM_FLAGS)
pSmInfo->dwFlags |= SMIF_DROPCASCADE | SMIF_TRACKPOPUP;
//■追加
if(pSmInfo->dwMask & SMIM_ICON)
pSmInfo->iIcon = -1; //追加メニューのアイコンを非表示にする。iIcon==0のときのみ-1にすべき?
return S_OK;
}
![]()
またIShellMenuを使う前にお気に入りが開いたことを検出するために「dummy」というメニュー項目を用意していた。それはもう不要なのでリソースから消しておく。
![]()
これでビルド/実行すると「お気に入りの整理」などのメニュー項目が追加され、利用できるようになった。
だんだんソースコードが荒れてきて利用されていない関数も多くなってきた。そのため次回は再びソースコードの整理をする。
![]()
今回は画像表示やスクリプト実行のON/OFFなどを制御するダウンロードコントロールが正常に働くようにする。
//■追加
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
HRESULT hr = E_FAIL;
CComPtr<IDispatch> pIDispatch;
//ダウンロードコントロール用のInvoke
if(_pIAmbientControl)
_pIAmbientControl->QueryInterface(&pIDispatch);
if(pIDispatch)
hr = pIDispatch->Invoke(dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
if(SUCCEEDED(hr))
return hr;
//IEコントロールのイベント用のInvoke
hr = IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>::Invoke(dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
if(SUCCEEDED(hr))
return hr;
//デフォルト(CAxHostWindow)用のInvoke
return CComObject<CAxHostWindow>::Invoke(dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
}
//IInputObjectSite
STDMETHOD(OnFocusChangeIS)(IUnknown *punkObj,BOOL fSetFocus)
{
//■内部処理削除
//HRESULT hr = E_FAIL;
//CComPtr<IOleInPlaceObject> pIOleInPlaceObject;
//if(_pIDispatch)
// _pIDispatch->QueryInterface(&pIOleInPlaceObject);
//if(pIOleInPlaceObject == NULL)
// return S_OK;
//if(fSetFocus)
//{
// hr = pIOleInPlaceObject->UIDeactivate();
// pIOleInPlaceObject->InPlaceDeactivate();
//}
//else
//{
// HWND hWnd;
// hr = pIOleInPlaceObject->GetWindow(&hWnd);
// if(SUCCEEDED(hr) && ::IsWindow(hWnd))
// ::ShowWindow(hWnd,SW_NORMAL);
//}
return S_OK;
}
![]()
これでビルド/実行すると画像表示のON/OFFなどのダウンロード関連機能が再び使えるようになった。
次回は「お気に入り」メニューに「お気に入りの整理」などのメニュー項目を追加する。
![]()
前回はフォーカス関連のバグ修正のためビューウインドウをCAxWindowからCAxHostWindowに移行した。今回はIEコントロールがフォーカスを受け取ったことを検出して、正常に処理されるようにする。
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
//■追加
{
CTabBrowser100View* pView;
pView = GetActivePageView();
if(pView && pView->PreTranslateMessage(pMsg))
return TRUE;
}
if(TranslatePopupMessage(pMsg->hwnd,pMsg->message,pMsg->wParam,pMsg->lParam,pMsg))
return TRUE;
if(_wndAddressBar.IsChild(::GetFocus()) == FALSE) //アドレスバーにフォーカスがあるときはPreTranslateMessageを回さない(アドレスバーでCtrl+Cなどを使えるようにするため)
{
if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
return TRUE;
}
return m_view.PreTranslateMessage(pMsg);
}
![]()
そしてビューウインドウ内にPreTranslateMessageを追加する。ビューに対してWM_FORWARDMSGを投げると、CAxHostWindow内で自動的にTranslateAcceleatorが呼び出される。
//■追加
//「Internet Explorer_Server」をサブクラス化
class CSubclassIEWnd : public CWindowImpl<CSubclassIEWnd>
{
CTabBrowser100View* _pView;
public:
CSubclassIEWnd(CTabBrowser100View* pView)
{
_pView = pView;
}
BEGIN_MSG_MAP(CSubclassIEWnd)
if(uMsg == WM_SETFOCUS || uMsg == WM_KILLFOCUS)// || uMsg == WM_MOUSEACTIVATE)
{
if(_pView)
_pView->SendMessage(uMsg,wParam,lParam);
}
if(uMsg == WM_DESTROY)
{
UnsubclassWindow();
}
END_MSG_MAP()
};
//■追加
CSubclassIEWnd _wndIEServer;
public:
DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName()) //CAxHostWindow::ではなくCAxWindow::のままなことに注意
CTabBrowser100View(CTabView* pTabView)
: _wndReflect(this)
,_wndIEServer(this)
{
_pTabView = pTabView;
_bPageError = false;
}
//■追加
BOOL PreTranslateMessage(MSG* pMsg)
{
if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
(pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
{
return FALSE;
}
BOOL bRet = FALSE;
//IOleInPlaceActiveObject::TranslateAccelerator(pMsg)の呼び出し
if(pMsg->hwnd == m_hWnd || IsChild(pMsg->hwnd))
bRet = (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
return bRet;
}
//HTMLページが読み終わったときに呼ばれる処理
void _stdcall OnDocumentComplete(IDispatch* pDisp, VARIANT* vURL)
{
if(pDisp != _pIWebBrowser2)
return; //フレームなどの読み込み終了は無視
//■追加
//IEをサブクラス化
if(_wndIEServer.IsWindow() == FALSE && _pIWebBrowser2)
{
HWND hWnd;
HRESULT hr = E_FAIL;
CComPtr<IDispatch> pIDispatch;
CComPtr<IOleWindow> pIOleWindow;
_pIWebBrowser2->get_Document(&pIDispatch);
if(pIDispatch)
pIDispatch->QueryInterface(&pIOleWindow);
if(pIOleWindow)
hr = pIOleWindow->GetWindow(&hWnd);
if(SUCCEEDED(hr))
_wndIEServer.SubclassWindow(hWnd);
}
ChangeUIHandler(); //UIHandlerを変更する
HICON hIcon;
CAtlString strFile;
BEGIN_MSG_MAP(CTabBrowser100View)
MESSAGE_HANDLER(WM_DNP_SCRIPTERROR, OnDnpScriptError)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_DNP_SHOWSCRIPTERROR, OnShowScriptError)
MESSAGE_HANDLER(WM_DNP_SHOWPRIVACYREPORT, OnShowPrivacyReport)
MESSAGE_HANDLER(WM_DNP_SHOWPSECURITYREPORT, OnShowSecurityReport)
MESSAGE_HANDLER(WM_DNP_SHOWZONECONFIGURE, OnShowZoneConfigure)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) //■追加
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) //■追加
CHAIN_MSG_MAP(CAxHostWindow)
END_MSG_MAP()
private:
//■追加
LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
ATLTRACE("●OnSetFocus\n");
SetFocusChange(true);
bHandled = FALSE;
return 0;
}
//■追加
LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
ATLTRACE("●OnKillFocus\n");
SetFocusChange(false);
bHandled = FALSE;
return 0;
}
//アドレスバー用クラス
class CAddressBarCtrl : public CWindowImpl<CAddressBarCtrl,CComboBoxEx>
{
//■内部処理削除
class CInnerComboBox : public CWindowImpl<CInnerComboBox>
{
public:
BEGIN_MSG_MAP(CInnerComboBox)
//MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
//MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
END_MSG_MAP()
////マウスによるフォーカス取得時の処理
//LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
//{
// GetTopLevelParent().SendMessage(WM_DNP_CHANGEFOCUS,(WPARAM)TRUE); //メインウインドウへフォーカスを受け取ったことを通知
// bHandled = FALSE;
// return 0;
//}
//LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
//{
// GetTopLevelParent().SendMessage(WM_DNP_CHANGEFOCUS,(WPARAM)FALSE); //メインウインドウへフォーカスを失ったことを通知
// bHandled = FALSE;
// return 0;
//}
};
//■変更
//IEコントロールがフォーカスを受け取ったときbGetFocus=true、
//IEコントロールがフォーカスを失ったときbGetFocus=falseとして呼び出す
bool SetFocusChange(bool bGetFocus)
{
if(_pIWebBrowser2 == NULL)
return false;
if(bGetFocus)
{
}
else
{
CComPtr<IOleInPlaceObject> pIOleInPlaceObject;
if(_pIWebBrowser2)
_pIWebBrowser2->QueryInterface(&pIOleInPlaceObject);
if(pIOleInPlaceObject)
pIOleInPlaceObject->UIDeactivate();
}
return true;
}
![]()
これでとりあえず、フォーカス関連のバグは修正された。しかし今回のバグ修正作業によりスクリプトの実行や画像の表示のON/OFF機能が使えなくなってしまっている。
次回はその部分を修正する。
![]()
今回から数回に分けてフォーカス関連のバグ修正をする。現状ではドロップダウンリストが正常に選択できなくなったり、アドレスバーにフォーカスを置いたままだとIEコントロールの描画が更新されないなどなどの不具合が多い。これらをなんとか治す。
これまで、ビューウインドウはATL/WTLアプリケーションウイザードの生成したCAxWindow派生のものを利用していた。このままだと修正が大変なので、派生元をCAxHostWindowに変更する。
CAxWindowはIEコントロールなどのActiveXコントロールをホストするための簡単な仕組みが備わっているのみだが、CAxHostWindowはかなり多くの実装がされていて便利なためだ。
class CTabBrowser100View : public CComObject<CAxHostWindow> //■変更
,public IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>
,public CIEUtility
{
//■追加
//CAxHostWindowの場合、メッセージハンドラが使えなくなるので
//このウインドウをサブクラス化してメッセージハンドラが使えるようにする
class CReflectWnd : public CWindowImpl<CReflectWnd>
{
CTabBrowser100View* _pView;
public:
CReflectWnd(CTabBrowser100View* pView)
{
_pView = pView;
}
BEGIN_MSG_MAP(CReflectWnd)
CHAIN_MSG_MAP_MEMBER((*_pView))
END_MSG_MAP()
};
CReflectWnd _wndReflect; //■追加
public:
DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName()) //CAxHostWindow::ではなくCAxWindow::のままなことに注意
CTabBrowser100View(CTabView* pTabView)
: _wndReflect(this) //■追加
{
_pTabView = pTabView;
_bPageError = false;
}
BEGIN_SINK_MAP(CTabBrowser100View)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW2, OnNewWindow2)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_STATUSTEXTCHANGE, OnStatusTextChange)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOWNLOADCOMPLETE, OnDownloadComplete)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_COMMANDSTATECHANGE, OnCommandStateChange)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN, OnDownloadBegin)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_PROGRESSCHANGE, OnProgressChange)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_PROPERTYCHANGE, OnPropertyChange)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_TITLECHANGE, OnTitleChange)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, OnNavigateComplete2)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR, OnNavigateError)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_PRIVACYIMPACTEDSTATECHANGE, OnPrivacyImpactedStateChange)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW3, OnNewWindow3)
SINK_ENTRY_EX(SINKID_EVENTS, DIID_DWebBrowserEvents2, DISPID_SETSECURELOCKICON, OnSetSecureLockIcon)
END_SINK_MAP()
BEGIN_MSG_MAP(CTabBrowser100View)
MESSAGE_HANDLER(WM_DNP_SCRIPTERROR, OnDnpScriptError)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_DNP_SHOWSCRIPTERROR, OnShowScriptError)
MESSAGE_HANDLER(WM_DNP_SHOWPRIVACYREPORT, OnShowPrivacyReport)
MESSAGE_HANDLER(WM_DNP_SHOWPSECURITYREPORT, OnShowSecurityReport)
MESSAGE_HANDLER(WM_DNP_SHOWZONECONFIGURE, OnShowZoneConfigure)
CHAIN_MSG_MAP(CAxHostWindow) //■追加
END_MSG_MAP()
![]()
そしてCreate()の処理を変更する。CAxHostWindow::Create()は使わずに、CWindow::Createを、CAxWindow(CAxHostWindowではない)のクラス名を使って呼び出してウインドウ生成をする。
//■変更
//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;
//__super::ではなく、CWindow::のCreateをCAxWindow::GetWndClassName()指定で利用
hWnd = CWindow::Create(CAxWindow::GetWndClassName(), hWndParent, rect, szWindowName, dwStyle, dwExStyle, MenuOrID, lpCreateParam);
if(hWnd == NULL)
return NULL; //Create失敗
_wndReflect.SubclassWindow(m_hWnd); //メッセージマップが使えるようにこのウインドウをサブクラス化
//CWindow::Createで作れたコントロールをこのウインドウにアタッチ
{
CComPtr<IUnknown> pIUnknown;
AtlAxGetControl(m_hWnd,&pIUnknown);
if(pIUnknown)
AttachControl(pIUnknown,m_hWnd);
QueryControl(IID_IWebBrowser2,(void**)&_pIWebBrowser2); //_pIWebBrowser2にこのビューに関連づいているIEをセットする
if(_pIWebBrowser2 == NULL)
return hWnd; //WebBrowser取得失敗
//サイトも設定
pIUnknown = NULL;
QueryHost(&pIUnknown);
SetSite(pIUnknown);
}
Advise(_pIWebBrowser2); //IEとの接続
//jacascriptや画像ダウンロードのコントロール設定
{
CComPtr<IOleObject> pIOleObject;
CComObject<CAmbientControl>* pCAmbientControl;
CComPtr<IAxWinAmbientDispatchEx> pIAxWinAmbientDispatchEx;
//ダウンロードコントロールの準備
CComObject<CAmbientControl>::CreateInstance(&pCAmbientControl);
if(pCAmbientControl)
pCAmbientControl->QueryInterface(&_pIAmbientControl);
//■削除CAxHostWindowを使うのでセットしない
// //IEコントロールへのセット
// _pIWebBrowser2->QueryInterface(&pIOleObject);
// if(pIOleObject && pCAmbientControl)
// pIOleObject->SetClientSite(pCAmbientControl);
//デフォルトのダウンロードコントロールを設定
//本来ならゾーン変化をチェックしてゾーンに応じて決定すべき?
SetAmbientDownloadControl(DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS);
}
return hWnd;
}
//■追加
//CAxWindowのソースからコピー。ホスト取得用関数
template <class Q>
HRESULT QueryHost(Q** ppUnk)
{
return QueryHost(__uuidof(Q), (void**)ppUnk);
}
HRESULT QueryHost(REFIID iid, void** ppUnk)
{
ATLASSERT(ppUnk != NULL);
if (ppUnk == NULL)
return E_POINTER;
HRESULT hr;
*ppUnk = NULL;
CComPtr<IUnknown> spUnk;
hr = AtlAxGetHost(m_hWnd, &spUnk);
if (SUCCEEDED(hr))
hr = spUnk->QueryInterface(iid, ppUnk);
return hr;
}
![]()
これでとりあえず今回は終了だ。まだCAxWindowからCAxHostWindowに切り替えただけでバグは修正されていない。
次回はバグ修正のためにフォーカスがIEコントロールにあるのか、それともそれ以外にあるのかを検出する処理を実装する。
![]()
前回は「お気に入り」メニューをIShellMenuベースに置き換えた。今回はIShellMenu用のコールバックインターフェースを用意して、選択した「お気に入り」がビューで開くようにする。
//■追加
//IEツールバーをホストするためのインターフェース
MIDL_INTERFACE("50AFAFED-815E-4ec4-9A78-231C95958716")
IShellMenuHost : public IUnknown
{
public:
STDMETHOD (put_IShellFolder)(IShellFolder* pIShellFolder) = 0;
STDMETHOD (put_NotifyWindow)(HWND hWnd,UINT nMessageID) = 0;
};
//■追加
class CShellMenuCallback :
public CComObjectRootEx<CComSingleThreadModel>
,public IShellMenuHost
,public IShellMenuCallback
{
UINT _nNotifyMessage;
HWND _hWndNotify;
CComPtr<IShellFolder> _pIShellFolder;
public:
BEGIN_COM_MAP(CShellMenuCallback)
COM_INTERFACE_ENTRY(IShellMenuHost)
COM_INTERFACE_ENTRY(IShellMenuCallback)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
CShellMenuCallback()
{
_nNotifyMessage = 0;
_hWndNotify = NULL;
}
//
STDMETHOD (put_IShellFolder)(IShellFolder* pIShellFolder)
{
_pIShellFolder = NULL;
if(pIShellFolder == NULL)
return E_POINTER;
return pIShellFolder->QueryInterface(IID_IShellFolder,(void**)&_pIShellFolder);
}
STDMETHOD (put_NotifyWindow)(HWND hWnd,UINT nMessageID)
{
if(hWnd == NULL || ::IsWindow(hWnd) == FALSE || nMessageID == 0)
return E_FAIL;
_hWndNotify = hWnd;
_nNotifyMessage = nMessageID;
return S_OK;
}
HRESULT OnSMC_INITMENU(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_FALSE;
}
//メニューが作られた
//処理は不要
HRESULT OnSMC_CREATE(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_FALSE;
}
HRESULT OnSMC_EXITMENU(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
//普通のメニュー項目に関する情報
HRESULT OnSMC_GETINFO(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
SMINFO* pSmInfo = (SMINFO*)lParam;
if(pSmInfo->dwMask & SMIM_FLAGS)
pSmInfo->dwFlags |= SMIF_DROPCASCADE | SMIF_TRACKPOPUP;
return S_OK;
}
//IShellFolder項目に関する情報
//処理必要なし
HRESULT OnSMC_GETSFINFO(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_GETOBJECT(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
REFIID iid = (REFIID)wParam;
void** ppv = (void**)lParam;
//処理していない
return S_FALSE;
}
HRESULT OnSMC_GETSFOBJECT(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
REFIID iid = (REFIID)wParam;
void** ppv = (void**)lParam;
//処理していない
return S_FALSE;
}
//選択された項目を開く
HRESULT OnSMC_SFEXEC(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
TCHAR pszFile[MAX_PATH];
TCHAR pszPath[MAX_PATH];
int nFind;
CAtlString strName;
*pszPath = NULL;
::SHGetPathFromIDList(psmd->pidlItem,pszFile);
::SHGetPathFromIDList(psmd->pidlFolder,pszPath);
strName = pszFile;
nFind = strName.ReverseFind(_T('\\'));
if(nFind >= 0)
strName = strName.Right(strName.GetLength() - nFind);
strName = pszPath + strName;
//ウインドウへメッセージを送る
if(*pszPath && _hWndNotify && ::IsWindow(_hWndNotify) && _nNotifyMessage)
::SendMessage(_hWndNotify,_nNotifyMessage,(WPARAM)(LPCTSTR)strName,NULL);
return S_OK;
}
HRESULT OnSMC_SFSELECTITEM(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_REFRESH(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_DEMOTE(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_PROMOTE(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_DEFAULTICON(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_NEWITEM(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_CHEVRONEXPAND(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_DISPLAYCHEVRONTIP(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_SETSFOBJECT(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_SHCHANGENOTIFY(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_CHEVRONGETTIP(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT On(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
HRESULT OnSMC_SFDDRESTRICTED(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
return S_OK;
}
STDMETHOD(CallbackSM)(LPSMDATA psmd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch(uMsg)
{
// The callback is called to init a menuband
case SMC_INITMENU:
return OnSMC_INITMENU(psmd,uMsg,wParam,lParam);
case SMC_CREATE:
return OnSMC_CREATE(psmd,uMsg,wParam,lParam);
// The callback is called when menu is collapsing
case SMC_EXITMENU:
return OnSMC_EXITMENU(psmd,uMsg,wParam,lParam);
// The callback is called to return DWORD values
case SMC_GETINFO:
return OnSMC_GETINFO(psmd,uMsg,wParam,lParam);
// The callback is called to return DWORD values
case SMC_GETSFINFO:
return OnSMC_GETSFINFO(psmd,uMsg,wParam,lParam);
// The callback is called to get some object
case SMC_GETOBJECT:
return OnSMC_GETOBJECT(psmd,uMsg,wParam,lParam);
// The callback is called to get some object
case SMC_GETSFOBJECT:
return OnSMC_GETSFOBJECT(psmd,uMsg,wParam,lParam);
// The callback is called to execute an shell folder item
case SMC_SFEXEC:
return OnSMC_SFEXEC(psmd,uMsg,wParam,lParam);
// The callback is called when an item is selected
case SMC_SFSELECTITEM:
return OnSMC_SFSELECTITEM(psmd,uMsg,wParam,lParam);
// Menus have completely refreshed. Reset your state.
case SMC_REFRESH:
return OnSMC_REFRESH(psmd,uMsg,wParam,lParam);
// Demote an item
case SMC_DEMOTE:
return OnSMC_DEMOTE(psmd,uMsg,wParam,lParam);
// Promote an item, wParam = SMINV_* flag
case SMC_PROMOTE:
return OnSMC_PROMOTE(psmd,uMsg,wParam,lParam);
// Returns Default icon location in wParam, index in lParam
case SMC_DEFAULTICON:
return OnSMC_DEFAULTICON(psmd,uMsg,wParam,lParam);
// Notifies item is not in the order stream.
case SMC_NEWITEM:
return OnSMC_NEWITEM(psmd,uMsg,wParam,lParam);
// Notifies of a expansion via the chevron
case SMC_CHEVRONEXPAND:
return OnSMC_CHEVRONEXPAND(psmd,uMsg,wParam,lParam);
// S_OK display, S_FALSE not.
case SMC_DISPLAYCHEVRONTIP:
return OnSMC_DISPLAYCHEVRONTIP(psmd,uMsg,wParam,lParam);
// Called to save the passed object
case SMC_SETSFOBJECT:
return OnSMC_SETSFOBJECT(psmd,uMsg,wParam,lParam);
// Called when a Change notify is received. lParam points to SMCSHCHANGENOTIFYSTRUCT
case SMC_SHCHANGENOTIFY:
return OnSMC_SHCHANGENOTIFY(psmd,uMsg,wParam,lParam);
// Called to get the chevron tip text. wParam = Tip title, Lparam = TipText Both MAX_PATH
case SMC_CHEVRONGETTIP:
OnSMC_CHEVRONGETTIP(psmd,uMsg,wParam,lParam);
break;
// Called requesting if it's ok to drop. wParam = IDropTarget.
case SMC_SFDDRESTRICTED:
OnSMC_SFDDRESTRICTED(psmd,uMsg,wParam,lParam);
break;
default:
break;
}
return S_FALSE;
}
};
NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelectForPopup)
// MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
MESSAGE_HANDLER(WM_DNP_NAVIGATEFAVORITE, OnDnpNavigateFavorite) //■追加
MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify)
// COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite) //■削除
CHAIN_MSG_MAP_MEMBER(_cFovMenu)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
//■追加
LRESULT OnDnpNavigateFavorite(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bool ret;
CAtlString strURL;
LPCTSTR pszFile = (LPCTSTR)wParam;
if(pszFile == NULL || *pszFile == NULL)
return 0;
ret = GetInternetShortcut(pszFile,strURL);
if(ret == false)
return 0;
//タブとしてURLを開く
{
CTabBrowser100View* pView;
pView = GetActivePageView();
if(pView == NULL)
{
//新しいタブを作成
pView = CreateNewTab(_T("about:blank"),NULL);
}
//URLを開く
if(pView)
pView->Navigate(strURL);
}
return 0;
}
bool PopupMenu(int nX,int nY)
{
LPITEMIDLIST pidl;
CComPtr<IShellFolder> pIShellFolder;
if(_pIMenuBand)
{
//すでにメニューが開いていたら、まずはそのメニューを閉じる
CComPtr<IOleCommandTarget> pIOleCommandTarget;
_pIMenuBand->QueryInterface(&pIOleCommandTarget);
if(pIOleCommandTarget)
pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
}
//「お気に入り」フォルダのPIDLとIShellFolderを取得
{
CComPtr<IShellFolder> pIShellFolderDesktop;
::SHGetDesktopFolder(&pIShellFolderDesktop);
if(pIShellFolderDesktop == NULL)
return false;
::SHGetSpecialFolderLocation(NULL,CSIDL_FAVORITES,&pidl);
pIShellFolderDesktop->BindToObject(pidl,NULL,IID_IShellFolder,(void **)&pIShellFolder);
}
CComPtr<IDeskBand> pIDeskBand;
//PIDLとIShellFolderの示すIShellMenu(IDeskBand)を取得
{
HRESULT hr;
CRegKey cRegOrder;
CComPtr<IShellMenu> pIShellMenu;
CComPtr<IDeskBar> pIDeskBar;
CComPtr<IMenuPopup> pIMenuPopup;
//並び順用レジストリ。必要ならCreateしてもいいが今回は「お気に入り」のみなのでopen
cRegOrder.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Favorites"));
::CoCreateInstance(CLSID_MenuBand,NULL,CLSCTX_INPROC_SERVER,IID_IShellMenu,(void**)&pIShellMenu);
//■処理変更
if(pIShellMenu)
{
CComPtr<IShellMenuHost> pIShellMenuHost;
CComPtr<IShellMenuCallback> pIShellMenuCallback;
//コールバック生成
pIShellMenuCallback = new CComObject<CShellMenuCallback>;
if(pIShellMenuCallback)
pIShellMenuCallback->QueryInterface(&pIShellMenuHost);
if(pIShellMenuHost)
{
pIShellMenuHost->put_IShellFolder(pIShellFolder);
pIShellMenuHost->put_NotifyWindow(m_hWnd,WM_DNP_NAVIGATEFAVORITE);
}
//IShellMenuの初期化
pIShellMenu->Initialize(pIShellMenuCallback,-1,ANCESTORDEFAULT,SMINIT_TOPLEVEL | SMINIT_VERTICAL);
#ifndef SMSET_USEBKICONEXTRACTION
#define SMSET_USEBKICONEXTRACTION 0x00000008
#endif
//メニューへのフォルダ割り当て
hr = pIShellMenu->SetShellFolder(pIShellFolder,pidl,cRegOrder.m_hKey,SMSET_BOTTOM | SMSET_USEBKICONEXTRACTION); //以下も指定したいが値が不明 | SMSET_HASEXPANDABLEFOLDERS)
if(SUCCEEDED(hr))
cRegOrder.Detach(); //レジストリのCloseはIShellMenuに任せる
ILFree(pidl);
pidl = NULL;
}
if(pIShellMenu)
pIShellMenu->QueryInterface(&pIMenuPopup);
if(pIMenuPopup)
pIMenuPopup->QueryInterface(&pIDeskBar);
if(pIDeskBar)
pIDeskBar->QueryInterface(&pIDeskBand);
if(pIDeskBand == NULL)
return false;
}
//IShellMenu(IDeskBand)をポップアップメニューで表示
{
POINTL ptl;
GUID rclsid;
CComPtr<IUnknown> pIUnknown;
CComPtr<IBandSite> pIBandSite;
CComPtr<IMenuPopup> pIMenuPopup;
//メニュー用のDeskBar「Menu Desk Bar」
::CLSIDFromString(L"{ECD4FC4F-521C-11D0-B792-00A0C90312E1}",&rclsid);
::CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pIUnknown);
//メニュー用のBandSite
::CoCreateInstance(CLSID_MenuBandSite,NULL,CLSCTX_INPROC_SERVER,IID_IBandSite,(void**)&pIBandSite);
if(pIUnknown)
pIUnknown->QueryInterface(&pIMenuPopup); //メニュー用DeskBarだからIMenuPopupを持っている
if(pIMenuPopup)
pIMenuPopup->SetClient(pIBandSite); //DeskBarにIBandSiteを割り当て
if(pIBandSite)
pIBandSite->AddBand(pIDeskBand); //IBandSiteに表示したいIShellMenuを割り当てる
//位置を指定してメニュー表示
ptl.x = nX;
ptl.y = nY;
if(pIMenuPopup)
pIMenuPopup->Popup(&ptl,NULL,MPPF_POS_MASK | MPPF_FORCEZORDER | MPPF_RIGHT | MPPF_BOTTOM);
}
//メニューの_pIMenuBandを保存
{
_pIMenuBand = NULL;
pIDeskBand->QueryInterface(&_pIMenuBand);
if(_pIMenuBand == NULL)
return false;
}
return true;
}
#define WM_DNP_NAVIGATEFAVORITE (WM_APP + 13) //■追加
![]()
これで「お気に入り」を選択したときにそのURLがビューで開くようになった。また「お気に入り」の表示順序をドラッグアンドドロップにより変更できるようになった。
次回は少しフォーカス関連のバグ修正をする。
これまでは「お気に入り」フォルダから保存されているインターネットショートカットを列挙して独自にメニューを生成/表示していた。これをInternet Explorerの「お気に入り」や「スタート」メニューと同じようにシェルメニュー(IShellMenu)ベースのものに変える。
Windows Vista以降であればITrackShellMenuを利用することで非常に簡単にIShellMenuをメニューとして表示できる。しかし2009年4月現在Windows Vistaはまだまだ普及率が悪い。そのためITrackShellMenuは使わずに力技でIShellMenuを表示することにした。
CMainFrameにIShellMenuを利用した「お気に入り」表示関数を追加する。
CComPtr<IMenuBand> _pIMenuBand;
bool PopupMenu(int nX,int nY)
{
LPITEMIDLIST pidl;
CComPtr<IShellFolder> pIShellFolder;
if(_pIMenuBand)
{
//すでにメニューが開いていたら、まずはそのメニューを閉じる
CComPtr<IOleCommandTarget> pIOleCommandTarget;
_pIMenuBand->QueryInterface(&pIOleCommandTarget);
if(pIOleCommandTarget)
pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
}
//「お気に入り」フォルダのPIDLとIShellFolderを取得
{
CComPtr<IShellFolder> pIShellFolderDesktop;
::SHGetDesktopFolder(&pIShellFolderDesktop);
if(pIShellFolderDesktop == NULL)
return false;
::SHGetSpecialFolderLocation(NULL,CSIDL_FAVORITES,&pidl);
pIShellFolderDesktop->BindToObject(pidl,NULL,IID_IShellFolder,(void **)&pIShellFolder);
}
CComPtr<IDeskBand> pIDeskBand;
//PIDLとIShellFolderの示すIShellMenu(IDeskBand)を取得
{
HRESULT hr;
CRegKey cRegOrder;
CComPtr<IShellMenu> pIShellMenu;
CComPtr<IDeskBar> pIDeskBar;
CComPtr<IMenuPopup> pIMenuPopup;
//並び順用レジストリ。必要ならCreateしてもいいが今回は「お気に入り」のみなのでopen
cRegOrder.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Favorites"));
::CoCreateInstance(CLSID_MenuBand,NULL,CLSCTX_INPROC_SERVER,IID_IShellMenu,(void**)&pIShellMenu);
if(pIShellMenu)
{
//とりあえずCallback指定していない
pIShellMenu->Initialize(NULL,-1,ANCESTORDEFAULT,SMINIT_TOPLEVEL | SMINIT_VERTICAL);
#ifndef SMSET_USEBKICONEXTRACTION
#define SMSET_USEBKICONEXTRACTION 0x00000008
#endif
//メニューへのフォルダ割り当て
hr = pIShellMenu->SetShellFolder(pIShellFolder,pidl,cRegOrder.m_hKey,SMSET_BOTTOM | SMSET_USEBKICONEXTRACTION); //以下も指定したいが値が不明 | SMSET_HASEXPANDABLEFOLDERS)
if(SUCCEEDED(hr))
cRegOrder.Detach(); //レジストリのCloseはIShellMenuに任せる
ILFree(pidl);
pidl = NULL;
}
if(pIShellMenu)
pIShellMenu->QueryInterface(&pIMenuPopup);
if(pIMenuPopup)
pIMenuPopup->QueryInterface(&pIDeskBar);
if(pIDeskBar)
pIDeskBar->QueryInterface(&pIDeskBand);
if(pIDeskBand == NULL)
return false;
}
//IShellMenu(IDeskBand)をポップアップメニューで表示
{
POINTL ptl;
GUID rclsid;
CComPtr<IUnknown> pIUnknown;
CComPtr<IBandSite> pIBandSite;
CComPtr<IMenuPopup> pIMenuPopup;
//メニュー用のDeskBar「Menu Desk Bar」
::CLSIDFromString(L"{ECD4FC4F-521C-11D0-B792-00A0C90312E1}",&rclsid);
::CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IUnknown,(void**)&pIUnknown);
//メニュー用のBandSite
::CoCreateInstance(CLSID_MenuBandSite,NULL,CLSCTX_INPROC_SERVER,IID_IBandSite,(void**)&pIBandSite);
if(pIUnknown)
pIUnknown->QueryInterface(&pIMenuPopup); //メニュー用DeskBarだからIMenuPopupを持っている
if(pIMenuPopup)
pIMenuPopup->SetClient(pIBandSite); //DeskBarにIBandSiteを割り当て
if(pIBandSite)
pIBandSite->AddBand(pIDeskBand); //IBandSiteに表示したいIShellMenuを割り当てる
//位置を指定してメニュー表示
ptl.x = nX;
ptl.y = nY;
if(pIMenuPopup)
pIMenuPopup->Popup(&ptl,NULL,MPPF_POS_MASK | MPPF_FORCEZORDER | MPPF_RIGHT | MPPF_BOTTOM);
}
//メニューの_pIMenuBandを保存
{
_pIMenuBand = NULL;
pIDeskBand->QueryInterface(&_pIMenuBand);
if(_pIMenuBand == NULL)
return false;
}
return true;
}
COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelectForPopup) //■追加
// MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect) //■削除
MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify)
COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
CHAIN_MSG_MAP_MEMBER(_cFovMenu)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
//■追加
LRESULT OnMenuSelectForPopup(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = FALSE;
UINT nMenuItem = LOWORD(wParam);
HMENU hMenu = (HMENU)lParam;
if(HIWORD(wParam) & MF_POPUP)
return 0;
if(nMenuItem != 4) //メニュー位置
return 0;
RECT rect;
m_CmdBar.GetItemRect(nMenuItem,&rect);
m_CmdBar.MapWindowPoints(NULL,&rect);
PopupMenu(rect.left,rect.bottom);
return 0;
}
//■追加
class CCommandBarCtrl2 : public CCommandBarCtrlImpl<CCommandBarCtrl2>
{
public:
BEGIN_MSG_MAP(CCommandBarCtrl2)
CHAIN_MSG_MAP(__super)
ALT_MSG_MAP(1) // Parent window messages
CHAIN_MSG_MAP_ALT(__super,1)
ALT_MSG_MAP(2) // MDI client window messages
CHAIN_MSG_MAP_ALT(__super,2)
ALT_MSG_MAP(3) // Message hook messages
CHAIN_MSG_MAP_ALT(__super,3)
END_MSG_MAP()
void DoPopupMenu(int nIndex, bool bAnimate)
{
if(nIndex == 4) //メニュー位置固定
{
HWND hWnd = ::GetAncestor(m_hWnd,GA_ROOT);
::SendMessage(hWnd,WM_MENUSELECT,MAKEWPARAM(nIndex,MF_HILITE),(LPARAM)GetMenu().m_hMenu);
return;
}
__super::DoPopupMenu(nIndex,bAnimate);
}
};
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler
{
CAddressBarCtrl _wndAddressBar; // アドレスバー用コンボボックス
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
CTabView m_view;
CCommandBarCtrl2 m_CmdBar;
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
//■追加
if(TranslatePopupMessage(pMsg->hwnd,pMsg->message,pMsg->wParam,pMsg->lParam,pMsg))
return TRUE;
if(_wndAddressBar.IsChild(::GetFocus()) == FALSE) //アドレスバーにフォーカスがあるときはPreTranslateMessageを回さない(アドレスバーでCtrl+Cなどを使えるようにするため)
{
if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
return TRUE;
}
return m_view.PreTranslateMessage(pMsg);
}
//■追加
//
//ポップアップメニューのメッセージ処理
//
//pMsg == NULLのときは開いているメニューを閉じるべきかどうかのチェックも併せておこなう
//(PreTranslateMessageではpMsgも指定する)
//
bool TranslatePopupMessage(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam,MSG* pMsg=NULL)
{
if(_pIMenuBand == NULL)
return false;
HRESULT hr;
LRESULT ret = 0;
MSG msg;
if(pMsg == NULL)
{
::ZeroMemory(&msg,sizeof(MSG));
msg.hwnd = hWnd;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
}
else
{
msg = *pMsg;
}
hr = _pIMenuBand->IsMenuMessage(&msg);
if(hr == S_OK)
hr = _pIMenuBand->TranslateMenuMessage(&msg,&ret);
if(hr == S_OK)
return true;
//メニューを閉じるべきかどうかのチェック
//もう少しチェック項目を増やした方がいい。。。
if(pMsg == NULL
&& (hr == E_FAIL
|| (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
|| uMsg == WM_INITMENUPOPUP
|| uMsg == WM_SIZE
|| uMsg == WM_MOVE
|| uMsg == WM_MOVING
|| uMsg == WM_INITMENU
|| 0))
{
//メニューを閉じる
CComPtr<IOleCommandTarget> pIOleCommandTarget;
_pIMenuBand->QueryInterface(&pIOleCommandTarget);
if(pIOleCommandTarget)
pIOleCommandTarget->Exec(&CLSID_MenuBand,22,0,NULL,NULL);
}
return false;
}
BEGIN_MSG_MAP(CMainFrame)
//■追加
{
//「お気に入り」メニュー用TranslateMessage
if(TranslatePopupMessage(hWnd,uMsg,wParam,lParam))
return TRUE;
}
MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
![]()
これでビルド/実行するとIShellMenuにより「お気に入り」が表示されるようになった。
まだ実装が不十分なため「お気に入り」メニューが開いているのにコマンドバーの「お気に入り」がハイライト状態になっていない、ドラッグアンドドロップによる「お気に入り」の移動ができない、メニュー表示がクラシックスタイルなどなど機能が足りない。
しかし「お気に入り」の並び順がInternet Explorerでの表示と同じだったり、アイコンの読み込みがバックグラウンドで行われる、TIPSが表示されるなど「お気に入り」メニューとしてそこそこ使える状態になっている。
次回は選択した「お気に入り」のURLをタブとして開くようにする。