Click here to Skip to main content
15,888,461 members
Articles / Desktop Programming / Win32

Writing a BHO in Plain C++

Rate me:
Please Sign up or sign in to vote.
4.88/5 (45 votes)
6 Jun 2009CPOL17 min read 210.8K   10.5K   128   45
How to write an Internet Explorer plug-in (Browser Helper Object - BHO) using just C++ and the Windows API; no ATL or MFC involved!
The BHO running in Internet Explorer 8

The BHO running in Internet Explorer 8

Contents

Introduction

Browser Helper Objects (also called BHOs) are COM components that act as Internet Explorer plug-ins. BHOs can be used for customizing Internet Explorer to any extent: from user-interface modifications to web filters to download managers.

In this article, we will learn how to write and install a simple BHO in C++ without the use of extraneous frameworks like ATL or MFC. This example BHO can be used as the starting point for your own BHO.

Background

COM — or Component Object Model — is a language-neutral technology used extensively in Windows to enable the componentization and re-use of software modules.

Most COM code written in C++ (and hence in examples on the Web) uses the Active Template Library (ATL) and/or Microsoft Foundation Classes (MFC) frameworks to help get the job done. However, learning to use ATL and MFC effectively can often be yet another barrier to creating COM objects, specially simple ones like BHOs.

This article will teach you what you need to know about COM and BHOs to start writing your own BHOs, and do it using just C++ and the Windows API without being forced into a more complex framework like ATL or MFC.

Getting Started

The best way to understand this article is to download the source code from the link above and follow along. The source code is well-documented, and should be easy to understand.

Understanding the COM Code

COM Terms

First, let's get some COM terminology out of the way:

  • interface — A set of methods that is visible to other objects. It is the equivalent of public methods in a C++ pure-virtual class.
  • coclass — Derives from one or more interfaces, and implements all their methods with concrete functionality. It is the equivalent of an instantiable C++ class derived from one or more pure-virtual classes.
  • object — An instance of a coclass.
  • GUIDGlobally Unique IDentifier — A GUID is a 128-bit unique number. It can be generated via the guidgen.exe utility.
  • IIDInterface IDentifier — A GUID that identifies an interface.
  • CLSIDCLaSs IDentifier — A GUID that identifies a COM component. You can find the example BHO's CLSID in the common.h file as CLSID_IEPlugin_Str. Every COM component has a different identifier, and you should generate a new one if you decide to build your own BHO from the example BHO code.

The IUnknown Interface

In order for us to create COM objects, we must write coclasses which implement interfaces. All COM objects must implement an interface known as the IUnknown interface. This interface has three very basic methods which allow other objects to memory-manage objects of a coclass as well as ask objects for other interfaces. These three methods are known as QueryInterface, AddRef, and Release. Since all the various coclasses we'll be implementing derive from IUnknown, it makes sense to create a coclass of IUnknown which implements the three IUnknown methods. We'll call this coclass CUnknown and have all our other coclasses derive from it so we don't have to write the implementation of the IUnknown methods separately for every coclass.

Note: You can find the definition and implementation of CUnknown in the unknown.h file.

COM DLL Exports

Every COM DLL exports four functions that are used by the COM system to create and manage COM objects from the DLL as well as to install and uninstall the DLL. These functions are:

  • DllGetClassObject
  • DllCanUnloadNow
  • DllRegisterServer
  • DllUnregisterServer

Note: You can find these functions in the main.cpp file, and they are declared as exports in the dll.def file.

Our DLL must have a coclass of the IClassFactory interface. We'll call this coclass CClassFactory. The DllGetClassObject function creates CClassFactory objects and returns interface pointers to them. The IClassFactory interface is explained in more detail shortly.

The DllCanUnloadNow function is called by COM to determine if our DLL can be unloaded from a process. All we have to do is check whether our DLL is currently managing any object, and return S_OK if we aren't, or S_FALSE if we are. We can do this by incrementing a DLL-global reference counter DllRefCount in the constructors of our coclasses and decrementing the counter in their destructors. If the reference counter is non-zero, it means that instances of our coclasses still exist and the DLL should not be unloaded at the moment.

DllRegisterServer is called by a program that wants our DLL to install itself. We have to register our COM component in the system and also as a BHO. We do this by creating the following Registry entries:

  • HKEY_CLASSES_ROOT\CLSID\<CLSID_IEPlugin_Str> — The default value of this key should be set to a human-readable description of the COM component. In this case, it's "CodeProject Example BHO".
  • HKEY_CLASSES_ROOT\CLSID\<CLSID_IEPlugin_Str>\InProcServer32 — The existence of this key identifies that this COM component can be loaded as a DLL into the process that wants to use it. There are two values we need to set for this key, as below:
    • (default) — A REG_SZ or REG_EXPAND_SZ value specifying the path to the DLL which contains the COM component.
    • ThreadingModel — This specifies the threading model of the COM component. It's a more advanced concept, and we don't need to worry about it. We just set it to "Apartment".
  • HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ Explorer\Browser Helper Objects\<CLSID_IEPlugin_Str> — The existence of this key registers our COM component as a BHO. We create one value under this key named NoExplorer and set it as a REG_DWORD with a value of 1. Normally, BHOs are also loaded by explorer.exe, but this value prevents explorer.exe from unnecessarily loading our BHO.

DllUnregisterServer is called to do the exact opposite of DllRegisterServer — unregister our COM component and also remove it as a BHO. In order to do this, we just need to delete the Registry keys we create in DllRegisterServer.

The IClassFactory Interface

COM uses an IClassFactory object created by our DLL's DllGetClassObject function to get instances of other interface-implementations supported by the DLL. The IClassFactory interface defines the methods CreateInstance and LockServer. We call our coclass implementing the IClassFactory interface CClassFactory.

Note: You can find the definition of CClassFactory in ClassFactory.h, and the implementation in ClassFactory.cpp.

CreateInstance does exactly what it says — creates an instance of a coclass in our DLL that supports a given IID. Since we are a BHO, we only need to create instances of a coclass that supports the IObjectWithSite interface.

LockServer is called to either lock or unlock our DLL in memory. Depending on whether it is locking or unlocking, our implementation of LockServer simply increments or decrements the DLL-global DllRefCount variable.

Understanding the BHO Code

How Internet Explorer Loads the BHO

When a BHO is to be loaded by Internet Explorer, it calls a COM function called CoCreateInstance, passing it the CLSID of our BHO and the IID of an interface called IObjectWithSite. IObjectWithSite is explained in more detail below. COM in-turn loads our DLL into the Internet Explorer process by looking up our CLSID in the Registry, and then calls our exported DllGetClassObject function to get an instance of our CClassFactory coclass. Once COM has a pointer to our CClassFactory object, COM calls its CreateInstance method, passing it the IID that Internet Explorer supplied. Our implementation of CreateInstance creates an instance of our implementation of IObjectWithSite, which is called CObjectWithSite, and gets the interface pointer of the requested IID from it, and gives it to back to COM, which passes it on to Internet Explorer. Internet Explorer then uses the IObjectWithSite interface pointer to interact with our BHO.

The IObjectWithSite Interface

BHOs are required to implement the IObjectWithSite interface, which is how Internet Explorer communicates with a BHO. This interface has two methods, SetSite and GetSite. The coclass in our DLL which implements the IObjectWithSite interface is known as CObjectWithSite.

Note: You can find the definition of CObjectWithSite in ObjectWithSite.h, and the implementation in ObjectWithSite.cpp.

SetSite is called by Internet Explorer to give us a pointer to a site object. A site object is merely a COM object which is created by Internet Explorer and can be used by our BHO to communicate with Internet Explorer. Our implementation of SetSite gets an interface pointer to the IConnectionPointContainer interface from the site object. We then use a method FindConnectionPoint in IConnectionPointContainer to get an IConnectionPoint interface pointer to an object that supports the DWebBrowserEvents2 dispatch interface. A dispatch interface is a special type of interface which is derived from IDispatch and is used to receive event notifications through its Invoke method. Our implementation of DWebBrowserEvents2 is known as CEventSink, and is explained in more detail in the next section. We use the Advise method of the IConnectionPoint interface to tell Internet Explorer to pass events on to our CEventSink object.

Our implementation of SetSite also gets an interface pointer to the IWebBrowser2 interface from the site object. The IWebBrowser2 interface is implemented by Internet Explorer's site object, and has methods which can be used by us to interact with Internet Explorer.

Note: Since this example BHO only receives event notifications from Internet Explorer, and does not actually control Internet Explorer, we don't make use of any of the methods in IWebBrowser2. However, I have included the code to get the IWebBrowser2 interface so that you can use it in your own BHO if needed. You can find the documentation for IWebBrowser2 here.

GetSite is called by Internet Explorer to know which object we have currently set as the site object. Internet Explorer passes us an IID for an interface it wants from our currently set site object, and we simply get that interface from our site object and give it back to Internet Explorer.

The CEventSink Coclass

The CEventSink coclass is our implementation of the DWebBrowserEvents2 dispinterface. DWebBrowserEvents2 derives from IDispatch but does not implement any methods of its own. Instead, DWebBrowserEvents2 exists only so that its unique DIID (dispatch IID) can exist. This DIID identifies the events that a coclass of DWebBrowserEvents2 can receive on its implementation of the IDispatch method Invoke.

Note: You can find the definition of CEventSink in EventSink.h, and the implementation in EventSink.cpp.

When Internet Explorer wants to notify us of an event, it will call the Invoke method of CEventSink, passing the event ID in the dispIdMember parameter and other information about the event in the pDispParams parameter. IDispatch also has three other methods besides Invoke (GetTypeInfoCount, GetTypeInfo, and GetIDsOfNames), but we don't need to implement any functionality for them because we only receive events.

Another difference that you might notice about CEventSink from our other coclasses is that it does not derive from CUnknown. This is because we only need one DLL-global statically-allocated instance of CEventSink, called EventSink. Because of this, we don't need to implement any reference counting, and hence don't need the reference counting and memory-management functionality of CUnknown.

Handling Events

Internet Explorer calls CEventSink::Invoke to notify us of events. The dispIdMember parameter contains an ID identifying which event is being fired, and the pDispParams->rgvarg[] array contains the arguments of the event itself as an array of VARIANTs. You can see what arguments an event takes by looking it up in the documentation of DWebBrowserEvents2, which can be found here. The arguments are passed in the pDispParams->rgvarg[] array in the opposite order they are listed in the event's documentation. In order to convert these arguments from a VARIANT to a more usable type, we first declare a VARIANT for every argument we will use and initialize it using the VariantInit API function. Then, we can use the VariantChangeType API function to convert the VARIANTs in the pDispParams->rgvarg[] array to a VARIANT of a more usable type. Once we have done this for all the arguments we will need, we can pass the value of the arguments to our own Event_* method by using an appropriate member of our converted VARIANT variables. After the event handler method returns, we free any resources used by our converted VARIANTs by calling the VariantClear method on each of them. A concrete example of how to do this is given below.

The BeforeNavigate2 Event

Our example BHO just handles one event, the BeforeNavigate2 event. The documentation for BeforeNavigate2 can be found here. This event is fired just before Internet Explorer navigates to a new location.

Taking a look at the documentation, we see that BeforeNavigate2 gives us seven arguments. We will not concern ourselves with the pDisp argument, which is just an IDispatch interface pointer to the site object.

The event arguments are stored in the pDispParams->rgvarg[] as variants. Before we can use these arguments, though, we need to convert the variants to different variant types which are easier to use. In order to do this, we have an array of variants in the Invoke method which stores the converted variants. We first need to initialize each of these variants using the VariantInit function, and when we are done, we free any used resources by using the VariantClear function.

The variant array in the Invoke method

C++
VARIANT v[5];
// Used to hold converted event parameters before
// passing them onto the event handling method

...
for(n=0;n<5;n++) VariantInit(&v[n]); // initialize the variant array
... // use the variant array here
for(n=0;n<5;n++) VariantClear(&v[n]); // free the variant array

The url argument contains the URL that is being navigated to. It is the fifth argument from last (the last one being the 0th), so we access it as pDispParams->rgvarg[5]. We convert this variant to a variant type of VT_BSTR as it may not be in that format, and store the converted variant into v[0]. We can then access the URL as a BSTR string by using v[0].bstrVal. A BSTR string is commonly used in COM to pass string data. It consists of a 4-byte prefix indicating the length of the string, followed by the string data as a double-byte character NULL-terminated Unicode string. A BSTR variable always points to the string data, and not to the 4-byte prefix before it, which conveniently allows us to use it as a C-style string. A double-byte character string type is declared in the Windows API headers as LPOLESTR, regardless of whether the program is using the wide-character set by default. So, we can pass the string v[0].bstrVal as an LPOLESTR parameter to the Event_BeforeNavigate2 event-handling method.

How to access the url argument

C++
VariantChangeType(&v[0],&pDispParams->rgvarg[5],0,VT_BSTR);
// make sure the argument is of variant type VT_BSTR

...
Event_BeforeNavigate2( (LPOLESTR) v[0].bstrVal , ... );
// pass the url argument to the event handler

The Flags argument contains information about whether the navigation is a result of an external window or tab. It is the fourth argument from last, so we access it as pDispParams->rgvarg[4]. We convert this variant to a variant type of VT_I4, and store the converted variant into v[1]. VT_I4 means a 4-byte signed integer, so we can then access the value as a long through v[1].lVal, and pass it on to Event_BeforeNavigate2.

How to access the Flags argument

C++
VariantChangeType(&v[1],&pDispParams->rgvarg[4],0,VT_I4);
// make sure the argument is of variant type VT_I4 (a long)

...
Event_BeforeNavigate2( ... , v[1].lVal , ... );
// pass the Flags argument to the event handler

The TargetFrameName argument contains the name of the frame in which the navigation is happening. It is the third argument from last, so we access it as pDispParams->rgvarg[3]. We convert this variant to a variant type of VT_BSTR and store the converted variant into v[2]. We can then access the string value as a LPOLESTR through v[2].bstrVal, just like with the url argument, and similarly pass it on to Event_BeforeNavigate2.

How to access the TargetFrameName argument

C++
VariantChangeType(&v[2],&pDispParams->rgvarg[3],0,VT_BSTR);
// make sure the argument is of variant type VT_BSTR
...
Event_BeforeNavigate2( ... , (LPOLESTR) v[2].bstrVal , ... );
// pass the TargetFrameName argument to the event handler

The PostData argument contains POST data if the navigation is due to an HTTP POST request. It is the second argument from last, so we access it as pDispParams->rgvarg[2]. The documentation states that PostData is of the variant type VT_BYREF|VT_VARIANT. This means that PostData is actually a pointer to another variant. Reading further in the Remarks section of the documentation, we can see that the variant that is pointed to contains a SAFEARRAY. A SAFEARRAY is often used in COM to contain array data. We convert the PostData variant to a variant of type VT_UI1|VT_ARRAY and store the converted variant into v[3]. VT_UI1|VT_ARRAY means that after the conversion, v[3] is a variant that points to a SAFEARRAY which has a 1-dimensional array of 1-byte unsigned integers. Before accessing the SAFEARRAY data in v[3], we first need to check if the conversion to VT_UI1|VT_ARRAY was successful. If the navigation did not contain any POST data, then the conversion would not have been successful and the variant type of v[3] would be VT_EMPTY. On the other hand, if the data exists, we can access the SAFEARRAY the variant points to by using v[3].parray, and we can access the data within that SAFEARRAY using the SafeArray*() API functions.

First, we get the size of the data in the array using the functions SafeArrayGetLBound and SafeArrayGetUBound. These functions retrieve the lower and upper bounds of the array, respectively. Subtracting the lower bound from the upper bound and adding 1 gives us the number of elements in the array. We then access the actual data by using the function SafeArrayAccessData, which gives us a pointer to the data and also locks the array. Since the array's elements are of type 1-byte unsigned integer, we can access the data as a C-style array of unsigned chars. We pass the data pointer and data size to Event_BeforeNavigate2. Afterwards, we unlock the array by calling SafeArrayUnaccessData.

How to access the PostData argument

C++
PVOID pv;
LONG lbound,ubound,sz;
...
VariantChangeType(&v[3],&pDispParams->rgvarg[2],0,VT_UI1|VT_ARRAY);
// make sure the argument is a variant containing
// a SAFEARRAY of 1-byte unsigned integers

if(v[3].vt!=VT_EMPTY) {
  // If the conversion was successful, we have POST data
  SafeArrayGetLBound(v[3].parray,0,&lbound); // get the lower bound (first element index)
  SafeArrayGetUBound(v[3].parray,0,&ubound); // get the upper bound (last element index)
  sz=ubound-lbound+1; // use the bounds to calculate the data size
  SafeArrayAccessData(v[3].parray,&pv); // get access to the data
} else {
  // If the conversion was not successful, we do not have any POST data
  sz=0; // set data size to zero
  pv=NULL; // set the data pointer to NULL
}
...
Event_BeforeNavigate2( ... , (PUCHAR) pv , sz , ... );
// pass the pointer to the data and the data size
// of the PostData argument to the event handler
...
if(v[3].vt!=EMTPY) SafeArrayUnaccessData(v[3].parray);
// if we had previously accessed the data in the SAFEARRAY, unaccess it now

The Headers argument contains any additional HTTP headers that were sent for the navigation. It is the next-to-last argument, so we access it as pDispParams->rgvarg[1]. We convert this variant to a variant of type VT_BSTR and store the converted variant into v[4]. We pass the data on to Event_BeforeNavigate2 in the same fashion as the url and TargetFrameName arguments.

How to access the Headers argument

C++
VariantChangeType(&v[4],&pDispParams->rgvarg[4],0,VT_BSTR);
// make sure the argument is of variant type VT_BSTR
...
Event_BeforeNavigate2( ... , (LPOLESTR) v[4].bstrVal , ... );
// pass the Headers argument to the event handler

The Cancel argument is a return value that indicates to Internet Explorer whether it should continue or cancel the navigation. It is a variant of type VT_BYREF|VT_BOOL, which means it contains a pointer to a VARIANT_BOOL type, which takes a value of either VARIANT_TRUE or VARIANT_FALSE. It is the last argument, so we access it as pDispParams->rgvarg[0]. We can access the VARIANT_BOOL that this argument points to via *(pDispParams->rgvarg[0].pboolVal). If we set the VARIANT_BOOL pointed to by the Cancel argument to VARIANT_TRUE, Internet Explorer cancels the navigation. If we set it to VARIANT_FALSE, Internet Explorer continues the navigation as normal. Because more than one BHO can be handling the BeforeNavigate2 event, the pre-existing value of the VARIANT_BOOL pointed to by the Cancel argument can correspond to a value set by a BHO that handled BeforeNavigate2 before our BHO. We pass the existing value to Event_BeforeNavigate2 as a bool value, so Event_BeforeNavigate2 can decide whether to override the existing value or keep it. The return value of Event_BeforeNavgiate2 is a bool that indicates the new value of the Cancel argument.

How to access the Cancel argument

C++
bool b;
...

b = Event_BeforeNavigate2( ... ,
    ( (*(pDispParams->rgvarg[0].pboolVal)) != VARIANT_FALSE ) );
// pass the pre-existing value of the Cancel argument
// to the event handler, and get the new value for it
...
// Set the new value of the Cancel argument based upon
// the return value of Event_BeforeNavigate2()
if(b) *(pDispParams->rgvarg[0].pboolVal)=VARIANT_TRUE;
else *(pDispParams->rgvarg[0].pboolVal)=VARIANT_FALSE;

Note: You can find the code for the BeforeNavigate2 event handler, Event_BeforeNavigate2, in the EventSink.cpp file.

Installing the BHO

Installing the BHO merely requires that a process calls our BHO's DllRegisterServer function. This is made simple via the regsvr32.exe utility. Simply run the command regsvr32.exe <path to the BHO dll>, and the BHO should be registered. To uninstall the BHO, a process needs to call our BHO's DllUnregisterServer function. This can also be accomplished via regsvr32.exe by running the command regsvr32.exe /u <path to the BHO dll>.

Using the Code

Use the source code for this example BHO as a starting point for your own BHO.

Note: Don't forget to generate a new CLSID_IEPlugin and CLSID_IEPlugin_Str for your own BHO! You can generate a new CLSID by using the guidgen.exe utility. You can find CLSID_IEPlugin defined in main.cpp and CLSID_IEPlugin_Str in common.h.

You can start by customizing the Event_BeforeNavigate2 event handler for your own needs. You can also handle more events by adding new event handler methods to the CEventSink class and calling them from CEventSink's Invoke method.

Note: You can find the definition of CEventSink in the EventSink.h file, and the implementation of CEventSink and Event_BeforeNavigate2 in the EventSink.cpp file.

Conclusion

I hope you have enjoyed reading this article and learned some new stuff. Feel free to leave comments with questions, corrections, and suggestions!

Revision History

  • 2009-06-06:
    • Initial posting.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to get the document object in your code Pin
Arie Levy23-Jun-09 21:37
Arie Levy23-Jun-09 21:37 
AnswerRe: How to get the document object in your code Pin
cefarix24-Jun-09 4:07
cefarix24-Jun-09 4:07 
GeneralRe: How to get the document object in your code Pin
Arie Levy24-Jun-09 6:49
Arie Levy24-Jun-09 6:49 
GeneralRe: How to get the document object in your code Pin
Arie Levy24-Jun-09 6:55
Arie Levy24-Jun-09 6:55 
GeneralRe: How to get the document object in your code Pin
Arie Levy24-Jun-09 7:39
Arie Levy24-Jun-09 7:39 
AnswerRe: How to get the document object in your code Pin
cefarix25-Jun-09 14:35
cefarix25-Jun-09 14:35 
QuestionRe: How to get the document object in your code Pin
just-a-nick2-Jul-09 14:51
just-a-nick2-Jul-09 14:51 
AnswerRe: How to get the document object in your code Pin
Arie Levy4-Jul-09 23:36
Arie Levy4-Jul-09 23:36 
I have solved it like that :
I would like to say i wrote is fast and not so elegant but it works :

1. Move the object IWebBrowser2 *pSite; from ObjectWithSite.h to EventSink.h as public
The every place in ObjectWithSite cile refere to EventSink.pSite see the code module ObjectWithSite

actually what the code does is getting the documnet completed event and reading the file for input fields tag and read the filed name and value from the specify ini file then automatically enter the pweb page i.e. page with password and usr name can be entered automatically if the values of filed name and field value (can be encrypted by you) are defined in the ini file hope it help and get you started

STDMETHODIMP CObjectWithSite::SetSite(IUnknown *pUnkSite)
{
HRESULT hr;

if (pUnkSite)
{
pUnkSite->AddRef(); // if a new site object is given, AddRef() it to make sure the object doesn't get deleted while we are working with it
}
DisconnectEventSink(); // disconnect any previous connection with IE
if (pUnkSite == NULL)
{
//////MessageBox(NULL,_T("SetSite : pUnkSite == NULL"),_T("CObjectWithSite"),MB_OK|MB_ICONINFORMATION);
return S_OK; // if only unsetting the site, return S_OK
}
hr = pUnkSite->QueryInterface(IID_IWebBrowser2,(void**)&EventSink.pSite); // query the site object for the IWebBrowser2 interface, from which we can access IE
pUnkSite->Release(); // we are done working with pUnkSite, so call Release() since we called AddRef() before
if (FAILED(hr))
{
//////MessageBox(NULL,_T("SetSite : pUnkSite->QueryInterface error"),_T("CObjectWithSite"),MB_OK|MB_ICONINFORMATION);
return hr; // if we couldn't find the IWebBrowser2 interface, return the error
}
ConnectEventSink(); // connect the new connection with IE

return S_OK;
}

// This is called by IE to get an interface from the currently set site object
STDMETHODIMP CObjectWithSite::GetSite(REFIID riid,void **ppvSite)
{
// Validate the ppvSite pointer
if (IsBadWritePtr(ppvSite, sizeof(void*)))
{
//////MessageBox(NULL,_T("GetSite : IsBadWritePtr error"),_T("CObjectWithSite"),MB_OK|MB_ICONINFORMATION);
return E_POINTER;
}
// Set *ppvSite to NULL
(*ppvSite) = NULL;
// If we don't have a current site set we must return E_FAIL
if (EventSink.pSite == NULL)
{
//////MessageBox(NULL,_T("GetSite : EventSink.pSite = NULL"),_T("CObjectWithSite"),MB_OK|MB_ICONINFORMATION);
return E_FAIL;
}
// Otherwise we let the site's QueryInterface method take care of it
return EventSink.pSite->QueryInterface(riid,ppvSite);
}

// This is called by us to get a connection to IE and start handling IE events
void CObjectWithSite::ConnectEventSink()
{
HRESULT hr;
IConnectionPointContainer* pCPC;

if (EventSink.pSite == NULL)
{
//////MessageBox(NULL,_T("ConnectEventSink : EventSink.pSite == NULL"),_T("CObjectWithSite"),MB_OK|MB_ICONINFORMATION);
return; // If we don't have a site, don't do anything
}
// Get an IConnectionPointContainer interface pointer from the site
hr = EventSink.pSite->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC);
if (FAILED(hr))
{
//////MessageBox(NULL,_T("ConnectEventSink : EventSink.pSite->QueryInterface error"),_T("CObjectWithSite"),MB_OK|MB_ICONINFORMATION);
return; // If we couldn't get it, abort
}
// Now we use the IConnectionPointContainer interface to get an IConnectionPoint interface pointer that will handle DWebBrowserEvents2 "dispatch interface" events.
// That means we have to plug our implementation of DWebBrowserEvents2 into the returned IConnectionPoint interface using its Advise() method, as below
hr = pCPC->FindConnectionPoint(DIID_DWebBrowserEvents2,&pCP);
if (FAILED(hr))
{ // If it failed, release the pCPC interface pointer and abort
//////MessageBox(NULL,_T("ConnectEventSink : pCPC->FindConnectionPoint error"),_T("CObjectWithSite"),MB_OK|MB_ICONINFORMATION);
pCPC->Release();
return;
}
// Finally we can plug our event handler object EventSink into the connection point and start receiving IE events
// The advise cookie is just a return value we use when we want to "unplug" our event handler object from the connection point
pCP->Advise((IUnknown*)&EventSink,&adviseCookie);
}

// This is called by us to remove our connection to IE, if it exists, and stop handling IE events
void CObjectWithSite::DisconnectEventSink()
{
if (pCP)
{ // if we have a valid connection point, unplug the event handler from it, then Release() it
pCP->Unadvise(adviseCookie);
adviseCookie=0;
pCP->Release();
pCP=NULL;
}
if (EventSink.pSite)
{ // if we have a valid site, Release() it
EventSink.pSite->Release();
EventSink.pSite=NULL;
}
}

I will attached more file EventSink and other to add to the project it also has sample of reading the document parameters and more

/*
Copyright (C) 2009 Moutaz Haq <cefarix@gmail.com>
This file is released under the Code Project Open License <http://www.codeproject.com/info/cpol10.aspx>

This file contains our implementation of the DWebBrowserEvents2 dispatch interface.
*/

//#include "Winbase.h"
#include "common.h"
#include "EventSink.h"
#include "comdef.h"
#include "CBstrImpl.h"


// The single global object of CEventSink
CEventSink EventSink;

STDMETHODIMP CEventSink::QueryInterface(REFIID riid,void **ppvObject)
{
// Check if ppvObject is a valid pointer
if (IsBadWritePtr(ppvObject,sizeof(void*)))
{
//////MessageBox(NULL,_T("QueryInterface not valid pointer"),_T("CEventSink"),MB_OK|MB_ICONINFORMATION);
return E_POINTER;
}
// Set *ppvObject to NULL
(*ppvObject) = NULL;
// See if the requested IID matches one that we support
// If it doesn't return E_NOINTERFACE
if (!IsEqualIID(riid,IID_IUnknown) && !IsEqualIID(riid,IID_IDispatch) && !IsEqualIID(riid,DIID_DWebBrowserEvents2))
{
//////MessageBox(NULL,_T("QueryInterface requested IID not matches"),_T("CEventSink"),MB_OK|MB_ICONINFORMATION);
return E_NOINTERFACE;
}
// If it's a matching IID, set *ppvObject to point to the global EventSink object
(*ppvObject) = (void*)&EventSink;
return S_OK;
}

STDMETHODIMP_(ULONG) CEventSink::AddRef()
{
return 1; // We always have just one static object
}

STDMETHODIMP_(ULONG) CEventSink::Release()
{
return 1; // Ditto
}

// We don't need to implement the next three methods because we are just a pure event sink
// We only care about Invoke() which is what IE calls to notify us of events

STDMETHODIMP CEventSink::GetTypeInfoCount(UINT *pctinfo)
{
UNREFERENCED_PARAMETER(pctinfo);

return E_NOTIMPL;
}

STDMETHODIMP CEventSink::GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo **ppTInfo)
{
UNREFERENCED_PARAMETER(iTInfo);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(ppTInfo);

return E_NOTIMPL;
}

STDMETHODIMP CEventSink::GetIDsOfNames(REFIID riid,LPOLESTR *rgszNames,UINT cNames,LCID lcid,DISPID *rgDispId)
{
UNREFERENCED_PARAMETER(riid);
UNREFERENCED_PARAMETER(rgszNames);
UNREFERENCED_PARAMETER(cNames);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(rgDispId);

return E_NOTIMPL;
}

// This is called by IE to notify us of events
// Full documentation about all the events supported by DWebBrowserEvents2 can be found at
// http://msdn.microsoft.com/en-us/library/aa768283(VS.85).aspx
STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(wFlags);
UNREFERENCED_PARAMETER(pVarResult);
UNREFERENCED_PARAMETER(pExcepInfo);
UNREFERENCED_PARAMETER(puArgErr);
VARIANT v[5]; // Used to hold converted event parameters before passing them onto the event handling method
int n;
bool b;
PVOID pv;
LONG lbound,ubound,sz;

//MessageBox(NULL,_T("Invoke has been called"),_T("CEventSink"),MB_OK|MB_ICONINFORMATION);

// if (!IsEqualIID(riid,DIID_DWebBrowserEvents2))
// {

// }

if(!IsEqualIID(riid,IID_NULL)) return DISP_E_UNKNOWNINTERFACE; // riid should always be IID_NULL
// Initialize the variants
for (n = 0; n < 5; n++)
{
VariantInit(&v[n]);
}
do
{
if (dispIdMember == DISPID_DOCUMENTCOMPLETE)
{ // Handle the AfterDocumentComplete event
VariantChangeType(&v[0],&pDispParams->rgvarg[0], 0, VT_BSTR); // url
b = Event_AfterDocumentComplete2((LPOLESTR)v[0].bstrVal);
if (b)
{
*(pDispParams->rgvarg[0].pboolVal) = VARIANT_TRUE;
}
else
{
*(pDispParams->rgvarg[0].pboolVal) = VARIANT_FALSE;
}
break ;
}
}while (0) ;


// Free the variants
for(n = 0; n < 5; n++)
{
VariantClear(&v[n]);
}
return S_OK;
}

// Return true to prevent the url from being opened
bool CEventSink::Event_BeforeNavigate2(LPOLESTR url,LONG Flags,LPOLESTR TargetFrameName,PUCHAR PostData,LONG PostDataSize,LPOLESTR Headers,bool Cancel)
{
// Do whatever you like here
// This is just an example
//TCHAR msg[1024];

//wsprintf(msg,_T("url=%ls\nFlags=0x%08X\nTargetFrameName=%ls\nPostData=%hs\nPostDataSize=%d\nHeaders=%ls\nCancel=%s"),url,Flags,TargetFrameName,(char*)PostData,PostDataSize,Headers,((Cancel)?(_T("true")):(_T("false"))));
////////MessageBox(NULL,msg,_T("CodeProject BHO Example - BeforeNavigate2 event fired!"),MB_OK|MB_ICONINFORMATION);
return Cancel;
}

// Return true to prevent the url from being opened
bool CEventSink::Event_AfterDocumentComplete2(LPOLESTR url)
{
static int iNumRetry = 0 ;
// Do whatever you like here
// This is just an example
CBstr sUrl(url) ;
TCHAR strIniFilename[255] ;
TCHAR strUserID[60] ;
TCHAR strPassword[60] ;
TCHAR strUserFieldID[60] ;
TCHAR strPasswordField[60] ;
TCHAR strURL[255] ;
TCHAR strKey[255] ;
int intLength ;

if (GetConfigFilename(&strIniFilename[0]) == false)
{
////////MessageBox(NULL,_T("filename not found"),_T("filename"),MB_OK|MB_ICONINFORMATION);
return false ;
}

intLength = GetPrivateProfileString(_T("Parameters"), _T("URL"), _T("0"), strURL, 255, strIniFilename) ;


if (sUrl.CompareNoCase(strURL) == 0)
{
intLength = GetPrivateProfileString(_T("Param"), _T("Key"), _T("0"), strKey, 255, strIniFilename) ;

int iNumFields = 2 ;

iNumFields = GetPrivateProfileInt(_T("Param"), _T("NumFields"), 0, strIniFilename) ;
if (iNumFields == 0)
{
return false ;
}



//iDecryptKey = CInt(Decrypt(4, strKey))
TCHAR strEntry[10] ;
bool bNeedSubmit = true ;
for (int i = 0; ((i < iNumFields) && (bNeedSubmit == true)); i++)
{
wsprintf(strEntry, _T("F%c"), CHAR(i + 49)) ;
////////MessageBox(NULL,strEntry,_T("Entry Fi"),MB_OK|MB_ICONINFORMATION);
intLength = GetPrivateProfileString(_T("Parameters"), strEntry, _T("0"), strUserID , 60, strIniFilename) ;
wsprintf(strEntry, _T("FN%c"), CHAR(i + 49)) ;
////////MessageBox(NULL,strEntry,_T("Entry FNi"),MB_OK|MB_ICONINFORMATION);
intLength = GetPrivateProfileString(_T("Parameters"), strEntry, _T("0"), strUserFieldID, 60, strIniFilename) ;
bNeedSubmit = FindUserNameReplaceValue(strUserFieldID, strUserID) ;
}
if (bNeedSubmit == true)
{
FindSubmitAndActivate(_T("submit")) ;
}
return true ;
}
return true;
}

bool CEventSink::FindSubmitAndActivate(TCHAR *ptName)
{
if (pSite == NULL)
{
return false;
}

HRESULT hr;
IDispatch* pHtmlDocDispatch = NULL;
IHTMLDocument2 * pHtmlDoc = NULL;
bool bSearch = true;

// Retrieve the document object.
hr = pSite->get_Document(&pHtmlDocDispatch);
if (SUCCEEDED(hr) && (pHtmlDocDispatch != NULL))
{
hr = pHtmlDocDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDoc);
if (SUCCEEDED(hr) && (pHtmlDoc != NULL))
{
IHTMLElementCollection* pColl = NULL;
hr = pHtmlDoc->get_all (&pColl);

if (SUCCEEDED(hr) && (pColl != NULL))
{
// Obtained the Anchor Collection...
long nLength = 0;
pColl->get_length (&nLength);

for (int i = 0; i < nLength && bSearch; i++)
{
VARIANT vIdx ;
vIdx.vt = VT_I4 ;
vIdx.lVal = (long)i ;

IDispatch* pElemDispatch = NULL;
IHTMLElement* pElem = NULL;

hr = pColl->item (vIdx, vIdx, &pElemDispatch);

if (SUCCEEDED(hr) && (pElemDispatch != NULL))
{
hr = pElemDispatch->QueryInterface(IID_IHTMLElement, (void**)&pElem);

if (SUCCEEDED(hr) && (pElem != NULL))
{
BSTR bstrTagName;
CBstr sTempTagName ;

if (!FAILED(pElem->get_tagName(&bstrTagName)))
{
sTempTagName = bstrTagName ;
SysFreeString (bstrTagName);
}

if (sTempTagName.CompareNoCase(_T ("input")) == 0)
{
IHTMLInputElement * pInputElem = NULL;
hr = pElemDispatch->QueryInterface(IID_IHTMLInputElement, (void**)&pInputElem);

if (SUCCEEDED(hr) && (pInputElem != NULL))
{
BSTR bstrType ;
CBstr sTempType ;
CBstr sTempName ;
CBstr sTempValue;

if (!FAILED(pInputElem->get_type(&bstrType)))
{
sTempType = bstrType;
SysFreeString (bstrType);

if (sTempType.CompareNoCase(ptName) == 0)
{
//MessageBox (NULL, _T("Before Submit pressed"), _T(""), MB_OK | MB_ICONINFORMATION);
pElem->click();

// can be clicked via Documnet as in the next 3 line below
//IHTMLFormElement *pLogOnForm ;
//pInputElem->get_form(&pLogOnForm);
//pLogOnForm->submit() ;
//MessageBox (NULL, _T("After Submit pressed"), _T(""), MB_OK | MB_ICONINFORMATION);
bSearch = false ;
}
}
pInputElem->Release();
}
}
pElem->Release();
}
pElemDispatch->Release();
}
}
pColl->Release();
}
pHtmlDoc->Release();
}
pHtmlDocDispatch->Release();
}

return (bSearch == false);
}

bool CEventSink::FindUserNameReplaceValue(TCHAR *ptName, TCHAR* ptNewValue)
{
if (pSite == NULL)
{
return false;
}

HRESULT hr;
IDispatch* pHtmlDocDispatch = NULL;
IHTMLDocument2 * pHtmlDoc = NULL;
bool bSearch = true;

// Retrieve the document object.
hr = pSite->get_Document(&pHtmlDocDispatch);
if (SUCCEEDED(hr) && (pHtmlDocDispatch != NULL))
{
hr = pHtmlDocDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDoc);
if (SUCCEEDED(hr) && (pHtmlDoc != NULL))
{
IHTMLElementCollection* pColl = NULL;
hr = pHtmlDoc->get_all (&pColl);

if (SUCCEEDED(hr) && (pColl != NULL))
{
// Obtained the Anchor Collection...
long nLength = 0;
pColl->get_length (&nLength);

for (int i = 0; i < nLength && bSearch; i++)
{
VARIANT vIdx ;
vIdx.vt = VT_I4 ;
vIdx.lVal = (long)i ;

IDispatch* pElemDispatch = NULL;
IHTMLElement* pElem = NULL;

hr = pColl->item (vIdx, vIdx, &pElemDispatch);

if (SUCCEEDED(hr) && (pElemDispatch != NULL))
{
hr = pElemDispatch->QueryInterface(IID_IHTMLElement, (void**)&pElem);

if (SUCCEEDED(hr) && (pElem != NULL))
{
BSTR bstrTagName;
CBstr sTempTagName ;

if (!FAILED(pElem->get_tagName(&bstrTagName)))
{
sTempTagName = bstrTagName ;
SysFreeString (bstrTagName);
}

if (sTempTagName.CompareNoCase(_T ("input")) == 0)
{
IHTMLInputElement * pInputElem = NULL;
hr = pElemDispatch->QueryInterface(IID_IHTMLInputElement, (void**)&pInputElem);

if (SUCCEEDED(hr) && (pInputElem != NULL))
{
BSTR bstrType ;
BSTR bstrName ;
CBstr sTempType ;
CBstr sTempName ;
CBstr sTempValue;

if (!FAILED(pInputElem->get_type(&bstrType)))
{
sTempType = bstrType;
SysFreeString (bstrType);
}
if (!FAILED(pInputElem->get_name(&bstrName)))
{
sTempName = bstrName;
SysFreeString (bstrName);
if (sTempName.CompareNoCase(ptName) == 0)
{
CBstr bstrNewValue = ::SysAllocString(ptNewValue);
pInputElem->put_value(bstrNewValue);
SysFreeString(bstrNewValue);
bSearch = false ;
}
}
pInputElem->Release();
}
}
pElem->Release();
}
pElemDispatch->Release();
}
}
pColl->Release();
}
pHtmlDoc->Release();
}
pHtmlDocDispatch->Release();
}

return (bSearch == false);
}


bool CEventSink::FindInput()
{
static int iNumRetry = 0 ;
if (pSite == NULL)
{
return false;
}

HRESULT hr;
IDispatch* pHtmlDocDispatch = NULL;
IHTMLDocument2 * pHtmlDoc = NULL;
bool bSearch = true;
BSTR bstrInput = ::SysAllocString(L"input");


// Retrieve the document object.
hr = pSite->get_Document(&pHtmlDocDispatch);
if (SUCCEEDED(hr) && (pHtmlDocDispatch != NULL))
{
hr = pHtmlDocDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDoc);
if (SUCCEEDED(hr) && (pHtmlDoc != NULL))
{
IHTMLElementCollection* pColl = NULL;
hr = pHtmlDoc->get_all (&pColl);

if (SUCCEEDED(hr) && (pColl != NULL))
{
// Obtained the Anchor Collection...
long nLength = 0;
pColl->get_length (&nLength);

for (int i = 0; i < nLength && bSearch; i++)
{
VARIANT vIdx ;
vIdx.vt = VT_I4 ;
vIdx.lVal = (long)i ;

IDispatch* pElemDispatch = NULL;
IHTMLElement* pElem = NULL;

hr = pColl->item (vIdx, vIdx, &pElemDispatch);

if (SUCCEEDED(hr) && (pElemDispatch != NULL))
{
hr = pElemDispatch->QueryInterface(IID_IHTMLElement, (void**)&pElem);

if (SUCCEEDED(hr) && (pElem != NULL))
{
BSTR bstrTagName;
CBstr sTempTagName ;

if (!FAILED(pElem->get_tagName(&bstrTagName)))
{
sTempTagName = bstrTagName ;
SysFreeString (bstrTagName);
}



if (sTempTagName.CompareNoCase(_T ("input")) == 0)
{
#if 0
wsprintf(msg1,_T("Input fiels found %ls"), (TCHAR*)sTempTagName ) ;;
MessageBox (NULL, msg1, _T(""), MB_OK | MB_ICONINFORMATION);
#endif

IHTMLInputElement * pInputElem = NULL;
hr = pElemDispatch->QueryInterface(IID_IHTMLInputElement, (void**)&pInputElem);

if (SUCCEEDED(hr) && (pInputElem != NULL))
{
BSTR bstrType ;
BSTR bstrName ;
BSTR bstrValue ;
CBstr sTempType ;
CBstr sTempName ;
CBstr sTempValue;

if (!FAILED(pInputElem->get_type(&bstrType)))
{
sTempType = bstrType;
SysFreeString (bstrType);
}
if (!FAILED(pInputElem->get_name(&bstrName)))
{
sTempName = bstrName;
SysFreeString (bstrName);
}
if (!FAILED(pInputElem->get_value(&bstrValue)))
{
sTempValue = bstrValue;
SysFreeString (bstrValue);
}
if (iNumRetry == 0)
{
if (sTempName.CompareNoCase(_T ("ZIHMIST")) == 0)
{
CBstr bstrNewValue = ::SysAllocString(L"AAAA");
pInputElem->put_value(bstrNewValue);
SysFreeString(bstrNewValue);
}
if (sTempName.CompareNoCase(_T ("KOD")) == 0)
{
CBstr bstrNewValue = ::SysAllocString(L"Levy");;
pInputElem->put_value(bstrNewValue);
SysFreeString(bstrNewValue);
}
if (sTempType.CompareNoCase(_T ("submit")) == 0)
{
//MessageBox (NULL, _T("Before Submit pressed"), _T(""), MB_OK | MB_ICONINFORMATION);
pElem->click();

// can be clicked via Documnet as in the next 3 line below
//IHTMLFormElement *pLogOnForm ;
//pInputElem->get_form(&pLogOnForm);
//pLogOnForm->submit() ;
//MessageBox (NULL, _T("After Submit pressed"), _T(""), MB_OK | MB_ICONINFORMATION);
}
}
pInputElem->Release();
}
}
pElem->Release();
}
pElemDispatch->Release();
}
}
iNumRetry++ ;
pColl->Release();
}
pHtmlDoc->Release();
}
pHtmlDocDispatch->Release();
}

::SysFreeString(bstrInput);

return (bSearch == false);
}


bool CEventSink::GetConfigFilename(TCHAR* psConfigWebFullName)
{
bool bFileOK = false ;
int iLen ;
TCHAR sComputerName[255] ;
TCHAR sConfigFileFullName[255] ;
DWORD nSize ;
GetComputerName(&sComputerName[0], &nSize) ;

//MessageBox (NULL, sComputerName, _T("computer name before swapping "), MB_OK | MB_ICONINFORMATION);
if (nSize > 0)
{
wsprintf(psConfigWebFullName, _T("C:\\%s.ini"), sComputerName) ;
bFileOK = true ;
}
return bFileOK ;
}

more file add those files to the project
// file : CBstr.h
////////////////////////////////////////////////////////
//
// CBStr : BSTR wrapper class with manipulation routines
//
// Gary Olliffe : Parallax Solutions Ltd
//
// e-mail : Gary_Olliffe@hotmail.com
//
// date : 14/07/1998
//
// updated : 19/10/98
// Fixed SetAt() to work with ANSI and Unicode
// Tested under UNICODE release and Debug.
// Added LoadString() method.
//
////////////////////////////////////////////////////////
//
// The public interface to this class was
// "inspired by"/"poached from" the NoMFC::CString example
// www.worldofatl.com. As a result it fairly closely mimics the
// MFC CString interface - the methods not implemented have been left
// in the class definition but are commented out.
// Additional constructorsto mimic _bstr_t have been added, these include a
// _variant_t constructor and a BSTR constructor with copy/attach flag
//
// If you have a string that will require manipulation then use this
// otherwise stick to the basic _bstr_t class.
//
// The reason I have written this _bstr_t based class rather than use the
// basic_string implementation is that the MS STL requires a support dll
// to be shipped to support the allocators, and not everyone want to use STL
//
// This is by no means intended as an optimised implementation of this class,
// most or the routines use the standard "c" string manipulation routines
//
// Users looking at the implementation should also note that the _bstr_t base class
// has two internal representations (one ASNI, one uNICODE) methods on this
// class that modify the string (eg. MakeUpper, SetAt, etc...) modify both internal
// strings to maintain consistency.
//
// Users should also note that where appropriate the class makes use of the TCHAR
// typedef, giving UNICODE compatibility.
//
/////////////////////////////////////////////////////////////

#ifndef _CBSTR_H_D130E080_1B3E_11d2_955A_00805FC3DFB4
#define _CBSTR_H_D130E080_1B3E_11d2_955A_00805FC3DFB4


class CBstr : public _bstr_t
{
// These methods groupings and names have been taken from
// NoMFC::CString from www.worldofatl.com
public:
// Constructors
CBstr();
CBstr( const CBstr& strInput);
CBstr( const _bstr_t& strInput);
CBstr( const TCHAR* strInput);
// _bstr_t mimic constructors
CBstr( const _variant_t& varinput );
CBstr( BSTR strinput, bool bcopy );

// CBstr(TCHAR ch, int nRepeat = 1);

//string population
bool LoadString(HINSTANCE hInstance, UINT uID);

// The string as an array
// int GetLength() const;
bool IsEmpty() const;
void Empty();
TCHAR GetAt(int nIndex) const;
void SetAt(int nIndex, TCHAR ch);

// Comparison
int Compare(const TCHAR* psz) ;//const;
int Compare(const CBstr& str) const;
int CompareNoCase(const TCHAR* psz) const;
int CompareNoCase(const CBstr& str) const;
int Collate(const TCHAR* psz) const;
int Collate(const CBstr& str) const;

// Extraction
// CBstr Mid(int nFirst) const;
CBstr Mid(int nFirst, int nCount) const;
CBstr Left(int nCount) const;
CBstr Right(int nCount) const;
CBstr SpanIncluding(const TCHAR* pszCharSet) const;
CBstr SpanExcluding(const TCHAR* pszCharSet) const;

// Other Conversions
void MakeUpper();
void MakeLower();
void MakeReverse();
void TrimLeft();
void TrimRight();
void Format(const TCHAR* pszFormat, ... );

// Searching
int Find(const TCHAR& ch) const;
int Find(const TCHAR* psz) const;
int ReverseFind(const TCHAR& ch) const;
int FindOneOf(const TCHAR* pszCharSet) const;

// Operators
TCHAR operator[](int nIndex) const;
operator const TCHAR*() const;

};

#endif // _CBSTR_H_D130E080_1B3E_11d2_955A_00805FC3DFB4

// file CBstrImpl.h

////////////////////////////////////////////////////////
//
// CBStr : BSTR wrapper class with manipulation routines
//
// Gary Olliffe : Parallax Solutions Ltd
//
// e-mail : Gary.Olliffe@parallax.co.uk
//
// updated : 19/10/98
// Fixed SetAt() to work with ANSI and Unicode
// Tested under UNICODE release and Debug.
// Added LoadString() method.

#ifndef _CBSTR_IMPLEMENTATION_H_00944610_1B3F_11d2_955A_00805FC3DFB4
#define _CBSTR_IMPLEMENTATION_H_00944610_1B3F_11d2_955A_00805FC3DFB4

#include <stdio.h>

#include <comdef.h>

#include <tchar.h>

#include "CBStr.h"

///////////////////////////////////////////////////////////////////////////
// The string as an array
///////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////
// Constructors
///////////////////////////////////////////////////////////////////////////

inline CBstr::CBstr()
: _bstr_t() {}

inline CBstr::CBstr(const CBstr& strInput)
: _bstr_t(strInput) {}

inline CBstr::CBstr(const _bstr_t& strInput)
: _bstr_t(strInput) {}

inline CBstr::CBstr(const TCHAR* strInput)
: _bstr_t(strInput) {}

inline CBstr::CBstr(const variant_t& varinput)
: _bstr_t(varinput) {}

inline CBstr::CBstr(const BSTR strInput, bool bcopy)
: _bstr_t(strInput, bcopy) {}


////////////////////////////////////////////////////////////////////////////
// Methods
////////////////////////////////////////////////////////////////////////////

// New Loadstring method
inline bool CBstr::LoadString(HINSTANCE hInstance, UINT uID)
{
bool bRetval = false;
TCHAR lpBuffer[256];
if (0 != ::LoadString(hInstance, uID, lpBuffer, 256))
{
bRetval = true;
// assign value to this class
*this = lpBuffer;
}
return bRetval;
}



inline bool CBstr::IsEmpty() const
{
return _bstr_t::length() == 0;
};

inline void CBstr::Empty()
{
_bstr_t::operator=( _T("") );
return;
};

inline TCHAR CBstr::GetAt(int nIndex) const
{
unsigned int len = _bstr_t::length();
if (len > 0 && len > static_cast<unsigned int>(nIndex))
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*() ;
return tmp[nIndex];
}
return L'\0';
};

inline void CBstr::SetAt(int nIndex, TCHAR ch)
{
unsigned int len = _bstr_t::length();
if (len > 0 && len > static_cast<unsigned int>(nIndex))
{
// We need to modify both internal representations in the
// base class
char* tmp = _bstr_t::operator char*() ;
wchar_t* tmpw = _bstr_t::operator wchar_t*() ;

#if defined(UNICODE) && defined(_UNICODE)
// This is a UNICODE build
wchar_t wch = ch;
char mbch;
WideCharToMultiByte(CP_ACP, 0, &ch, 1,
&mbch, 1, NULL,NULL);

#else
// This is an ANSI build
wchar_t wch;
char mbch = ch;
MultiByteToWideChar(CP_ACP, 0, &ch, 1,
&wch, 1 );
#endif

tmp[nIndex] = mbch;
tmpw[nIndex] = wch;
}
return;
};

///////////////////////////////////////////////////////////////////////////
// Comparison
///////////////////////////////////////////////////////////////////////////

inline int CBstr::Compare(LPCTSTR psz) //const
{
const TCHAR* tmp = _bstr_t::operator TCHAR*();
return _tcscmp(tmp, psz);
};

inline int CBstr::Compare(const CBstr& str) const
{
const TCHAR* tmpthis = _bstr_t::operator const TCHAR*();
const TCHAR* tmpOther = static_cast<const TCHAR*>(str);

return _tcscmp(tmpthis, tmpOther);
};

inline int CBstr::CompareNoCase(const TCHAR* psz) const
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*();
return _tcsicmp(tmp, psz);
};

inline int CBstr::CompareNoCase(const CBstr& str) const
{
const TCHAR* tmpthis = _bstr_t::operator const TCHAR*();
const TCHAR* tmpOther = static_cast<const TCHAR*>(str);

return _tcsicmp(tmpthis, tmpOther);
};

inline int CBstr::Collate(const TCHAR* psz) const
{
const TCHAR* tmpthis = _bstr_t::operator const TCHAR*();

return _tcscoll(tmpthis, psz);
};

inline int CBstr::Collate(const CBstr& str) const
{
const TCHAR* tmpthis = _bstr_t::operator const TCHAR*();
const TCHAR* tmpOther = static_cast<const TCHAR*>(str);

return _tcscoll(tmpthis, tmpOther);
};

///////////////////////////////////////////////////////////////////////////
// Extraction
///////////////////////////////////////////////////////////////////////////

/* inline CBstr<T> CBstr::Mid(int nFirst) const
{
const TCHAR* tmp = _bstr_t::operator TCHAR*();
return _tcsnstr(nFirst);
};
*/
inline CBstr CBstr::Mid(int nFirst, int nCount) const
{
TCHAR* tmp = _bstr_t::operator TCHAR*();
int len = _bstr_t::length() + 1;
TCHAR *result = new TCHAR[ len ];

memset( result, '\0', len * sizeof(TCHAR) );
_tcsncpy( result, &tmp[nFirst], nCount );

CBstr cbRes = result;
delete result;

return cbRes;
};

inline CBstr CBstr::Left(int nCount) const
{
return CBstr::Mid(0, nCount);
};

inline CBstr CBstr::Right(int nCount) const
{
return CBstr::Mid(length() - nCount, nCount);
};

inline CBstr CBstr::SpanIncluding(const TCHAR* pszCharSet) const
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*();
int strlen = _tcsspn( tmp, pszCharSet );

return CBstr::Mid(0, strlen);
};

inline CBstr CBstr::SpanExcluding(const TCHAR* pszCharSet) const
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*();
int pos = _tcscspn( tmp, pszCharSet );

return CBstr::Mid(0, pos);
};

///////////////////////////////////////////////////////////////////////////
// Other Conversions
///////////////////////////////////////////////////////////////////////////

inline void CBstr::MakeUpper()
{
// We need to modify both internal representations in the
// base class
char* tmp = _bstr_t::operator char*() ;
wchar_t* tmpw = _bstr_t::operator wchar_t*() ;
strupr(tmp);
_wcsupr(tmpw);
return;
};

inline void CBstr::MakeLower()
{
// We need to modify both internal representations in the
// base class
char* tmp = _bstr_t::operator char*() ;
wchar_t* tmpw = _bstr_t::operator wchar_t*() ;
strlwr(tmp);
_wcslwr(tmpw);
return;
};

inline void CBstr::MakeReverse()
{
// We need to modify both internal representations in the
// base class
char* tmp = _bstr_t::operator char*() ;
wchar_t* tmpw = _bstr_t::operator wchar_t*() ;
strrev(tmp);
_wcsrev(tmpw);

return;
};

inline void CBstr::TrimLeft()
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*();
TCHAR* trimstr = _tcsspnp( tmp, _T(" \t") );

*this = trimstr;
return;
};

inline void CBstr::TrimRight()
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*();
TCHAR* trimstr = _tcsspnp( tmp, _T(" \t") );
int pos = _bstr_t::length() - 1;
while (trimstr[pos] == ' ')
{
trimstr[pos--] = '\0';
}

*this = trimstr;
return;
};

inline void CBstr::Format(const TCHAR* pszFormat, ... )
{
// Doesn't have all the features of CString::Format()
va_list vl;
va_start(vl, pszFormat);

TCHAR* pszTemp = NULL;
int nBufferSize = 0;
int nRetVal = -1;

do
{
// Increment the buffer 100 characters at a time
// I'm not sure whether this is robust!
nBufferSize += 100;
delete [] pszTemp;
pszTemp = new TCHAR [nBufferSize];
nRetVal = _vstprintf(pszTemp, pszFormat, vl);
} while (nRetVal < 0);

*this = pszTemp;
delete [] pszTemp;

va_end(vl);
return;
};

///////////////////////////////////////////////////////////////////////////
// Searching
///////////////////////////////////////////////////////////////////////////

inline int CBstr::Find(const TCHAR& ch) const
{
// npos == -1 in this implementation, but it might not always be, so this is
// a bit dodgy.
const TCHAR* tmp = _bstr_t::operator const TCHAR*() ;
//TCHAR* pdest = _tcschr( tmp, ch );
// return pdest - tmp;
return 0 ;
};

inline int CBstr::Find(const TCHAR* psz) const
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*() ;
// TCHAR* pdest = _tcsstr( tmp, psz );
// return pdest - tmp;
return 0 ;
};

inline int CBstr::ReverseFind(const TCHAR& ch) const
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*() ;
// TCHAR* pdest = _tcsrchr( tmp, ch );
// return pdest - tmp;
return 0 ;
};

inline int CBstr::FindOneOf(const TCHAR* psz) const
{
const TCHAR* tmp = _bstr_t::operator const TCHAR*() ;
int pos = _tcscspn( tmp, psz );
if (pos == static_cast<long>(_bstr_t::length()))
{
pos = -1;
}
return pos;
};

///////////////////////////////////////////////////////////////////////////
// Operators
///////////////////////////////////////////////////////////////////////////
inline CBstr::operator const TCHAR*() const
{ return _bstr_t::operator const TCHAR*(); };

inline TCHAR CBstr::operator[](int nIndex) const
{ return GetAt(nIndex); };

///////////////////////////////////////////////////////////////////////////
// Comparison Operators
///////////////////////////////////////////////////////////////////////////
inline bool operator==(const CBstr& s1, const CBstr& s2)
{ return s1.Compare(s2) == 0; }
inline bool operator==(const CBstr& s1, const TCHAR* s2)
{ return s1.Compare(s2) == 0; }
inline bool operator==(const TCHAR* s1, const CBstr& s2)
{ return s2.Compare(s1) == 0; }

inline bool operator!=(const CBstr& s1, const CBstr& s2)
{ return s1.Compare(s2) != 0; }
inline bool operator!=(const CBstr& s1, const TCHAR* s2)
{ return s1.Compare(s2) != 0; }
inline bool operator!=(const TCHAR* s1, const CBstr& s2)
{ return s2.Compare(s1) != 0; }

inline bool operator<(const CBstr& s1, const CBstr& s2)
{ return s1.Compare(s2) < 0; }
inline bool operator<(const CBstr& s1, const TCHAR* s2)
{ return s1.Compare(s2) < 0; }
inline bool operator<(const TCHAR* s1, const CBstr& s2)
{ return s2.Compare(s1) > 0; }

inline bool operator>(const CBstr& s1, const CBstr& s2)
{ return s1.Compare(s2) > 0; }
inline bool operator>(const CBstr& s1, const TCHAR* s2)
{ return s1.Compare(s2) > 0; }
inline bool operator>(const TCHAR* s1, const CBstr& s2)
{ return s2.Compare(s1) < 0; }

inline bool operator<=(const CBstr& s1, const CBstr& s2)
{ return s1.Compare(s2) <= 0; }
inline bool operator<=(const CBstr& s1, const TCHAR* s2)
{ return s1.Compare(s2) <= 0; }
inline bool operator<=(const TCHAR* s1, const CBstr& s2)
{ return s2.Compare(s1) >= 0; }

inline bool operator>=(const CBstr& s1, const CBstr& s2)
{ return s1.Compare(s2) >= 0; }
inline bool operator>=(const CBstr& s1, const TCHAR* s2)
{ return s1.Compare(s2) >= 0; }
inline bool operator>=(const TCHAR* s1, const CBstr& s2)
{ return s2.Compare(s1) <= 0; }


#endif //_CBSTR_IMPLEMENTATION_H_00944610_1B3F_11d2_955A_00805FC3DFB4

Hope it helps you can send mail to arieelahlevy@gmail.com for more help
AnswerRe: How to get the document object in your code Pin
Peter Warren10-Dec-10 11:15
Peter Warren10-Dec-10 11:15 
Question[out] Parameters in Events? Pin
chaau9-Jun-09 14:06
chaau9-Jun-09 14:06 
GeneralThanks! Pin
baloneyman8-Jun-09 5:19
baloneyman8-Jun-09 5:19 
GeneralRe: Thanks! Pin
ThatsAlok19-Jul-09 3:01
ThatsAlok19-Jul-09 3:01 
GeneralGreat article. Pin
transoft7-Jun-09 3:14
transoft7-Jun-09 3:14 
GeneralRe: Great article. Pin
cefarix7-Jun-09 10:54
cefarix7-Jun-09 10:54 
AnswerRe: Great article. Pin
ilhando10-Jun-09 23:59
ilhando10-Jun-09 23:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.