Click here to Skip to main content
15,881,709 members
Articles / Programming Languages / C++
Article

Extended Outlook Textbox to prevent text erase on lost focus

Rate me:
Please Sign up or sign in to vote.
4.56/5 (8 votes)
17 Aug 20052 min read 42.1K   588   13   4
This article is provided to change the default behaviour of the Outlook textbox. The lost Win32 focus message is handled to save the text before the textbox looses focus.

Introduction

Some time ago I had to implement a toolbar in Outlook that contained a textbox with a 'Go' button that started a search. After creating the textbox and the button I noticed that the textbox was losing the text when it changed the focus. That was unacceptable in a search. So, I found this solution to that issue. It's a bit complex but I couldn't find another solution. It can be extended to handle the key press to get the Enter or any other key press. This article was written together with Gabriel Benmergui.

Using the code

You can use this code by copying the hook.cpp / h and OutlookEdit.cpp / h in your project and instantiating COutlookEdit objects in your toolbar. This project creates an Outlook addin that creates a toolbar with two textboxes inside.

Points of interest

Outlook implements the TextBox control with a RichEdit20W (in Office 97 a EDIT is created) Win32 control inside. The problem is that RichEdit20W is not created when the textbox is created so it's necessary to create a window creation hook to know when it is created. To do this I've installed a WH_CBT hook to handle the creation of the RichEdit20W. When it is created we have to post a message to the COutlookEditMgr window created for this purpose.

LRESULT CALLBACK CBTProc(int nCode, 
                        WPARAM wParam, LPARAM lParam)
{
  if(nCode == HCBT_CREATEWND) {
    LPCBT_CREATEWND cw = (LPCBT_CREATEWND) lParam;
    LPCREATESTRUCT cs = (LPCREATESTRUCT) cw->lpcs;
    TCHAR *className = (TCHAR *) cs->lpszClass;
    HWND hWnd = (HWND) wParam;
    TCHAR aux[1024];

    if(!HIWORD(cs->lpszClass)) {
      if(GetClassName(hWnd, aux, 
                 sizeof(aux)/sizeof(TCHAR)-1) == 0) {
        className = NULL;
      }
      else {
        className = aux;
      }
    }

    if(className) {
      // textbox is represented by a RichEdit20W in 
      // 2K and upper and it is created
      // in a mouse over or when it gets focus
      if(!_tcscmp(className, _T("RichEdit20W"))) {
        HRESULT hr = CallNextHookEx(HookHandle, 
                               nCode, wParam, lParam);

        // post the message to the edit manager window
        PostMessage(COutlookEditMgr::Get()->GetWndHandle(), 
                          RICHEDIT20W_WND_CREATED_MESSAGE_CODE,
                          (WPARAM) hWnd, 0);
        return hr;
      }
    }
  }

  // Call the next handler in the chain
  return CallNextHookEx(HookHandle, nCode, 
                              wParam, lParam);
}

When the RichEdit20W is created we have to detect if the new window is owned by any of our COutlookEdit controls and subclass the RichEdit20W if affirmative. Here all the COutlookEdit controls are checked and if any one matches the window proc is changed.

void COutlookEditMgr::OnNewRichEdit20W(HWND hWnd)
{
  COutlookEdit *edit;

  // Once the RichEdit is called, check if it 
  // belongs to one of the edit controls
  for(COutlookEditList::iterator it=_editList.begin(); 
                             it!=_editList.end(); ++it) {
    edit = *it;

    // Check if the window handle belongs to 
    // any of the controls, and hook it
    if(edit->VerifyWindow(hWnd)) {
      edit->HookWindow(hWnd);
      break;
    }
  }
}

BOOL COutlookEdit::VerifyWindow(HWND hwnd)
{
  if(_hCtrlWnd != NULL) {
    return FALSE;
  }

  // Get the handle's rectangle, and check 
  // if the control is inside it
  POINT p;
  RECT rect;

  GetWindowRect(hwnd, &rect);

  p.x = m_pEdit->GetLeft() + 
                m_pEdit->GetWidth() / 2;
  p.y = m_pEdit->GetTop() + 
                m_pEdit->GetHeight() / 2;

  return (p.x < rect.right && 
              p.x > rect.left &&
              p.y < rect.bottom && 
              p.y > rect.top);
}

void COutlookEdit::HookWindow(HWND hWnd)
{
  _hCtrlWnd = hWnd;
  _oldWndProc = (WNDPROC) GetWindowLong(hWnd, 
                                   GWL_WNDPROC);
  SetWindowLong(hWnd, GWL_WNDPROC, 
                         (LONG) TextboxWndProc);
}

Now, we're almost done. Here is the new RichEdit20W window proc where you have to handle the Win32 control messages:

LRESULT CALLBACK TextboxWndProc(HWND hWnd, UINT msg, 
                              WPARAM wParam, LPARAM lParam)
{
  COutlookEdit *edit = 
      COutlookEditMgr::Get()->GetTextbox(hWnd);
  WNDPROC lpOldProc;

  if(edit == NULL) {
    OutputDebugStr("TextboxWndProc: edit == NULL.\n");
    return 0;
  }

  lpOldProc = edit->GetOldWndProc();

  switch(msg)
  {
    // if we don't handle this event the text 
    // is lost when the control lose focus
    // without pressing enter or tab
    case WM_KILLFOCUS:
    {
      int length = SendMessage(hWnd, 
                          WM_GETTEXTLENGTH, 0, 0);
      TCHAR *buf = new TCHAR[length+1];

      SendMessage(hWnd, WM_GETTEXT, length+1, 
                                    (LPARAM) buf);
      edit->SetText(buf);

      delete [] buf;
      break;
    }
  }

  return CallWindowProc(lpOldProc, hWnd, 
                             msg, wParam, lParam);
}

History

  • 2nd Aug, 2005 - Article released.

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
Technical Lead http://www.nektra.com
Argentina Argentina
Pablo Yabo is a Software Developer since he was young, specialized in system internals.
In 2003 years ago founded with Sebastian Wain a Company named Nektra a custom software development company specialized in Outlook Plugin Development, Data Loss Prevention Solution Development and Windows Driver Development.

Comments and Discussions

 
GeneralOffice 97 Pin
jprandi23-Mar-06 5:28
jprandi23-Mar-06 5:28 
GeneralRe: Office 97 Pin
Pablo Yabo23-Mar-06 5:31
professionalPablo Yabo23-Mar-06 5:31 
QuestionVB.NET / C# ?? Pin
RobSA16-Feb-06 22:24
RobSA16-Feb-06 22:24 
I have been looking for a solution like this for ages. Can this be done in either VB.net or C#
AnswerRe: VB.NET / C# ?? Pin
Pablo Yabo17-Feb-06 1:52
professionalPablo Yabo17-Feb-06 1:52 

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.