Transforming Static Text Controls into Active Hyperlinks Using the Win32 API

Transform any static text control:

Into an active hyperlink:

Environment: VC6, VC7, and likely others

In need of a quick hyperlink control in an About box, I noticed that CodeGuru had a couple of options available to MFC users. Unfortunately, I needed a solution that dealt directly with the Win32 API. Because I have benefited from CodeGuru in the past, I thought this would make a nice opportunity to contribute something of my own. With a little work, here's what I put together.

It seems that a static text control is almost a hyperlink, but it needs four customizations:

  1. Clicking the text needs to open a browser window to the location specified by the text.
  2. The cursor needs to change from the standard arrow cursor to a pointing index finger when it moves over the control.
  3. The text in the control needs to be underlined when the cursor moves over the control.
  4. A hyperlink control needs to display text in a different color—black just won't do.

The first customization can be done rather easily by changing the style of the static text control to include notifications to the parent window whenever the control is clicked. This style can be changed in the Visual C++ dialog editor, but it is just as easy to do from within the source code.

  // Make sure the control will send notifications.

  DWORD dwStyle = GetWindowLong(hwndCtl, GWL_STYLE);
  SetWindowLong(hwndCtl, GWL_STYLE, dwStyle | SS_NOTIFY);

A simple call to ShellExecute() from within the parent window's message handler can be used to browse to the desired location.

  case WM_COMMAND:
    switch (LOWORD(wParam))
        ShellExecute(hwnd, "open",
                 NULL, NULL, SW_SHOWNORMAL);
        return TRUE;

The second customization can be handled by subclassing the static text control and handling the WM_SETCURSOR message.

  // Subclass the existing control.

  WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwnd,
  SetProp(hwnd, PROP_ORIGINAL_PROC, (HANDLE) pfnOrigProc);
  SetWindowLong(hwnd, GWL_WNDPROC, (LONG) (WNDPROC)


  // Handle the WM_SETCURSOR message to use the desired cursor.

      // Because IDC_HAND is not available on all operating
      // systems, we will load the arrow cursor if IDC_HAND is not
      // present.
      if (NULL == hCursor)
        hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
      return TRUE;

After the static text control is subclassed, monitoring the WM_MOUSEMOVE message to see when the cursor travels into and out of the control is no problem at all. When the cursor comes in, we set an underline font for the control. When the cursor moves out, we set the font back to its original state. Now, there's only one customization remaining.

  // Create an underline version of the control's font and
  // store both fonts where we can get at them

  HFONT hOrigFont = (HFONT) SendMessage(hwnd, WM_GETFONT, 0, 0);
  SetProp(hwnd, PROP_ORIGINAL_FONT, (HANDLE) hOrigFont);

  GetObject(hOrigFont, sizeof(lf), &lf);
  lf.lfUnderline = TRUE;

  HFONT hFont = CreateFontIndirect(&lf);
  SetProp(hwnd, PROP_UNDERLINE_FONT, (HANDLE) hFont);


  // Handle the WM_MOUSEMOVE message to set the appropriate font.

      if (GetCapture() != hwnd)
        HFONT hFont = (HFONT) GetProp(hwnd, PROP_UNDERLINE_FONT);
        SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
        InvalidateRect(hwnd, NULL, FALSE);
        RECT rect;
        GetWindowRect(hwnd, &rect);

        POINT pt = { LOWORD(lParam), HIWORD(lParam) };
        ClientToScreen(hwnd, &pt);

        if (!PtInRect(&rect, pt))
          HFONT hFont = (HFONT) GetProp(hwnd, PROP_ORIGINAL_FONT);
          SendMessage(hwnd, WM_SETFONT, (WPARAM) hFont, FALSE);
          InvalidateRect(hwnd, NULL, FALSE);

To change the text color in the control, we can catch the WM_CTLCOLORSTATIC message. However, we need to subclass the control's parent to gain access to the message. After we have done that, we're all set.

  // Subclass the parent so we can color the controls as we desire.

  HWND hwndParent = GetParent(hwnd);
  if (NULL != hwndParent)
    WNDPROC pfnOrigProc = (WNDPROC) GetWindowLong(hwndParent,
    if (pfnOrigProc != _HyperlinkParentProc)
      SetProp(hwndParent, PROP_ORIGINAL_PROC, (HANDLE)
      SetWindowLong(hwndParent, GWL_WNDPROC, (LONG)
                   (WNDPROC) _HyperlinkParentProc);


  // Set a flag on the control so we know what color it should be.
  // This allows us to distinguish between static text controls.
  // We need to decide what color the control should be.



  // Handle the WM_CTLCOLORSTATIC message to change the color.
  // We need to make sure the control is intended to be a
  // hyperlink before changing the color.

      HDC hdc = (HDC) wParam;
      HWND hwndCtl = (HWND) lParam;

      BOOL fHyperlink = (NULL != GetProp(hwndCtl,
      if (fHyperlink)
        LRESULT lr = CallWindowProc(pfnOrigProc, hwnd, message,
                                    wParam, lParam);
        SetTextColor(hdc, RGB(0, 0, 192));
        return lr;


Now, it's a simple matter to transform any static text control into an active hyperlink. The included source code makes this all rather easy. A simple function call is all you need. Two functions are available to convert a static text control into a hyperlink.

  BOOL ConvertStaticToHyperlink(HWND hwndCtl);
  BOOL ConvertStaticToHyperlink(HWND hwndParent, UINT uiCtlId);

You can call one of these functions from within a dialog box's WM_INITDIALOG message handler and everything should work fine.

      ConvertStaticToHyperlink(hwnd, IDC_SNAGGLEHOMEPAGE);

To summarize, there are just two steps you need to take to quickly transform a static text control into an active hyperlink:

  1. Download the source code and include "Hyperlinks.cpp" and "Hyperlinks.h" in your application.
  2. From within your dialog's OnInitDialog() (MFC) or WM_INITDIALOG handler (straight Win32 API), call:
  ConvertStaticToHyperlink(GetSafeHwnd(), IDC_MY_HYPERLINK_CTL);
    // MFC

- or -

  ConvertStaticToHyperlink(hwndDialog, IDC_MY_HYPERLINK_CTL);
    // Win32

That's all there is to it! This example could certainly be extended to have the static control automatically open the Web page, so the control's parent wouldn't need to do it, but I will leave that for another CodeGuru-ite to implement.


Download source - 2 Kb

This article was originally published on November 11th, 2002

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date