I have an app that uses FindWindow to find the window handle to another
app's window, and it looks like it is hanging. It doesn't do it every
time, but from my logging statements, I can tell that FindWindow is the
last thing called when it's hung up.
I've seen the various articles/comments about the dangers of using
FindWindow, ie: uses SendMessage internally & can hang if another app's
thread is waiting/hung, etc.
I have written an alternative function for some other purpose that works
similarly to FindWindow but checking all the top level windows, starting
w/ the desktop window, but that too uses GetWindowText (ie:
SendMessage()) & I think will have the same liability.
My question is: What's a good alternative to using FindWindow ?
I would alter my similar function to use SendMessageTimeout, but I am
using Windows CE, and that function is not supported.
Please help, won't you?
What are the alternatives to using FindWindow?
Here are some details about how I'm using FindWindow:
-------------
LPCTSTR szPossibleNames[] = { _T("name1"), _T("name2"),...,_T("nameN") };
for(int i=0; i < X; ++i){
if( FindWindow(NULL, szPossibleNames[i]) )
break;
}
//other stuff...
-------------
Is using FindWindow in a loop like this just asking for trouble? It's
like I'm making it do risky things over & over again....
Just for fun, and in the case it can help somebody point out an
alternative to me, here's the code for my 'alternative func':
-----------------------------------
HWND FindChildAmongManyChildDialogs(
HWND hWndMain,
LPCTSTR szClassName,
LPCTSTR szWindowTitle)
//
//This function was written to
//find a child window when there is a main
//window, and then several dialogs that
//are children, and the window we want
//is on one of those dialogs.
//
//This is based on the AirCard 555 watcher program,
//and Remote Spy++
//
//
//
{
CString str, strTitle;
TCHAR szClass[ 200 + 1 ] = {0};
CWnd * pChild = CWnd::FromHandle(::GetWindow(hWndMain, GW_CHILD)
); CWnd * pLast = NULL;
if( pChild )
pLast = pChild->GetWindow(GW_HWNDLAST);
if( !pChild )
return NULL;
do{
GetClassName(pChild->GetSafeHwnd(), szClass, 200);
pChild->GetWindowText(strTitle);
if(_tcsicmp(szClass, _T("Dialog")) == 0 )
{
HWND hChild = FindChildWindow(pChild,
szClassName,
szWindowTitle);
if( hChild )
return hChild;
}
pChild = pChild->GetWindow(GW_HWNDNEXT);
}while( pChild && (pChild != pLast) );
return NULL;
}
HWND FindChildWindow(CWnd * pParent,
LPCTSTR szClassName,
LPCTSTR szWindowTitle)
{
ASSERT( szClassName || szWindowTitle );
ASSERT( pParent );
CString strTitle;
TCHAR szClass[ 200 + 1] = {NULL};
CWnd * pChild = pParent->GetWindow(GW_CHILD);
ASSERT( pChild );
CWnd * pLast = NULL;
if( pChild )
pLast = pChild->GetWindow(GW_HWNDLAST);
if( !pChild )
return NULL;
do
{
GetClassName(pChild->GetSafeHwnd(), szClass, 200);
pChild->GetWindowText(strTitle);
if( (!szClassName || _tcsicmp(szClass, szClassName) == 0)
&& (!szWindowTitle || strTitle.CompareNoCase(szWindowTitle)
== 0) )
{
return pChild->GetSafeHwnd();
}
pChild = pChild->GetWindow(GW_HWNDNEXT);
}while( pChild && (pChild != pLast) );
return NULL;
}
-----------------------------------
Last question:
Is using FindWindow(_T("dialog"), )
better than using FindWindow("dialog", ) ?
Does specifying the class name prevent it from using the sendmessage
call internally for those cases where the window is obviously not a dialog?
Thanks for any help...
Wes
Since you are on CE, you could have additional problems that we don't see in desktop
systems.
But if you are writing both apps, there are a number of things you can do. For example, a
shared data segment in a DLL where the app that is the "service" puts its handle, and the
app that is the "client" can read the handle, would be a more reliably approach. Of
course, the "server" should, on its ExitInstance, set this handle to NULL, and the
"client" should occasionaly do an ::IsWindow, just to be sure everything is valid
If you are looking for some other app that you didn't write, you are probably in trouble.
If you use "Dialog", this gives you an 8-bit character string. This will not work on CE,
which uses Unicode. You could explicitly say L"Dialog", and it would only work in Unicode
versions, which would be fine for CE but if you wanted a desktop version, this might be an
annoyance. _T("Dialog") says "Make this an 8-bit or 16-bit literal depending on the
compilation mode", and is the preferred method.
Since I don't trust FindWindow as far as I can throw a mainframe, I can't answer the
question about whether or not there would be any difference in the nature of the calls.
And I've never written a CE program, so I'm not sure what other properties of CE differ
from desktop systems (CE is a totally separate code base, written from scratch, and shares
only the abstract API interface with the desktop systems. There are substantial
differences, some necessary, some merely gratuitous).
joe
Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
void CEnumChildrenDlg::EnumerateWindows(HWND tWnd)
{
//
HWND hWnd = ::GetWindow(tWnd, GW_CHILD);
//
if(hWnd != NULL && hWnd != tWnd)
{
//
hWnd = ::GetWindow(hWnd, GW_HWNDFIRST);
//
while(hWnd != NULL)
{
//
[ ... add your processing here with the hWnd ... for example GetWindowText()
... ]
//
EnumerateWindows(hWnd);
//
hWnd = ::GetWindow(hWnd, GW_HWNDNEXT);
}
}
}
void CWndProc::DisplayWindow(HWND tWnd)
{
TCHAR sWindowClass[81];
TCHAR sWindowText[81];
long nCount;
if(tWnd != NULL)
{
nCount = ::GetClassName(tWnd, sWindowClass, 80);
if(nCount > 80)
{
nCount = 0;
}
sWindowClass[nCount] = '\0';
CString aWindowClass = sWindowClass;
if(aWindowClass == _T("DesktopExplorerWindow"))
{
sWindowText[0] = '\0';
}
else
{
nCount = ::GetWindowText(tWnd, sWindowText, 80);
if(nCount > 80)
{
nCount = 0;
}
sWindowText[nCount] = '\0';
}
CString aWindowText = sWindowText;
if(aWindowText.GetLength() > 0)
{
if(aWindowText.GetAt(0) == 65535)
{
aWindowText = _T("");
}
}
CString aTrace;
aTrace.Format(_T("%ld %30s %30s\n"), tWnd, sWindowClass, sWindowText);
TRACE(aTrace);
}
}
This will get all the windows if you initially pass it a NULL ... however
... trying to get the window text can and has hung my application ... that's
why I gave up this approach also. So not sure if this will get you any
further than using the FindWindow()
KS
Joseph M. Newcomer [MVP]
FindWindow(pszClassName, NULL) won't hang, so if you can get by with that,
then use that.
Otherwise your stuck. You could create a thread to do the work, and then,
if the thread hasn't completed within some time, terminate it and consider
it a failure. There's actually a problem with this approach, too, but you
might decide it's such a rare case that it doesn't matter. Consider: let's
say you give the thread a second to do the job. If it doesn't complete
within a second, you terminate the thread. The thread, by calling
FindWindow/GetWindowText/SendMessage(WM_TEXT) essentially *posted* a
WM_GETTEXT message to the thread of an owner window, passing a buffer. So
the message is sitting in the message queue of some thread. Let's say, now,
that the message does get processed, but after the one second threshold, and
your thread is no longer there.
What are the potential problems in this scenario:
1) If the thread passed a stack buffer, the stack is no longer there,
and you have a crash.
2) If you use a global buffer instead, it could be that it overwrites it
(think that your FindWindow search moved on to a new window, after having
given up on the one you timed out, and that new window responded and, at the
same time, the one you timed out responded).
3) If you use a dynamically allocated buffer, then you must leak that
buffer. If you don't, you get heap corruption.
Choose your poison: hang, crash, buffer contention, corruption. Buffer
contention (number 2 above) won't hang/crash/corrupt, so that's the "best"
case in terms of execution, but can lead to unpredictable results.
--
Michael Salamone [eMVP]
Entrek Software, Inc.
www.entrek.com
"Wes Jones" <nos...@spam.com> wrote in message
news:eU37wx9T...@TK2MSFTNGP12.phx.gbl...
KS
"Joseph M. Newcomer" <newc...@flounder.com> wrote in message
news:259lc0ptmprpp7n3l...@4ax.com...