EternalWindows
WebBrowser コントロール / レバーコントロールの実装

今回は、レバーコントロールの実装について説明します。 レバーコントロールは、ブラウザのこの部分に相当します。

レバーコントロールには、上記のように4つのウインドウが乗ることがあります。 これらのウインドウの実体は全てツールバーであり、 1番目のウインドウはアドレスバーの役割を果たします。 2番目のウインドウはOpenSearchの役割を果たし、任意の検索プロバイダによる検索をサポートします。 3番目のウインドウはタブの役割を果たし、これを選択することで表示するページを切り替えることができます。 4番目のウインドウはDLLとして実装されたバンドオブジェクトであり、Googleツールバーなどが代表的です。 バンドオブジェクトを表示するためには、タブが作成されている必要があります。

レバーコントロールはCRebarMgrで識別され、CWebBrowserContainerによって使用されます。

void CRebarMgr::Create(HWND hwndParent)
{
	TBBUTTON tbButton[] = {
		{HIST_BACK, ID_BUTTON_BACK, 0 , BTNS_BUTTON | BTNS_DROPDOWN, {0}, 0, 0},
		{HIST_FORWARD, ID_BUTTON_FORWARD, 0, BTNS_BUTTON | BTNS_DROPDOWN, {0}, 0, 0}
	};
	int           nCount = sizeof(tbButton) / sizeof(tbButton[0]);
	REBARBANDINFO bandInfo;
	DWORD         dwStyle;

	m_hwndRebar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |  WS_CLIPCHILDREN | CCS_NODIVIDER | RBS_BANDBORDERS, 0, 0, 0, 0, hwndParent, (HMENU)ID_REBAR, NULL, NULL);

	m_hwndToolbarAddress = CreateToolbarEx(m_hwndRebar, WS_CHILD | WS_VISIBLE | CCS_NORESIZE | CCS_NODIVIDER, ID_ADDRESS_TOOLBAR, 0, HINST_COMMCTRL, IDB_HIST_SMALL_COLOR, tbButton, nCount, 0, 0, 0, 0, sizeof(TBBUTTON));
	dwStyle = (DWORD)SendMessage(m_hwndToolbarAddress, TB_GETSTYLE, 0, 0) | TBSTYLE_FLAT;
	SendMessage(m_hwndToolbarAddress, TB_SETSTYLE, 0, (LPARAM)dwStyle);
	SendMessage(m_hwndToolbarAddress, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS);
	
	m_hwndComboBoxAddress = CreateWindowEx(0, WC_COMBOBOXEX, TEXT(""), WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, 80, 3, 450, 140, m_hwndToolbarAddress, (HMENU)ID_ADDRESS_COMBOBOX, NULL, NULL);
	m_hwndEditAddress = (HWND)SendMessage(m_hwndComboBoxAddress, CBEM_GETEDITCONTROL, 0, 0);
	SHAutoComplete(m_hwndEditAddress, SHACF_URLMRU);
	InitializeTypedURLs();

	bandInfo.cbSize     = sizeof(REBARBANDINFO);
	bandInfo.fMask      = RBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZE;
	bandInfo.fStyle     = RBBS_CHILDEDGE;
	bandInfo.hwndChild  = m_hwndToolbarAddress;
	bandInfo.cxMinChild = 230;
	bandInfo.cyMinChild = 25;
	SendMessage(m_hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&bandInfo);
	
	m_pOpenSearch = new COpenSearch;
	m_pOpenSearch->Create(m_hwndRebar);

	m_hwndToolbarTab = CreateToolbarEx(m_hwndRebar, WS_CHILD | WS_VISIBLE | TBSTYLE_LIST | CCS_NORESIZE | CCS_NODIVIDER, ID_TAB_TOOLBAR, 0, HINST_COMMCTRL, IDB_HIST_SMALL_COLOR, NULL, 0,0, 0, 0, 0, sizeof(TBBUTTON));
	
	bandInfo.fStyle |= RBBS_BREAK;
	bandInfo.hwndChild = m_hwndToolbarTab;
	SendMessage(m_hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&bandInfo);

	m_hdpa = DPA_Create(1);

	m_hwndParent = hwndParent;
}

レバーコントロールを作成するには、ウインドウクラスにREBARCLASSNAMEを指定してCreateWindowExを呼び出します。 これが成功したら、アドレスバーの役割を果たすツールバーを作成するために、CreateToolbarExを呼び出します。 このツールバーの戻るボタンと進むボタンは、tbButtonに定義されています。 各種ボタンはフラットにしてドロップダウンメニューを表示したいため、 そのような設定をTB_SETSTYLEとTB_SETEXTENDEDSTYLEで行っています。 ツールバーを作成したら、その子ウインドウとして拡張コンボボックスを作成します。 拡張コンボボックスからエディットコントロールのハンドルを取得しているのは、 SHAutoCompleteでオートコンプリートを有効にするためです。 SHACF_URLMRUのMRU(Most Recently Used)とは最近使用したデータのことであり、 この場合は最近した使用したURLが文字を入力した際に補完されます。 InitializeTypedURLsは、拡張コンボボックスにURLの履歴を追加するために呼び出しています。 ツールバーをレバーコントロールにバンドとして追加するには、RB_INSERTBANDを送信します。 REBARBANDINFO構造体のfMaskにRBBIM_STYLE | RBBIM_CHILD | RBBIM_CHILDSIZEを指定した場合は、 fStyle、hwndChild、cx(y)MinChildを初期化できます。 hwndChildには追加するウインドウのハンドルを指定し、cx(y)MinChildにはバンドの最低限のサイズを指定します。 2番目のウインドウはOpenSearchに関するものですが、これはCOpenSearchという独立したクラスで作成することになっています。 3番目のウインドウはタブを実現するツールバーであり、CreateToolbarExで作成することになります。 このツールバーのボタンはテキストで構成されるため、ウインドウクラスにTBSTYLE_LISTを指定しています。 RB_INSERTBANDの送信では既に初期化したREBARBANDINFO構造体を使用しますが、 hwndChildの部分は追加するウインドウに変更しておきます。 また、スタイルにRBBS_BREAKを指定することで、バンドを改行して追加できます。 DPA_Createを呼び出しているのは、タブ毎に作成するCWebBrowserHostを管理するためです。

新しいタブが作成されるときというのは、メニューから「新規タブ」が選択された場合や、 お気に入りから何らかのURLにアクセスした場合などです。 このような場合には、CRebarMgr::CreateNewTabが呼ばれます。

void CRebarMgr::CreateNewTab(LPWSTR lpszUrl)
{
	int             n;
	int             nId = ID_BUTTON_TAB + m_nTabIdCount;
	TBBUTTON        tbButton;
	CWebBrowserHost *p;

	p = new CWebBrowserHost();
	p->Create(m_hwndParent, m_hwndRebar, lpszUrl, nId);
	
	n = DPA_GetPtrCount(m_hdpa);
	DPA_InsertPtr(m_hdpa, n, (void *)p);

	ChangeActiveBrowser(p, FALSE);
	
	ZeroMemory(&tbButton, sizeof(TBBUTTON));
	tbButton.idCommand = nId;
	tbButton.iBitmap   = I_IMAGENONE;
	tbButton.fsState   = TBSTATE_ENABLED | TBSTATE_PRESSED;
	tbButton.fsStyle   = BTNS_BUTTON;
	tbButton.iString   = 0;
	SendMessage(m_hwndToolbarTab, TB_ADDBUTTONS, 1, (LPARAM)&tbButton);

	m_nTabIdCount++;
}

新しいタブを作成するにあたって、ページを表示するためのオブジェクトが別個必要になるため、 CWebBrowserHostを作成します。 第4引数はCWebBrowserHostの内部で作成されるウインドウのIDであり、 これはID_BUTTON_TABをベースにカウントされていきます。 DPA_GetPtrCountとDPA_InsertPtrの組み合わせにより、 作成されたCWebBrowserHostはDPAの最後尾に追加されます。 タブの正体はツールバーのボタンであるため、TB_ADDBUTTONSで新しいボタンが追加されます。 idCommandがnIdであることから、WebBrowserをホストするウインドウとボタンのIDは同一になります。 iStringにはボタンの名前を指定できますが、この名前はTitleChangeメソッドで設定することになります。

ChangeActiveBrowserは、現在表示しているCWebBrowserHostを第1引数のものに切り替えるために呼び出します。 ただし、現在のWebBrowserを破棄するために呼ぶこともでき、このような場合は第1引数がNULLになります。 また、切り替えというより、新しいWebBrowserを表示する目的で呼ばれた場合は第2引数がFALSEになります。 メソッドの実装は次のようになっています。

void CRebarMgr::CommandStateChange(long Command, BOOL bEnable)
{
	int   nId;
	DWORD dwState;
	
	if (Command == CSC_NAVIGATEFORWARD)
		nId = ID_BUTTON_FORWARD;
	else
		nId = ID_BUTTON_BACK;

	dwState = (DWORD)SendMessage(m_hwndToolbarAddress, TB_GETSTATE, nId, 0);

	if (bEnable)
		dwState |= TBSTATE_ENABLED;
	else
		dwState &= ~TBSTATE_ENABLED;
	
	SendMessage(m_hwndToolbarAddress, TB_SETSTATE, nId, dwState);
}

新しいWebBrowserを表示するためには、現在アクティブなWebBrowserを非表示しなければなりません。 よって、Showを通じてWebBrowserを非表示にするのは当然ですが、 タブや履歴ボタン、ステータスバーなどもアクティブなWebBrowserの情報が反映されているため、 これらも調整しなければなりません。 タブの状態からTBSTATE_PRESSEDを取り除けば、タブが押下されているように見えなくなります。 CommandStateChangeの第2引数にFALSEを指定すれば第1引数のボタンが無効になり、 「進む」や「戻る」などの履歴機能が実行できなくなります。 また、OnNavigateComplete2にNULLを指定すれば、ステータスバーの文字列を空白にする処理が行われます。 アクティブなWebBrowserの情報を非表示にしたら、第1引数のWebBrowserをアクティブにするための処理を行います。 SetActiveBrowserを呼び出しているのは、新しいWebBrowserのポインタをCWebBrowserContainerに渡すためです。 ShowによってWebBrowserは表示され、TB_SETSTATEによってタブは押下状態になります。 GetTravelStateを呼び出せば履歴のボタンの状態を取得できるため、 これを基にボタン状態を復元します。 bChangeがTRUEであるということはWebBrowserの切り替えであり、 この場合はアクティブなWebBrowserの情報をステータスバーに設定する必要があります。 新しくWebBrowserを作成している場合は、既に情報が設定されているはずですから、この処理は不要になります。

レバーコントロールで生成されたWM_NOTIFYは、CRebarMgr::OnCommandとして通知されます。

void CRebarMgr::OnCommand(WPARAM wParam, LPARAM lParam)
{
	int             nId = LOWORD(wParam);
	CWebBrowserHost *pWebBrowserHost = g_pWebBrowserContainer->GetActiveBrowser();

	if (nId == ID_BUTTON_FORWARD)
		pWebBrowserHost->ForwardOrBack(TRUE);
	else if (nId == ID_BUTTON_BACK)
		pWebBrowserHost->ForwardOrBack(FALSE);
	else if (nId >= ID_BUTTON_TAB) {
		int             i, n;
		CWebBrowserHost *p;

		if (pWebBrowserHost->GetId() == nId) {
			DWORD dwState = (DWORD)SendMessage(m_hwndToolbarTab, TB_GETSTATE, nId, 0) | TBSTATE_PRESSED;
			SendMessage(m_hwndToolbarTab, TB_SETSTATE, nId, dwState);
			return;
		}

		n = DPA_GetPtrCount(m_hdpa);
		for (i = 0; i < n; i++) {
			p = (CWebBrowserHost *)DPA_GetPtr(m_hdpa, i);
			if (p->GetId() == nId) {
				ChangeActiveBrowser(p, TRUE);
				break;
			}
		}
	}
	else if (HIWORD(wParam) == CBN_SELCHANGE) {
		int    nIndex = (int)SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
		LPWSTR lpszUrl = (LPWSTR)DPA_GetPtr(m_hdpaTyped, nIndex);
		g_pWebBrowserContainer->Navigate(lpszUrl, FALSE);
		AddTypedURLs(lpszUrl);
	}
	else
		m_pOpenSearch->OnCommand(nId);
}

通知されたIDがID_BUTTON_FORWARDの場合は「進む」ボタンが押されたことを意味し、 ID_BUTTON_BACKの場合は「戻る」ボタンが押されたことを意味します。 これらを実行するためには、ForwardOrBackを呼び出すことになります。 IDがID_BUTTON_TABよりも大きい場合は、タブが選択されたことを意味します。 この場合は、その選択されたタブに関連するWebBrowserをアクティブにするために、 ChangeActiveBrowserを呼び出します。 CBN_SELCHANGEが送られた場合は、コンボボックスのセルが選択されたことを意味します。 この場合は、その選択されたセルに対応するURLにアクセスするようにし、 URLの並び順をAddTypedURLsで調整します。 ここで処理しない通知については、COpenSearchに渡すようにします。

レバーコントロールで生成されたWM_COMMANDは、CRebarMgr::OnNotifyとして通知されます。

void CRebarMgr::OnNotify(WPARAM wParam, LPARAM lParam)
{
	int             nId = (int)((LPNMHDR)lParam)->idFrom;
	int             nCode = (int)((LPNMHDR)lParam)->code;
	CWebBrowserHost *pWebBrowserHost = g_pWebBrowserContainer->GetActiveBrowser();

	if (nId == ID_TAB_TOOLBAR) {
		if (nCode == NM_RCLICK) {
			if (((LPNMMOUSE)lParam)->dwItemSpec == -1) {
				if (pWebBrowserHost == NULL)
					return;
				if (pWebBrowserHost->ShowBandMenu())
					g_pWebBrowserContainer->ResizeWindow();
			}
			else if (((LPNMMOUSE)lParam)->dwItemSpec >= ID_BUTTON_TAB) {
				int             i, n;
				int             nIdTab = (int)((LPNMMOUSE)lParam)->dwItemSpec;
				DWORD           dwIndex;
				CWebBrowserHost *p;

				n = DPA_GetPtrCount(m_hdpa);
				for (i = 0; i < n; i++) {
					p = (CWebBrowserHost *)DPA_GetPtr(m_hdpa, i);
					if (p->GetId() == nIdTab) {
						DPA_DeletePtr(m_hdpa, i);
						if (p == pWebBrowserHost)
							ChangeActiveBrowser((CWebBrowserHost *)DPA_GetPtr(m_hdpa, 0), FALSE);
						p->Destroy();
						p->Release();
						break;
					}
				}

				dwIndex = (DWORD)SendMessage(m_hwndToolbarTab, TB_COMMANDTOINDEX, nIdTab, 0);
				SendMessage(m_hwndToolbarTab, TB_DELETEBUTTON, dwIndex, 0);
			}
		}
	}
	else if (nId == ID_ADDRESS_TOOLBAR) {
		if (nCode == TBN_DROPDOWN) {
			if (((LPNMTOOLBAR)lParam)->iItem == ID_BUTTON_BACK)
				ShowTravelMenu(pWebBrowserHost, TRUE);
			if (((LPNMTOOLBAR)lParam)->iItem == ID_BUTTON_FORWARD)
				ShowTravelMenu(pWebBrowserHost, FALSE);
		}
	}
	else if (nId == ID_ADDRESS_COMBOBOX) {
		if (nCode == CBEN_ENDEDIT && ((LPNMCBEENDEDIT)lParam)->iWhy == CBENF_RETURN) {
			g_pWebBrowserContainer->Navigate(((LPNMCBEENDEDIT)lParam)->szText, FALSE);
			AddTypedURLs(((LPNMCBEENDEDIT)lParam)->szText);
		}
	}
	else if (nId == ID_REBAR) {
		if (nCode == RBN_HEIGHTCHANGE)
			g_pWebBrowserContainer->ResizeWindow();
	}
	else
		m_pOpenSearch->OnNotify(wParam, lParam);
}

タブの役割を果たすツールバー上で右クリックが行われた場合の処理は2通りあります。 1つは、タブ上でない場所をクリックした際の処理であり、この場合はバンドオブジェクトを表示するためのメニューを表示します。 そしてもう1つは、タブ上でクリックが行われた際の処理です。 この場合は、そのタブと現在表示しているWebBrowserを削除しますが、 この削除に伴って新しくアクティブになるWebBrowserを先に決定しておく必要があります。 これは、DPAの中で先頭に存在するWebBrowserとし、これをChangeActiveBrowserに指定します。 これが完了すれば、それまでアクティブだったWebBrowserをDestroyで破棄し、 TB_DELETEBUTTONでタブのボタンも削除します。 アドレスバーの役割を果たすツールバーでTBN_DROPDOWNが通知されるというのは、 履歴ボタンの矢印が押下されたことを意味します。 この場合は、ShowTravelMenuを呼び出すことで、履歴の名前を含んだポップアップメニューを表示します。 アドレスバーのコンボボックスでCBEN_ENDEDITが通知された場合、 その理由がCBENF_RETURNであれば、コンボボックスでEnterキーが押下されたことを意味します。 この場合は、そのURLへアクセスすると共に、URLをAddTypedURLsで追加します。 レバーコントロールにRBN_HEIGHTCHANGEが通知された場合は、レバーコントロールの高さが変更されたことを意味します。 この場合はウインドウのサイズを変更するためにResizeWindowを呼び出しています。

ShowTravelMenuの実装は次のようになっています。

void CRebarMgr::ShowTravelMenu(CWebBrowserHost *pWebBrowserHost, BOOL bBack)
{
	HRESULT             hr;
	IWebBrowser2        *pWebBrowser2;
	ITravelLogStg       *pTravelLogStg;
	ITravelLogEntry     *pTravelLogEntry;
	IEnumTravelLogEntry *pEnumTravelLogEntry;
	TLENUMF             tlenumf = bBack ? TLEF_RELATIVE_BACK : TLEF_RELATIVE_FORE;
	LPWSTR              lpszTitle, lpszUrl;
	DWORD               i, dwCount, dwIndex;
	HMENU               hmenuPopup;
	POINT               pt;
	MENUITEMINFOW       mii;
	int                 nId;
	int                 nTravelMenuFirstId = 100;
	
	pWebBrowserHost->QueryBrowserInterface(IID_PPV_ARGS(&pWebBrowser2));

	hr = IUnknown_QueryService(pWebBrowser2, SID_STravelLogCursor, IID_PPV_ARGS(&pTravelLogStg));
	if (FAILED(hr)) {
		pWebBrowser2->Release();
		return;
	}

	hr = pTravelLogStg->EnumEntries(tlenumf, &pEnumTravelLogEntry);
	if (FAILED(hr)) {
		pTravelLogStg->Release();
		pWebBrowser2->Release();
		return;
	}
	
	pTravelLogStg->GetCount(tlenumf, &dwCount);

	hmenuPopup = CreatePopupMenu();
	
	for (i = 0; i < dwCount; i++) {
		pEnumTravelLogEntry->Next(1, &pTravelLogEntry, NULL);
		pTravelLogEntry->GetTitle(&lpszTitle);
		pTravelLogEntry->Release();
		
		mii.cbSize     = sizeof(MENUITEMINFO);
		mii.fMask      = MIIM_ID | MIIM_TYPE | MIIM_STATE;
		mii.fState     = MFS_UNCHECKED;
		mii.wID        = nTravelMenuFirstId + i;
		mii.fType      = MFT_STRING;
		mii.dwTypeData = lpszTitle;
		InsertMenuItemW(hmenuPopup, i, FALSE, &mii);
		
		CoTaskMemFree(lpszTitle);
	}	

	GetCursorPos(&pt);
	nId = TrackPopupMenu(hmenuPopup, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwndRebar, NULL);
	if (nId == 0) {
		DestroyMenu(hmenuPopup);
		pEnumTravelLogEntry->Release();
		pTravelLogStg->Release();
		pWebBrowser2->Release();
		return;
	}
	
	dwIndex = nId - nTravelMenuFirstId;
	pEnumTravelLogEntry->Reset();

	for (i = 0; i < dwCount; i++) {
		pEnumTravelLogEntry->Next(1, &pTravelLogEntry, NULL);
		pTravelLogEntry->GetURL(&lpszUrl);
		pTravelLogEntry->Release();

		if (dwIndex == i) {
			pWebBrowserHost->Navigate(lpszUrl);
			CoTaskMemFree(lpszUrl);
			break;
		}
		
		CoTaskMemFree(lpszUrl);
	}	

	DestroyMenu(hmenuPopup);
	pEnumTravelLogEntry->Release();
	pTravelLogStg->Release();
	pWebBrowser2->Release();
}

履歴の情報を取得するためには、IWebBrowser2からITravelLogStgを取得する必要があります。 これは、QueryInterfaceからは取得できないため、 IUnknown_QueryServiceにSID_STravelLogCursorを指定して取得します。 ITravelLogStg::EnumEntriesを呼び出せば、履歴を列挙するためのIEnumTravelLogEntryを取得できます。 第1引数にTLEF_RELATIVE_BACKを指定した場合は戻るための履歴を取得し、 TLEF_RELATIVE_FOREを指定した場合は進むための履歴を取得します。 列挙できるエントリの数はGetCountで取得できるため、 この数だけIEnumTravelLogEntry::Nextを呼び出して、履歴のエントリを列挙します。 Nextで返されるITravelLogEntryは1つのエントリを表しており、 GetTitleを呼び出すことでエントリのタイトルを取得できます。 このタイトルは、ポップアップメニューに追加されることになります。 ポップアップメニューの表示はTrackPopupMenuで可能であり、 第2引数にTPM_RETURNCMDを指定しているため、選択された項目のIDが戻り値として返ります。 Resetを呼び出すことで履歴の列挙を再び行えるようにし、 今度は選択されたエントリを調べるためにループします。 一致した場合は、そのエントリのURLをNavigateに指定することで実際にアクセスします。

アドレスバーに存在する履歴ボタンは、状況によって有効になったり無効になったりします。 こうした変換が必要な際には、CommandStateChangeが呼ばれます。

void CRebarMgr::CommandStateChange(long Command, BOOL bEnable)
{
	int   nId;
	DWORD dwState;
	
	if (Command == CSC_NAVIGATEFORWARD)
		nId = ID_BUTTON_FORWARD;
	else
		nId = ID_BUTTON_BACK;

	dwState = (DWORD)SendMessage(m_hwndToolbarAddress, TB_GETSTATE, nId, 0);

	if (bEnable)
		dwState |= TBSTATE_ENABLED;
	else
		dwState &= ~TBSTATE_ENABLED;
	
	SendMessage(m_hwndToolbarAddress, TB_SETSTATE, nId, dwState);
}

まず、CSCから始まる定数をボタンの識別用に変換します。 次に、TB_GETSTATEでボタンの状態を取得し、ボタンを有効にする場合はTBSTATE_ENABLEDを加えます。 一方、ボタンを無効にする場合はTBSTATE_ENABLEDを取り除きます。 TB_SETSTATEによって、ボタンの状態が新しく設定されます。

タブの名前は、現在アクセスしているページによって変化します。

void CRebarMgr::TitleChange(BSTR Text)
{
	int             nId;
	TBBUTTONINFOW   buttonInfo;
	CWebBrowserHost *pWebBrowserHost = g_pWebBrowserContainer->GetActiveBrowser();
	
	nId = pWebBrowserHost->GetId();

	buttonInfo.cbSize  = sizeof(TBBUTTONINFO);
	buttonInfo.dwMask  = TBIF_TEXT;
	buttonInfo.pszText = Text;
	buttonInfo.cchText = SysStringLen(Text);
	SendMessage(m_hwndToolbarTab, TB_SETBUTTONINFO, nId, (LPARAM)&buttonInfo);

	SendMessage(m_hwndToolbarTab, TB_SETBUTTONWIDTH, 0, (LPARAM)MAKELPARAM(10, 130));
}

ボタンの情報を設定するには、TB_SETBUTTONINFOを送信します。 TBBUTTONINFO構造体のdwMaskにTBIF_TEXTを指定した場合は、 pszTextにボタンのテキストを指定できます。 TB_SETBUTTONWIDTHを送信しているのは、ボタンの最小の幅と最大の幅を設定するためです。

IEのアドレスバーにURLを入力してEnterキーを押した場合、そのURLがTypedURLsとしてレジストリに記録されます。 ドロップダウンから表示できるURLはこのTypedURLsの中身を反映しており、 今回作成するコンボボックスにもこの機能が備わっています。 次に示すInitializeTypedURLsは、コンボボックスにTypedURLsを追加します。

void CRebarMgr::InitializeTypedURLs()
{
	int             i = 0;
	HKEY            hKey;
	LONG            lResult;
	LPWSTR          lp;
	WCHAR           szValue[256], szData[256];
	DWORD           dwValue, dwData, dwType;
	COMBOBOXEXITEMW item;
	
	lResult = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\TypedURLs"), 0, KEY_QUERY_VALUE, &hKey);
	if (lResult != ERROR_SUCCESS)
		return;
	
	m_hdpaTyped = DPA_Create(1);
	
	for (;;) {
		dwData = sizeof(szData);
		dwValue = sizeof(szValue);
		lResult = RegEnumValueW(hKey, i, szValue, &dwValue, 0, &dwType, (LPBYTE)szData, &dwData);
		if (lResult == ERROR_NO_MORE_ITEMS)
			break;
		
		item.mask    = CBEIF_TEXT;
		item.iItem   = i;
		item.pszText = szData;
		SendMessage(m_hwndComboBoxAddress, CBEM_INSERTITEM, i, (LPARAM)&item);

		lp = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (lstrlenW(szData) + 1) * sizeof(WCHAR));
		lstrcpyW(lp, szData);
		DPA_InsertPtr(m_hdpaTyped, i, (void *)lp);

		i++;
	}
}

TypedURLsを表すレジストリキーをオープンし、そこで列挙したURLを格納するためのDPAを作成します。 ループ内では、RegEnumValueで列挙したURLをコンボボックスに追加すると共に、DPAにも追加しておきます。

コンボボックスのドロップダウンからURLを選択した場合や、コンボボックスでEnterキーが押下された場合はAddTypedURLsが呼ばれます。 このメソッドでは渡された文字列をDPAの先頭に追加し、コンボボックスのドロップダウンを再構成します。

void CRebarMgr::AddTypedURLs(LPWSTR lpszUrl)
{
	int             i, n;
	LPWSTR          lpsz, lpszUrlAdd;
	COMBOBOXEXITEMW item;
	
	lpszUrlAdd = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpszUrl) + 1) * sizeof(WCHAR));
	lstrcpyW(lpszUrlAdd, lpszUrl);
	
	n = DPA_GetPtrCount(m_hdpaTyped);
	for (i = 0; i < n; i++) {
		lpsz = (LPWSTR)DPA_GetPtr(m_hdpaTyped, i);
		if (lstrcmpW(lpszUrlAdd, lpsz) == 0) {
			HeapFree(GetProcessHeap(), 0, lpsz);
			DPA_DeletePtr(m_hdpaTyped, i);
			break;
		}
	}

	DPA_InsertPtr(m_hdpaTyped, 0, (void *)lpszUrlAdd);

	SendMessage(m_hwndComboBoxAddress, CB_RESETCONTENT, 0, 0);
 
	n = DPA_GetPtrCount(m_hdpaTyped);
	for (i = 0; i < n; i++) {
		lpsz = (LPWSTR)DPA_GetPtr(m_hdpaTyped, i);

		item.mask    = CBEIF_TEXT;
		item.iItem   = i;
		item.pszText = lpsz;
		SendMessage(m_hwndComboBoxAddress, CBEM_INSERTITEM, i, (LPARAM)&item);
	}
}

追加するURLが既にDPAに存在する場合はそれを削除します。 渡されたURLは先頭に追加したいため、DPA_InsertPtrの第2引数には0を指定します。 CB_RESETCONTENTでコンボボックスのドロップダウンをリセットし、 DPAを基にコンボボックスに文字列を追加します。


戻る