Click to See Complete Forum and Search --> : Using FindWindow inside a DLL
Notsosuperhero
July 6th, 2008, 11:41 PM
Hey guys. Lately I've been writing an application that will hook into Direct3D apps and display the song currently playing in WinAmp and Windows Media Player but I have run into a problem. Since I am supporting 2 players I have made DLLs for each one's functionality(that way I can update the program as new versions come out as need be). But whenever I call FindWindow from inside the DLL it does not work. I put in the same code to test in my EXE file and all is well with the code.
Is this some crazy FindWindow limitation? Is there some way around this besides having to send the HWND to the DLL's function? Since I'm trying to keep my EXE as clean as possible.
Thanks guys.
-NotSo
brahmma
July 7th, 2008, 05:39 AM
What does "not working" mean? Does FindWindow() return NULL?
Microsoft MVP - Visual C++ (http://snipr.com/2u64f)
Notsosuperhero
July 7th, 2008, 02:59 PM
Yea its just coming up as NULL.
Arjay
July 7th, 2008, 04:28 PM
FindWindow[Ex] doesn't care whether its called from an exe or from inside a dll. It does care whether the title and/or class name are exact matches.
Different versions of WinApp and Media player may have different titles and class names which will result in the api not finding the window.
I personally like to use EnumChildWindows and limit the search to windows within a specific process. This approach ensures that you don't grab the window with a title and class name match, but in the wrong process.
There are several techniques you can use to reliably find windows. I've done quite a bit of this sort of work when developing test automation harnesses. In general, you want to limit the windows search to a specific process, and if you have the parent window hWnd available, use it. This will allow you to find a window without needing to have the title and class.
The following is the call back proc I use with EnumWindows. It has several flags that allow the caller to specify whether to use the processID, partial title and class matches and a few other things.
BOOL CALLBACK CAFImpl::EnumChildWindowsProc( HWND hwnd, DWORD lParam )
{
BOOL bClassMatched = FALSE;
BOOL bTitleMatched = FALSE;
PCHILD_WND_ENUM pChildWndEnum = reinterpret_cast<PCHILD_WND_ENUM>(lParam);
if( (FW_USEPID & pChildWndEnum->lFlags) == FW_USEPID )
{
DWORD pid = 0;
DWORD tid = 0;
// Get the pid for this window
tid = GetWindowThreadProcessId( hwnd, &pid );
if( pChildWndEnum->pid != pid )
{
// This window isn't in our target process
// so continue the enumeration
return TRUE;
}
}
// Check if parent window is correct
if( (FW_USEPARENTWND & pChildWndEnum->lFlags) == FW_USEPARENTWND )
{
if( pChildWndEnum->hwndParent != ::GetParent( hwnd ) )
{
// This window isn't a child of the parent
// so continue the enumeration
return TRUE;
}
}
// Check if visible
if( (FW_VISIBLE & pChildWndEnum->lFlags) == FW_VISIBLE )
{
if( !::IsWindowVisible( hwnd ) )
{
// Invisible window, continue enumeration
return TRUE;
}
}
TCHAR szClass[MAX_PATH];
GetClassName(hwnd, szClass, sizeof( szClass ) );
Tstring sClass = szClass;
TCHAR szTitle[ 2049 ];
::GetWindowText(hwnd, szTitle, sizeof( szTitle ) );
Tstring sTitle = szTitle;
// ATLTRACE(_T( "\tTitle: %s\t Class: %s\n" ), szTitle, szClass);
if( _tcslen( pChildWndEnum->szClass ) )
{
// Partial match class flag set, so perform partial match
if( (pChildWndEnum->lFlags & FW_PARTIAL_CLASS_MATCH) == FW_PARTIAL_CLASS_MATCH )
{
if( INVALID_INDEX != sClass.find( pChildWndEnum->szClass ) )
{
bClassMatched = TRUE;
}
}
else
{
// Full match for class, so just compare full length strings
if( 0 == sClass.compare( pChildWndEnum->szClass ) )
{
bClassMatched = TRUE;
}
}
}
// Check for USE_INDEX flag
// NOTE: This method is typically for edit boxes where the text is unknown at
// the time of search. The caller will Set the FW_USEINDEX flag and pass
// the index into the title (preceded by an '@')
if( (pChildWndEnum->lFlags & FW_USEINDEX) == FW_USEINDEX )
{
// Title originally contain an zero-based index ordinal (e.g., '@2' - finds the 3rd edit control)
if(bClassMatched)
{
if(0 == pChildWndEnum->lIndex--)
{
bTitleMatched = TRUE;
}
}
}
else
{
if( _tcslen( pChildWndEnum->szTitle ) )
{
// Partial match title flag set, so perform partial match
if( (pChildWndEnum->lFlags & FW_PARTIAL_TITLE_MATCH) == FW_PARTIAL_TITLE_MATCH )
{
if( INVALID_INDEX != sTitle.find( pChildWndEnum->szTitle ) )
{
bTitleMatched = TRUE;
}
}
else
{
// Full match for class, so just compare full length strings
if( 0 == sTitle.compare( pChildWndEnum->szTitle ) )
{
bTitleMatched = TRUE;
}
}
}
else
{
// When title isn't specified we assume the match
// when the parent is the same and the title is null
if( 0 == sTitle.size() && ( pChildWndEnum->hwndParent == ::GetParent( hwnd ) ) )
{
bTitleMatched = TRUE;
}
}
}
if( bClassMatched && bTitleMatched )
{
pChildWndEnum->hwnd = hwnd;
return FALSE;
}
// Continue the enumeration
return TRUE;
}
I think I wrote this code in about 2003. Looking back at the code, I see I'm mixing using a TCHAR buffer with std string classes (Tstring == std::string or std::wstring). There's better ways to do this now. Oh well.
This code was part of a automation framework that used Active Accessibility to detect and manipulate the Windows controls. It resided in a COM dll so that's why there are references to ATL.
kirants
July 7th, 2008, 05:25 PM
Does GetLastError indicate anything?
How do you infer FindWindow is not doing things right ? In the hooked application, the window heirarchy might be not similar to what you have in a standalone app situation. More details on what you pass in as parameters and how you arrived at those might help
Notsosuperhero
July 7th, 2008, 10:54 PM
OK. Guys I'm sorry for wasting your time. But I am dumb. I accidentally copied them over to the wrong place.
FindWindow does work just fine. It was loading a old DLL that contained no functionality in it. :blush:
And, I know that the different versions have different class and window names, that is why I decided to put them into DLLs so that I can make separate DLLs for the versions.
Thanks for the help guys.
codeguru.com
Copyright 2007 Jupitermedia Corporation All Rights Reserved.