前の10件 3  4  5  6  7  8  9  10  11  12  13

記事一覧

2009年03月24日

WTLの警告「warning C4996...use _CRT_SECURE_NO_WARNINGS」を解決する

WTLを最新のものに入れ替えたら、それまで正常にビルドできていたソースコードで以下のような警告がでるようになった。

2>d:\_____sourcecode\_wtl\include\atlapp.h(851) : warning C4996: '_vswprintf': This function or variable may be unsafe. Consider using vswprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
2>        c:\program files\microsoft visual studio 9.0\vc\include\wchar.h(773) : '_vswprintf' の宣言を確認してください。
「atlapp.h」内の警告が出た個所を見てみると以下のようになっていた。
	inline int vsprintf_x(LPTSTR lpstrBuff, size_t cchBuff, LPCTSTR lpstrFormat, va_list args)
	{
#if _SECURE_ATL && !defined(_ATL_MIN_CRT) && !defined(_WIN32_WCE)
		return _vstprintf_s(lpstrBuff, cchBuff, lpstrFormat, args);
#else
		cchBuff;   // Avoid unused argument warning
		return _vstprintf(lpstrBuff, lpstrFormat, args);
#endif
	}

#ifによる分岐で、_SECURE_ATLが定義されていて、かつ、_ATL_MIN_CRTと_WIN32_WCEが未定義だと「_vstprintf_s」が、そうでなければ警告の出る「_vstprintf」が使われるようだ。

ここで「_WIN32_WCE」はWindows CE用の定義。通常のPC向けソースコードでは未定義がデフォルトなので問題にはならないだろう。

「_ATL_MIN_CRT」はATLの動的リンク時にランタイムライブラリの利用を極力減らすための定義。昔のプロジェクトではデフォルトで定義されていることがあったり、ビルドした実行ファイルのサイズが小さくなるというメリットがあるため敢えて定義する人もいる。そのため_ATL_MIN_CRTを利用する(利用したい)プロジェクトでは警告を解決することができない(警告を非表示にすることはできる)。

「_SECURE_ATL」は正体がよく分からないのでVisual Studioのincludeフォルダ内を検索してみた。
  D:\_____sourcecode\_WTL\include\atlapp.h(694):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(706):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(727):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(754):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(790):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(802):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(823):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(835):#if _SECURE_ATL
  D:\_____sourcecode\_WTL\include\atlapp.h(847):#if _SECURE_ATL && !defined(_ATL_MIN_CRT) && !defined(_WIN32_WCE)
  D:\_____sourcecode\_WTL\include\atlapp.h(857):#if _SECURE_ATL && !defined(_ATL_MIN_CRT) && !defined(_WIN32_WCE)
どうやらWTL内でのみ使われている定義のようだ。今回の警告が出た箇所と同じような分岐処理で「_s」タイプの関数とそうでない関数の使い分けをしているのだろう。バージョン関連の定義のように数値による分岐はなく、定義が有効かどうかだけを見ている。

以上のことから今回の警告を解決するには、_ATL_MIN_CRTの利用を諦めて、
#define	_SECURE_ATL	1
という定義をWTL関連がincludeされる前、「stdafx.h」の先頭に置く。 これで「_vstprintf_s」というより安全(とされている)関数が利用され、警告が表示されなくなる。

2009年03月23日

WTLのタブ型ビューでタブを下側に配置する

tabview01.gif
最新のWTLではタブ型ビューがサポートされ、(MFCや.NET Frameworkなどにはまだまだ追いつけないものの)少しだけUIの幅が広がった。

新しいタブ型ビューではタブのドラッグにも対応するなど、使える機能を備えたものだ。しかし残念ながらタブを下側に配置する機能は用意されていなかった。ここではWTLのCTabView派生クラスを作ることでタブの下側表示に対応させた。

利用するときは、それまでCTabView型だった変数宣言をCDnpTabViewに変更して、タブを下に配置したいときにCDnpTabView::SwitchTabType(CDnpTabView::TAB_DOWN);を呼ぶ。逆にCDnpTabView::SwitchTabType(CDnpTabView::TAB_UP);を呼べば通常通りの上側表示に戻る。

サンプルプロジェクトでは「バージョン表示」用のメニューやボタンを操作したときにタブ位置が上側と下側に交互に変わるようにした。

(CTabViewImpl<>派生で作ればstatic_castを利用してもっとスマートに実装できたことに後から気付き後悔中。気が向いたときに作り直します)

依存環境:ATL/WTL
#pragma once

#include "atlctrlx.h"

///
///\brief
///	WTLのタブビューの下側タブ対応版
///
///\attention
///WTL 8.0での動作を前提として作られています。今後のWTLバージョンアップによっては
///使えなくなる可能性があります。
///
class	CDnpTabView	: public CTabView
{
public:


	///
	///\brief
	///	タブ配置位置指定用列挙子
	///
	enum	TAB_POSITION
	{
		TAB_UP,			//!< タブを上側に配置する
		TAB_DOWN		//!< タブを下側に配置する
	};

private:

	TAB_POSITION	_enumUpTabOrDown;		//!< 現在設定されているタブ配置位置

public:
	CDnpTabView()
	{
		_enumUpTabOrDown = TAB_UP;
	}

	BEGIN_MSG_MAP(CDnpTabView)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
		CHAIN_MSG_MAP(CTabView)
	ALT_MSG_MAP(1)	//タブコントロール
		MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChanging)
		CHAIN_MSG_MAP_ALT(CTabView,1)
	END_MSG_MAP()




	///
	///\brief
	///	タブ配置位置の動的変更
	///
	///\param	enumType
	///	新しいタブの配置位置\n
	///	CDnpTabView::TAB_UP と CDnpTabView::TAB_DOWN のみサポート
	///
	void	SwitchTabType(TAB_POSITION enumType)
	{
		if(_enumUpTabOrDown == enumType)
			return;

		switch(_enumUpTabOrDown)
		{
		case	TAB_UP:
			m_tab.ModifyStyle(TCS_BOTTOM,0);
			break;

		case	TAB_DOWN:
			m_tab.ModifyStyle(0,TCS_BOTTOM);
			break;

		default:
			ATLASSERT(0);		//対応していない配置位置指定された
			return;
		}

		_enumUpTabOrDown = enumType;

		UpdateLayout();		//このクラスのUpdateLayout()を実行
	}



	///
	///\brief
	///	生成時処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		if(_enumUpTabOrDown == TAB_UP)
		{
			//デフォルトの動作に任せる
			bHandled = FALSE;
			return	0;
		}

		//以下のコードはWTL 8.0の CTabViewImpl::OnCreate() と同じにしている
		{
			CreateTabControl();
		}

		if(_enumUpTabOrDown == TAB_DOWN)
			m_tab.ModifyStyle(0,TCS_BOTTOM);		//タブを下タイプに変更。ComCtl32.dll Ver6では無視される

		return	0;
	}




	///
	///\brief
	///	タブ配置位置の強制変更
	///
	///	CTabViewImpl::UpdateLayout() が仮想関数でなく、直接いじることができないため、
	///	CTabViewImpl::UpdateLayout() 内で指定されたタブ配置位置を強制変更する
	///
	LRESULT OnWindowPosChanging(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		//タブが上側なら処理の必要なし
		if(_enumUpTabOrDown == TAB_UP)
			return	0;

		WINDOWPOS*	pPos = (WINDOWPOS*)lParam;
		if(pPos == NULL)
			return	0;

		if(!(pPos->flags & SWP_NOMOVE))
		{
			if(_enumUpTabOrDown == TAB_DOWN && pPos->y == 0)
			{
				//タブの位置を下側に変更
				RECT	rect;

				GetClientRect(&rect);
				pPos->y = rect.bottom - m_cyTabHeight;
			}
		}

		return	0;
	}



	///
	///\brief
	///	タブ選択時処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
	{
		if(_enumUpTabOrDown == TAB_UP)
		{
			//デフォルトの処理に任せる
			bHandled = FALSE;
			return	0;
		}

		//以下のコードはWTL 8.0の CTabViewImpl::OnTabChanged() と同じにしている
		{
			SetActivePage(m_tab.GetCurSel());
			OnPageActivated(m_nActivePage);
			UpdateLayout();				//このクラスのUpdateLayout()が実行される
		}

		return 0;
	}



	///
	///\brief
	///	親ウインドウサイズ変更時処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if(_enumUpTabOrDown == TAB_UP)
		{
			//デフォルトの処理に任せる
			bHandled = FALSE;
			return	0;
		}

		//以下のコードはWTL 8.0の CTabViewImpl::OnSize() と同じにしている
		{
			UpdateLayout();		//このクラスのUpdateLayout()が実行される
		}
		return 0;
	}



	///
	///\brief
	///	ページ追加処理
	///
	///\attention
	///	WTL 8.0の動作を前提としています。
	///
	bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
	{
		//以下のコードはWTL 8.0の CTabViewImpl::OnSize() と同じにしている
		{
			//このクラスのInsertPageが実行される
			return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
		}
	}


	///
	///\brief
	///	ページ挿入処理
	///
	bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
	{
		bool	ret;

		ret = __super::InsertPage(nPage,hWndView,lpstrTitle,nImage,pData);

		UpdateLayout();		//このクラスのUpdateLayout()を実行する

		return	ret;
	}

 
	///
	///\brief
	///	レイアウト設定処理
	///
	void UpdateLayout()
	{
		RECT rect;
		GetClientRect(&rect);

		if(_enumUpTabOrDown == TAB_UP)
		{
			//タブの位置をウインドウ上側に変更
			if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
				m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);

			//ビューウインドウの位置を下側に変更
			if(m_nActivePage != -1)
				::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, m_cyTabHeight, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
		}
		else if(_enumUpTabOrDown == TAB_DOWN)
		{
			//タブの位置をウインドウ下側に変更
			if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
				m_tab.SetWindowPos(NULL, 0, rect.bottom - m_cyTabHeight, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);

			//ビューウインドウの位置を上側に変更
			if(m_nActivePage != -1)
				::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
		}
		else
		{
			ATLASSERT(0);	//対応していないタブ配置位置
		}
	}
};

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

2009年03月22日

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

wtl80jp_01.gif
■はじめに

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


wtl80jp_02.gif
■メリット

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

また、タブ型ビューといった最新(?)のUIを簡単に利用できます。


wtl80jp_03.gif
■日本語化内容:

 ・Windows用アプリケーションウィザード

 ・生成されるソースコード

 ・生成されるソースコードのコメント



※サンプルやWindows CE用のアプリケーションウィザードはダウンロード可能なファイルに含まれません。

※incldueファイルやreadme.html、cpl.txtは日本語化されていません。


wtl80jp_04.gif
■ダウンロード





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



(2009年3月22日現在最新版のWTLを元にしています)


IEコントロールのスクリプトエラー表示を消す

IEコントロールでJavascriptなどを含むページを表示しているときにスクリプトエラーダイアログが表示されないようにするにはIOleCommandTarget::Exec()でOLECMDID_SHOWSCRIPTERRORがIEに処理されないようにすればいい。

IOleCommandTarget::Exec()を自前で処理するには、HTMLページの読み込みが終わった時にICustomDoc::SetUIHandler()を使い、IEコントロールに対してIDocHostUIHandlerを設定する。


1.Visual Studio 2008の「ファイル」メニューにある「新規」メニューから「プロジェクト」を選択する。

2.「新しいプロジェクト」の画面で「ATL/WTLアプリケーションウイザード」を選択する。ここではプロジェクト名を「HideScriptError」とした。

3.アプリケーションウイザードが開いたら、「ユーザーインターフェース機能」のタブにある「ビューウインドウの形式」で「HTML」ページを選択して「完了」ボタンを押す。

4.(以上の操作でアプリケーションウイザードにより自動的にひな形となるソースコードが自動生成される)

5.生成したソースコードのViewクラスを開き、以下のソースコードを追加する。

#pragma once

//■以下の宣言を3行追加
#define SINKID_COMTESTEVENTS 0
#include "Exdispid.h"
#include "DnpDocHostUIHandlerImpl.h"


class CHideScriptErrorView : public CWindowImpl<CHideScriptErrorView, CAxWindow>
	,public CComObjectRootEx<CComSingleThreadModel>		//■追加
	,public IDnpDocHostUIHandlerImpl	//■追加
	,public	IDispatchImpl<IDispatch>	//■追加
	,public IOleCommandTarget	//■追加
	,public IDispEventImpl<SINKID_COMTESTEVENTS, CHideScriptErrorView, &DIID_DWebBrowserEvents2>	//■追加
{
	//■メンバー変数を追加
	CComPtr<IWebBrowser2>	_pIWebBrowser2;

public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
		   (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
			return FALSE;

		// このメッセージを翻訳する機会をHTML ページに与える
		return (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
	}


	//■以下3行を追加
	BEGIN_SINK_MAP(CHideScriptErrorView)
		SINK_ENTRY_EX(SINKID_COMTESTEVENTS, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
	END_SINK_MAP()

	//■以下5行を追加
	BEGIN_COM_MAP(CHideScriptErrorView)
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IDocHostUIHandler)
		COM_INTERFACE_ENTRY(IOleCommandTarget)
	END_COM_MAP()

	BEGIN_MSG_MAP(CHideScriptErrorView)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)	//■1行追加
	END_MSG_MAP()


	//■OnDocumentCompleteを追加
	HRESULT	_stdcall OnDocumentComplete(IDispatch* pDisp, VARIANT* URL)
	{
		//ドキュメントを読み込み終わった時点でUIHandlerを変更する

		CComPtr<IDispatch>	pIDispatch;
		CComPtr<ICustomDoc>	pICustomDoc;

		_pIWebBrowser2->get_Document(&pIDispatch);
		if(pIDispatch)
			pIDispatch->QueryInterface(IID_ICustomDoc,(void **)&pICustomDoc);
		if(pICustomDoc)
			pICustomDoc->SetUIHandler(static_cast<IDocHostUIHandler*>(this));

		return	S_OK;
	}



	//■IOleCommandTargetの関数追加
    STDMETHODIMP QueryStatus( const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText ) 
	{
		return E_NOTIMPL; 
    }


	//■IOleCommandTargetの関数追加
    STDMETHODIMP Exec( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
    {
		//コマンドがスクリプトエラーを示すものなら、エラー表示を無効にする
		if(nCmdID == OLECMDID_SHOWSCRIPTERROR)
		{
			(*pvaOut).vt		= VT_BOOL;
			(*pvaOut).boolVal	= VARIANT_TRUE;
			return S_OK;
		}

		return E_NOTIMPL;
    }


	//■生成時処理用に関数を追加
	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;

		_pIWebBrowser2 = NULL;
		QueryControl(&_pIWebBrowser2);
		if(_pIWebBrowser2 == NULL)
		{
			ATLASSERT(0);
			return	hWnd;
		}

		//IEとの接続
		Advise(_pIWebBrowser2);

		return	hWnd;
	}


	//■IEからの切断処理を追加
	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE;

		if(_pIWebBrowser2)
			Unadvise(_pIWebBrowser2);
		_pIWebBrowser2 = NULL;

		return	0;
	}
};
6.「MainFrm.h」を開き、
	CHideScriptErrorView		m_view;
という部分を
	CComObject<CHideScriptErrorView> m_view;
に変更する。


7.最後に「DnpDocHostUIHandlerImpl.h」というファイルを作成し、その内容を以下のようにする。

#pragma	once


///
///\brief
///	IDocHostUIHandler実装補助用クラス
///
///実処理はないがS_FALSEやS_OKを返す仮想関数もある
///
class	IDnpDocHostUIHandlerImpl :
	public IDocHostUIHandler
{
	virtual HRESULT STDMETHODCALLTYPE ShowContextMenu( 
		/* [in] */ DWORD dwID,
		/* [in] */ POINT *ppt,
		/* [in] */ IUnknown *pcmdtReserved,
		/* [in] */ IDispatch *pdispReserved)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetHostInfo( 
		/* [out][in] */ DOCHOSTUIINFO *pInfo)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE ShowUI( 
		/* [in] */ DWORD dwID,
		/* [in] */ IOleInPlaceActiveObject *pActiveObject,
		/* [in] */ IOleCommandTarget *pCommandTarget,
		/* [in] */ IOleInPlaceFrame *pFrame,
		/* [in] */ IOleInPlaceUIWindow *pDoc)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE HideUI( void)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE UpdateUI( void)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE EnableModeless( 
		/* [in] */ BOOL fEnable)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate( 
		/* [in] */ BOOL fActivate)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate( 
		/* [in] */ BOOL fActivate)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE ResizeBorder( 
		/* [in] */ LPCRECT prcBorder,
		/* [in] */ IOleInPlaceUIWindow *pUIWindow,
		/* [in] */ BOOL fRameWindow)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( 
		/* [in] */ LPMSG lpMsg,
		/* [in] */ const GUID *pguidCmdGroup,
		/* [in] */ DWORD nCmdID)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath( 
		/* [out] */ 
		__out  LPOLESTR *pchKey,
		/* [in] */ DWORD dw)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDropTarget( 
		/* [in] */ IDropTarget *pDropTarget,
		/* [out] */ IDropTarget **ppDropTarget)
	{
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE GetExternal( 
		/* [out] */ IDispatch **ppDispatch)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateUrl( 
		/* [in] */ DWORD dwTranslate,
		/* [in] */ 
		__in __nullterminated  OLECHAR *pchURLIn,
		/* [out] */ 
		__out  OLECHAR **ppchURLOut)
	{
		if(ppchURLOut)
			*ppchURLOut = NULL;
		return	S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE FilterDataObject( 
		/* [in] */ IDataObject *pDO,
		/* [out] */ IDataObject **ppDORet)
	{
		return	S_FALSE;
	}
};

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

IEコントロールの右クリックメニューを実装する

IEコントロールの右クリックメニューを自分で制御したいときにはIDocHostUIHandlerを実装して、SetExternalUIHandlerで設定すればいい。
ここでの処理はIE8のアクセラレーター項目には対応しない。

1.Visual Studio 2008の「ファイル」メニューにある「新規」メニューから「プロジェクト」を選択する。

2.「新しいプロジェクト」の画面で「ATL/WTLアプリケーションウイザード」を選択する。ここではプロジェクト名を「RightClickTest」とした。

3.アプリケーションウイザードが開いたら、「ユーザーインターフェース機能」のタブにある「ビューウインドウの形式」で「HTML」ページを選択して「完了」ボタンを押す。

4.(以上の操作でアプリケーションウイザードにより自動的にひな形となるソースコードが自動生成される)

5.生成したソースコードのViewクラスを開き、以下のように6箇所ソースコードを追加する。

#pragma once

#include "DnpDocHostUIHandlerDispatchImpl.h"	//■追加

class CRightClickTestView : public CWindowImpl<CRightClickTestView, CAxWindow>
	,public CComObjectRootEx<CComSingleThreadModel>		//■追加
	,public IDnpDocHostUIHandlerDispatchImpl			//■追加
{
public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
		   (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
			return FALSE;

		// このメッセージを翻訳する機会をHTML ページに与える
		return (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
	}

	BEGIN_MSG_MAP(CRightClickTestView)
	END_MSG_MAP()


	//■以下4行を追加
	BEGIN_COM_MAP(CRightClickTestView)
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IDocHostUIHandlerDispatch)
	END_COM_MAP()


	//■右クリック表示用の仮想関数を追加
	STDMETHOD(ShowContextMenu)(DWORD dwID,DWORD x,DWORD y,IUnknown *pcmdTarget,IDispatch *pdispReserved,HRESULT *dwRetVal)
	{
		bool	ret;

		HMENU	hMenu;
		HWND	hWnd;
		bool	bUseShellExtention;
		bool	bUseLanguage;
		bool	bMenuUserCtrl;

		hMenu = NULL;
		hWnd = NULL;
		bUseShellExtention	= true;		//シェルエクステンションメニューを追加する
		bUseLanguage		= true;		//エンコードメニューを追加する
		bMenuUserCtrl		= true;		//メニュー項目の有効/無効を自分で決定する

		ret = GetDefaultContextMenu(hMenu,hWnd,dwID,pcmdTarget,bUseShellExtention,bUseLanguage);
		if(ret == false)
			return	E_NOTIMPL;		//メニュー取得失敗。IEデフォルト処理に任せる

		//メニューの表示とその処理
		{
			int		nSelection;

			if(bMenuUserCtrl)
			{
				//自分でメニューの有効/無効を決定する(WM_INITMENUPOPUPもしくはこの関数内で設定可能になる)
				nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,m_hWnd,(RECT*)NULL);
			}
			else
			{
				//IEにメニューの有効/無効を決定させる
				nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,hWnd,(RECT*)NULL);
			}
			::DestroyMenu(hMenu);

			//メニュー選択結果の処理
			if(nSelection)
				::SendMessage(hWnd,WM_COMMAND,nSelection,NULL);
		}

		*dwRetVal = S_OK;
		return S_OK;
	}


	//■生成時処理用に関数を追加
	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 = CWindowImpl<CRightClickTestView, CAxWindow>::Create(hWndParent,rect,szWindowName,dwStyle,dwExStyle,MenuOrID,lpCreateParam);
		if(hWnd == NULL)
			return	NULL;

		//右クリックメニュー処理をこのクラスで行う
		SetExternalUIHandler(static_cast<IDocHostUIHandlerDispatch*>(this));

		return	hWnd;
	}
};
6.「MainFrm.h」を開き、
	CRightClickTestView		m_view;
という部分を
	CComObject<CRightClickTestView> m_view;
に変更する。

7.最後に「DnpDocHostUIHandlerDispatchImpl.h」というファイルを作成し、その内容を以下のようにする。
#pragma	once

#include "Mshtmcid.h"

///
///\brief
///	IDocHostUIHandlerDispatch実装補助用クラス
///
///仮想関数はすべてE_NOTIMPLを返す
///
class	IDnpDocHostUIHandlerDispatchImpl :
	public IDispatchImpl<IDocHostUIHandlerDispatch, &IID_IDocHostUIHandlerDispatch, &LIBID_ATLLib>
{
public:
	virtual HRESULT STDMETHODCALLTYPE ShowContextMenu( 
		/* [in] */ DWORD dwID,
		/* [in] */ DWORD x,
		/* [in] */ DWORD y,
		/* [in] */ IUnknown *pcmdtReserved,
		/* [in] */ IDispatch *pdispReserved,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetHostInfo( 
		/* [out][in] */ DWORD *pdwFlags,
		/* [out][in] */ DWORD *pdwDoubleClick)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE ShowUI( 
		/* [in] */ DWORD dwID,
		/* [in] */ IUnknown *pActiveObject,
		/* [in] */ IUnknown *pCommandTarget,
		/* [in] */ IUnknown *pFrame,
		/* [in] */ IUnknown *pDoc,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE HideUI( void)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE UpdateUI( void)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE EnableModeless( 
		/* [in] */ VARIANT_BOOL fEnable)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE OnDocWindowActivate( 
		/* [in] */ VARIANT_BOOL fActivate)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE OnFrameWindowActivate( 
		/* [in] */ VARIANT_BOOL fActivate)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE ResizeBorder( 
		/* [in] */ long left,
		/* [in] */ long top,
		/* [in] */ long right,
		/* [in] */ long bottom,
		/* [in] */ IUnknown *pUIWindow,
		/* [in] */ VARIANT_BOOL fFrameWindow)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( 
		/* [in] */ DWORD_PTR hWnd,
		/* [in] */ DWORD nMessage,
		/* [in] */ DWORD_PTR wParam,
		/* [in] */ DWORD_PTR lParam,
		/* [in] */ BSTR bstrGuidCmdGroup,
		/* [in] */ DWORD nCmdID,
		/* [retval][out] */ HRESULT *pdwRetVal)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetOptionKeyPath( 
		/* [out] */ BSTR *pbstrKey,
		/* [in] */ DWORD dw)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDropTarget( 
		/* [in] */ IUnknown *pDropTarget,
		/* [out] */ IUnknown **ppDropTarget)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE GetExternal( 
		/* [out] */ IDispatch **ppDispatch)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE TranslateUrl( 
		/* [in] */ DWORD dwTranslate,
		/* [in] */ BSTR bstrURLIn,
		/* [out] */ BSTR *pbstrURLOut)
	{
		return	E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE FilterDataObject( 
		/* [in] */ IUnknown *pDO,
		/* [out] */ IUnknown **ppDORet)
	{
		return	E_NOTIMPL;
	}





	///
	///\brief
	///	IEの右クリックメニューの取得
	///
	///\param[in,out]	hMenu
	///取得/生成した右クリックメニュー
	///必ずNULLを渡すこと!
	///
	///\param	hWnd
	///メニュー選択結果の送信先HWND。\n
	///必ずNULLを渡すこと!
	///
	///\param[in]	dwID
	///IDocHostUIHandler::ShowContextMenu() の第1引数
	///
	///\param	*pcmdTarget
	///IDocHostUIHandler::ShowContextMenu() の第3引数IOleCommandTargetのIUnknown
	///
	///\param[in]	bUseShellExtention
	///シェルエクステンションメニューを利用するかどうか
	///たとえばフリーソフトなどで追加される「○○でダウンロード」などのメニュー。trueなら追加する。
	///
	///\param[in]	bUseLanguage
	///エンコードメニューを利用するかどうか
	///ページの言語エンコード指定用のメニュー。trueなら追加する。
	///
	///\retval	true	取得成功
	///\retval	false	失敗
	///
	///取得方法は以下のHPを参照
	///「WebBrowser Customization (Part 2)」
	///http://msdn.microsoft.com/en-us/library/aa770042(VS.85).aspx
	///
	///- 利用例
	/** \code
		STDMETHOD(ShowContextMenu)(DWORD dwID,DWORD x,DWORD y,IUnknown *pcmdTarget,IDispatch *pdispReserved,HRESULT *dwRetVal)
		{
			bool	ret;

			HMENU	hMenu;
			HWND	hWnd;
			bool	bUseShellExtention;
			bool	bUseLanguage;
			bool	bMenuUserCtrl;

			hMenu = NULL;
			hWnd = NULL;
			bUseShellExtention	= true;		//シェルエクステンションメニューを追加する
			bUseLanguage		= true;		//エンコードメニューを追加する
			bMenuUserCtrl		= true;		//メニュー項目の有効/無効を自分で決定する

			ret = GetDefaultContextMenu(hMenu,hWnd,dwID,pcmdTarget,bUseShellExtention,bUseLanguage);
			if(ret == false)
				return	E_NOTIMPL;		//メニュー取得失敗。IEデフォルト処理に任せる

			//メニューの表示とその処理
			{
				int		nSelection;

				if(bMenuUserCtrl)
				{
					//自分でメニューの有効/無効を決定する(WM_INITMENUPOPUPもしくはこの関数内で設定可能になる)
					nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,m_hWnd,(RECT*)NULL);
				}
				else
				{
					//IEにメニューの有効/無効を決定させる
					nSelection = ::TrackPopupMenu(hMenu,TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,x,y,0,hWnd,(RECT*)NULL);
				}
				::DestroyMenu(hMenu);

				//メニュー選択結果の処理
				if(nSelection)
					::SendMessage(hWnd,WM_COMMAND,nSelection,NULL);
			}

			*dwRetVal = S_OK;
			return S_OK;
		}
	\endcode
	*/
	///
	///\todo
	///IE8では未来クリックメニューにアクセラレーターがあるが、SHDVIDが不明なため追加できない
	///
	bool	GetDefaultContextMenu(HMENU& hMenu,HWND& hWnd,DWORD dwID,IUnknown *pcmdTarget,bool bUseShellExtention,bool bUseLanguage)
	{
		#define IDR_BROWSE_CONTEXT_MENU  24641
		#define SHDVID_GETMIMECSETMENU   27
		#define SHDVID_ADDMENUEXTENSIONS 53

		HRESULT		hr;
		CComPtr<IOleCommandTarget>	pIOleCommandTarget;

		if(pcmdTarget == NULL)
			return	false;

		ATLASSERT(hMenu == NULL);
		ATLASSERT(hWnd == NULL);

		pcmdTarget->QueryInterface(IID_IOleCommandTarget,(void**)&pIOleCommandTarget);

		//IEのhWndの取得
		{
			CComPtr<IOleWindow>	pIOleWindow;

			hr = pcmdTarget->QueryInterface(IID_IOleWindow,(void**)&pIOleWindow);
			hr = pIOleWindow->GetWindow(&hWnd);
		}

		//DLLからのメニューリソース読み込み
		{
			HINSTANCE	hDll;

			hDll = ::LoadLibrary(TEXT("SHDOCLC.DLL"));			//XPまではこのDLL
			if(hDll == NULL)
				hDll = ::LoadLibrary(_T("ieframe.dll"));		//VistaではこのDLL

			if(hDll == NULL)
				return	false;

			hMenu = ::LoadMenu(hDll,MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));
			hMenu = ::GetSubMenu(hMenu,dwID);

			::FreeLibrary(hDll);
		}

		//言語サブメニューの追加
		if(bUseLanguage)
		{
			CComVariant		var;
			MENUITEMINFO	mii = {0};

			hr = pIOleCommandTarget->Exec(&CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &var);

			mii.cbSize = sizeof(mii);
			mii.fMask  = MIIM_SUBMENU;
			mii.hSubMenu = (HMENU) var.byref;

			::SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &mii);
		}


		//IEのExtentionのメニューを追加
		//たとえばフリーソフトなどで追加される「○○でダウンロード」などのメニュー
		if(bUseShellExtention)
		{
			CComVariant	var1;
			CComVariant	var2;

			V_VT(&var1) = VT_INT_PTR;
			V_BYREF(&var1) = hMenu;

			V_VT(&var2) = VT_I4;
			V_I4(&var2) = dwID;
			hr = pIOleCommandTarget->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);
		}



		//IEコントロールデフォルトの設定でメニュー項目の有効/無効/チェック設定をする
		{
			int	i;

			for (i = 0; i < GetMenuItemCount(hMenu); i++)
			{
				OLECMD olecmd;
				olecmd.cmdID = GetMenuItemID(hMenu, i);

				//■■通常のOLECMDFとは値が異なる。要調査
				if (olecmd.cmdID > 0)
				{
					UINT mf;
					pIOleCommandTarget->QueryStatus(&CGID_MSHTML,1,&olecmd,NULL);
					switch (olecmd.cmdf)
					{
					case OLECMDF_ENABLED:
					case 3:
						mf = MF_BYCOMMAND | MF_ENABLED | MF_UNCHECKED;
						break;

					case OLECMDF_LATCHED:
						mf = MF_BYCOMMAND | MF_ENABLED | MF_CHECKED;
						break;

					case 1:
					default:
						mf = MF_BYCOMMAND | MF_DISABLED | MF_GRAYED;
						break;
					}
					::CheckMenuItem(hMenu, olecmd.cmdID, mf);
					::EnableMenuItem(hMenu, olecmd.cmdID, mf);
				}
			}
		}

		return	true;
	}
};

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

2009年03月21日

Windows SDK、DDK、WDK、MSDNライブラリのダウンロードリンク(2010年5月版)

Microsoftのホームページ上をさまよっていたらいくつかのリンクを見つけた。ここではそれらを紹介する。


Windows 7用SDKのダウンロード(2010/5追記)

「Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1 (ISO)」のダウンロードは http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=71deb800-c591-4f97-a900-bea146e4fae1で可能。x86用、AMD64、x64用別にISOファイルが1つずつ用意されていて、isoファイルは1つ1.5GBほど。x86用だけでいいのであれば、[GRMSDK_EN_DVD.iso ]を1つダウンロードすればいい。


DDK/WDKのダウンロード

Microsoftのホームページ内に「Download Kits and Tools」というページを発見した。

まず目についたのが
Windows Server 2003 DDK」のサイトへのリンク。たどると
Download the Windows Server 2003 SP1 DDK [236 MB ISO file]」がダウンロードできるようになっていた。このDDKを用いればWindows 2000/XP/VistaとWindows Server 2003に対応したドライバーを開発できるようだ。これは過去のDDKバージョンになる。ほかのDDKよりもダウンロードサイズが小さいのが気になるが、勝手な想像をするとおそらくMSDN Libraryが省略されているのだろう。


さらにドライバー開発用のリンクを探してみると「Windows Driver Kit on Microsoft Connect」がある。これはMicrosoftの無料登録制のホームページへのリンクになっていて、たどると「WDK for Windows Server 2008 - Build 6001_18002」がダウンロードできる。ファイル名は「6.1.6001.18002.081017-1400_wdksp-WDK18002SP_EN_DVD.iso」でファイルサイズは605MBほど。
このDDK(WDK)はWindows 2000/XP/VistaとWindows Server 2003/2008に対応したドライバーを開発できるようだ。

Windows 7用のベータ版WDKに関してはありますよというアナウンスだけで、Microsoftに電子メールで申請するかMSDNに入っていない限りダウンロードはできないようだ。



Windows SDKのダウンロード

DDKを探したついでにSDKの方も検索してみた。結果「Microsoft Windows ソフトウェア開発キット」というページを発見。

ここには「適切な SDK の検索 (英語)」へのリンクが用意されていて、どのバージョンのSDKが最新になっているのか、どのSDKがどのOSに対応しているのかがひとめでわかるようになっていた。

2009年3月21日現在では、2009年2月8日リリースの「6.0.6001.18000.367」(サイズ1.3GB、ファイル名6.0.6001.18000.367-KRMSDK_EN.iso)というバージョンが一番対応範囲が広く、Windows XP/VistaおよびWindows Server 2003/2008に対応するようだ。

2009年2月7日リリースでビルドバージョンが「6.1.6000.16384.10」(サイズ1.4GB、ファイル名)というのもある。isoでのダウンロードではなく、setup.exeから動的にダウンロードするバージョンになる。対象OSからWindows Server 2008が除外、.NET Frameworkも3.5に対応しないバージョンだが、基本的な内容は2009年2月8日版と同じなのだろう。

Windows 7用のSDKに関しては、「Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA」というベータ版が用意されている。サイズは1.3GB、ファイル名は7.0.7000.0.4011.WindowsSDK_Windows7_idw.WindowsSDK.DVD.Release.isoになる。OSはWindows 7のベータ版に加え、Windows XP/VistaとWindows Server 2008に対応するようだ。



MSDNライブラリのダウンロード

最後に開発で必須なヘルプとなるMSDNライブラリのダウンロード用リンク。

日本語版は「MSDN Library for Visual Studio 2008 SP1 (2008年12月更新版)」(サイズ2.6GB、ファイル名JPNMSDNX1530336.ISO)が、英語版は「MSDN Library for Visual Studio 2008 SP1」(サイズ2.2GB、ファイル名VS2008SP1MSDNENUX1506188.iso)が最新版のようだ。

連続接続時間が6時間となるイーモバイルでは2GBもあるMSDNライブラリを1度の接続でダウンロードすることは不可能。ちょっと調べてみたら「Irvine」が2008年7月15日にバージョンアップをしていて2GB超えのファイルのダウンロードにも対応していた。このダウンローダーを利用することで無事にイーモバイル環境でも落とすことができた。

2009年03月20日

ディスプレイデバイスを列挙する

Windows 2000以上で用意されているEnumDisplayDevicesを利用すると、そのPCにインストールされているディスプレイデバイスを一覧できる。

■取得結果例(マルチディスプレイ環境なのでQuadroが2つ列挙されている模様)
NVIDIA Quadro FX 2500M
NVIDIA Quadro FX 2500M
RDPDD Chained DD
RDP Encoder Mirror Driver


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

///
///\brief
///	ディスプレイデバイス名取得
///
///仮にディスプレイデバイスが1つでも以下のように1つ以上のデバイス名が取得されることがある
///
///	Windows 2000以降対応
///
///\param	astrDeviceName
///取得したデバイス名の配列
///
///\retval	true	
///\retval	false	
///
///-取得結果例1
///		- NeoMagic MagicGraph256AV driver
///		- NetMeeting driver
///		- RDPDD Chained DD
///-取得結果例2
///		- NVIDIA Quadro FX 2500M
///		- NVIDIA Quadro FX 2500M
///		- RDPDD Chained DD
///		- RDP Encoder Mirror Driver

bool	GetDisplayDeviceName(CAtlArray<CAtlString>& astrDeviceName)
{
	HMODULE			hDLL;
	BOOL			ret;
	DWORD			dwDevice;
	DISPLAY_DEVICE	sDD;

	BOOL (CALLBACK* pfnEnumDisplayDevices)(PVOID Unused,DWORD iDevNum,PDISPLAY_DEVICE lpDisplayDevice,DWORD dwFlags);

	astrDeviceName.RemoveAll();

	hDLL = ::LoadLibrary(_T("user32.dll"));
	if(hDLL == NULL)
		return	false;

#ifdef _UNICODE
	(FARPROC&)pfnEnumDisplayDevices = ::GetProcAddress(hDLL,"EnumDisplayDevicesW");
#else
	(FARPROC&)pfnEnumDisplayDevices = ::GetProcAddress(hDLL,"EnumDisplayDevicesA");
#endif

	if(pfnEnumDisplayDevices == NULL)
	{
		::FreeLibrary(hDLL);
		return	false;
	}

	dwDevice = 0;
	while(1)
	{
		::ZeroMemory(&sDD,sizeof(DISPLAY_DEVICE));
		sDD.cb = sizeof(DISPLAY_DEVICE);

		//dwDevice番目のディスプレイデバイスを取得
		ret = pfnEnumDisplayDevices(NULL,dwDevice,&sDD,0);
		if(ret == FALSE)
			break;

		astrDeviceName.Add(sDD.DeviceString);
		dwDevice++;
	}

	::FreeLibrary(hDLL);

	return	true;
}



bool	Test()
{
	size_t		i;
	size_t		nSize;

	CAtlArray<CAtlString>	astrName;

	GetDisplayDeviceName(astrName);

	nSize = astrName.GetCount();
	for(i = 0; i < nSize; i++)
	{
		ATLTRACE("%s\n",astrName[i]);
	}

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

スレッドを作り、HTTPでファイルをダウンロードする

最近はネット上にさまざまな情報やファイルがある。ブロードバンドの普及により通信速度も向上したこともあり、自分のプログラムからネット上の情報を参照したい場面がよくある。
ここではATL環境でマルチスレッドを利用してHTTPによりファイルをダウンロード/保存するクラスを作成した。

ここでは簡単のためスレッド生成数は1つのみで、キューにURLリストをためておき順次ダウンロードしている。

スレッドの生成にはCDnpThreadImpl、キューはCDnpQueを利用した。




■使用イメージ
	CDnpDownloadThread2	_cThread;

	//スレッドの生成とダウンロード開始
	{
		CAtlString	strURL;
		CAtlString	strFile;

		strURL = _T("http://www.usefullcode.net/atom.xml");
		strFile = _T("_down.xml");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);

		strURL = _T("http://www.usefullcode.net/index.html");
		strFile = _T("_down.html");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);
	}

	//メッセージハンドラ設定
	BEGIN_MSG_MAP(CMainDlg)
		MESSAGE_HANDLER(WM_DNP_DOWNLOAD_COMPLETE, OnDnpDownloadComplete)
	END_MSG_MAP()


	//ダウンロード結果の受信
	LRESULT OnDnpDownloadComplete(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bool	ret = lParam ? true : false;

		ATLTRACE("■ダウンロード%s\n",(ret ? _T("成功") : _T("失敗")));

		return	0;
	}


■「DnpDownloadThread2.h」の内容
#pragma	once


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

#include "atlstr.h"
#include "atlfile.h"

#include "DnpThreadImpl.h"
#include "DnpQue.h"

#define	WM_DNP_DOWNLOAD_COMPLETE	(WM_APP + 66)



///
///\brief
///	HTTPを利用したダウンロードスレッド
///
///
///
/** \code
	CDnpDownloadThread2	_cThread;

	//スレッドの生成とダウンロード開始
	{
		CAtlString	strURL;
		CAtlString	strFile;

		strURL = _T("http://www.usefullcode.net/atom.xml");
		strFile = _T("_down.xml");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);

		strURL = _T("http://www.usefullcode.net/index.html");
		strFile = _T("_down.html");
		_cThread.StartDownload(strURL,strFile,m_hWnd,NULL);
	}

	//メッセージハンドラ設定
	BEGIN_MSG_MAP(CMainDlg)
		MESSAGE_HANDLER(WM_DNP_DOWNLOAD_COMPLETE, OnDnpDownloadComplete)
	END_MSG_MAP()


	//ダウンロード結果の受信
	LRESULT OnDnpDownloadComplete(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bool	ret = lParam ? true : false;

		ATLTRACE("■ダウンロード%s\n",(ret ? _T("成功") : _T("失敗")));

		return	0;
	}
\endcode
*/
///
class CDnpDownloadThread2	: protected CDnpThreadImpl
{

	HINTERNET	_hInternet;			//!< 接続用
	HINTERNET	_hConnect;			//!< 接続用

	HANDLE		_hEvent;			//!< ダウンロード情報追加検出イベント用


	///
	///\brief
	///	ダウンロード情報格納用
	///
	class	CItem
	{
	public:
		CAtlString	strURL;			//!< ダウンロードURL
		CAtlString	strFile;		//!< 保存先ファイル名
		HWND		hWnd;			//!< ダウンロード結果通知先
		WPARAM		wParam;			//!< 結果通知時のユーザー定義パラメーター

		CItem()
		{
			wParam	= NULL;
			hWnd	= NULL;
		}

		CItem(LPCTSTR pszURL,LPCTSTR pszFile,HWND hUserWnd,WPARAM wUserParam)
		{
			strURL	= pszURL;
			strFile	= pszFile;
			hWnd	= hUserWnd;
			wParam	= wUserParam;
		}
	};

	//
	//ダウンロード情報格納用データとそのクリティカルセクション
	//
	CDnpCriticalSection	_sec_queItems;		//!< put_queItems() get_queItems() get_queItemsSize() 以外からはアクセスしないこと
	CDnpQue<CItem>		_queItems;			//!< put_queItems() get_queItems() get_queItemsSize()  以外からはアクセスしないこと

protected:

	///
	///\brief
	///	ダウンロード情報の追加
	///
	///\param[in]	cItem
	///ダウンロード情報
	///
	///\retval	true	追加成功
	///\retval	false	追加失敗
	///
	bool	put_queItems(const CItem& cItem)
	{
		HRESULT	hr;

		hr = _sec_queItems.Lock();
		if(FAILED(hr))
			return	false;

		_queItems.AddData(cItem);
		_sec_queItems.Unlock();

		return	true;
	}


	///
	///\brief
	///	ダウンロード情報の取得
	///
	///\param[out]	cItem
	///ダウンロード情報
	///
	///\retval	true	取得成功
	///\retval	false	取得失敗
	///
	bool	get_queItems(CItem& cItem)
	{
		bool	ret;
		HRESULT	hr;

		hr = _sec_queItems.Lock();
		if(FAILED(hr))
			return	false;

		ret = _queItems.GetData(cItem);
		_sec_queItems.Unlock();

		return	ret;
	}



	///
	///\brief
	///	まだ残っているダウンロード情報数
	///
	///\param[out]	nSize
	///ダウンロード情報数
	///
	///\retval	true	取得成功
	///\retval	false	取得失敗
	///
	bool	get_queItemsSize(size_t& nSize)
	{
		HRESULT	hr;

		hr = _sec_queItems.Lock();
		if(FAILED(hr))
		{
			nSize = 0;
			return	false;
		}

		nSize = _queItems.GetSize();
		_sec_queItems.Unlock();

		return	true;
	}




public:

	CDnpDownloadThread2()
	{
		_hEvent		= NULL;
		_hInternet	= NULL;
		_hConnect	= NULL;
	}

	virtual	~CDnpDownloadThread2()
	{
		////スレッドが破棄されていなければASSERT
		//ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		DestroyThread(2000);			//スレッド終了を最大2秒待つ
	}




	///
	///\brief
	///	ダウンロード情報の追加およびダウンロード開始
	///
	///ほかにダウンロード中のファイルがある場合はそのダウンロードが終わるまで
	///ダウンロードは開始しない
	///
	///\param[in]	pszURL
	///ダウンロードしたいURL
	///
	///\param[in]	pszFile
	///保存先ファイル
	///
	///\param[in]	hWnd
	///結果の通知先HWND
	///
	///\param[in]	wParam
	///結果通知時のユーザー定義パラメーター
	///
	///\retval	true	成功(情報の追加成功であって、ダウンロード結果ではない)
	///\retval	false	失敗
	///
	bool	StartDownload(LPCTSTR pszURL,LPCTSTR pszFile,HWND hWnd,WPARAM wParam)
	{
		bool	ret;

		ret = put_queItems(CItem(pszURL,pszFile,hWnd,wParam));
		if(ret == false)
			return	false;

		if(_hEvent == NULL)
			_hEvent = ::CreateEvent(NULL,FALSE,TRUE,_T("CDnpDownloadThread2"));

		::SetEvent(_hEvent);

		if(IsThreadRunning())
			return	true;

		return	CreateThread(NULL) ? true : false;
	}



	///
	///\brief
	///	ダウンロードの中断
	///
	bool	AbortDownload(void)
	{
		return	AbortThread();
	}



protected:

	///
	///\brief
	///	スレッドメイン処理
	///
	/// CDnpThreadImpl::ThradMain_() から呼ばれる
	///ユーザーは呼び出さないこと
	///
	///\param	pParam
	///	CreateThread() で渡したユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam)
	{
		bool	ret;
		size_t	nSize;
		HANDLE	hEvent;

		hEvent = ::OpenEvent(EVENT_ALL_ACCESS,FALSE,_T("CDnpDownloadThread2"));
	
		while(1)
		{
			//500ミリ秒ごともしくはイベントセット時に処理する
			::WaitForSingleObject(hEvent,500);

			// IsAbort() でスレッド中断を検出して関数から抜ける
			if(IsAbort())
				break;

			ret = get_queItemsSize(nSize);
			if(ret && nSize == 0)
			{
				::ResetEvent(hEvent);
				continue;
			}


			{
				CItem		cItem;
				bool		ret;
				HANDLE		hFile;
				CAtlFile	cFile;

				ret = get_queItems(cItem);
				if(ret == false)
					continue;

				ret = false;
				cFile.Create(cItem.strFile,GENERIC_WRITE,0,CREATE_ALWAYS);
				hFile = cFile.Detach();
				if(hFile)
				{
					ret = Download(cItem.strURL,hFile);
					::CloseHandle(hFile);
				}
				if(cItem.hWnd && ::IsWindow(cItem.hWnd))
					::PostMessage(cItem.hWnd,WM_DNP_DOWNLOAD_COMPLETE,cItem.wParam,(LPARAM)ret);
			}
		}
		::CloseHandle(hEvent);

		return	0;
	}





	///
	///\brief
	///	スレッド中断処理開始用
	///
	///スレッドを中断したいときに呼び出す
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	bool	AbortThread(void)
	{
		if(_hEvent)
			::SetEvent(_hEvent);

		if(_hConnect)
			::InternetCloseHandle(_hConnect);
		if(_hInternet)
			::InternetCloseHandle(_hInternet);
		_hConnect = NULL;
		_hInternet = NULL;

		if(_hEvent)
			::CloseHandle(_hEvent);
		_hEvent = NULL;

		return	CDnpThreadImpl::AbortThread();
	}






	///
	///\brief
	///		HTTPを利用したダウンロード関数
	///
	///ダウンローが終了(もしくは失敗や中断)するまでreturnしない
	///
	///\param[in]	pszURL
	///ダウンロードするURL
	///
	///\param	hFile
	///保存先ファイルのハンドル
	///
	///\param[in]	dwBuffSize
	///受信バッファーサイズ
	///
	///\retval	true	ダウンロードは正常に終了
	///\retval	false	失敗
	///
	///
	virtual	bool	Download(LPCTSTR pszURL,HANDLE hFile,DWORD dwBuffSize=1024)
	{
		TCHAR		pszHeader[] = _T("Accept: */*");
		BOOL		ret;
		DWORD		dwReadSize;
		DWORD		dwWrittenSize;
		BYTE*		pcbBuff;

		pcbBuff = new BYTE[dwBuffSize];
		if(pcbBuff == NULL || IsAbort())
			return	false;

		_hInternet = ::InternetOpen(NULL,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
		if(_hInternet == NULL || IsAbort())
		{
			delete	pcbBuff;
			return	false;
		}

		_hConnect = ::InternetOpenUrl(_hInternet,pszURL,pszHeader,lstrlen(pszHeader),INTERNET_FLAG_DONT_CACHE,0);
		if(_hConnect == NULL || IsAbort())
		{
			::InternetCloseHandle(_hInternet);
			_hInternet = NULL;
			delete	pcbBuff;
			return	false;
		}

		while(1)
		{
			//if(IsAbort())
			//	break;

			::Sleep(0);
			ret = ::InternetReadFile(_hConnect,pcbBuff,dwBuffSize,&dwReadSize);
			if(ret == FALSE)
				break;

			if(dwReadSize == 0 || IsAbort())
				break;

			ret = ::WriteFile(hFile,pcbBuff,dwReadSize,&dwWrittenSize,NULL);
			if(ret == FALSE)
				break;
		}

		if(ret)
			ret = ::FlushFileBuffers(hFile);
		::InternetCloseHandle(_hConnect);
		::InternetCloseHandle(_hInternet);
		_hConnect = NULL;
		_hInternet = NULL;

		delete	pcbBuff;

		return	(ret == FALSE || IsAbort()) ? false : true;
	}

};


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

ワーカースレッドを生成するためのクラス

マルチスレッド化するとメインウインドウに与える影響を少なくしつつバックグラウンドで処理しやすい。ここではATLを利用した環境で簡単にスレッドを生成できるクラス「CDnpThreadImpl」を作成した。

■使用例
#include "DnpThreadImpl.h"

///
///\brief
///	テスト用のスレッドクラス
///
class CTestThread	: public CDnpThreadImpl
{
protected:

	///
	///\brief
	///	スレッドメイン処理
	///
	/// CDnpThreadImpl::ThreadMain_() から呼ばれる
	///ユーザーは呼び出さないこと
	///
	///\param	pParam
	///	CreateThread() で渡したユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam)
	{
		ATLTRACE("■スレッド開始\n");

		while(1)
		{
			//処理は無限ループ

			::Sleep(1000);

			// IsAbort() でスレッド中断を検出して関数から抜ける
			if(IsAbort())
				break;
		}

		ATLTRACE("■スレッド終了\n");
		return	0;
	}
};

以上のようにスレッド本体処理用のクラスをCDnpThreadImplから派生する形で作ったら、
CTestThread cThread;

cThread.CreateThread(NULL);
とするだけでワーカースレッドが生成して処理が実行される。処理を中断したい場合は
cThread.AbortThread();
のようにする。
スレッドが完全に終了するまで待ちたい場合は
_cThread.DestroyThread(5000);  //最大5秒待つ
のようにする。


■「DnpCriticalSection.h」の内容
#pragma	once

#include "atlexcept.h"


/*!
 * \brief
 * クリティカルセクションクラス
 * 
 * 例外を処理しているだけで、 CComAutoCriticalSection をそのまま利用。
 * 
 */
class CDnpCriticalSection
{
	CComAutoCriticalSection		_sec;		//!< クリティカルセクション本体
public:


	~CDnpCriticalSection()
	{
		HRESULT	hr;
	
		hr = Unlock();

		ATLASSERT(SUCCEEDED(hr));
	}


	/*!
	 * \brief
	 * ロック
	 * 
	 */
	HRESULT Lock(void)
	{
		HRESULT	hr;

		try
		{
			hr = _sec.Lock();
			if(FAILED(hr))
				return	hr;
		}
		catch(CAtlException& e)
		{
			ATLTRACE("■例外発生(HRESULT=%08X)\n",e.m_hr);
		}

		return	hr;
	}


	/*!
	 * \brief
	 * ロック解除
	 * 
	 */
	HRESULT	Unlock(void)
	{
		HRESULT	hr;

		try
		{
			hr = _sec.Unlock();
			if(FAILED(hr))
				return	hr;
		}
		catch(CAtlException& e)
		{
			ATLTRACE("■例外発生(HRESULT=%08X)\n",e.m_hr);
		}

		return	hr;
	}
};


■「DnpThreadImpl.h」の内容
#pragma	once

#include "DnpCriticalSection.h"

///
///\brief
///		スレッド実装用クラス
///
///■利用例
/*! \code
	//
	{
		CTestThread		_cThread;

		_cThread.CreateThread(NULL);
		_cThread.AbortThread();
		_cThread.DestroyThread(1000);
	}
	//
	class CTestThread	: public CDnpThreadImpl
	{
	protected:

		///
		///\brief
		///	スレッドメイン処理
		///
		/// CDnpThreadImpl::ThradMain_() から呼ばれる
		///ユーザーは呼び出さないこと
		///
		///\param	pParam
		///	CreateThread() で渡したユーザー定義のパラメータ
		///
		virtual	DWORD	ThradMain(void* pParam)
		{
			ATLTRACE("■スレッド開始\n");

			while(1)
			{
				::Sleep(1000);

				// IsAbort() でスレッド中断を検出して関数から抜ける
				if(IsAbort())
					break;
			}

			ATLTRACE("■スレッド終了\n");
			return	0;
		}
	};
///\endcode
*/
///
class CDnpThreadImpl
{
	///
	///\brief
	///		スレッドパラメーター
	///
	///内部利用するのみなのでprivate構造体
	///
	struct	THREADPARAM
	{
		CDnpThreadImpl*	pThis;		//!< このクラスのポインタ
		void*	pParam;				//!< ユーザーが CreateThread() を読んだ時に指定されたパラメータ
	};


	///
	///\attention
	///以下のスレッド中断用フラグとクリティカルセクションは
	///put_bAbort() と get_bAbort() 以外からはアクセスしないこと!
	///
	CDnpCriticalSection	_sec_bAbort;	//!< スレッド中断フラグ用クリティカルセクション
	bool	_bAbort;					//!< スレッド中断用フラグ


protected:
	DWORD	_dwThreadID;			//!< 生成したスレッドのID
	HANDLE	_hThread;				//!< 生成したスレッドのハンドル

private:

	///
	///\brief
	///	スレッド中断用フラグ設定用関数
	///
	///\param[in]	bNew
	///新しいフラグ。trueならスレッド中断開始、falseなら無視
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	bool	put_bAbort(bool bNew)
	{
		HRESULT	hr;

		hr = _sec_bAbort.Lock();
		if(FAILED(hr))
			return	false;

		_bAbort = bNew;
		_sec_bAbort.Unlock();

		return	true;
	}


	///
	///\brief
	///	スレッド中断用フラグ取得用関数
	///
	///\param[out]	bValue
	///取得した中断フラグ。trueならスレッド中断処理中もしくは中断終了
	///
	///\retval	true	取得成功
	///\retval	false	失敗
	///
	bool	get_bAbort(bool& bValue)
	{
		HRESULT	hr;

		hr = _sec_bAbort.Lock();
		if(FAILED(hr))
			return	false;

		bValue = _bAbort;
		_sec_bAbort.Unlock();

		return	true;
	}



protected:

	///
	///\brief
	///	スレッド中断中(もしくは中断終了した)かどうか
	///
	///\retval	true	中断中
	///\retval	false	それ以外
	///
	///生成したスレッド内で中断処理をする必要があるかどうかを
	///チェックするのに利用する。
	///
	virtual	bool	IsAbort(void)
	{
		bool	bValue;

		if(get_bAbort(bValue))
			return	bValue;

		//取得に失敗したときはabortフラグfalseとして処理
		return	false;
	}


public:

	CDnpThreadImpl()
	{
		_dwThreadID = 0;
		_hThread = 0;
		put_bAbort(false);
	}

	virtual	~CDnpThreadImpl()
	{
		////スレッドが破棄されていなければASSERT
		//ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		DestroyThread(2000);			//スレッド終了を最大2秒待つ
	}



	///
	///\brief
	///	スレッド中断処理開始用
	///
	///スレッドを中断したいときに呼び出す
	///
	///\retval	true	設定成功
	///\retval	false	失敗
	///
	virtual	bool	AbortThread(void)
	{
		return	put_bAbort(true);
	}


	///
	///\brief
	///	スレッドの終了を待つ
	///
	///生成したスレッドからは呼ばないこと
	///
	///\param[in]	dwWait
	///	終了を待つ最大時間(単位:ミリ秒)\n
	///	INFINITE なら無限に待つ
	///
	///\retval	true	スレッドは終了した
	///\retval	false	スレッド終了待ちに失敗
	///
	bool	WaitUntilThreadEnd(DWORD dwWait=INFINITE)
	{
		DWORD	dwRet;

		if(_hThread == NULL)
			return	true;

		dwRet = ::WaitForSingleObject(_hThread,dwWait);
		if(dwRet == WAIT_TIMEOUT)
			return	false;

		return	true;
	}


	///
	///\brief
	///		スレッド破壊処理
	///
	///スレッド中断フラグを立ててスレッドが終了するのを待つ
	///
	///生成したスレッドからは呼ばないこと
	///
	///\param[in]	dwWait
	///	終了を待つ最大時間(単位:ミリ秒)\n
	///	INFINITE なら無限に待つ
	///
	///\retval	true	スレッドは破壊された
	///\retval	false	失敗
	///
	bool	DestroyThread(DWORD dwWait=INFINITE)
	{
		if(_hThread == NULL)
			return	true;

		//スレッド強制終了フラグを立てる
		AbortThread();

		WaitUntilThreadEnd(dwWait);

		if(IsThreadRunning() == false)
		{
			::CloseHandle(_hThread);
			_hThread	= NULL;
			_dwThreadID	= 0;

			return	true;
		}

		return	false;
	}



	///
	///\brief
	///		スレッドが現在実行中かどうか
	///
	///生成したスレッドからは呼ばないこと
	///
	///\retval	true	実行中
	///\retval	false	実行していない
	///
	bool	IsThreadRunning(void)	const
	{
		BOOL	ret;
		DWORD	dwCode;

		if(_hThread == NULL)
			return	false;

		ret = ::GetExitCodeThread(_hThread,&dwCode);
		if(ret == FALSE)
			return	false;

		return	(dwCode == STILL_ACTIVE) ? true : false;
	}





	///
	///\brief
	///		スレッド生成処理
	///
	///\param[in]	pParam
	///	スレッドに渡したいユーザー定義パラメーター。自由に利用していい
	///
	///\return
	///		ゼロなら失敗。ゼロ以外なら成功
	///
	DWORD	CreateThread(void* pParam)
	{
		ATLASSERT(_dwThreadID == 0 && _hThread == 0);

		THREADPARAM*	pData;

		pData = new THREADPARAM;
		pData->pParam	= pParam;
		pData->pThis	= this;

		put_bAbort(false);
		_hThread = ::CreateThread(NULL,0,ThreadMain_,pData,0,&_dwThreadID);
		if(_hThread == NULL)
		{
			delete	pData;
			return 0;
		}

		return _dwThreadID;
	}



private:

	///
	///\brief
	///		スレッドメイン関数
	///
	///APIの CreateThread から呼ばれる。それ以外からは利用されない。
	///
	///内部で仮想関数の ThreadMain() を呼びだしている
	///
	static DWORD WINAPI ThreadMain_(void* pParam)
	{
		THREADPARAM* pData = (THREADPARAM*) pParam;

		CDnpThreadImpl*	pThis = pData->pThis;
		void*	pUseParam = pData->pParam;

		delete	pParam;

		return	pThis->ThreadMain(pUseParam);
	}


	///
	///\brief
	///	スレッドメイン処理(仮想関数のため実装なし)
	///
	///\param	pParam
	///	CreateThread() の引数として渡されたユーザー定義のパラメータ
	///
	virtual	DWORD	ThreadMain(void* pParam) = 0;
};

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

2009年03月18日

IEコントロールからのイベントを簡単に受信する

ATLを利用すると比較的簡単にイベントの受信ができる。ここではBEGIN_SINK_MAPによりイベントを受信する方法を紹介する。

まずひな形となるプロジェクトを以下の手順で作成する。

1.Visual Studio 2008の「ファイル」メニューの「新規作成」から「プロジェクト」を選択する。

2.「新しいプロジェクト」ウインドウが開いたら、テンプレートから「ATL/WTLアプリケーションウイザード」を選択してプロジェクトを作成する。

3.「ATL/WTLアプリケーションウイザード」が開いたら「ユーザーインターフェース機能」タブにある「ビューウインドウの形式」で「HTMLページ」を選択して「完了」ボタンを押す。

以上の操作により、シングルドキュメントインターフェースでビューの形式がHTMLページのプロジェクトが自動生成される。



ウイザードにより自動生成されたソースコードのうち、Viewクラスのヘッダーファイルを以下ソースコードのコメントで示したように変更する。
#pragma once

//■以下の宣言を5行追加
#define SINKID_COMTESTEVENTS 0		//Viewとして使うだけならいちいちdefineしなくてもいいが。。。
#include "mshtml.h"
#include "mshtmdid.h"
#include "Exdisp.h"
#include "Exdispid.h"


class CMy20090318_htmleventView : public CWindowImpl<CMy20090318_htmleventView, CAxWindow>
	,public IDispEventImpl<SINKID_COMTESTEVENTS, CMy20090318_htmleventView, &DIID_DWebBrowserEvents2>	//■この行を追加
{
	CComPtr<IWebBrowser2>	_pIWebBrowser2;			//■この行を追加。このビューがホストしているIWebBrowser2を使いやすいようにメンバーとして用意する

public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
		   (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
			return FALSE;

		// このメッセージを翻訳する機会をHTML ページに与える
		return (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);
	}

	//■以下の3行を追加(IEコントロールのイベントマップ。ここではDISPID_DOCUMENTCOMPLETEのみ処理している)
	BEGIN_SINK_MAP(CMy20090318_htmleventView)
		SINK_ENTRY_EX(SINKID_COMTESTEVENTS, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
	END_SINK_MAP()

	BEGIN_MSG_MAP(CMy20090318_htmleventView)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)		//■WM_CREATEのメッセージハンドラを追加
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)		//■WM_DESTROYのメッセージハンドラを追加
	END_MSG_MAP()


	///////////////////////////////////////////////////////
	//■これ以降の3つの関数を追加
	//


	//
	//	IEコントロールのイベントによりHTMLページの読み込みが終わった時に呼ばれる
	//
	STDMETHOD(OnDocumentComplete)(IDispatch* pDisp, VARIANT* URL)
	{
		ATLTRACE("■OnDocumentComplete Received.\n");

		return S_OK;
	}


	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		LRESULT	ret;

		//IWebBrowser2はOnCreate後でないと取得できないのでDefWindowProcで先に生成してしまう
		ret = DefWindowProc(uMsg,wParam,lParam);

		//IWebBroser2の取得
		_pIWebBrowser2 = NULL;
		QueryControl(&_pIWebBrowser2);
		if(_pIWebBrowser2 == NULL)
			return	ret;


		HRESULT		hr;
		CComVariant	vEmpty;

		//IEコントロールへの接続。これによりイベントが受信できるようになる。
		hr = Advise(_pIWebBrowser2);

		//URLの読み込み
		vEmpty.Clear();
		hr = _pIWebBrowser2->Navigate(L"http://www.usefullcode.net/",&vEmpty,&vEmpty,&vEmpty,&vEmpty);

		return	ret;
	}


	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		bHandled = FALSE;

		if(_pIWebBrowser2)
		{
			//IEコントロールからの切断
			Unadvise(_pIWebBrowser2);
			_pIWebBrowser2 = NULL;
		}

		return	0;
	}
};


たったこれだけの手順でIEコントロールからのDISPID_DOCUMENTCOMPLETEを処理できる。BEGIN_SINK_MAP内にDISPID_BEFORENAVIGATE2やDISPID_NAVIGATECOMPLETE2などなど、処理したいイベントのシンクマップを追加すればそれだけで処理できるようになる。

プロジェクトファイルをダウンロード
前の10件 3  4  5  6  7  8  9  10  11  12  13