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) {
if(!_tcscmp(className, _T("RichEdit20W"))) {
HRESULT hr = CallNextHookEx(HookHandle,
nCode, wParam, lParam);
PostMessage(COutlookEditMgr::Get()->GetWndHandle(),
RICHEDIT20W_WND_CREATED_MESSAGE_CODE,
(WPARAM) hWnd, 0);
return hr;
}
}
}
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;
for(COutlookEditList::iterator it=_editList.begin();
it!=_editList.end(); ++it) {
edit = *it;
if(edit->VerifyWindow(hWnd)) {
edit->HookWindow(hWnd);
break;
}
}
}
BOOL COutlookEdit::VerifyWindow(HWND hwnd)
{
if(_hCtrlWnd != NULL) {
return FALSE;
}
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)
{
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.