EternalWindows
BHO / BHOサンプル

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);


// CBho


CBho::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;
}


// CClassFactory


STDMETHODIMP 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 Export


STDAPI 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;
}


// Function


void 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がロードされることになります。


戻る