Click here to Skip to main content
15,881,715 members
Articles / Desktop Programming / MFC
Article

Win32 Hyperlink Control

Rate me:
Please Sign up or sign in to vote.
4.60/5 (7 votes)
25 Aug 20055 min read 46.2K   712   19   7
Introducing the modified worker object pattern by developing a hyperlink control without using the MFC.

Introduction

There are lots of hyperlink controls on CodeProject. So why is there any need for this one? Indeed, when I implemented this class couple of years ago, I never thought of publishing it, because it had really nothing special worth for an article. But three weeks ago, I included this class in my ODBCTracer-project (ODBCTracer) and someone on CodeProject was interested in it. Especially, the fact that it was not a CWnd derivate was the main point of interest.

Why not using the MFC versions?

At this point, I want to avoid any heated discussion about the advantages and disadvantages of MFC in different kinds of projects. Generally, I don't know of any disadvantage about MFC in client-side software systems if you know about the pitfalls. But sometimes, any dependency between your application and a specific MFC version is somewhat annoying. Just think about how things can get messy if you mix different versions of the MFC in different modules used by the same application. In such a case, it is better to rely on Win32 and steer clear of the MFC. This was exactly the case, when I worked on my ODBCTracer project.

Backgrounds

Although the implementation is very straightforward, there is an aspect about it which is worth mentioning. I am using a modified worker object pattern for initialization of the class when the first instance of it is created. Initialization means that the window class of the Win32 control is registered by calling the Win32 API function RegisterClass and the hand cursor is loaded from a specific module. These two jobs have to be done only once, and normally, to assure this, we are using something like a flag which indicates (when it was set) that the initialization is finished. Let's see how this technique could be implemented:

CHyperlink::CHyperlink()
{
    //check the initialazation flag
    if (!class_was_initialized)
        initialize(); //we to initialize it
    
    //do whatever is needed
    .
    .
}

The disgusting thing about this technique is that within the constructor there is a check for initialization all the time, and besides the first invocation of the constructor, this check will return false. By using the modified worker object pattern, you are able to invoke initialization operations before the first instance is created and you can avoid such a check.

Worker object pattern

The worker object pattern is used in situations where you want to execute some piece of code and assure that this is done in any circumstance. Imagine, you have a code fragment where you have to take care about thread safety so you want to assure that only one thread is within this piece of code. You can use a CRITICAL_SECTION and the Win32 API functions InitializeCriticalSection() and DeleteCriticalSection() to ascertain the exclusive access of a single thread. But if you use these functions directly within your code, there are many ways which can lead you to a perfect system deadlock. Let's see how this can happen:

//global lock
CRITICAL_SECTION lock;

void func1()
{
    //code block here...
    
    //critical section enter
    InitializeCriticalSection(&lock);
    
    //fragile code which may only accessed by a single thread
    
    //critical section exit
    DeleteCriticalSection(&lock);
    
    //code block here...
}

The most important point is that you always delete the lock when you leave the critical section. This is done by calling DeleteCriticalSection(&lock). But what happens when the code within the critical section throws an exception? It's likely that DeleteCriticalSection(&lock) won't be called and the next thread which wants to acquire the lock in InitializeCriticalSection(&lock) will have to wait a very long period of time...

By using a worker object pattern, you can avoid such things. A worker object is an instance of a class which does execute code in its constructor and / or destructor. So, what we need to do is to invoke the functions for acquiring and releasing the lock within the constructor and destructor of a specific class which may be called a mutex.

void func1()
{
    //code block here...
    
    //critical section enter
    {
        Mutex lock; //when the instance is created the lock is acquired.

        //fragile code which may only accessed by a single thread    
    } //at this point the object disappears from the stack and the lock is released
    
    //code block here...
}

The modified worker object pattern

Let me first emphasise that I don't think that the idea introduced here is something new. The only fact is that I have never read about it on CodeProject nor in any other community. This doesn't mean that there are no articles on this topic anywhere. It's just I've never come into contact with them...

The modified worker object pattern is based on the worker object pattern. It describes a way of executing some piece of code while the application or the module is loaded. Let's have a look at the class declaration of CHyperlink.

class CHyperlink  
{
//one instance of this private class 
//is created as a static member of CHyperlink
class _autoinitializer
{
public:
    _autoinitializer();
    ~_autoinitializer();
protected:
    HMODULE hModule;
};
friend class _autoinitializer;
public:
    CHyperlink();
    virtual ~CHyperlink();

    bool create(int resourceid, HWND parent);
    bool create(RECT rect, const char *url, HWND parent);
    bool create(int x1, int y1, int x2, 
                int y2, const char *url, HWND parent);

protected:
    std::string m_Url;
    HWND m_hWnd;

    //static member. because _autoinitializer is a private class no one 
    //else then CHyperlink is able to create 
    //instances of it: this is an unique instance
    static _autoinitializer __autoinitializer;
    static HCURSOR handcursor;
    static int WndProc(HWND hwnd,WORD wMsg,WPARAM wParam,LPARAM lParam);
};

The worker object in this class is the static member called __autoinitializer of the class _autoinitializer. When the application or module is loaded, the common runtime creates all global or static instances. We can verify that by putting a breakpoint in the constructor of _autoinitializer to trace back the invocation.

  • CHyperlink::_autoinitializer::_autoinitializer() line 43
  • $E16() line 75 + 34 bytes
  • $E20() + 29 bytes
  • _initterm(void (void)* * 0x00429114 $S21, void (void)* * 0x00429228 ___xc_z) line 525
  • _cinit() line 192 + 15 bytes
  • mainCRTStartup() line 205
  • KERNEL32! 77e614c7()

Once the instance of _autoinitializer is created it initializes the class by registering the window class and loading the hand cursor. When the application is unloaded, the runtime calls the destructor which frees all the resources. The technique of using the runtime to invoke initialization routines even work with DLLs. If you use this class in any kind of DLL, the initialization operation will be invoked when you call LoadLibrary() for the first time from your application. Once you unload the library, the runtime automatically calls the destructor which in turn handles the deinitialization process. In DLLs, you can also export the DllMain function to do initialization jobs once the module is attached to the process space. But in this case, initialization and class implementation are scattered through your module which leads to bad design which in turn complicates maintenance.

Pitfalls

You cannot influence the sequence in which modified worker objects will be created! When we put another static member to the declaration of CHyperlink like the following update, we cannot say which instance will be created first: __autoinitializer1 or __autoinitializer2.

//static member. because _autoinitializer is a private class no one 
//else then CHyperlink is able to create instances of it: this is an unique instance
static _autoinitializer __autoinitializer1;
static _autoinitializer __autoinitializer2;

It is not specified by the ANSI C++ standard how compiler vendors have to generate code for creation of this kind of objects. Be aware of this and refer to Scott Meyers :)

By the way, hope you can use the hyperlink class.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Chief Technology Officer W3L
Germany Germany
-Since 1th August 2007: Chief Technology Officer of W3L
-2002/08/01-2007/07/31: PhD student
-1997/10/15-2002/07/31: Studied Electrical Engineering and Computer Science

Comments and Discussions

 
GeneralCritical section usage Pin
Anders Dalvander26-Aug-05 3:59
Anders Dalvander26-Aug-05 3:59 
GeneralRe: Critical section usage Pin
Anders Dalvander26-Aug-05 4:07
Anders Dalvander26-Aug-05 4:07 
GeneralRe: Critical section usage Pin
Doga Arinir26-Aug-05 4:59
Doga Arinir26-Aug-05 4:59 
QuestionWorker object pattern?!? Pin
WREY25-Aug-05 3:35
WREY25-Aug-05 3:35 
AnswerRe: Worker object pattern?!? Pin
Doga Arinir25-Aug-05 4:40
Doga Arinir25-Aug-05 4:40 
GeneralRe: Worker object pattern?!? Pin
Anonymous31-Aug-05 7:29
Anonymous31-Aug-05 7:29 
GeneralRe: Worker object pattern?!? Pin
Doga Arinir31-Aug-05 7:51
Doga Arinir31-Aug-05 7:51 

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.