BHOのサンプルコードを示します。まず、DLLのdefファイルを示します。
LIBRARY "mydll"EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE
次に、sample.hを示します。
class CBho : public IObjectWithSite, public IDispatch{public: STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP SetSite(IUnknown *pUnkSite); STDMETHODIMP GetSite(REFIID riid, void **ppvSite); STDMETHODIMP GetTypeInfoCount(UINT *pctinfo); STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo); STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId); STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr); CBho(); ~CBho();private: HINTERNET OpenInternetSession();private: LONG m_cRef; IUnknown *m_pSite; IWebBrowser2 *m_pWebBrowser2; IConnectionPoint *m_pConnectionPoint; DWORD m_dwCookie; CMouseMoveListener *m_pMouseMoveListener; HINTERNET m_hSession;};class CClassFactory : public IClassFactory{public: STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject); STDMETHODIMP LockServer(BOOL fLock);};
次に、mousemove.hを示します。
class CMouseMoveListener : public IDispatch{public: STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP GetTypeInfoCount(UINT *pctinfo); STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo); STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId); STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr); CMouseMoveListener(IWebBrowser2 *pWebBrowser2, HINTERNET hSession); ~CMouseMoveListener();private: void CreatePopup(); BOOL SendRequest(LPWSTR lpszUrl, LPBYTE lpBuffer, DWORD dwBufferSize); private: LONG m_cRef; HWND m_hwndParent; HWND m_hwndPopup; IWebBrowser2 *m_pWebBrowser2; IHTMLDocument2 *m_pDocument2; HINTERNET m_hSession; BSTR m_bstrHrefPrev;};
次に、ソースファイル(CBhoの実装)を示します。
#include <windows.h>#include <shlwapi.h>#include <shlobj.h>#include <mshtml.h>#include <exdispid.h>#include <wininet.h>#include "mousemove.h"#include "sample.h"#pragma comment (lib, "shlwapi.lib")#pragma comment (lib, "urlmon.lib")#pragma comment (lib, "wininet.lib")const CLSID CLSID_BhoSample = {0x5b10edef, 0xb228, 0x4629, {0x8e, 0x82, 0x34, 0xe0, 0x69, 0xc8, 0x60, 0x80}};const TCHAR g_szClsid[] = TEXT("{5B10EDEF-B228-4629-8E82-34E069C86080}");LONG g_lLocks = 0;HINSTANCE g_hinstDll = NULL;void LockModule(BOOL bLock);BOOL CreateRegistryKey(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPTSTR lpszData);BOOL CreateRegistryKeyDword(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPDWORD lpdw);// CBhoCBho::CBho(){ m_cRef = 1; m_pSite = NULL; m_pWebBrowser2 = NULL; m_pConnectionPoint = 0; m_dwCookie = 0; m_pMouseMoveListener = NULL; m_hSession = NULL; LockModule(TRUE);}CBho::~CBho(){ LockModule(FALSE);}STDMETHODIMP CBho::QueryInterface(REFIID riid, void **ppvObject){ *ppvObject = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IObjectWithSite)) *ppvObject = static_cast<IObjectWithSite *>(this); else if (IsEqualIID(riid, IID_IDispatch) || IsEqualIID(riid, DIID_DWebBrowserEvents2)) *ppvObject = static_cast<IDispatch *>(this); else return E_NOINTERFACE; AddRef(); return S_OK;}STDMETHODIMP_(ULONG) CBho::AddRef(){ return InterlockedIncrement(&m_cRef);}STDMETHODIMP_(ULONG) CBho::Release(){ if (InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef;}STDMETHODIMP CBho::SetSite(IUnknown *pUnkSite){ HRESULT hr = S_OK; if (m_pSite != NULL){ m_pSite->Release(); m_pSite = NULL; } if (pUnkSite != NULL) { IConnectionPointContainer *pConnectionPointContainer; m_pSite = pUnkSite; m_pSite->AddRef(); IUnknown_QueryService(pUnkSite, SID_SWebBrowserApp, IID_PPV_ARGS(&m_pWebBrowser2)); hr = m_pWebBrowser2->QueryInterface(IID_PPV_ARGS(&pConnectionPointContainer)); if (FAILED(hr)) return hr; pConnectionPointContainer->FindConnectionPoint(DIID_DWebBrowserEvents2, &m_pConnectionPoint); pConnectionPointContainer->Release(); m_pConnectionPoint->Advise(static_cast<IDispatch *>(this), &m_dwCookie); m_hSession = OpenInternetSession(); return S_OK; } else { if (m_hSession != NULL) InternetCloseHandle(m_hSession); if (m_pMouseMoveListener != NULL) m_pMouseMoveListener->Release(); if (m_pConnectionPoint != NULL) { m_pConnectionPoint->Unadvise(m_dwCookie); m_pConnectionPoint->Release(); } if (m_pWebBrowser2 != NULL) m_pWebBrowser2->Release(); } return hr;}STDMETHODIMP CBho::GetSite(REFIID riid, void **ppvSite){ HRESULT hr; *ppvSite = NULL; if (m_pSite != NULL) hr = m_pSite->QueryInterface(riid, ppvSite); else hr = E_FAIL; return hr;}STDMETHODIMP CBho::GetTypeInfoCount(UINT *pctinfo){ *pctinfo = 0; return S_OK;}STDMETHODIMP CBho::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo){ return E_NOTIMPL;}STDMETHODIMP CBho::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId){ return E_NOTIMPL;}STDMETHODIMP CBho::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr){ if (dispIdMember == DISPID_NAVIGATECOMPLETE2) { IHTMLDocument2 *pDocument2; IDispatch *pDispatch; VARIANT varDispatch; m_pWebBrowser2->get_Document(&pDispatch); pDispatch->QueryInterface(IID_PPV_ARGS(&pDocument2)); pDispatch->Release(); if (m_pMouseMoveListener != NULL) m_pMouseMoveListener->Release(); m_pMouseMoveListener = new CMouseMoveListener(m_pWebBrowser2, m_hSession); varDispatch.vt = VT_DISPATCH; varDispatch.pdispVal = m_pMouseMoveListener; pDocument2->put_onmousemove(varDispatch); pDocument2->Release(); } else return DISP_E_MEMBERNOTFOUND; return S_OK;}HINTERNET CBho::OpenInternetSession(){ CHAR szUserAgentA[256]; WCHAR szUserAgentW[256]; DWORD dwLength; HINTERNET hSession; DWORD dwTimeOut; UrlMkGetSessionOption(URLMON_OPTION_USERAGENT, szUserAgentA, sizeof(szUserAgentA), &dwLength, 0); #ifdef UNICODE MultiByteToWideChar(CP_ACP, 0, szUserAgentA, -1, szUserAgentW, 256); hSession = InternetOpen(szUserAgentW, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);#else hSession = InternetOpen(szUserAgentA, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);#endif dwTimeOut = 2 * 1000; InternetSetOption(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeOut, sizeof(DWORD)); return hSession;}// CClassFactorySTDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void **ppvObject){ *ppvObject = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) *ppvObject = static_cast<IClassFactory *>(this); else return E_NOINTERFACE; AddRef(); return S_OK;}STDMETHODIMP_(ULONG) CClassFactory::AddRef(){ LockModule(TRUE); return 2;}STDMETHODIMP_(ULONG) CClassFactory::Release(){ LockModule(FALSE); return 1;}STDMETHODIMP CClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject){ CBho *p; HRESULT hr; *ppvObject = NULL; if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION; p = new CBho(); if (p == NULL) return E_OUTOFMEMORY; hr = p->QueryInterface(riid, ppvObject); p->Release(); return hr;}STDMETHODIMP CClassFactory::LockServer(BOOL fLock){ LockModule(fLock); return S_OK;}// DLL ExportSTDAPI DllCanUnloadNow(void){ return g_lLocks == 0 ? S_OK : S_FALSE;}STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv){ static CClassFactory serverFactory; HRESULT hr; *ppv = NULL; if (IsEqualCLSID(rclsid, CLSID_BhoSample)) hr = serverFactory.QueryInterface(riid, ppv); else hr = CLASS_E_CLASSNOTAVAILABLE; return hr;}STDAPI DllRegisterServer(void){ TCHAR szModulePath[MAX_PATH]; TCHAR szKey[256]; DWORD dwValue; wsprintf(szKey, TEXT("CLSID\\%s"), g_szClsid); if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, NULL, TEXT("BHO Sample"))) return E_FAIL; GetModuleFileName(g_hinstDll, szModulePath, sizeof(szModulePath) / sizeof(TCHAR)); wsprintf(szKey, TEXT("CLSID\\%s\\InprocServer32"), g_szClsid); if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, NULL, szModulePath)) return E_FAIL; wsprintf(szKey, TEXT("CLSID\\%s\\InprocServer32"), g_szClsid); if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, TEXT("ThreadingModel"), TEXT("Apartment"))) return E_FAIL; wsprintf(szKey, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\Browser Helper Objects\\%s"), g_szClsid); if (!CreateRegistryKey(HKEY_LOCAL_MACHINE, szKey, NULL, NULL)) return E_FAIL; dwValue = 1; wsprintf(szKey, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\Browser Helper Objects\\%s"), g_szClsid); if (!CreateRegistryKeyDword(HKEY_LOCAL_MACHINE, szKey, TEXT("NoExplorer"), &dwValue)) return E_FAIL; return S_OK;}STDAPI DllUnregisterServer(void){ TCHAR szKey[256]; wsprintf(szKey, TEXT("CLSID\\%s"), g_szClsid); SHDeleteKey(HKEY_CLASSES_ROOT, szKey); wsprintf(szKey, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\Browser Helper Objects\\%s"), g_szClsid); SHDeleteKey(HKEY_LOCAL_MACHINE, szKey); return S_OK;}BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved){ switch (dwReason) { case DLL_PROCESS_ATTACH: g_hinstDll = hinstDll; DisableThreadLibraryCalls(hinstDll); return TRUE; } return TRUE;}// Functionvoid LockModule(BOOL bLock){ if (bLock) InterlockedIncrement(&g_lLocks); else InterlockedDecrement(&g_lLocks);}BOOL CreateRegistryKey(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPTSTR lpszData){ HKEY hKey; LONG lResult; DWORD dwSize; lResult = RegCreateKeyEx(hKeyRoot, lpszKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); if (lResult != ERROR_SUCCESS) return FALSE; if (lpszData != NULL) dwSize = (lstrlen(lpszData) + 1) * sizeof(TCHAR); else dwSize = 0; RegSetValueEx(hKey, lpszValue, 0, REG_SZ, (LPBYTE)lpszData, dwSize); RegCloseKey(hKey); return TRUE;}BOOL CreateRegistryKeyDword(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPDWORD lpdw){ HKEY hKey; LONG lResult; lResult = RegCreateKeyEx(hKeyRoot, lpszKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); if (lResult != ERROR_SUCCESS) return FALSE; RegSetValueEx(hKey, lpszValue, 0, REG_DWORD, (LPBYTE)lpdw, sizeof(DWORD)); RegCloseKey(hKey); return TRUE;}
次に、ソースファイル(CMouseMoveListenerの実装)を示します。
#include <windows.h>#include <shlwapi.h>#include <shlobj.h>#include <mshtml.h>#include <wininet.h>#include "mousemove.h"CMouseMoveListener::CMouseMoveListener(IWebBrowser2 *pWebBrowser2, HINTERNET hSession){ IDispatch *pDispatch; m_cRef = 1; m_hwndParent = NULL; m_hwndPopup = NULL; m_pWebBrowser2 = pWebBrowser2; m_hSession = hSession; m_bstrHrefPrev = NULL; m_pWebBrowser2->get_Document(&pDispatch); pDispatch->QueryInterface(IID_PPV_ARGS(&m_pDocument2)); pDispatch->Release(); CreatePopup();}CMouseMoveListener::~CMouseMoveListener(){ m_pDocument2->Release(); DestroyWindow(m_hwndPopup);}STDMETHODIMP CMouseMoveListener::QueryInterface(REFIID riid, void **ppvObject){ *ppvObject = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDispatch)) *ppvObject = static_cast<IDispatch *>(this); else return E_NOINTERFACE; AddRef(); return S_OK;}STDMETHODIMP_(ULONG) CMouseMoveListener::AddRef(){ return InterlockedIncrement(&m_cRef);}STDMETHODIMP_(ULONG) CMouseMoveListener::Release(){ if (InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef;}STDMETHODIMP CMouseMoveListener::GetTypeInfoCount(UINT *pctinfo){ *pctinfo = 0; return S_OK;}STDMETHODIMP CMouseMoveListener::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo){ return E_NOTIMPL;}STDMETHODIMP CMouseMoveListener::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId){ return E_NOTIMPL;}STDMETHODIMP CMouseMoveListener::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr){ POINT pt; BSTR bstrUrl, bstrHref; IHTMLElement *pElement; IHTMLAnchorElement *pAnchorElement; CHAR szText[4096]; GetCursorPos(&pt); ScreenToClient(m_hwndParent, &pt); m_pDocument2->elementFromPoint(pt.x, pt.y, &pElement); if (pElement == NULL) { ShowWindow(m_hwndPopup, SW_HIDE); SysFreeString(m_bstrHrefPrev); m_bstrHrefPrev = NULL; return E_FAIL; } pElement->QueryInterface(IID_PPV_ARGS(&pAnchorElement)); pElement->Release(); if (pAnchorElement == NULL) { ShowWindow(m_hwndPopup, SW_HIDE); SysFreeString(m_bstrHrefPrev); m_bstrHrefPrev = NULL; return E_FAIL; } pAnchorElement->get_href(&bstrHref); if (lstrcmpW(bstrHref, m_bstrHrefPrev) == 0) { pAnchorElement->Release(); return S_OK; } m_bstrHrefPrev = bstrHref; if (PathIsURL(bstrHref)) bstrUrl = SysAllocString(bstrHref); else { m_pDocument2->get_URL(&bstrUrl); PathRemoveFileSpec(bstrUrl); lstrcat(bstrUrl, bstrHref); } if (SendRequest(bstrUrl, (LPBYTE)szText, sizeof(szText))) SetWindowTextA(m_hwndPopup, szText); else SetWindowTextA(m_hwndPopup, "error"); MoveWindow(m_hwndPopup, pt.x, pt.y, 600, 500, TRUE); ShowWindow(m_hwndPopup, SW_SHOW); SysFreeString(bstrUrl); pAnchorElement->Release(); return S_OK;}void CMouseMoveListener::CreatePopup(){ IShellBrowser *pShellBrowser; IUnknown_QueryService(m_pWebBrowser2, SID_STopLevelBrowser, IID_PPV_ARGS(&pShellBrowser)); IUnknown_GetWindow(pShellBrowser, &m_hwndParent); m_hwndPopup = CreateWindowEx(0, TEXT("STATIC"), TEXT(""), WS_CHILD, 0, 0, 0, 0, m_hwndParent, (HMENU)1, 0, NULL); pShellBrowser->Release();}BOOL CMouseMoveListener::SendRequest(LPWSTR lpszUrl, LPBYTE lpBuffer, DWORD dwBufferSize){ HINTERNET hConnect, hRequest; URL_COMPONENTSW urlComponents; WCHAR szHostName[INTERNET_MAX_HOST_NAME_LENGTH], szUrlPath[INTERNET_MAX_PATH_LENGTH]; DWORD dwSize; DWORD dwStatusCode; ZeroMemory(&urlComponents, sizeof(URL_COMPONENTSW)); urlComponents.dwStructSize = sizeof(URL_COMPONENTSW); urlComponents.lpszHostName = szHostName; urlComponents.dwHostNameLength = sizeof(szHostName) / sizeof(WCHAR); urlComponents.lpszUrlPath = szUrlPath; urlComponents.dwUrlPathLength = sizeof(szUrlPath) / sizeof(WCHAR); if (!InternetCrackUrlW(lpszUrl, lstrlen(lpszUrl), 0, &urlComponents)) return FALSE; hConnect = InternetConnectW(m_hSession, szHostName, urlComponents.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); if (hConnect == NULL) return FALSE; hRequest = HttpOpenRequestW(hConnect, L"GET", szUrlPath, NULL, NULL, NULL, 0, 0); if (hRequest == NULL) { InternetCloseHandle(hConnect); return FALSE; } if (!HttpSendRequest(hRequest, NULL, 0, NULL, 0)) { InternetCloseHandle(hRequest); InternetCloseHandle(hConnect); return FALSE; } dwSize = sizeof(DWORD); HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &dwStatusCode, &dwSize, NULL); if (dwStatusCode == HTTP_STATUS_OK){ DWORD dwRead = 0; InternetReadFile(hRequest, lpBuffer, dwBufferSize, &dwRead); } InternetCloseHandle(hRequest); InternetCloseHandle(hConnect); return dwStatusCode == HTTP_STATUS_OK;}
BHOはCOMサーバーであるため、DllRegisterServerではCOMサーバーとしての情報をレジストリに書き込んでいます。これに加えてBHOの情報を書き込まなければならないため、次のキーを参照しています。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objectsこのキーのサブキーとして、オブジェクトのCLSIDを持ったキーを作成するようにします。これにより、BHOがIEにロードされることになります。既定ではBHOはエクスプローラーにもロードされますが、 NoExplorerというエントリを作成して1を指定すれば、ロードされないようになります。
DLLを作成したら、コマンドプロント上で次の文字列を入力します。ファイルパスの部分は、BHOが存在するパスに置き換えてください。
regsvr32 C:\sample.dll (登録の解除時は regsvr32 /u C:\sample.dll のようにする)
regsvr32にDLLのパスを指定すれば、DLLのDllRegisterServerを呼び出してくれます。これによってBHOの情報がレジストリに登録されることになり、 IEの起動時にBHOがロードされることになります。
联系客服